Android 可分組的 RecyclerViewAdapter

airongbin 7年前發布 | 15K 次閱讀 安卓開發 Android開發 移動開發

今天給大家介紹的是一個可以實現數據分組顯示的RecyclerViewAdapter: GroupedRecyclerViewAdapter 。它可以很方便的實現RecyclerView的分組顯示,并且每個組都可以包含組頭、組尾和子項;可以方便實現多種Type類型的列表,可以實現如QQ聯系人的列表一樣的列表展開收起功能等。下面先讓我們看一下它所能夠實現的一些效果:

分組的列表

不帶組尾的列表

不帶組頭的列表

子項為Grid的列表

子項為Grid的列表(各組子項的Span不同)

頭、尾和子項都支持多種類型的列表

子項為Grid的列表

還可以很容易的實時列表的展開收起效果:

可展開收起的列表

以上展示的只是GroupedRecyclerViewAdapter能實現的一些常用效果,其實使用GroupedRecyclerViewAdapter還可以很容易的實現一些更加復雜的列表效果。在我的GroupedRecyclerViewAdapter項目給出的Demo中給出了上面幾種效果的實現例子,并且有詳細的注釋說明,有興趣的同學可以到我的GitHub下載源碼。下面直接講解GroupedRecyclerViewAdapter的使用。

1、引入依賴

在Project的build.gradle在添加以下代碼

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

在Module的build.gradle在添加以下代碼

compile 'com.github.donkingliang:GroupedRecyclerViewAdapter:1.0.1'

2、繼承GroupedRecyclerViewAdapter

public class GroupedListAdapter extends GroupedRecyclerViewAdapter {
}

3、實現GroupedRecyclerViewAdapter里的方法

GroupedRecyclerViewAdapter是一個抽象類,它提供了一系列需要子類去實現的方法。

//返回組的數量
    public abstract int getGroupCount();

    //返回當前組的子項數量
    public abstract int getChildrenCount(int groupPosition);

    //當前組是否有頭部
    public abstract boolean hasHeader(int groupPosition);

    //當前組是否有尾部
    public abstract boolean hasFooter(int groupPosition);

    //返回頭部的布局id。(如果hasHeader返回false,這個方法不會執行)
    public abstract int getHeaderLayout(int viewType);

    //返回尾部的布局id。(如果hasFooter返回false,這個方法不會執行)
    public abstract int getFooterLayout(int viewType);

    //返回子項的布局id。
    public abstract int getChildLayout(int viewType);

    //綁定頭部布局數據。(如果hasHeader返回false,這個方法不會執行)
    public abstract void onBindHeaderViewHolder(BaseViewHolder holder, int groupPosition);

    //綁定尾部布局數據。(如果hasFooter返回false,這個方法不會執行)
    public abstract void onBindFooterViewHolder(BaseViewHolder holder, int groupPosition);

    //綁定子項布局數據。
    public abstract void onBindChildViewHolder(BaseViewHolder holder,
                                               int groupPosition, int childPosition);

還可是重寫GroupedRecyclerViewAdapter方法實現頭、尾和子項的多種類型item。效果就像上面的第6張圖一樣。

//返回頭部的viewType。
    public int getHeaderViewType(int groupPosition);

    //返回尾部的viewType。
    public int getFooterViewType(int groupPosition) ;

    //返回子項的viewType。
    public int getChildViewType(int groupPosition, int childPosition) ;

4、設置點擊事件的監聽

GroupedRecyclerViewAdapter提供了對列表的點擊事件的監聽方法。

//設置組頭點擊事件
    public void setOnHeaderClickListener(OnHeaderClickListener listener) {
        mOnHeaderClickListener = listener;
    }

    //設置組尾點擊事件
    public void setOnFooterClickListener(OnFooterClickListener listener) {
        mOnFooterClickListener = listener;
    }

    // 設置子項點擊事件
    public void setOnChildClickListener(OnChildClickListener listener) {
        mOnChildClickListener = listener;
    }

