欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-18 13:35:58
...

转载请注意: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实现固定悬浮式Item的思路


开始

在开始搞这个效果之前,我们先熟悉一下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