Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hack RecyclerView.ItemDecoration

Hack RecyclerView.ItemDecoration

Moyuru Aizawa

April 12, 2019
Tweet

More Decks by Moyuru Aizawa

Other Decks in Technology

Transcript

  1. ‣ An ItemDecoration allows the application to add a special

    drawing and layout offset to specific item views ‣ This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more RecyclerView.ItemDecoration
  2. override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State)

    { super.getItemOffsets(outRect, view, parent, state) if (parent.getChildLayoutPosition(view) < parent.childCount - 1) outRect.bottom = dividerWidth } Divider
  3. override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State)

    { super.getItemOffsets(outRect, view, parent, state) if (parent.getChildLayoutPosition(view) < parent.childCount - 1) outRect.bottom = dividerWidth } Divider
  4. override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State)

    { super.getItemOffsets(outRect, view, parent, state) if (parent.getChildLayoutPosition(view) < parent.childCount - 1) outRect.bottom = dividerWidth } Divider
  5. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { super.onDraw(c,

    parent, state) (0 until parent.childCount) .mapNotNull(parent::getChildAt) .forEach { val left = it.left.toFloat() val right = it.right.toFloat() val bottom = it.bottom.toFloat() c.drawRect(left, bottom, right, bottom + dividerWidth, paint) } } Divider
  6. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { super.onDraw(c,

    parent, state) (0 until parent.childCount) .mapNotNull(parent::getChildAt) .forEach { val left = it.left.toFloat() val right = it.right.toFloat() val bottom = it.bottom.toFloat() c.drawRect(left, bottom, right, bottom + dividerWidth, paint) } } Divider
  7. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { super.onDraw(c,

    parent, state) (0 until parent.childCount) .mapNotNull(parent::getChildAt) .forEach { val left = it.left.toFloat() val right = it.right.toFloat() val bottom = it.bottom.toFloat() c.drawRect(left, bottom, right, bottom + dividerWidth, paint) } } Divider
  8. override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State)

    { super.getItemOffsets(outRect, view, parent, state) outRect.left = width } Timeline
  9. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    (0 until parent.childCount) .mapNotNull(parent::getChildAt) .windowed(2, step = 2, partialWindows = true) .forEach { val text = "HH:mm" val top = it.first().top.toFloat() + margin val centerX = width / 2f val baseX = centerX - textPaint.measureText(text) / 2 val textBounds = Rect() .apply { textPaint.getTextBounds(text, 0, text.length - 1, this) } val baseY = top + textBounds.height() c.drawText(text, baseX, baseY, textPaint) c.drawLine(centerX, top + textBounds.height() + margin, centerX, it.last().bottom.toFloat(), linePaint) } } Timeline
  10. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    (0 until parent.childCount) .mapNotNull(parent::getChildAt) .windowed(2, step = 2, partialWindows = true) .forEach { val text = "HH:mm" val top = it.first().top.toFloat() + margin val centerX = width / 2f val baseX = centerX - textPaint.measureText(text) / 2 val textBounds = Rect() .apply { textPaint.getTextBounds(text, 0, text.length - 1, this) } val baseY = top + textBounds.height() c.drawText(text, baseX, baseY, textPaint) c.drawLine(centerX, top + textBounds.height() + margin, centerX, it.last().bottom.toFloat(), linePaint) } } Timeline
  11. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    (0 until parent.childCount) .mapNotNull(parent::getChildAt) .windowed(2, step = 2, partialWindows = true) .forEach { val text = "HH:mm" val top = it.first().top.toFloat() + margin val centerX = width / 2f val baseX = centerX - textPaint.measureText(text) / 2 val textBounds = Rect() .apply { textPaint.getTextBounds(text, 0, text.length - 1, this) } val baseY = top + textBounds.height() c.drawText(text, baseX, baseY, textPaint) c.drawLine(centerX, top + textBounds.height() + margin, centerX, it.last().bottom.toFloat(), linePaint) } } Timeline
  12. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    (0 until parent.childCount) .mapNotNull(parent::getChildAt) .windowed(2, step = 2, partialWindows = true) .forEach { val text = "HH:mm" val top = it.first().top.toFloat() + margin val centerX = width / 2f val baseX = centerX - textPaint.measureText(text) / 2 val textBounds = Rect() .apply { textPaint.getTextBounds(text, 0, text.length - 1, this) } val baseY = top + textBounds.height() c.drawText(text, baseX, baseY, textPaint) c.drawLine(centerX, top + textBounds.height() + margin, centerX, it.last().bottom.toFloat(), linePaint) } } Timeline
  13. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    (0 until parent.childCount) .mapNotNull(parent::getChildAt) .windowed(2, step = 2, partialWindows = true) .forEach { val text = "HH:mm" val top = it.first().top.toFloat() + margin val centerX = width / 2f val baseX = centerX - textPaint.measureText(text) / 2 val textBounds = Rect() .apply { textPaint.getTextBounds(text, 0, text.length - 1, this) } val baseY = top + textBounds.height() c.drawText(text, baseX, baseY, textPaint) c.drawLine(centerX, top + textBounds.height() + margin, centerX, it.last().bottom.toFloat(), linePaint) } } Timeline
  14. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val rightView = parent.children.maxBy { it.right } ?: return val rightColumnNumber = getColumnNumber(rightView.layoutPosition) val range = … var offsetX = rightView.right range.forEach { c.drawTextAtCenter(getColumnName(it), Rect(offsetX - columnWidth, 0, offsetX, height), textPaint) offsetX -= columnWidth } } Timetable
  15. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val rightView = parent.children.maxBy { it.right } ?: return val rightColumnNumber = getColumnNumber(rightView.layoutPosition) val range = … var offsetX = rightView.right range.forEach { c.drawTextAtCenter(getColumnName(it), Rect(offsetX - columnWidth, 0, offsetX, height), textPaint) offsetX -= columnWidth } } Timetable
  16. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val rightView = parent.children.maxBy { it.right } ?: return val rightColumnNumber = getColumnNumber(rightView.layoutPosition) val range = … var offsetX = rightView.right range.forEach { c.drawTextAtCenter(getColumnName(it), Rect(offsetX - columnWidth, 0, offsetX, height), textPaint) offsetX -= columnWidth } } Timetable
  17. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val rightView = parent.children.maxBy { it.right } ?: return val rightColumnNumber = getColumnNumber(rightView.layoutPosition) val range = … var offsetX = rightView.right range.forEach { c.drawTextAtCenter(getColumnName(it), Rect(offsetX - columnWidth, 0, offsetX, height), textPaint) offsetX -= columnWidth } } Timetable
  18. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val startAtList = (0 until adapter.itemCount).map(this::getStartUnixMillis) val first = parent.children.minBy { it.top } ?: return val baseEpochMillis = startAtList.getOrNull(first.layoutPosition) ?: return startAtList .filterIndexed { i, startAt -> startAt >= baseEpochMillis && canDecorate(i) } .distinct() .forEach { startAt -> val gap = TimeUnit.MILLISECONDS .toMinutes(startAt - baseEpochMillis) * heightPerMinute val top = first.top + gap if (top > parent.height) return@forEach c.drawTextAtCenter( formatUnixMillis(startAt), Rect(0, top.toInt(), width, (top + textHeight).toInt()), textPaint ) } } Timetable
  19. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val startAtList = (0 until adapter.itemCount).map(this::getStartUnixMillis) val first = parent.children.minBy { it.top } ?: return val baseEpochMillis = startAtList.getOrNull(first.layoutPosition) ?: return startAtList .filterIndexed { i, startAt -> startAt >= baseEpochMillis && canDecorate(i) } .distinct() .forEach { startAt -> val gap = TimeUnit.MILLISECONDS .toMinutes(startAt - baseEpochMillis) * heightPerMinute val top = first.top + gap if (top > parent.height) return@forEach c.drawTextAtCenter( formatUnixMillis(startAt), Rect(0, top.toInt(), width, (top + textHeight).toInt()), textPaint ) } } Timetable
  20. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val startAtList = (0 until adapter.itemCount).map(this::getStartUnixMillis) val first = parent.children.minBy { it.top } ?: return val baseEpochMillis = startAtList.getOrNull(first.layoutPosition) ?: return startAtList .filterIndexed { i, startAt -> startAt >= baseEpochMillis && canDecorate(i) } .distinct() .forEach { startAt -> val gap = TimeUnit.MILLISECONDS .toMinutes(startAt - baseEpochMillis) * heightPerMinute val top = first.top + gap if (top > parent.height) return@forEach c.drawTextAtCenter( formatUnixMillis(startAt), Rect(0, top.toInt(), width, (top + textHeight).toInt()), textPaint ) } } Timetable
  21. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val startAtList = (0 until adapter.itemCount).map(this::getStartUnixMillis) val first = parent.children.minBy { it.top } ?: return val baseEpochMillis = startAtList.getOrNull(first.layoutPosition) ?: return startAtList .filterIndexed { i, startAt -> startAt >= baseEpochMillis && canDecorate(i) } .distinct() .forEach { startAt -> val gap = TimeUnit.MILLISECONDS .toMinutes(startAt - baseEpochMillis) * heightPerMinute val top = first.top + gap if (top > parent.height) return@forEach c.drawTextAtCenter( formatUnixMillis(startAt), Rect(0, top.toInt(), width, (top + textHeight).toInt()), textPaint ) } } Timetable
  22. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val startAtList = (0 until adapter.itemCount).map(this::getStartUnixMillis) val first = parent.children.minBy { it.top } ?: return val baseEpochMillis = startAtList.getOrNull(first.layoutPosition) ?: return startAtList .filterIndexed { i, startAt -> startAt >= baseEpochMillis && canDecorate(i) } .distinct() .forEach { startAt -> val gap = TimeUnit.MILLISECONDS .toMinutes(startAt - baseEpochMillis) * heightPerMinute val top = first.top + gap if (top > parent.height) return@forEach c.drawTextAtCenter( formatUnixMillis(startAt), Rect(0, top.toInt(), width, (top + textHeight).toInt()), textPaint ) } } Timetable
  23. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { …

    val startAtList = (0 until adapter.itemCount).map(this::getStartUnixMillis) val first = parent.children.minBy { it.top } ?: return val baseEpochMillis = startAtList.getOrNull(first.layoutPosition) ?: return startAtList .filterIndexed { i, startAt -> startAt >= baseEpochMillis && canDecorate(i) } .distinct() .forEach { startAt -> val gap = TimeUnit.MILLISECONDS .toMinutes(startAt - baseEpochMillis) * heightPerMinute val top = first.top + gap if (top > parent.height) return@forEach c.drawTextAtCenter( formatUnixMillis(startAt), Rect(0, top.toInt(), width, (top + textHeight).toInt()), textPaint ) } } Timetable