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

Hack RecyclerView.ItemDecoration

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Hack RecyclerView.ItemDecoration

Avatar for Moyuru Aizawa

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