为什么 Canvas 在协程后台线程中绘制的位置很奇怪?

Why did Canvas draw in Coroutine background thread draw in weird position?

我有一个简单的 customView,它画了一条线,如下所示。

class ViewCustom @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0)
    : View(context, attrs, defStyleAttr) {

    private val strokePaint = Paint()
        .apply { color = Color.BLACK}
        .apply { strokeWidth = 6f }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
            canvas.drawLine(
                0f, 0f,
                width.toFloat(), height.toFloat(),
                strokePaint
            )
    }

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

        val desiredWidth = suggestedMinimumWidth + paddingLeft + paddingRight
        val desiredHeight = suggestedMinimumHeight + paddingTop + paddingBottom

        setMeasuredDimension(resolveSize(desiredWidth, widthMeasureSpec),
            resolveSize(desiredHeight, heightMeasureSpec))
    }
}

效果很好,把对角线端到端画出来。

但是,如果我在它周围添加一个协程线程

class ViewCustom @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0)
    : View(context, attrs, defStyleAttr) {

    private val treeDrawing by lazy {
        TreeDrawingCanvasCustom()
    }

    private val strokePaint = Paint()
        .apply { color = Color.BLACK}
        .apply { strokeWidth = 6f }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        CoroutineScope(Dispatchers.Default).launch { // <-- add coroutine
            canvas.drawLine(
                0f, 0f,
                width.toFloat(), height.toFloat(),
                strokePaint
            )
        }
    }

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

        val desiredWidth = suggestedMinimumWidth + paddingLeft + paddingRight
        val desiredHeight = suggestedMinimumHeight + paddingTop + paddingBottom

        setMeasuredDimension(resolveSize(desiredWidth, widthMeasureSpec),
            resolveSize(desiredHeight, heightMeasureSpec))
    }
}

它仍然在绘制,但是在一个奇怪的位置,如下图所示。我已经检查过 heightwidth 值在有或没有协程的情况下仍然相同。

为什么会这样?

刚刚发现原因是所有绘图中只有一个 canvas 共享。当onDraw在后台线程时,不能保证其他人也在操纵主线程绘制canvas,因此并发绘制会导致绘制坐标与原始坐标不对齐

在下面的图例中,绘图是在Caption区域,是因为Text Caption绘图是同时发生的,所以它是在Text Caption区域绘制的。