注意事項:

1、對方法重寫的注意。

如果我們直接繼承RecyclerView.Adapter去實現自己的Adapter時,一般會重寫Adapter中的以下幾個方法:

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

public int getItemCount();

public int getItemViewType(int position);

但如果是使用GroupedRecyclerViewAdapter,就一定不能去重寫這幾個方法,因為在GroupedRecyclerViewAdapter中已經對這幾個方法做了實現,而且是對實現列表分組至關重要的,如果子類重寫了這幾個方法,可能會破壞GroupedRecyclerViewAdapter的功能。

從前面給出的GroupedRecyclerViewAdapter的方法我們可以看到,這些方法其實就是對應RecyclerView.Adapter的這4個方法的,所以我們直接使用GroupedRecyclerViewAdapter提供的方法即可。

RecyclerView.Adapter中的

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

對應GroupedRecyclerViewAdapter中的

//返回頭部的布局id。(如果hasHeader返回false,這個方法不會執行)
    public abstract int getHeaderLayout(int viewType);

    //返回尾部的布局id。(如果hasFooter返回false,這個方法不會執行)
    public abstract int getFooterLayout(int viewType);

    //返回子項的布局id。
    public abstract int getChildLayout(int viewType);

這里之所以返回的是布局id而不是ViewHolder ,是因為在GroupedRecyclerViewAdapter項目中已經提供了一個通用的ViewHolder:BaseViewHolder。所以使用者只需要提供布局的id即可,不需要自己去實現ViewHolder。

@Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(getLayoutId(mTempPosition, viewType), parent, false);
        return new BaseViewHolder(view);
    }

    private int getLayoutId(int position, int viewType) {
        int type = judgeType(position);
        if (type == TYPE_HEADER) {
            return getHeaderLayout(viewType);
        } else if (type == TYPE_FOOTER) {
            return getFooterLayout(viewType);
        } else if (type == TYPE_CHILD) {
            return getChildLayout(viewType);
        }
        return 0;
    }

RecyclerView.Adapter中的

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

對應GroupedRecyclerViewAdapter中的

//綁定頭部布局數據。(如果hasHeader返回false,這個方法不會執行)
    public abstract void onBindHeaderViewHolder(BaseViewHolder holder, int groupPosition);

    //綁定尾部布局數據。(如果hasFooter返回false,這個方法不會執行)
    public abstract void onBindFooterViewHolder(BaseViewHolder holder, int groupPosition);

    //綁定子項布局數據。
    public abstract void onBindChildViewHolder(BaseViewHolder holder,
                                               int groupPosition, int childPosition);

RecyclerView.Adapter中的

public int getItemCount();

對應GroupedRecyclerViewAdapter中的

//返回組的數量
    public abstract int getGroupCount();

    //返回當前組的子項數量
    public abstract int getChildrenCount(int groupPosition);

RecyclerView.Adapter中的

public int getItemViewType(int position);

對應GroupedRecyclerViewAdapter中的

//返回頭部的viewType。
    public int getHeaderViewType(int groupPosition);

    //返回尾部的viewType。
    public int getFooterViewType(int groupPosition) ;

    //返回子項的viewType。
    public int getChildViewType(int groupPosition, int childPosition) ;

2、對應列表操作的注意

RecyclerView.Adapter提供了一系列對列表進行操作的方法。如:

//更新操作
public final void notifyDataSetChanged();
public final void notifyItemChanged(int position);
public final void notifyItemChanged(int position, Object payload);
public final void notifyItemRangeChanged(int positionStart, int itemCount);
public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload);

//插入操作
public final void notifyItemInserted(int position);
public final void notifyItemRangeInserted(int positionStart, int itemCount);

//刪除操作
public final void notifyItemRemoved(int position)
public final void notifyItemRangeRemoved(int positionStart, int itemCount);

