正文
版权声明:本文为博主原创文章,未经博主允许不得转载
系列教程:
Android开发之从零开始系列
源码:
AnliaLee/android-RecyclerViews
,欢迎star
大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论
前言
最近项目中要实现
列表分组
和
粘性头部
的效果,网上翻了很多资料和开源库,感觉都不是太好用,有的扩展性不强有的用起来又太复杂,于是决定自己动手造轮子。行动之前,研究了许多前人的源码,决定了几点开发方向
-
尽可能方便用户使用,
减少调用的代码量及与其他类的耦合度
-
使用
RecyclerView
实现列表功能
-
自定义
RecyclerView.ItemDecoration
绘制
分组Item
和
粘性头部
-
通过
layoutInflater.inflate
获取
layout
中的布局并传入
ItemDecoration
进行绘制(方便用户布局分组Item)
-
在
ItemDecoration
中提供
接口
让用户对列表数据
进行分组
和
设置分组Item的显示内容
目前
GroupItemDecoration
第一阶段已开发完成(会继续更新和扩展功能),源码及示例已上传至
Github
,具体效果如图
GroupItemDecoration使用简介
GroupItemDecoration
目前只支持
LinearLayoutManager.VERTICAL
类型,使用流程如下
LayoutInflater layoutInflater = LayoutInflater.from(this);
View groupView = layoutInflater.inflate(R.layout.item_group,null);
-
调用
recyclerView.addItemDecoration
添加
GroupItemDecoration
recyclerView.addItemDecoration(new GroupItemDecoration(this,groupView,new GroupItemDecoration.DecorationCallback() {
@Override
public void setGroup(List<GroupItem> groupList) {
GroupItem groupItem = new GroupItem(0);
groupItem.setData("name","第1组");
groupList.add(groupItem);
groupItem = new GroupItem(5);
groupItem.setData("name","第2组");
groupList.add(groupItem);
}
@Override
public void buildGroupView(View groupView, GroupItem groupItem) {
TextView textName = (TextView) groupView.findViewById(R.id.text_name);
textName.setText(groupItem.getData("name").toString());
}
}));
如果还是不清楚可以去看下demo
实现思路
在我们
自定义ItemDecoration
之前首先得了解
ItemDecoration
有什么用,不清楚的可以看下这两篇博客
RecyclerView之ItemDecoration由浅入深
深入理解 RecyclerView 系列之一:ItemDecoration
简单来说,我们实现分组及粘性头部效果分三步
-
重写
ItemDecoration.getItemOffsets
在
RecyclerView
中为
GroupView
预留位置
-
重写
ItemDecoration.onDraw
在上一步预留的位置中绘制
GroupView
-
重写
ItemDecoration.onDrawOver
绘制
顶部悬停的GroupView(粘性头部
)
我们按顺序一步步讲,首先,创建
GroupItemDecoration
继承自
ItemDecoration
,在初始化方法中获取用户设置的
GroupView
,并提供接口给用户设置分组相关
public class GroupItemDecoration extends RecyclerView.ItemDecoration {
private Context context;
private View groupView;
private DecorationCallback decorationCallback;
public GroupItemDecoration(Context context,View groupView,DecorationCallback decorationCallback) {
this.context = context;
this.groupView = groupView;
this.decorationCallback = decorationCallback;
}
public interface DecorationCallback {
void setGroup(List<GroupItem> groupList);
void buildGroupView(View groupView, GroupItem groupItem);
}
}
然后重写
getItemOffsets
方法,根据用户设置的分组为
GroupView
预留位置,其中最主要的是测量出
GroupView
的
宽高和位置
。
measureView
方法中按着
View的绘制顺序
调用
View.measure
和
View.layout
,只有先完成了这两步,才能将
View
绘制到屏幕上,关于如何测量
View
大家可以看下这篇博客
Android如何在初始化的时候获取加载的布局的宽高
。接下来是具体的实现代码
public class GroupItemDecoration extends RecyclerView.ItemDecoration {
private List<GroupItem> groupList = new ArrayList<>();
private Map<Object,GroupItem> groups = new HashMap<>();
private int[] groupPositions;
private int positionIndex;
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if(!isLinearAndVertical(parent)){
return;
}
if(isFirst){
measureView(groupView,parent);
decorationCallback.setGroup(groupList);
if(groupList.size()==0){
return;
}
groupPositions = new int[groupList.size()];
positionIndex = 0;
int a = 0;
for(int i=0;i<groupList.size();i++){
int p = groupList.get(i).getStartPosition();
if(groups.get(p)==null){
groups.put(p,groupList.get(i));
groupPositions[a] = p;
a++;
}
}
isFirst = false;
}
int position = parent.getChildAdapterPosition(view);
if(groups.get(position)!=null){
outRect.top = groupViewHeight;
}
}
private void