美文网首页AndroidAndroid效果/自定义基础
Android ItemDecoration打造分组悬浮

Android ItemDecoration打造分组悬浮

作者: 1da4ea6f4995 | 来源:发表于2017-06-30 16:48 被阅读221次

    参考博客:http://www.jianshu.com/p/359b7524fa88

    首先看一下实现效果
    QQ20170630-122625.gif

    关于 ItemDecoration 是什么能做什么不在这篇文章的包含范围以内,各位可以自行百度和google,这边主要是讲述使用 ItemDecoration 实现分组悬浮的效果。

    前面简单的实现数据展示这里就不做叙述了,都是简单的操作,我们直接切入 ItemDecoration 自定义与使用。


    一、首先自定义一个 ItemDecoration

    新建一个类,继承 RecyclerView.ItemDecoration 类,并实现四个方法,分别是构造方法、onDraw()、onDrawOver()、getItemOsets()。

    • 构造方法:用于后面的参数传递,需要将 RecyclerView 显示的数据传入其中。
    • onDraw(): 可以进行绘制,但是是绘制在 ItemView 的下方。
    • onDrawOver(): 在 itemview 的上面进行绘制,不会受到 itemview 的影响。
    • getItemOffsets(): 设置 itemview 四周的间隙,最后设置的 left、top、right、bottom 都会在 RecyclerView 进行 itemview 布局测量的时候计入 itemview 的 margin 当中。
      对于具体介绍可以参考这篇文章 深入理解 RecyclerView 系列之一:ItemDecoration
      还有就是我们要明白这三个方法在 ItemDecoartion 中是怎么调用的,建议先使用 Log 输出查看一下,是先调用 getItemOffsets() 有多少个 item 就会调用多少次,完成以后再调用 onDraw() ,然最后 onDrawOver(),后面两个方法金调用一次,而当 RecyclerView 每滑动一次上述步骤就会重新走一波。
    二、对数据进行分组

    我们不能一口吃掉一个胖子,一步一步来分组悬浮第一步对信息进行分组。我们的目标是在分组之间添加分割线完成分组。
    所以这一步操作我们要在 getOffsets() 中执行,主要代码如下,注释比较详细科技直接看代码,我的模拟数据为一组List<String>,循环加入30个数据,每组5条数据共六组,大家看上面演示就知道。

        /* 分组头部高度
    ![
    ![](http:https://img.haomeiwen.com/i2904262/a1abb1ce9b90b260.gif?imageMogr2/auto-orient/strip)
    ](http:https://img.haomeiwen.com/i2904262/e7adbc8f81ef4642.gif?imageMogr2/auto-orient/strip)
     */
        private int offsesSize = 30;
        private List<String> strings;
        /* 构造方法传入数据 */
        public DemoItemDecoration(List<String> strings) {
            super();
            this.strings = strings;
        }
    @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            /* 当前 item 的位置 */
            int position = parent.getChildAdapterPosition(view);
            /* 判断当前 item 是否和上一个 item 属于同一组,不同就需要设置分割线 */
            if (beforAndLater(position)) {
                outRect.set(0, offsesSize, 0, 0);
            }
        }
    
     /**
         * e
         * 当前 item 的数据和上一个数据进行比对,分组信息是否有差异。
         * @param postition
         * @return
         */
        private boolean beforAndLater(int postition) {
            /* positon 要大于零,要小于数据数量 */
            if (postition > 0) {
                /* 获取当前 item 的数据 */
                String beforStr = strings.get(postition);
                /* 获取上一个 item 的数据 */
                String laterStr = strings.get(postition - 1);
                /* 进行比对 */
                if (beforStr.substring(0).equals(laterStr.substring(0))) {
                    return false;
                } else {
                    return true;
                }
            }
             /* 第一条数据必定设置分组头部间隙 */
            if (postition == 0) {
                return true;
            }
            return false;
        }
    

    效果如下:

    QQ20170630-161426.gif
    三、为头部绘制文字

    分组成功了,但是每组头部并没有显示组名,现在我们来为分组头部添加组名。这个操作我们要在 onDraw() 方法进行操作。首先我们需要在构造方法中初始化两个 Paint(画笔) 一个背景,一个文字,这样我们在 onDraw() 中使用 canvas 进行绘制。

    @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
            /* 获取子view个数 */
            int itemSum = parent.getChildCount();
    
            /* 获取 item 左右的 margin */
            int itemLeft = parent.getPaddingLeft();
            int itemRifht = parent.getWidth() - parent.getPaddingRight();
    
            /* 遍历 item 给需要绘制分组信息的绘制头部*/
            int j = 0;
            for (int i = 0; i < itemSum; i++) {
                /* 获取子 View */
                View childView = parent.getChildAt(i);
                /* 获取子 view的位置 */
                int position = parent.getChildAdapterPosition(childView);
                /*  获取子 view 的布局参数,方便确定头部位置 */
                RecyclerView.LayoutParams childParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
    
                int itemTop = childView.getTop() - childParams.topMargin - offsesSize;
                int itemButtom = childView.getTop() - childParams.topMargin;
    
                /* 判定当前子 view 是否需要绘制头部,注意位置 */
                if (beforAndLater(position)) {
                    /* 绘制头部背景 */
                    c.drawRect(itemLeft, itemTop, itemRifht, itemButtom, testPaint);
                    /* 绘制文字 */
                    c.drawText(strings.get(position), itemLeft, itemButtom, textPaint);
                }
            }
        }
    

    效果如下:

    QQ20170630-163019.gif
    四、绘制悬浮头部

    高地已破,现在只要拔掉两个狼牙,干掉水晶我们就 Victory 了!
    记住!记住!ItemDecoration 里面三个方法的调用步骤!这个很重要。我就在这个地方猜坑了,耽误的很多时间。
    好了,我的思路是这样的。获取第一个可见的 itemview ,然后判断后面的一个 itemview 是否和第一个可见的 itemview 属于同一分组,如果不同我们就获取后面一个 itemview 底部距离 RecyclerView 顶部的距离(即 view.getBottom),然后与悬浮头部的固定 bottom 距离做比较,去相对较小的一个,然后进行绘制,代码如下:

        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
            /* 获取第一个可见view itemview */
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) parent.getLayoutManager();
            int firstChildViewPosition = linearLayoutManager.findFirstVisibleItemPosition();
            /* 这里出现了一个bug,用parent.getCildAt(),出现childView为空的现象,所以用这个方法 */
            View firseChildView = parent.findViewHolderForLayoutPosition(firstChildViewPosition).itemView;
            int left = parent.getLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
            int top = parent.getPaddingTop();
            int bottom = parent.getPaddingTop() + offsesSize;
    
            /**
             * 上拉时,判定位于第一个可见 item 与下一个 item 是不是属于同一分组的,如果是悬浮条的位置( bottom )依然取 parent.getPaddingTop() + offsesSize
             *        当不是时,在获取可一个可见 itemview 的 Bottom(getBottom() 它的数值就是 itemview 距离 parent 顶部的距离)与 bottom 大小进行比较,哪个小就选哪个进行绘制悬浮条。
             * 下拉亦是如此。
             *
             */
            if (nowContrastNext(firstChildViewPosition)) {
                bottom = Math.min(firseChildView.getBottom(), bottom);
            }
            c.drawRect(0, top, right, bottom, testPaint);
            c.drawText(strings.get(firstChildViewPosition).substring(0), left, bottom, textPaint);
        }
    

    然后运行就能得到头部悬停的效果了!

    相关文章

      网友评论

        本文标题:Android ItemDecoration打造分组悬浮

        本文链接:https://www.haomeiwen.com/subject/yiibcxtx.html