繼承SectionIndexer,實現聯繫人側邊欄

一,介紹SectionIndexer

package android.widget;

/**
 * 接口可以被adapter實現,使AbsListView的sections之間可以快速滑動
 * 一個secton是一羣列表,有很多相似點,例如他們可能以相同的字母開頭,或者是來自同一個歌手的歌曲
 * ExpandableListAdapters認爲groups和sections是同一個縮放組,應該返回一個合適的位置
 * 可以看一下AbsListView中setFastScrollEnabled(boolean)方法
 */
public interface SectionIndexer {

    //返回一個代表sections列表的非空的數組對象
    //當滑動的時候,這個list view可以調用toString()來顯示預覽文本。
    //例如,一個adapter可以返回代表字母表中字母的字符串數組,或者返回section titles的對象數組
    Object[] getSections();


    //提供section索引,通過section數組對象,返回adapter中section開始的位置
    int getPositionForSection(int sectionIndex);


    //提供adapter中的位置,在section數組對象中返回相應的section索引
    //如果postion位置在索引開始位置之前,則返回0
    int getSectionForPosition(int position);
}

這麼一說彷彿有點抽象,給個圖就知道了:

getSectionForPosition() 通過該項的位置,獲得所在分類組的索引號

getPositionForSection() 根據分類列的索引號獲得該序列的首個位置

二,具體實現

2.1 定義側面字母欄

public class SideBar extends AppCompatTextView {

    private String[] letters = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z", "#"};

    /**
     * 三種不同的畫筆
     */
    private Paint textPaint;

    private Paint bigTextPaint;

    private Paint scaleTextPaint;

    private Canvas canvas;

    private int itemH;

    private int w;

    private int h;

    /**
     * 普通情況下字體大小
     */
    float singleTextH;

    /**
     * 縮放離原始的寬度
     */
    private float scaleWidth;

    /**
     * 滑動的Y
     */
    private float eventY = 0;

    /**
     * 縮放的倍數
     */
    private int scaleSize = 1;

    /**
     * 縮放個數item,即開口大小
     */
    private int scaleItemCount = 6;

    private ISideBarSelectCallBack callBack;

    /**
     * 回調接口
     */
    public interface ISideBarSelectCallBack {
        void onSelectStr(int index, String selectStr);
    }

    public SideBar(Context context) {
        super(context);
    }

    public SideBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public SideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    /**
     * 初始化函數
     *
     * @param attrs attr
     */
    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);
            scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);
            scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);
            scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));
            ta.recycle();
        }

        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setColor(getCurrentTextColor());
        textPaint.setTextSize(getTextSize());
        textPaint.setTextAlign(Paint.Align.CENTER);

        bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bigTextPaint.setColor(getCurrentTextColor());
        bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));
        bigTextPaint.setTextAlign(Paint.Align.CENTER);

        scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scaleTextPaint.setColor(getCurrentTextColor());
        scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));
        scaleTextPaint.setTextAlign(Paint.Align.CENTER);
    }

    public void setDataResource(String[] data) {
        letters = data;
        invalidate();
    }

    public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {
        this.callBack = callBack;
    }

    /**
     * 設置字體縮放比例
     */
    public void setScaleSize(int scale) {
        scaleSize = scale;
        invalidate();
    }

    /**
     * 設置縮放字體的個數,即開口大小
     */
    public void setScaleItemCount(int scaleItemCount) {
        this.scaleItemCount = scaleItemCount;
        invalidate();
    }

    private int dp(int px) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (px * scale + 0.5f);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {
                    eventY = event.getY();
                    invalidate();
                    return true;
                }
                else {
                    eventY = 0;
                    invalidate();
                    break;
                }
            case MotionEvent.ACTION_CANCEL:
                eventY = 0;
                invalidate();
                return true;
            case MotionEvent.ACTION_UP:
                if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {
                    eventY = 0;
                    invalidate();
                    return true;
                }
                else
                    break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        this.canvas = canvas;
        DrawView(eventY);
    }

    private void DrawView(float y) {
        int currentSelectIndex = -1;
        if (y != 0) {
            for (int i = 0; i < letters.length; i++) {
                float currentItemY = itemH * i;
                float nextItemY = itemH * (i + 1);
                if (y >= currentItemY && y < nextItemY) {
                    currentSelectIndex = i;
                    if (callBack != null) {
                        callBack.onSelectStr(currentSelectIndex, letters[i]);
                    }
                    //畫大的字母
                    Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();
                    float bigTextSize = fontMetrics.descent - fontMetrics.ascent;
                    canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize, singleTextH + itemH * i, bigTextPaint);
                }
            }
        }
        drawLetters(y, currentSelectIndex);
    }

    private void drawLetters(float y, int index) {
        w = getMeasuredWidth();
        h = getMeasuredHeight();
        itemH = h / letters.length;
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        singleTextH = fontMetrics.descent - fontMetrics.ascent;
        for (int i = 0; i < letters.length; i++) {
            canvas.drawText(letters[i], w - getPaddingRight(), singleTextH + itemH * i, textPaint);
        }
    }
}

