Как рисовать разделители только между элементами или рисовать разделители везде? - PullRequest
0 голосов
/ 13 июля 2020

Я попытался написать собственный класс DividerItemDecoration, который позволяет мне указать, где нарисованы разделители.

Я хотел, чтобы он поддерживал четыре случая:

  1. TOP: рисовать разделители на верхней стороне элемента recyclerview (включая верх первого элемента).
  2. BOTTOM: рисовать разделители на нижней стороне элемента recyclerview (включая нижнюю часть последнего элемента) ). (вот как androidx.recyclerview.widget.DividerItemDecoration уже делает это.)
  3. ONLYBETWEEN: рисовать разделители только между элементами recyclerview (не будет разделитель над первым элементом, и не будет разделителя ниже последнего элемента).
  4. EVERYWHERE: нарисуйте разделители везде (разделитель должен быть выше первого элемента, между каждым другим элементом и ниже последний элемент).

Первые два потенциально могут быть опущены, потому что очень легко создать аналогичный эффект, используя макеты XML.

Если у вас есть класс который уже поддерживает эти вещи, я бы тоже принял их как ответ. Но поскольку я ничего не нашел сам, я решил попробовать создать свой собственный класс.

В настоящее время мой CustomDividerItemDecoration класс выглядит так:

import android.content.Context
import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.View
import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.roundToInt

class CustomDividerItemDecoration(
    context: Context,
    orientation: Int,
    private val dividerPosition: DividerPositions
) : RecyclerView.ItemDecoration() {
    
    enum class DividerPositions {
        ONLYBETWEEN, BOTTOM, TOP, START, END, EVERYWHERE
    }
    
    companion object {
        private const val TAG = "CustomDividerItem"
        private val ATTRS = intArrayOf(android.R.attr.listDivider)
    }
    
    var mDivider: Drawable?
    var mOrientation: Int = orientation
    
    private val mBounds = Rect()
    
    init {
        val a: TypedArray = context.obtainStyledAttributes(ATTRS)
        mDivider = a.getDrawable(0)
        if (mDivider == null) {
            Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
                    + "DividerItemDecoration. Please set that attribute all call setDrawable()")
        }
        a.recycle()
    }
    
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.layoutManager == null || mDivider == null) {
            return
        }
        if (mOrientation == LinearLayout.VERTICAL) {
            drawVertical(c, parent)
        } else if (mOrientation == LinearLayout.HORIZONTAL) {
            drawHorizontal(c, parent)
        }
    }
    
    private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
        canvas.save()
        val left: Int
        val right: Int
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.clipToPadding) {
            left = parent.paddingLeft
            right = parent.width - parent.paddingRight
            canvas.clipRect(
                left,
                parent.paddingTop,
                right,
                parent.height - parent.paddingBottom
            )
        } else {
            left = 0
            right = parent.width
        }
        
        val childCount = parent.childCount
        for (i in 0 until childCount) {
            when (dividerPosition) {
                DividerPositions.BOTTOM -> {
                    val child = parent.getChildAt(i)
                    parent.getDecoratedBoundsWithMargins(child, mBounds)
                    val bottom = mBounds.bottom + child.translationY.roundToInt()
                    val top = bottom - mDivider!!.intrinsicHeight
                    mDivider!!.setBounds(left, top, right, bottom)
                    mDivider!!.draw(canvas)
                }
                DividerPositions.TOP -> {
                    //this doesn't work
                    val child = parent.getChildAt(i)
                    parent.getDecoratedBoundsWithMargins(child, mBounds)
                    val top = mBounds.top + child.translationY.roundToInt()
                    val bottom = top + mDivider!!.intrinsicHeight
                    mDivider!!.setBounds(left, top, right, bottom)
                    mDivider!!.draw(canvas)
                }
                DividerPositions.EVERYWHERE -> {
                    TODO()
                }
                DividerPositions.ONLYBETWEEN -> {
                    TODO()
                }
                else -> {
                    throw IllegalStateException("Can't draw dividers in position ${dividerPosition.name} while drawing vertical.")
                }
            }
        }
        canvas.restore()
    }
    
    private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
        canvas.save()
        val top: Int
        val bottom: Int
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.clipToPadding) {
            top = parent.paddingTop
            bottom = parent.height - parent.paddingBottom
            canvas.clipRect(parent.paddingLeft, top,
                parent.width - parent.paddingRight, bottom)
        } else {
            top = 0
            bottom = parent.height
        }
        
        val childCount = parent.childCount
        for (i in 0 until childCount) {
            when (dividerPosition) {
                DividerPositions.END -> {
                    val child = parent.getChildAt(i)
                    parent.layoutManager?.getDecoratedBoundsWithMargins(child, mBounds)
                    val right = mBounds.right + child.translationX.roundToInt()
                    val left = right - mDivider!!.intrinsicWidth
                    mDivider!!.setBounds(left, top, right, bottom)
                    mDivider!!.draw(canvas)
                }
                DividerPositions.START -> {
                    //this doesn't work.
                    val child = parent.getChildAt(i)
                    parent.layoutManager?.getDecoratedBoundsWithMargins(child, mBounds)
                    val left = mBounds.left - child.translationX.roundToInt()
                    val right = left + mDivider!!.intrinsicWidth
                    mDivider!!.setBounds(left, top, right, bottom)
                    mDivider!!.draw(canvas)
                }
                DividerPositions.EVERYWHERE -> {
                    TODO()
                }
                DividerPositions.ONLYBETWEEN -> {
                    TODO()
                }
                else -> {
                    throw IllegalStateException("Can't draw dividers in position ${dividerPosition.name} while drawing horizontal.")
                }
            }
            
            val child = parent.getChildAt(i)
            parent.layoutManager?.getDecoratedBoundsWithMargins(child, mBounds)
            val right = mBounds.right + child.translationX.roundToInt()
            val left = right - mDivider!!.getIntrinsicWidth()
            mDivider!!.setBounds(left, top, right, bottom)
            mDivider!!.draw(canvas)
        }
        canvas.restore()
    }
    
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        if (mDivider == null) {
            outRect.set(0, 0, 0, 0)
            return
        }
        if (mOrientation == LinearLayout.VERTICAL) {
            outRect.set(0, 0, 0, mDivider!!.getIntrinsicHeight())
        } else {
            outRect.set(0, 0, mDivider!!.getIntrinsicWidth(), 0)
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...