RecyclerView自定义”分割线“

RecyclerView.ItemDecoration 大家应该比较熟悉。
但是你们当真以为它只能自定义分割线么?
其实它可以:

  • 最基础的自定义分割线(亦可以绘制自定义View)
  • 给ViewHolder实现类似padding的效果(原理将ViewHolder四周补充出来)
  • 给ViewHolder绘制背景,实现水印(在ViewHolder下层绘制)
  • 实现StickyHeader悬浮分组(在ViewHolder上层绘制)

RecyclerView.ItemDecoration 介绍

  • RecyclerView.ItemDecoration 可以做什么?

    • 允许应用程序添加偏移量从适配器的数据集特定项目视图的特殊的绘图和布局。
    • 也可以理解为用于对RecyclerView的每个ViewHolder做额外试图绘制,将内容View与额外自定义View分开。
  • RecyclerView.ItemDecoration 怎么自定义?

    • 继承 RecyclerView.ItemDecoration
    • 实现方法1 onDraw (在ViewHolder绘制之前执行绘制,非透明可能会被ViewHolder覆盖)
    • 实现方法2 onDrawOver(在ViewHolder绘制之后执行绘制,非透明可能会覆盖ViewHolder)
    • 实现方法3 getItemOffsets(检索给定项的偏移量,即给定要自定义绘制View的空间)

实战1:实现带padding的分割线PaddingDecoration

  • 步骤一 实现RecyclerView绑定Adapter

    1
    过程略了吧,略了吧,略了,略...
  • 步骤二 实现自定义PaddingDecoration

    • 重写getItemOffsets方法就够了
    • 该padding实现的每个ViewHolder上下左右各留10px
    • 你或许有疑问padding怎么给色呀,透明的,透明,透… (ps:难不成你还想再绘制一层View)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
      super.getItemOffsets(outRect, view, parent, state)
      val itemCount = state.getItemCount()
      val childCount = parent.getChildCount()
      val firstVisiblePos = (parent.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
      outRect!!.top = 10 //类似加了一个top padding 10px
      outRect.left = 10 //类似加了一个left padding 10px
      outRect.right = 10 //类似加了一个right padding 10px
      if((itemCount==firstVisiblePos+childCount))
      outRect.bottom = 10 //类似加了一个bottom padding 10px
      }
  • 步骤三 给RecyclerView添加Decoration

    1
    2
    3
    4
    5
    //主要代码
    demo_rv.addItemDecoration(PaddingDecoration())
    //次要必须代码
    demo_rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
    demo_rv.adapter = SimpleAdapter(this)

实战2:实现自定义分割线LineDecoration

  • 步骤一 同上

  • 步骤二 实现自定义LineDecoration

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class LineDecoration() : ItemDecoration() {

    private var dividerHeight = 10// 分割线,可在构造设置
    private lateinit var dividerPaint: Paint//用于绘制分割线的画笔

    constructor(context: Context, dividerheight: Int) : this() {
    this.dividerHeight = dividerheight
    dividerPaint = Paint()
    dividerPaint.color = context.resources.getColor(android.R.color.white)
    }

    override fun onDrawOver(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
    super.onDrawOver(c, parent, state)
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State?) {
    super.onDraw(c, parent, state)
    var childCount = parent.childCount//获取当前绘制的child数量

    val left = parent.paddingLeft
    val right = parent.width - parent.paddingRight

    for (i in 0..childCount) {
    var child: View = parent.getChildAt(i) ?: continue//如果当前childView为null跳过这次绘制
    var top = child.bottom // 将ViewHolder的底部位置获取为绘制分割线的top
    var bottom = top + dividerHeight;//计算分割线底部的位置
    c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), dividerPaint)//绘制分割线
    }
    }

    override fun getItemOffsets(outRect: Rect, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
    super.getItemOffsets(outRect, view, parent, state)
    outRect.bottom = dividerHeight//设置给分割线的高度
    }
    }
  • 步骤三 给RecyclerView添加Decoration

    1
    2
    3
    4
    5
    //主要代码
    demo_rv.addItemDecoration((LineDecoration(this, 1))
    //次要必须代码
    demo_rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
    demo_rv.adapter = SimpleAdapter(this)

绘制自定义View的分割线CustomViewDecoration

  • 主要不同点,关键代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    val groupView = getView(position) ?: return // 这里省略了获取View代码
    val layoutParams = ViewGroup.LayoutParams(right, mViewHeight)
    groupView.layoutParams = layoutParams//给自定义View设置宽高
    groupView.isDrawingCacheEnabled = true // 开启绘制缓存
    groupView.measure(
    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))//测量
    groupView.layout(0, 0, right, mGroupHeight)//指定高度、宽度的groupView
    groupView.buildDrawingCache()//生成缓存
    val bitmap = groupView.drawingCache //将缓存转换的Bitmap
    val marginLeft = if (isAlignLeft) 0 else right - groupView.measuredWidth//判断设置是否左对齐
    c.drawBitmap(bitmap, (left + marginLeft).toFloat(), (top - mGroupHeight).toFloat(), Paint())//绘制自定义view
  • 其他同上

小提示

  • RecyclerView.ItemDecoration 可以多个叠加使用

本篇完~

(注意:转载文章请注明来源[RecyclerView自定义”分割线“](http://goluck.top/2017/08/13/RecyclerView自定义”分割线“/))