android listview 分组,Android 可分组的RecyclerViewAdapter

今天给大家介绍的是一个可以实现数据分组显示的RecyclerViewAdapter:GroupedRecyclerViewAdapter。它可以很方便的实现RecyclerView的分组显示,并且每个组都可以包含组头、组尾和子项;可以方便实现多种Type类型的列表,可以实现如QQ联系人的列表一样的列表展开收起功能等。下面先让我们看一下它所能够实现的一些效果:

302d24db5423

分组的列表

302d24db5423

不带组尾的列表

302d24db5423

不带组头的列表

302d24db5423

子项为Grid的列表

302d24db5423

子项为Grid的列表(各组子项的Span不同)

302d24db5423

头、尾和子项都支持多种类型的列表

302d24db5423

多种子项类型的列表

还可以很容易的实时列表的展开收起效果:

302d24db5423

可展开收起的列表

以上展示的只是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.3.0'

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 notifyDataChanged();

//通知一组数据刷新,包括组头,组尾和子项

public void notifyGroupChanged(int groupPosition);

//通知多组数据刷新,包括组头,组尾和子项

public void notifyGroupRangeChanged(int groupPosition, int count);

// 通知组头刷新

public void notifyHeaderChanged(int groupPosition);

// 通知组尾刷新

public void notifyFooterChanged(int groupPosition);

// 通知一组里的某个子项刷新

public void notifyChildChanged(int groupPosition, int childPosition);

// 通知一组里的多个子项刷新

public void notifyChildRangeChanged(int groupPosition, int childPosition, int count);

// 通知一组里的所有子项刷新

public void notifyChildrenChanged(int groupPosition);

//****** 删除操作 *****//

// 通知所有数据删除

public void notifyDataRemoved();

// 通知一组数据删除,包括组头,组尾和子项

public void notifyGroupRemoved(int groupPosition);

// 通知多组数据删除,包括组头,组尾和子项

public void notifyGroupRangeRemoved(int groupPosition, int count);

// 通知组头删除

public void notifyHeaderRemoved(int groupPosition);

// 通知组尾删除

public void notifyFooterRemoved(int groupPosition);

// 通知一组里的某个子项删除

public void notifyChildRemoved(int groupPosition, int childPosition);

// 通知一组里的多个子项删除

public void notifyChildRangeRemoved(int groupPosition, int childPosition, int count);

// 通知一组里的所有子项删除

public void notifyChildrenRemoved(int groupPosition);

//****** 插入操作 *****//

// 通知一组数据插入

public void notifyGroupInserted(int groupPosition);

// 通知多组数据插入

public void notifyGroupRangeInserted(int groupPosition, int count);

// 通知组头插入

public void notifyHeaderInserted(int groupPosition);

// 通知组尾插入

public void notifyFooterInserted(int groupPosition);

// 通知一个子项到组里插入

public void notifyChildInserted(int groupPosition, int childPosition);

// 通知一组里的多个子项插入

public void notifyChildRangeInserted(int groupPosition, int childPosition, int count);

// 通知一组里的所有子项插入

public void notifyChildrenInserted(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);

4、BaseViewHolder的使用

项目中提供了一个通用的ViewHolder:BaseViewHolder。提供了根据viewId获取View的方法和对View、TextView、ImageView的常用设置方法。

//根据id获取View

TextView textView = holder.get(R.id.tv_header);

//View、TextView、ImageView的常用设置方法。并且支持方法连缀调用

holder.setText(R.id.tv_header, "内容")

.setImageResource(R.id.iv_image, 资源id)

.setBackgroundRes(R.id.view,资源id);

BaseViewHolder是可以通用的,在普通的Adapter中也可以使用,可以省去每次都要创建ViewHolder的麻烦。

下面看一个简单的使用列子:

public class GroupedListAdapter extends GroupedRecyclerViewAdapter {

private ArrayList mGroups;

public GroupedListAdapter(Context context, ArrayList groups) {

super(context);

mGroups = groups;

}

@Override

public int getGroupCount() {

return mGroups == null ? 0 : mGroups.size();

}

@Override

public int getChildrenCount(int groupPosition) {

ArrayList 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);

}

}