自定義view,主要作用就是,顯示側面的字母欄,後期點擊或者滑動時,能顯示出點擊的大字母。

2.2 xml佈局

聯繫人界面的xml佈局:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragment.ChatFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclercontactlistId"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <com.Jason.materialdesign.widget.SideBar
        android:id="@+id/sidebar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:background="@android:color/transparent"
        android:paddingRight="10dp"
        android:textSize="15sp" />
</RelativeLayout>

 

2.3 實現 adapter  主要是實現SectionIndexer接口

這個adapter主要是聯繫人的item佈局,這個相對來說能理解,都直接原生庫原生接口,應該能明白。主要是就是實現sectionIndexer接口,這兒稍微注意點。

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ContactsListHolder> implements SectionIndexer {

    /**
     * 聯繫人分區
     */
    private SparseIntArray mPositionOfSection;
    /**
     * 聯繫人分區
     */
    private SparseIntArray mSectionOfPosition;

    // 聯繫人列表
    private List<ContactData> mContactDataList;

    public ContactsAdapter(List<ContactData> contactDatas) {
        this.mContactDataList = contactDatas;
    }

    @NonNull
    @Override
    public ContactsListHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list_item_contact, parent,false);
        ContactsListHolder holder = new ContactsListHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ContactsListHolder holder, int position) {
        ContactData contactData = mContactDataList.get(position);
        if (contactData != null){
            holder.avaterView.setImageResource(R.mipmap.default_avatar);
            holder.nameTxt.setText(contactData.getUserName());
        }
    }

    @Override
    public int getItemCount() {
        return mContactDataList.size();
    }

    /**
     *SectionIndexer分區方法
     */
    @Override
    public Object[] getSections() {
        mPositionOfSection = new SparseIntArray();
        mSectionOfPosition = new SparseIntArray();

        // 傳入的聯繫人數量
        int count = mContactDataList.size();

        // list後期轉換爲array
        List<String> list = new ArrayList<>();
        list.add("");
        mPositionOfSection.put(0, 0);
        mSectionOfPosition.put(0, 0);

        // 開始分區
        for (int i = 1; i < count; i++)
        {
            String letter = mContactDataList.get(i).getFirstLetter();
            int section = list.size() - 1;
            if (list.get(section) != null && !list.get(section).equals(letter))
            {
                list.add(letter);
                section++;
                mPositionOfSection.put(section, i);
            }
            mSectionOfPosition.put(i, section);
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     *SectionIndexer分區方法
     */
    @Override
    public int getPositionForSection(int sectionIndex) {
        return mPositionOfSection.get(sectionIndex);
    }

    /**
     *SectionIndexer分區方法
     */
    @Override
    public int getSectionForPosition(int position) {
        return mSectionOfPosition.get(position);
    }

    /**
     * 普通聯繫人holder
     */
    class ContactsListHolder extends RecyclerView.ViewHolder {

        ImageView avaterView;
        TextView nameTxt;

        public ContactsListHolder(View  view) {
            super(view);
            avaterView = (ImageView)view.findViewById(R.id.avatar);
            nameTxt = (TextView)view.findViewById(R.id.name);
        }
    }
}

2.4 ContactFragment的代碼,包括一些初始化,比較排序等

public class ContactFragment extends Fragment {

    private ContactsAdapter mContactsAdapter;

    private RecyclerView mRecyclerView;

    private SideBar mSideBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_contact, container, false);
        initContacts(view);
        return view;
    }

    /**
     * 裝載數據
     * @param view 根view
     */
    private void initContacts(View view){
        mRecyclerView = view.findViewById(R.id.recyclercontactlistId);
        mSideBar = view.findViewById(R.id.sidebar);

        mContactsAdapter = new ContactsAdapter(initTestData());
        LinearLayoutManager verticalLayoutManager = new LinearLayoutManager(this.getContext());
        mRecyclerView.setLayoutManager(verticalLayoutManager);
        mRecyclerView.setAdapter(mContactsAdapter);
    }

    /**
     * 初始化測試數據
     * @return
     */
    private List<ContactData> initTestData(){
        List<ContactData> contactData = new ArrayList<>();
        contactData.add(new ContactData("alibaba", "123456", 1));
        contactData.add(new ContactData("tencent", "654321", 2));
        contactData.add(new ContactData("spaceon", "999999", 3));
        contactData.add(new ContactData("666", "0000", 4));
        contactData.add(new ContactData("老黃", "18381680077", 0));
        contactData.add(new ContactData("ligj", "1550805", 9));
        return contactData;
    }
}

稍微等一下,下午繼續完善。

三,結果圖

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章