【问题标题】:How to draw a view in an ItemDecoration?如何在 ItemDecoration 中绘制视图?
【发布时间】:2018-06-15 15:09:10
【问题描述】:

我已经知道如何在ItemDecoration 中绘制东西,但现在我想在ItemDecoration 中绘制View

由于设置有点复杂,所以我创建了一个sample project,可以重现问题。

我想要达到的目标

我有一个 RecyclerView 有 20 个项目,只显示数字。我想在第 5 项上方添加一个带有文本“This is Number 5”的黑色标题。

当然,这是我的真实问题的简化版,在我的真实问题中我必须通过 ItemDecoration 来做,所以请不要给出不使用 ItemDecoration 的替代方案。

问题

如下图所示,装饰物大小正确,可以绘制xml的第一层(有android:background="@color/black");但不是包含 TextView 的子视图,它应该显示“这是 5 号”。

我现在是怎么做的

FiveHeaderDecoration.kt:

class FiveHeaderDecoration: RecyclerView.ItemDecoration() {

    private var header: Bitmap? = null
    private val paint = Paint()

    override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
        val params = view?.layoutParams as? RecyclerView.LayoutParams
        if (params == null || parent == null) {
            super.getItemOffsets(outRect, view, parent, state)
        } else {
            val position = params.viewAdapterPosition
            val number = (parent.adapter as? JustAnAdapter)?.itemList?.getOrNull(position)
            if (number == 5) {
                outRect?.set(0, 48.dp(), 0, 0)
            } else {
                super.getItemOffsets(outRect, view, parent, state)
            }
        }
    }

    override fun onDraw(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        initHeader(parent)
        if (parent == null) return
        val childCount = parent.childCount
        for (i in 0 until childCount) {
            val view = parent.getChildAt(i)
            val position = parent.getChildAdapterPosition(view)
            val number = (parent.adapter as? JustAnAdapter)?.itemList?.getOrNull(position)
            if (number == 5) {
                header?.let {
                    c?.drawBitmap(it, 0.toFloat(), view.top.toFloat() - 48.dp(), paint)
                }
            } else {
                super.onDraw(c, parent, state)
            }
        }
    }

    private fun initHeader(parent: RecyclerView?) {
        if (header == null) {
            val view = parent?.context?.inflate(R.layout.decoration, parent, false)
            val bitmap = Bitmap.createBitmap(parent?.width?:0, 40.dp(), Bitmap.Config.ARGB_8888)
            val canvas = Canvas(bitmap)
            view?.layout(0, 0, parent.width, 40.dp())
            view?.draw(canvas)
            header = bitmap
        }
    }

}

您可以在示例项目中找到其他类。但我想它们并没有真正的关系。

如您所见,我首先尝试将视图布局并绘制到位图。这是因为我只能在 onDraw() 的画布上绘制一些东西,但不能放大视图(我什至没有 ViewGroupaddView())。

通过使用调试器,我已经可以看到initHeader() 中生成的位图只是一块黑色。所以问题可能在于我如何initHeader()

【问题讨论】:

  • 您可能还想在布局和绘制之前测量视图,view?.measure(MeasureSpec.makeMeasureSpec(parent.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(40.dp() , MeasureSpec.EXACTLY))
  • @Luksprog 我也想通了。但仍在尝试将这些子视图定位在正确的位置。

标签: android android-recyclerview item-decoration


【解决方案1】:

想通了(哎呀,我的赏金)

为了正确创建View,需要 3 个步骤:

  1. 测量 (view.measure())
  2. 布局 (view.layout())
  3. 画图 (view.draw())

这些通常由父 ViewGroup 或 addView() 完成。但是现在因为我们没有做这些,所以我们需要自己调用所有这些。

问题显然是我错过了第一步。

所以initHeader 方法应该是:

private fun initHeader(parent: RecyclerView?) {
    if (header == null) {
        val view = parent?.context?.inflate(R.layout.decoration, parent, false)
        val bitmap = Bitmap.createBitmap(parent?.width?:0, 40.dp(), Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        val widthSpec = View.MeasureSpec.makeMeasureSpec(parent?.width ?: 0, View.MeasureSpec.EXACTLY)
        val heightSpec = View.MeasureSpec.makeMeasureSpec(40.dp(), View.MeasureSpec.EXACTLY)
        view?.measure(widthSpec, heightSpec)
        view?.layout(0, 0, parent.width, 40.dp())
        view?.draw(canvas)
        header = bitmap
    }
}

请注意,widthSpec 和 heightSpec 将根据您的用例而有所不同。那是另外一个话题,这里就不解释了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 2011-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多