代码之家  ›  专栏  ›  技术社区  ›  Sira Lam

如何在项目装饰中绘制视图?

  •  3
  • Sira Lam  · 技术社区  · 6 年前

    我已经知道怎么画东西了 ItemDecoration ,但现在我想画一个 View 在一个 物品装饰 .

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

    我想达到的目标

    我有一个 RecyclerView 有20个项目,只显示数字。我想在第5项上面加上一个黑色的标题,上面写着“这是5号”。

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

    问题

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

    enter image description here

    我现在怎么做

    五头节:

    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() 但不要夸大观点(我甚至没有 ViewGroup addView() )

    通过使用调试器,我已经可以看到在 initHeader() 只是一块黑色的。所以问题可能在于我 In the the the thiad() .

    1 回复  |  直到 6 年前
        1
  •  6
  •   Sira Lam    6 年前

    明白了(哦,我的赏金)

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

    1. 量度(测量) view.measure() )
    2. 版式设计( view.layout() )
    3. 绘制(绘制) view.draw() )

    通常由父视图组完成,或 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将根据您的用例而有所不同。这是另一个话题,所以我不在这里解释。