在GroupedRecyclerViewAdapter不建議使用RecyclerView.Adapter的任何對列表的操作方法,因為這些方法都是基于列表的操作,它的position是相對于整個列表而言的,而GroupedRecyclerViewAdapter是分組的列表,它對列表的操作應該是基于組的。同時GroupedRecyclerViewAdapter使用了組結構來維護整個列表的結構,使我們可以對列表進行組的操作,在列表發生變化時GroupedRecyclerViewAdapter需要及時對組結構進行調整,如果使用了RecyclerView.Adapter中的方法對列表進行更新,GroupedRecyclerViewAdapter可能因為無法及時調整組結構而方式異常。所以在使用中應該避免使用這些方法。GroupedRecyclerViewAdapter同樣提供了一系列對列表進行操作的方法,我們應該使用GroupedRecyclerViewAdapter所提供的方法。

//****** 刷新操作 *****//

    //刷新數據列表。對應 notifyDataSetChanged();
    public void changeDataSet();

    //刷新一組數據,包括組頭,組尾和子項
    public void changeGroup(int groupPosition);

    //刷新多組數據,包括組頭,組尾和子項
    public void changeRangeGroup(int groupPosition, int count);

    // 刷新組頭
    public void changeHeader(int groupPosition);

    /刷新組尾
    public void changeFooter(int groupPosition);

    // 刷新一組里的某個子項
    public void changeChild(int groupPosition, int childPosition);

    //刷新一組里的多個子項
    public void changeRangeChild(int groupPosition, int childPosition, int count);

    // 刷新一組里的所有子項
    public void changeChildren(int groupPosition);

    //****** 刪除操作 *****//
    // 刪除所有數據
    public void removeAll();

    //刪除一組數據,包括組頭,組尾和子項
    public void removeGroup(int groupPosition);

    // 刪除多組數據,包括組頭,組尾和子項
    public void removeRangeGroup(int groupPosition, int count);

    // 刪除組頭
    public void removeHeader(int groupPosition);

    // 刪除組尾
    public void removeFooter(int groupPosition);

    //刪除一組里的某個子項
    public void removeChild(int groupPosition, int childPosition);

    // 刪除一組里的多個子項
    public void removeRangeChild(int groupPosition, int childPosition, int count);

    //刪除一組里的所有子項
    public void removeChildren(int groupPosition);

    //****** 插入操作 *****//
    // 插入一組數據
    public void insertGroup(int groupPosition);

    //插入一組數據
    public void insertRangeGroup(int groupPosition, int count);

    //插入組頭
    public void insertHeader(int groupPosition);

    // 插入組尾
    public void insertFooter(int groupPosition);

    //插入一個子項到組里
    public void insertChild(int groupPosition, int childPosition);

    // 插入一組里的多個子項
    public void insertRangeChild(int groupPosition, int childPosition, int count);

    //插入一組里的所有子項
    public void insertChildren(int groupPosition);

3、使用GridLayoutManager的注意

如果有使用GridLayoutManager,一定要使用項目中所提供的GroupedGridLayoutManager。因為分組列表如果要使用GridLayoutManager實現網格布局。要保證組的頭部和尾部是要單獨占用一行的。否則組的頭、尾可能會跟子項混著一起,造成布局混亂。而且GroupedGridLayoutManager提供了對子項的SpanSize的修改方法,使用GroupedGridLayoutManager可以實現更多的復雜列表布局。

//直接使用GroupedGridLayoutManager實現子項的Grid效果
    GroupedGridLayoutManager gridLayoutManager = new GroupedGridLayoutManager(this, 2, adapter);
   rvList.setLayoutManager(gridLayoutManager);


   GroupedGridLayoutManager gridLayoutManager = new GroupedGridLayoutManager(this, 4, adapter){
       //重寫這個方法 改變子項的SpanSize。
       //這個跟重寫SpanSizeLookup的getSpanSize方法的使用是一樣的。
       @Override
       public int getChildSpanSize(int groupPosition, int childPosition) {
            if(groupPosition % 2 == 1){
                 return 2;
            }
            return super.getChildSpanSize(groupPosition, childPosition);
       }
   };
   rvList.setLayoutManager(gridLayoutManager);

