自定义RecyclerView分割线

现在自己所写的项目中用到列表的地方基本全部使用的是RecyclerView,而使用列表就会有非常大的几率用到分割线功能。以前在用到RecyclerView的分割线功能的时候,都是利用RecyclerView自身设置的背景色和每个Item的背景色的差异,然后继承RecyclerView.ItemDecoration,并且简单的重写
getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
方法就能达到效果了。简单实现一个分割线效果代码如下

  public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        switch (mOrientation) {
            case LinearLayoutManager.HORIZONTAL:
                outRect.right += mSpace;
                if (parent.getChildAdapterPosition (view) == 0)
                    outRect.left += mSpace;
                break;
            case LinearLayoutManager.VERTICAL:
                outRect.bottom += mSpace;
                if (parent.getChildAdapterPosition (view) == 0)
                    outRect.top += mSpace;
                break;
        }
    }

这样就能实现最简单的一个分割线的效果了。可是今天在写项目的时候遇到了分割线颜色可能会变动的情况。因此上面这种简单的方式就不能满足要求了。

首先,Google了一番发现,原来ItemDecoration能做的事远远不止这么简单,它还能定制出更多的酷炫的效果,不仅能在“Item下面”绘制分割线效果,还能在“Item上面”盖章。要定制更多的效果就要重写ItemDecoration的另外两个方法
onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)

onDrawOver (Canvas c, RecyclerView parent, RecyclerView.State state)
onDraw方法要比Item 的onDraw方法先执行,所以它画出的东西总是在Item的下面,在 onDraw 为 divider 设置绘制范围,并绘制到 canvas 上,而这个绘制范围可以超出在 getItemOffsets 中设置的范围,但由于 decoration 是绘制在 childView 的底下,所以并不可见,但是会存在overDraw。而onDrawOver 方法是在Item的onDraw执行之后才执行,所以onDrawOver画出的东西是在Item的上面。仔细学习了一下onDrawOver方法,简直像发现了新大陆一样。此次项目中,有个列表左边需要展示一张图片,中间是4行文字描述,然后在右边还是一张图片,不过这张图片要有盖在文字上的效果类似于下面这样

没有原型,自己画了个

我原本是用xml布局文件拼凑了这个效果。但是,仔细研究了onDrawOver方法后,发现用onDrawOver也可以很好的实现此效果。xml文件代码行数固然减少了很多而且加载效率肯定也有所提升,不过最重要的是感觉B格瞬间上升了很大一截

下面先放上自定义ItemDecoration的完整代码

public class MyItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDivider;
    private int mSize;
    private int mOrientation;
    private Paint mPaint;
    private Bitmap bitmap;

    public MyItemDecoration (Context context, int color, int drawableId, int size, int orientation) {
        mDivider = new ColorDrawable (color);
        mSize = size;
        mOrientation = orientation;
        bitmap = BitmapFactory.decodeResource (context.getResources (), drawableId);

        mPaint = new Paint ();
        mPaint.setColor (Color.RED);
        mPaint.setStyle (Paint.Style.FILL);
    }

    @Override
    public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
      switch (mOrientation){
            case  LinearLayoutManager.HORIZONTAL:
                outRect.right += mSize;
                break;
            case LinearLayoutManager.VERTICAL:
                outRect.bottom += mSize;
                break;
        }
    }


    @Override
    public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left, right, top, bottom;
        if (mOrientation == LinearLayoutManager.VERTICAL) {
            left = parent.getPaddingLeft ();
            right = parent.getWidth () + parent.getPaddingRight ();
            int count = parent.getChildCount ();
            for (int i = 0; i < count; i++) {
                View child = parent.getChildAt (i);
                RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams ();
                top = child.getBottom () + params.bottomMargin;
                bottom = top + mSize;
                mDivider.setBounds (left, top, right, bottom);
                mDivider.draw (c);
            }
        }
    }

    @Override
    public void onDrawOver (Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left, top;
        if (mOrientation == LinearLayoutManager.VERTICAL) {
            int childCount = parent.getChildCount ();
            for (int i = 0; i < childCount; i++) {
                View child = parent.getChildAt (i);
                left = child.getWidth () - bitmap.getWidth () - 15;
                top = child.getBottom () / 2 + (child.getBottom () / 2 * i) / (i + 1) - bitmap.getHeight () / 2;
                c.drawBitmap (bitmap, left, top, mPaint);
            }
        }
    }
}

下面是自己测试的实现效果



感觉很完美,右边“异常”标签的位置也可以通过计算调整。

在自己摸索着写这个MyItemDecoration 的过程中,遇到了个问题一并记录一下。以前都是简单的用,并没有深入的了解过ItemDecoration,也算给自己提个醒,以后不管学习还是工作,尽量多多的去延伸知识面
问题:只重写onDraw方法也可以实现分割线的效果,那么getItemOffsets 的作用是又是什么呢?
在遇到这个问题的时候,我觉得我占了一点好运气,因为我给每个Item都设置了一个边框,如果没有这个边框,可能我会以为只重写onDraw的效果就是正确的,因为它“看起来”的效果确实是正确的。只重写onDraw方法的时候出现的效果是这样的

不重写getItemOffsets方法.png

有分割线的效果,但是边框却“越过”了分割线和下一个Item相连了,于是,我又重写getItemOffsets方法,此时的效果就正常了,如下
设置分割线.png

可是,为什么会出现“越过”分割线的效果呢?经过我自己多次对照代码运行测试后发现,画第一个分割线的开始位置是这样计算的

public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
           ......
     for (int i = 0; i < childCount; i++) {
          View child = parent.getChildAt (i);
          top = child.getBottom () + params.bottomMargin;
          bottom = top + mSize;
           ......
        }

取得Item1.getBottom()作为第一条分割线的top,然后让top加上设置的分割线的高度mSize作为第一条分割线的bottom,余下的分割线以此类推,此时不重写getItemOffsets的情况下,画个草图更加一目了然


我是草图.png

虽然通过onDraw方法画出了分割线的效果,但是由于没有重写getItemOffsets()方法,所以RecyclerView的每个Item还是按照原始的布局位置设置,从3个红色箭头也可以看出
图片顶部到Item1 top的距离 > 图片到Item(x) top的距离,(x=2,3,4...)
这样从Item2开始每个Item的顶部都少了一截mSize大小的高度,这就也解释了为什么会出现边框“越过”分割线的原因

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容