自定义视图组中的自定义视图不绘制

时间:2021-03-01 06:01:46

标签: android kotlin

我想在 LudoBoard 上绘制 LudoDeck。我创建了一个自定义视图组并禁用了 willNotDraw 并设置了子视图的位置和大小,但它在某种程度上没有呈现到屏幕上。我在 logcat 中看到了 LudoDeck onDraw 的日志,但我不确定为什么它没有绘制,是不是因为我没有正确设置视图大小?

谁能帮我弄清楚我的错误在哪里?谢谢。

LudoBoard.kt

package io.github.andraantariksa.ludo

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup

const val RATIO = 1.0f

class LudoBoard(context: Context, attributeSet: AttributeSet):
        ViewGroup(context, attributeSet) {
    private val ludoPawnsInLane = arrayOf<LudoPawn>()
    private val totalGridToTarget = 6
    private var gridSideSize = 0F
    private val colors = arrayOf(Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN)
    private val deck = arrayOf<LudoDeck>(
            LudoDeck(context))

    init {
        setWillNotDraw(false)
        deck.forEach {
            addView(it)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        var width = measuredWidth
        var height = measuredHeight
        val widthWithoutPadding = width - paddingLeft - paddingRight
        val heightWithoutPadding = height - paddingTop - paddingBottom

        val maxWidth = (heightWithoutPadding * RATIO).toInt()
        val maxHeight = (widthWithoutPadding / RATIO).toInt()

        if (widthWithoutPadding > maxWidth) {
            width = maxWidth + paddingLeft + paddingRight
        } else {
            height = maxHeight + paddingTop + paddingBottom
        }

        gridSideSize = width / (totalGridToTarget * 2 + 3).toFloat()

        val deckSideSize = gridSideSize * 6F
        deck.forEach {
            it.measure(deckSideSize.toInt(), deckSideSize.toInt())
            it.x = 0F
            it.y = 0F
        }

        setMeasuredDimension(width, height)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        // some code to draw the board
    }
}

LudoDeck.kt

package io.github.andraantariksa.ludo

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View

class LudoDeck(context: Context): View(context) {
    private var totalPawn = 4

    init {
        setWillNotDraw(false)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onDraw(canvas: Canvas) {
        Log.d("zzzzz", "Draw")
        val p = Paint()
        p.color = Color.BLACK
        val rect = Rect(0, 0, measuredWidth, measuredHeight)
        canvas.drawRect(rect, p)
    }
}

1 个答案:

答案 0 :(得分:1)

所以基本上我稍微调整了你的代码,并在这个过程中也学习了 ViewGroups。谢谢!

我已经评论了代码中所做更改的解释,您可以参考,如果有任何疑问,请随时提问..

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.ViewGroup

const val RATIO = 1.0f

class LudoBoard1(context: Context, attributeSet: AttributeSet):
        ViewGroup(context, attributeSet) {
//    private val ludoPawnsInLane = arrayOf<LudoPawn>()
    private val totalGridToTarget = 6
    private var gridSideSize = 0F
    private val colors = arrayOf(Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN)
    private val deck = arrayOf<LudoDeck>(
            LudoDeck(context, attributeSet), LudoDeck(context, attributeSet))


    init {
        //setting this will call the onDraw method of the viewGroup. If we just want to treat this as a container
        //then set this as 'true'. This way it will not draw anything.
        setWillNotDraw(false)

        //Add all your custom views here in the beginning.
        deck.forEach {
            addView(it)
        }
    }

    /**
     * This method is used to measure the size of the view itself and also the size of the children.
     * Here we calculate the size and allocate it to the children also by calling their "measure()"
     * method.
     * It's extremely important to call the "setMeasuredDimension()" at the end as this method will
     * allocated the measured width and the height.
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        var width = measuredWidth
        var height = measuredHeight
        val widthWithoutPadding = width - paddingLeft - paddingRight
        val heightWithoutPadding = height - paddingTop - paddingBottom

        val maxWidth = (heightWithoutPadding * RATIO).toInt()
        val maxHeight = (widthWithoutPadding / RATIO).toInt()

        if (widthWithoutPadding > maxWidth) {
            width = maxWidth + paddingLeft + paddingRight
        } else {
            height = maxHeight + paddingTop + paddingBottom
        }

        gridSideSize = width / (totalGridToTarget * 2 + 3).toFloat()

        val deckSideSize = gridSideSize * 6F

        deck.forEach {
            it.measure(deckSideSize.toInt(), deckSideSize.toInt())
        }

        setMeasuredDimension(width, height)
    }

    /**
     * For a viewGroup, its better if we don't draw anything, but still if we have to, then we can.
     * The view group is designed as a container where it determines it's own size, it's children's size
     * and their positions.
     */
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        // some code to draw the board
    }

    /**
     * This is the method where we calculate the positions for every child. Here we determine the
     * starting and ending point for every child element of this viewGroup.
     */
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        /*
        Here you will determine the position for every child. Calculate the left top right bottom for every child
        and allocate it to the layout.
        For now, i am just positioning the ludo deck besides each other.
         */
        var previousXStartPoint = 0

        deck.forEachIndexed { index, it ->

            it.layout(previousXStartPoint , 0, previousXStartPoint.plus(it.measuredWidth), (it.measuredHeight))
            previousXStartPoint = it.right + 20

        }
    }
}

这是 LudoDeck 类:

    class LudoDeck(context: Context, attrs: AttributeSet?): View(context, attrs) {
    private var totalPawn = 4
    private val rect = Rect(0, 0, 50, 50)
    private val p = Paint()

    /*
    As we are drawing something in this view, it's appropriate to set call this method in the beginning.
     */
    init {
        setWillNotDraw(false)
    }

    /**
     * Its advised not to initialize anything in the onMeasure() method. So, we have already initialized
     * the rect in the beginning and now we will just update its params.
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        rect.right = widthMeasureSpec
        rect.bottom = heightMeasureSpec
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec)
    }

    /**
     * Its also advised not to initialize anything in onDraw() method, so we have already created the paint
     * object. Here we simply draw!
     */
    override fun onDraw(canvas: Canvas) {
        p.color = Color.BLACK
        canvas.drawRect(rect, p)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        //Uncomment to check whether the dims set in onLayout of Ludo Board are properly allocated. :)
        //Log.e("ludoDeck", "left: $left, top: $top, right: $right, bottom: $bottom")
    }

最后,我能够理解一些事情并使其工作的来源是:https://academy.realm.io/posts/360andev-huyen-tue-dao-measure-layout-draw-repeat-custom-views-and-viewgroups-android/

告诉我结果如何!

相关问题