这是我在项目Demo中的实现的其中一个列子。效果是上面的图1。想看源码和更多的使用列子的欢迎访问我的GitHub。

头部悬浮吸顶功能

应一些朋友的反馈,我在1.2.0版本中新加了列表的头部悬浮吸顶功能。使用起来非常的简单,只需要用框架里提供的StickyHeaderLayout包裹一下你的RecyclerView就可以了。当然,你需要使用GroupedRecyclerViewAdapter才能看到效果。

android:id="@+id/sticky_layout"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/rv_list"

android:layout_width="match_parent"

android:layout_height="match_parent" />

StickyHeaderLayout提供了一个设置是否显示悬浮吸顶的方法。

//是否吸顶,默认为true。

stickyLayout.setSticky(true);

302d24db5423

头部吸顶的列表.gif

使用DataBinding

GroupedRecyclerViewAdapter在1.3.0版本加入了对DataBinding的支持。要想在Adapter中使用DataBinding,只需要在GroupedRecyclerViewAdapter的构造函数的useBinding参数传true即可。

public class BindingAdapter extends GroupedRecyclerViewAdapter {

public BindingAdapter(Context context) {

//只要在这里传true,Adapter就会用DataBinding的方式加载列表的item布局。默认为false。

super(context, true);

}

}

然后同过BaseViewHolder的getBinding()就可以获取到item对应的ViewDataBinding对象。

@Override

public void onBindHeaderViewHolder(BaseViewHolder holder, int groupPosition) {

//获取ViewDataBinding对象。

AdapterBindingHeaderBinding binding = holder.getBinding();

}

@Override

public void onBindFooterViewHolder(BaseViewHolder holder, int groupPosition) {

//获取ViewDataBinding对象。

AdapterBindingFooterBinding binding = holder.getBinding();

}

@Override

public void onBindChildViewHolder(BaseViewHolder holder, int groupPosition, int childPosition) {

//获取ViewDataBinding对象。

AdapterBindingChildBinding binding = holder.getBinding();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RecyclerViewAndroid 的一个强大的视图组件,用于在移动应用程序中显示大量数据。 RecyclerView 允许您动态添加、更新和删除元素,并且比 ListView 更加灵活和高效。 对于分组RecyclerView,您需要实现一个自定义的 RecyclerView.Adapter,该 Adapter 能够处理多种类型的视图,并且能够显示分组标题和项目列表。以下是实现分组RecyclerView 的基本步骤: 1. 创建一个 RecyclerView.Adapter 子类,并实现以下方法: - getItemViewType(int position): 返回给定位置的项目的视图类型。在这里,您需要根据项目类型返回不同的视图类型,例如标题或普通项目。 - onCreateViewHolder(ViewGroup parent, int viewType): 根据给定的视图类型返回一个新的 RecyclerView.ViewHolder。在这里,您需要创建并返回一个适当的 ViewHolder,例如标题 ViewHolder 或普通项目 ViewHolder。 - onBindViewHolder(RecyclerView.ViewHolder holder, int position): 将数据绑定到给定 ViewHolder 中。在这里,您需要根据 ViewHolder 的类型将数据绑定到适当的视图中。 2. 创建一个自定义的数据结构,该数据结构包含分组标题和项目列表。 3. 在 RecyclerView.Adapter 中添加一个适当的数据结构,该数据结构保存分组标题和项目列表。 4. 在 RecyclerView.Adapter 中将项目列表分组,并为每个分组添加一个标题。 5. 根据需要在 RecyclerView.Adapter 中添加其他方法,例如获取项目数和获取项目位置。 6. 在 RecyclerView 中设置适当的 LayoutManager 和 Adapter。 7. 运行应用程序并检查 RecyclerView 是否按照您的预期工作。 这些是实现分组RecyclerView 的基本步骤。具体实现方法可以根据您的需求和应用程序的复杂性进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值