getItemOffsets
为了给绘制 Decoration 腾出绘制区域,RecyclerView 通过调用 ItemDecoration 的 getItemOffsets,获取需要的绘制区域尺寸,保存到 RecyclerView.LayoutParams 的 mDecorInsets 变量里。
RecyclerView measure 和 layout 过程中会考虑到 mDecorInsets,也就是说除了 Padding,Margin 之外,RecyclerView 还增加了 mDecorInsets 进行布局。mDecorInsets 的作用Margin 比较相似。
onDraw,onDrawOver
onDraw 和 onDrawOver 都是绘制 Decoration,区别在于,onDraw 在 RecyclerView 绘制 Children 之前,onDrawOver 在 RecyclerView 绘制 Children 之后。
以 DividerItemDecoration 为例分析
getItemOffsets 根据方向,为 outRect 的 bottom 或者 left 设置了 mDivider 的高度或宽度。
onDraw 过程,根据方向,调用 drawVertical 或者 drawHorizontal。
以 drawVertical 为例进行分析。
先调用 RecyclerView 的 getDecorationBoundsWithMargins 获取到一个 Rect mBounds。
根据 bottom 和 mDivider 的 height,得到 Decoration 绘制区域。
RecyclerView 的 getDecorationBoundsWithMargins 其实调用的是 RecyclerView 的 static 方法 getDecoratedBoundsWithMarginsInt。可以看到,outBounds 根据 View 的位置参数,margin 以及 insets 生成。这个 insets 与前面 getItemOffset 获取到的 Rect 属性相同。
自己定制
综上而言,要定制自己的 ItemDecoration 其实非常简单,只需要 override getItemOffsets 函数, 为 View 的上下左右腾出绘制区域。然后在 onDraw 或者 onDrawOver 时,通过 RecyclerView.LayoutParams 和 View 的位置信息,计算绘制区域,绘制 Decoration 即可。
获取 View 布局参数等过程可以参考 DividerItemDecoration。
思考
RecyclerView 添加 ItemDecoration 的方法是 addItemDecoration 而不是 setItemDecoration,说明一个 RecyclerView 可以有多个 ItemDecoration。一般来说,一个 Decoration 已经够用了。但当有多个 ItemDecoration 时,它们的 ItemOffsets 是重叠(最终大小由最大的 ItemOffset 决定)?还是相加呢?