RecyclerView.LayoutManagerとRecyclerView.ItemDecoration
本稿は2018年に執筆したものをZennに移行したものです。
前回の記事 RecyclerView.LayoutManagerの実装方法では触れなかったItemDecorationとLayoutManagerのItemDecoration対応について書きたいと思います。
RecyclerView.ItemDecoration
RecyclerViewには各childViewに装飾をしたりoffsetを設けるためにRecyclerView.ItemDecoration
というクラスが提供されています。
用途に合わせて以下3つの関数をoverrideして実装します。
getItemOffsets
onDraw
onDrawOver
getItemOffsets
各childViewのleft, top, right, bottomにoffsetを設けることができます。
引数のoutRectにpxを渡すことでoffsetを設定できます。
childViewのbottomに10dpのoffsetを設定してみましょう。
recyclerView.addItemDecoration(Decoration())
class Decoration : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.bottom = 10.dp
}
}
RecyclerView#getChildCount
, RecyclerView#getChildAdapterPosition
などを用いれば最後のchildViewにはoffsetを付けないといったことも可能です。
onDraw, onDrawOver
RecyclerViewのcanvasに対して直接描き込むことでchildViewを装飾する事ができます。
onDrawはchildViewが描画される前に、onDrawOverはchildViewが描画された後にcanvasに書き込む事ができます。childViewに被せてなにかを描き込みたい場合はonDrawOverを使うといいでしょう。
この2つの関数はスクロールの度に実行されるので重い処理は避けるようにすべきです。
childViewのbottomに10dpのoffsetを設定した上で、そこに10dpのdividerを描画してみましょう。
class Decoration : RecyclerView.ItemDecoration() {
private val paint = Paint().apply { color = Color.BLACK }
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.bottom = 10.dp
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
(0 until parent.childCount).forEach {
val v = parent.getChildAt(it)
c.drawRect(Rect(parent.left, v.bottom, parent.right, parent.bottom + 10.dp), paint)
}
}
}
LayoutManagerとItemDecoration
ItemDecorationがoffsetの設定を行っていても、LayoutManager側がそのoffsetを考慮してViewを配置していなければoffsetは反映されません。
LayoutManagerを実装する際にItemDecorationにも対応するには以下の関数を知っておくと良いです。
ItemDecorationに対応したLayoutManagerを実装するには、これらの関数だけを使ってchildViewの配置をしていればItemDecoration対応は問題ありません。
-
measureChild
,measureChildWithMargins
-
getDecoratedMeasuredWidth
,getDecoratedMeasuredHeight
-
layoutDecorated
,layoutDecoratedWithMargins
-
getDecoratedLeft
,getDecoratedTop
,getDecoratedRight
,getDecoratedBottom
measureChild, measureChildWithMargins
childViewのmeasureを行ってくれる関数です。その際に、RecyclerViewにaddsあれているItemDecorationから、そのchildViewに対するoffsetを取得してoffsetを考慮したmeasureを行ってくれます。
measureChildWithMargins
はchildViewに設定されているmarginも考慮してくれます。
getDecoratedMeasuredWidth, getDecoratedMeasuredHeight
ItemDecorationによるoffsetを含んだ、childViewのwidth/heightを取得する関数です。
onLayoutChildren
などでchildViewを配置していく際には、この関数をつかって次に配置するchildViewの位置を計算しましょう。
layoutDecorated, layoutDecoratedWithMargins
ItemDecorationによるoffsetを考慮してlayoutを行ってくれます。
getDecoratedLeft, getDecoratedTop, getDecoratedRight, getDecoratedBottom
ItemDecorationによるoffsetを加味したviewのleft, top, right, bottomを反してくれます。
Discussion