ItemDecoration解析(三) 实现stickyHeader效果
来源:互联网 发布:最牛不要钱淘宝刷粉丝 编辑:程序博客网 时间:2024/06/10 21:07
前两篇介绍完了ItemDecoration的基本用法。这次打算用ItemDecoration
做点好玩的——实现stickyHeader
效果。如图:
我们从动画可以看出,其实头部有种,一种跟ItemView
在同一层级,类似一个不同type
的Item
;另外一个始终保持在最上层,并且滑动到每组的最后一个item
时,会有一个被顶上去,或者被拉下来的效果。
我们继续分析下,应该如何实现:
- 与
itemView
同一层级的header
,我们可以把它当做是一个divider
,通过getItemOffsets
和onDraw
来处理,且每组第一个Item
的上方才显示。 - 浮在
itemView
上层的header
,很明显可以通过onDrawOver
来实现。 浮在
itemView
上层的header
被顶上去和被拉下来的效果,只需要当每组最后一个Item
的bottom
小于header
的height
时,让header
跟随这个item
就行,换句话说就是此时让header
的bottom
等于firstItem
的bottom
。(有点绕的话,结合下面的图看看)
还有最后一个问题需要确认,那就是哪个item
是每组的开始,哪个Item
是每组的结束。 这个问题跟我们能拿到的数据集合有关。有可能拿到的是类似IOS那样:title
是List<A>
集合,Item是分好组的List<List<B>>
集合(IOS的小伙伴说的,应该没骗我,indexPath
我听过);也有可能拿到就是一个List<B>
集合,泛型B
有个字段作为title
,title
相同便是同一分组。总之不管数据集合是怎么样的,我们都需要确认那个item
是每组的开始,哪个item是每组的结束。假定我们拿到的就是一个List<B>
形式的集合,item的实体类如下:
public class AppInfoBean { public String name; public String url; public String tag; public AppInfoBean(String name, String url,String tag) { this.name = name; this.url = url; this.tag = tag; }}
具体应该如何判断呢,首先,position
为0肯定是第一组的开始;position
为List.size()-1
肯定是最后一组的结束;其他position
,如果当前的tag
与上一个position
的tag
不等,那么肯定是某组的开始,如果当前的tag
与下一个position
的tag
不等,那么肯定是某组的结束。定义一个类来描述每个Item的开始,结束状态:
public class SectionBean { public boolean isGroupStart; public boolean isGroupEnd;}
上面的分析,可以总结成如下代码:
SectionBean tag; if (position == 0) { tag.isGroupStart = true; tag.isGroupEnd = !mDatas.get(position).tag.equals(mDatas.get(position + 1).tag); } else if (position == mDatas.size() - 1) { tag.isGroupStart = !mDatas.get(position).tag.equals(mDatas.get(position - 1).tag); tag.isGroupEnd = true; } else { tag.isGroupStart = !mDatas.get(position).tag.equals(mDatas.get(position - 1).tag); tag.isGroupEnd = !mDatas.get(position).tag.equals(mDatas.get(position + 1).tag); }
至于这个判断放在什么地方,就看大家的见解了。我这里选择在adapter
的onBindViewHolder
中生成每个position
对应的SectionBean
,通过itemview
的setTag
方法存起来,这样的话,在ItemDecoration
可以通过getTag
取出来,具体代码如下:(当然直接在ItemDecoration
中做判断也可以)
@Override public void onBindViewHolder(StickHeaderVH holder, int position) { holder.sdvPic.setImageURI(mDatas.get(position).url); holder.tvTitle.setText(mDatas.get(position).name); generateTag(holder, position); } private void generateTag(StickHeaderVH holder, int position) { SectionBean tag; // 没有tag的话 new 一个, 有的话 复用 if (holder.itemView.getTag() == null) { tag = new SectionBean(); } else { tag = (SectionBean) holder.itemView.getTag(); } // 判断当前position的开始结束状态 if (position == 0) { tag.isGroupStart = true; tag.isGroupEnd = !mDatas.get(position).tag.equals(mDatas.get(position + 1).tag); } else if (position == mDatas.size() - 1) { tag.isGroupStart = !mDatas.get(position).tag.equals(mDatas.get(position - 1).tag); tag.isGroupEnd = true; } else { tag.isGroupStart = !mDatas.get(position).tag.equals(mDatas.get(position - 1).tag); tag.isGroupEnd = !mDatas.get(position).tag.equals(mDatas.get(position + 1).tag); } holder.itemView.setTag(tag); }
经过最开始的分析,与ItemView
同一层级的header
其实就是一个有文字的divider
,只有每组的第一个Item
才显示,这个很好处理,代码如下(文字居中的处理其实有很多细节可以抠的,有机会单独列出):
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { // 如果是头部 if (((SectionBean) view.getTag()).isGroupStart) { outRect.set(0, (int) mDividerHeight, 0, 0); } } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); // 如果是头 if (position != RecyclerView.NO_POSITION && ((SectionBean) view.getTag()).isGroupStart) { drawHeader(c, parent, view, position); } } } /** * 画头部 * * @param c * @param parent * @param view * @param position */ private void drawHeader(Canvas c, RecyclerView parent, View view, int position) { RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); int bottoom = view.getTop() - params.topMargin - Math.round(ViewCompat.getTranslationY(view)); int top = (int) (bottoom - mDividerHeight); // 计算文字居中时候的基线 Rect targetRect = new Rect(left, top, right, bottoom); Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt(); int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2; c.drawRect(left, top, right, bottoom, mPaint); c.drawText(mDatas.get(position).tag, left, baseline, mTextPaint); }
接下来再处理浮在顶层的header
,上面已经分析过了,这个header
一般情况下都是固定在recyclerView
的顶部,只有达到临界点后,其底部才会跟随第一个可见ItemView
的底部,所以我们只需要着重留意下临界点就行了,代码如下:
@Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { View view = parent.getChildAt(0); View view2 = parent.getChildAt(1); if (view != null && view2 != null) { SectionBean section1 = (SectionBean) view.getTag(); SectionBean section2 = (SectionBean) view2.getTag(); int position = parent.getChildAdapterPosition(view); final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); int bottoom = (int) mDividerHeight; int top = 0; // 判断是否达到临界点 // (第一个可见item是每组的最后一个,第二个可见tiem是下一组的第一个,并且第一个可见item的底部小于header的高度) // 这里直接判断item的底部位置小于header的高度有点欠妥,应该还要考虑paddingtop以及margintop,这里展示不考虑了 if (section1.isGroupEnd && section2.isGroupStart && view.getBottom() <= mDividerHeight) { bottoom = view.getBottom(); top = (int) (bottoom - mDividerHeight); } // 计算文字居中时候的基线 Rect targetRect = new Rect(left, top, right, bottoom); Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt(); int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2; // 背景 c.drawRect(left, top, right, bottoom, mPaint); // 文字 c.drawText(mDatas.get(position).tag, left, baseline, mTextPaint); } }
这样基本上就OK了, 唯一的遗憾就是header不能点击啊。。。。源码点我
作者:static_sadhu
链接:http://www.jianshu.com/p/3221b5c8fc38
- ItemDecoration解析(三) 实现stickyHeader效果
- RecyclerView探索之通过ItemDecoration实现StickyHeader效果
- 使用RecyclerView的ItemDecoration实现StickyHeader效果(笔记)
- RecycleView自定义ItemDecoration,实现时间轴效果
- RecyclerView使用ItemDecoration实现吸顶效果
- RecyclerView机制解析: ItemDecoration
- ItemDecoration解析(一) getItemOffsets
- ItemDecoration解析(一) getItemOffsets
- RecyclerView利用ItemDecoration实现头部悬停效果【类似微信通讯录效果】
- ItemDecoration解析(二) onDraw onDrawOver
- ItemDecoration解析(二) onDraw onDrawOver
- Android-使用RecyclerView的ItemDecoration 实现炫酷的 吸顶效果
- ItemDecoration详解以及用ItemDecoration实现按字母排序列表
- 使用ItemDecoration打造列表顶部悬浮效果
- Android学习之RecyclerView(三)-ItemDecoration
- HTML5 Matrix效果实现解析
- RecycleView添加分割线(ItemDecoration)以及交互动画效果
- RecycleView与ItemDecoration实现悬停头部分组列表
- MyEclipse Spring开发教程:使用基本的Spring功能(四)
- 463. Island Perimeter
- Git详解之四 服务器上的Git
- thinkphp二维数组插入数据库
- Java_基础—LinkedList的特有功能
- ItemDecoration解析(三) 实现stickyHeader效果
- Mac中启动另一个程序并将窗口置于最前面
- 通过Arrary.prototype.slice.call浅谈类数组
- java 构造方法
- 驱动模块(driver) 和桩模块(stub)
- CF831A-Unimodal Array
- php上传图片
- DisallowMultipleComponent
- 如何在ViewPager中的各个Fragment间传递数据并刷新Fragment界面?