解析RecyclerView.ItemDecoration

1.简介

RecyclerView.ItemDecoration的作用是给ItemView添加装饰,绘制更多内容,增强itemUI的效果。例子:

1)绘制分割线

2) 数据分组等

2.使用方法:

ItemDecoration类中仅有3个方法,具体如下:

public class TestDividerItemDecoration extends RecyclerView.ItemDecoration {

    // 方法1:getItemOffsets()
    // 作用:设置ItemView的内嵌偏移长度(inset)
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
       ...
  }

    // 方法2:onDraw()
    // 作用:在子视图上设置绘制范围,并绘制内容
    // 类似平时自定义View时写onDraw()一样
    // 绘制图层在ItemView以下,所以如果绘制区域与ItemView区域相重叠,会被遮挡
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    ...
  }

    // 方法3:onDrawOver()
    // 作用:同样是绘制内容,但与onDraw()的区别是:绘制在图层的最上层
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
      ...
}

2.1 getItemOffsets()

1) 作用

设置ItemView的内嵌偏移长度(inset)

  • 如图,其实RecyclerView 中的 ItemView 外面会包裹着一个矩形(outRect
  • 内嵌偏移长度 是指:该矩形(outRect)与 ItemView的间隔
  • 内嵌偏移长度分为4个方向:上、下、左、右,并由outRect 中的 top、left、right、bottom参数 控制

2)使用

@Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
      // 参数说明:
        // 1. outRect:全为 0 的 Rect(包括着Item)
        // 2. view:RecyclerView 中的 视图Item
        // 3. parent:RecyclerView 本身
        // 4. state:状态

      outRect.set(50, 0, 0,50);
      // 4个参数分别对应左(Left)、上(Top)、右(Right)、下(Bottom)
      // 上述语句代表:左&下偏移长度=50px,右 & 上 偏移长度 = 0
       ...
  }

结论:outRect4个属性值影响着ItemView的Padding值
具体过程:在RecyclerView进行子View宽高测量时(measureChild()),会将getItemOffsets()里设置的 outRect4个属性值(Top、Bottom、Left、Right)通过insert值累加 ,并最终添加到子View的 Padding属性中 
2.2 onDraw

作用:通过 Canvas 对象绘制内容

使用:

@Override
    public void onDraw(Canvas c, RecyclerView parent, 
                                  RecyclerView.State state) {
      ....
      // 使用类似自定义View时的 onDraw()

}

注意:

注意点1:ItemdecorationonDraw()绘制会先于ItemViewonDraw()绘制,所以如果在ItemdecorationonDraw()中绘制的内容在ItemView边界内,就会被ItemView遮挡住。

解决方案:配合前面的 getItemOffsets() 一起使用在outRect矩形 与 ItemView的间隔区域 绘制内容

即:通过getItemOffsets() 设置与 Item 的间隔区域,从而获得与ItemView不重叠的绘制区域

注意点2: getItemOffsets() 针对是每一个 ItemView的,而 onDraw() 针对 RecyclerView 本身

解决方案:在 使用onDraw()绘制时,需要先遍历RecyclerView 的所有ItemView分别获取它们的位置信息,然后再绘制内容

  1. 此处遍历的RecyclerViewItemView(即Child view),并不是 Adapter 设置的每一个 item,而是可见的 item
  2. 因为只有可见的Item 才是RecyclerViewChild view

3.实例讲解: 绘制分割线

实例说明:在ItemView设计一个高度为 10 px 的红色分割线
思路 :
通过getItemOffsets()设置与 Item 的下间隔区域 = 10 px  

 

  设置好onDraw()可绘制的区域
  通过onDraw()绘制一个高度 = 10px的矩形(填充颜色=红色)

 

 

 

步骤1:自定义ItemDecoration类

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private Paint mPaint;

    // 在构造函数里进行绘制的初始化,如画笔属性设置等
    public DividerItemDecoration() {

        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        // 画笔颜色设置为红色
    }

    // 重写getItemOffsets()方法
    // 作用:设置矩形OutRect 与 Item 的间隔区域
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);


        int itemPosition = parent.getChildAdapterPosition(view);
        // 获得每个Item的位置

        // 第1个Item不绘制分割线
        if (itemPosition != 0) {
            outRect.set(0, 0, 0, 10);
            // 设置间隔区域为10px,即onDraw()可绘制的区域为10px
        }
    }

    // 重写onDraw()
    // 作用:在间隔区域里绘制一个矩形,即分割线
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        // 获取RecyclerView的Child view的个数
        int childCount = parent.getChildCount();

        // 遍历每个Item,分别获取它们的位置信息,然后再绘制对应的分割线
        for ( int i = 0; i < childCount; i++ ) {
            // 获取每个Item的位置
            final View child = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(child);
            // 第1个Item不需要绘制
            if ( index == 0 ) {
                continue;
            }

            // 获取布局参数
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            // 设置矩形(分割线)的宽度为10px
            final int mDivider = 10;

            // 根据子视图的位置 & 间隔区域,设置矩形(分割线)的2个顶点坐标(左上 & 右下)

            // 矩形左上顶点 = (ItemView的左边界,ItemView的下边界)
            // ItemView的左边界 = RecyclerView 的左边界 + paddingLeft距离 后的位置
            final int left = parent.getPaddingLeft();
            // ItemView的下边界:ItemView 的 bottom坐标 + 距离RecyclerView底部距离 +translationY
            final int top = child.getBottom() + params.bottomMargin +
                    Math.round(ViewCompat.getTranslationY(child));

            // 矩形右下顶点 = (ItemView的右边界,矩形的下边界)
            // ItemView的右边界 = RecyclerView 的右边界减去 paddingRight 后的坐标位置
            final int right = parent.getWidth() - parent.getPaddingRight();
            // 绘制分割线的下边界 = ItemView的下边界+分割线的高度
            final int bottom = top + mDivider;


            // 通过Canvas绘制矩形(分割线)
            c.drawRect(left,top,right,bottom,mPaint);
        }
    }
}

 2.3 onDrawOver()

  • onDraw()类似,都是绘制内容
  • 但与onDraw()的区别是:ItemdecorationonDrawOver()绘制 是后于 ItemViewonDraw()绘制

即不需要考虑绘制内容被ItemView遮挡的问题,反而 ItemView会被onDrawOver()绘制的内容遮挡 
  绘制时机比较: 
  Itemdecoration.onDraw()> ItemView.onDraw() > Itemdecoration.onDrawOver()
如图:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值