下面看一個簡單的使用列子:

public class GroupedListAdapter extends GroupedRecyclerViewAdapter {

    private ArrayList<GroupEntity> mGroups;

    public GroupedListAdapter(Context context, ArrayList<GroupEntity> groups) {
        super(context);
        mGroups = groups;
    }

    @Override
    public int getGroupCount() {
        return mGroups == null ? 0 : mGroups.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        ArrayList<ChildEntity> children = mGroups.get(groupPosition).getChildren();
        return children == null ? 0 : children.size();
    }

    @Override
    public boolean hasHeader(int groupPosition) {
        return true;
    }

    @Override
    public boolean hasFooter(int groupPosition) {
        return true;
    }

    @Override
    public int getHeaderLayout(int viewType) {
        return R.layout.adapter_header;
    }

    @Override
    public int getFooterLayout(int viewType) {
        return R.layout.adapter_footer;
    }

    @Override
    public int getChildLayout(int viewType) {
        return R.layout.adapter_child;
    }

    @Override
    public void onBindHeaderViewHolder(BaseViewHolder holder, int groupPosition) {
        GroupEntity entity = mGroups.get(groupPosition);
        holder.setText(R.id.tv_header, entity.getHeader());
    }

    @Override
    public void onBindFooterViewHolder(BaseViewHolder holder, int groupPosition) {
        GroupEntity entity = mGroups.get(groupPosition);
        holder.setText(R.id.tv_footer, entity.getFooter());
    }

    @Override
    public void onBindChildViewHolder(BaseViewHolder holder, int groupPosition, int childPosition) {
        ChildEntity entity = mGroups.get(groupPosition).getChildren().get(childPosition);
        holder.setText(R.id.tv_child, entity.getChild());
    }
}
public class GroupedListActivity extends AppCompatActivity {

    private TextView tvTitle;
    private RecyclerView rvList;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_group_list);

        tvTitle = (TextView) findViewById(R.id.tv_title);
        rvList = (RecyclerView) findViewById(R.id.rv_list);

        tvTitle.setText(R.string.group_list);

        rvList.setLayoutManager(new LinearLayoutManager(this));
        GroupedListAdapter adapter = new GroupedListAdapter(this, GroupModel.getGroups(10, 5));
        adapter.setOnHeaderClickListener(new GroupedRecyclerViewAdapter.OnHeaderClickListener() {
            @Override
            public void onHeaderClick(GroupedRecyclerViewAdapter adapter, BaseViewHolder holder,
                                      int groupPosition) {
                Toast.makeText(GroupedListActivity.this, "組頭:groupPosition = " + groupPosition,
                        Toast.LENGTH_LONG).show();
            }
        });
        adapter.setOnFooterClickListener(new GroupedRecyclerViewAdapter.OnFooterClickListener() {
            @Override
            public void onFooterClick(GroupedRecyclerViewAdapter adapter, BaseViewHolder holder,
                                      int groupPosition) {
                Toast.makeText(GroupedListActivity.this, "組尾:groupPosition = " + groupPosition,
                        Toast.LENGTH_LONG).show();
            }
        });
        adapter.setOnChildClickListener(new GroupedRecyclerViewAdapter.OnChildClickListener() {
            @Override
            public void onChildClick(GroupedRecyclerViewAdapter adapter, BaseViewHolder holder,
                                     int groupPosition, int childPosition) {
                Toast.makeText(GroupedListActivity.this, "子項:groupPosition = " + groupPosition
                                + ", childPosition = " + childPosition,
                        Toast.LENGTH_LONG).show();
            }
        });
        rvList.setAdapter(adapter);

    }
}

 

 

來自:https://juejin.im/post/58d3dc96b123db3f6b5f5642

 

 本文由用戶 airongbin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!