我在上一篇《小甜点,RecyclerView 之 ItemDecoration 讲解及高级特性实践 》 讲解了 ItemDecoration 的基本用法及它的一些实践,抱着学习研究的态度,这一篇作为实践篇主要目的是尝试通过 ItemDecoration 来实现 RecyclerView 中的 StickyHeader 功能。
关于 StickyHeader 想必大家已经很清楚了,如果不有不清楚的,看下图:
如果要实现 StickyHeader 的话,首先,我们得明白普通的 Header 是怎么实现的。
ItemDecoration 实现普通的 Header
上面这张图是我微信的通讯录界面,大家可以看到微信按拼音和英文名首字母给账号进行了分组,上面灰色的 B 和 C 就是 Header。
之前在 ListView 时代,实现头部功能就是通过 ItemView 的 layout 布局实现的。
一个 ItemView 分为两个部分,如果这个 ItemView 是小组的第一个,那么它的 Header 就应该显示出来,不然就得隐藏,所以只要好处理分组与 ItemView 的位置关系,这个 Header 功能就很容易实现了。
现在,用 ItemDecoration 来实现头部,就不需要在每个 ItemView 中设置这个隐藏的 Header 部分了,ItemView 只需要关心它自己真正要表现的界面效果就好了,像这种零碎的事情就专门交给 ItemDecoration 来处理。
但不管是 ItemView 还是 ItemDecoration 来实现 Header,正确的数据分组永远是第一步。
而数据的分组离不开 Adapter 的配合,所以数据的分组应该由外部来完成,而不是 ItemDecoration 本身,那好,创建 ItemDecoration 第一步就是定义一个接口,用来获取分组信息。
public class GroupInfo {
//组号
private int mGroupID;
// Header 的 title
private String mTitle;
public GroupInfo(int groupId, String title) {
this.mGroupID = groupId;
this.mTitle = title;
}
public int getGroupID() {
return mGroupID;
}
public void setGroupID(int groupID) {
this.mGroupID = groupID;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
this.mTitle = title;
}
}
上面代码 Header 的相关信息。
public class SectionDecoration extends RecyclerView.ItemDecoration {
public interface GroupInfoCallback {
GroupInfo getGroupInfo(int position);
}
}
有了 GroupInfoCallback 回调,SectionItemDecoration 就可以通过它的 getGroupInfo() 方法来获取每个 ItemView 对应的分组信息。
我们再回到 Header 话题上来,因为是通过 ItemDecoration 来完成它,所以肯定要借助于它的 getItemOffsets() 方法。我们组与组之间的间隔设置成为一个 Header 的高度,然后组内的 ItemView 之间的间距是指定的间距值,通常为 1 px 或者 2 px。大家看图就明白了。
这张图与上面的那张差不多,但是灰色区域都是通过 ItemDecoration 中 getItemOffsets 方法操纵 outRect 参数撑开的。我们绘制 Header 只要计算出对应的位置然后通过 Canvas 就能为所欲为了。关键的一点在于 Header 只绘制在组内第一个 ItemView 的上方,所以我们还需要一个途径来获知 ItemView 在组内的位置。我们可以升级 GroupInfo 类,添加一个域用来标记 ItemView 在组内的位置,还需要提供一个方法来判断它是不是组内的第一个。
public class GroupInfo {
//组号
private int mGroupID;
// Header 的 title
private String mTitle;
//ItemView 在组内的位置
private int position;
//代码有精简
......
public boolean isFirstViewInGroup () {
return position == 0<