ItemDecoration实现固定悬浮式Item的思路

来源:互联网 发布:魔兽世界mac版 编辑:程序博客网 时间:2024/04/30 18:31

转载请注意: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

原创粉丝点击