ItemDecoration实现固定悬浮式Item的思路
转载请注意:http://blog.csdn.net/wjzj000/article/details/78177509
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
写在前面
这个国庆好像啥事都没有做就已经过去,为了避免整个国庆的浑浑噩噩。那就在国庆的尾巴之时学一下习,记一个知识点。关于ItemDecoration的作用。在很久之前,我一直把ItemDecoration定义在仅仅是画Item的分隔线。
然而当我看了很多大神的代码和思路,才发现ItemDecoration能做的这么多。今天主要就是记录我们经常见到的一个效果,这个效果叫什么名字,还真不清楚,不过看了下面的效果图,觉得就知道了:
此效果的源码放在我的GitHub之中:https://github.com/zhiaixinyang/PersonalCollect
开始
在开始搞这个效果之前,我们先熟悉一下ItemDecoration的简单用法。
public class TestItemDecoration extends RecyclerView.ItemDecoration {
/**
* 可以实现类似绘制背景的效果,内容在正常的Item下面,被覆盖。正常我们要结合
* getItemOffsets将正常Item错开,免得被正常Item覆盖掉
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
//可以绘制在内容的上面,覆盖在正常的Item内容
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
//实现Item的类似padding的效果,也就是让我们正常的Item进行移动
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
}
我们所看到的XX省,其实就是我们的ItemDecoration,也就是分割线做出来的。其实我们,知道这三个方法的用法,通过这三个方法的组合,我们就可以做出这个效果了。
第一步getItemOffsets():
首先,我们在getItemOffsets这个方法之中先进行Item的移动操作,在特定的position之间移开我们用于显示XX省的间隔位置,用作分隔符,并未后续的绘制操作做好准备。
(我们可以通过parent.getChildAdapterPosition(view)
这个方法获取到当前Item的position,拥有了position,我们可以通过外部回调的方式获取对应的Adapter中的对应Data,然后就可以根据我们的业务判断是否需要操作getItemOffsets这个方法。)
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int pos = parent.getChildAdapterPosition(view);
//省略一些判空操作,详细内容请移步GitHub:https://github.com/zhiaixinyang/PersonalCollect
if (pos == 0 || isFirstInGroup(pos)) {
/**
* isFirstInGroup(pos)自己的方法,通过外部回调,判断是不是我们想要显示的XX省,
* 如果是,移动我们想要显示的高度。
* outRect.top:可以理解为:内边距的高度
*/
outRect.top = mGroupHeight;
}
}
接下来我们就要开始绘制显示XX省的这个Item(本质就是分割线ItemDecoration)。这里我们需要考虑的有点多,我们我们可以看到,在下一个的省替换上一个省显示的时候,我们有一个挤压的效果。
第二部onDrawOver():
因为我们的顶部ItemDecoration有悬浮的效果,所以这里我们使用onDrawOver
这个方法进行绘制。
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
final int itemCount = state.getItemCount();
final int childCount = parent.getChildCount();
final int left = parent.getLeft() + parent.getPaddingLeft();
final int right = parent.getRight() - parent.getPaddingRight();
//标记上一个item对应的Group
String preGroupName;
//当前item对应的Group
String currentGroupName = null;
//遍历所有的子View(所有的Item)
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
preGroupName = currentGroupName;
//如果拿不到我们想要绘制的XX省的名字,跳过遍历过程。也就是正常的Item不做处理(这不是废话么- -!)
currentGroupName = getGroupName(position);
if (currentGroupName == null || TextUtils.equals(currentGroupName, preGroupName))
continue;
//获取需要操作的ItemDecoration的底部距离屏幕顶部的高度
int viewBottom = view.getBottom();
//决定当前顶部第一个悬浮ItemDecoration的bottom
float bottom = Math.max(mGroupHeight, view.getTop());
//下一XX省的ItemDecoration一步步逼近我们的首部的悬浮ItemDecoration
if (position + 1 < itemCount) {
//获取下个GroupName(下一个XX省)
String nextGroupName = getGroupName(position + 1);
//下一组的第一个View(ItemDecoration)接近头部
if (!currentGroupName.equals(nextGroupName) && viewBottom < bottom) {
//bottom最小等于mGroupHeight,而当viewBottom(view.getBottom())小于bottom时,说明当前首都悬浮的ItemDecoration已经被挤压。不断更新bottom值。(因为我们绘制它时需要坐标信息,也就是这个bottom)
bottom = viewBottom;
}
}
//根据bottom绘制ItemDecoration
c.drawRect(left, bottom - mGroupHeight, right, bottom, mGroutPaint);
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
//文字竖直居中显示
float baseLine = bottom - (mGroupHeight - (fm.bottom - fm.top)) / 2 - fm.bottom;
c.drawText(currentGroupName, left + mLeftMargin, baseLine, mTextPaint);
}
}
大概上边的过程看起来很复杂。其实如果理顺了还是很好理解的:
1、我们需要的是绘制我们的XX省的ItemDecoration,因此我们不要判断方式,这里我是通过外部数据源的方式去判断(每个人可能会有不同的处理思路)。
2、然后在onDrawOver()
方法中通过我们的处理思路,遍历所有的View,针对我们要处理的ItemDecoration进行相关绘制。(这里因为,我们使用getItemOffsets()
已经为我们的ItemDecoration移好位置了),通过判断view的getTop,getBottom,mGroupHeight等进行判断是否俩个ItemDecoration进行碰撞,(具体思想可以回过去理解源码),不断记录bottom的值。
3、最后进行正常的Canvas绘制即可。
尾声
理解之后实现起来还是比较的简单。主要还是这种思维模式吧,怎么去理解ItemDecoration分隔符的作用。
明天就要结束假期开始工作了,希望一切顺利!
最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp
上一篇: C语言实现扫雷