关于在Compose中自定义View的使用中绘制区域的问题

289 阅读2分钟

最近在使用Compose进行绘制页面的时候,碰到一个需求,需要用到原生的自定义View。本来在原生里面绘制区域的计算本不需要考虑,因为View的绘制是无法超过自身区域的,就譬如说当我在代码中写上:

<com.zq.myapplication.CustomView
    android:layout_width="200dp"
    android:layout_height="200dp" />

在onDraw里面可以这么写

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    canvas.drawColor(Color.BLACK)
}

那么在实际运行后会显示

xml绘制结果.jpg

于是我打算在compose中也这么写,先用AndroidView套个壳,然后加上外部加上尺寸限制。

@Composable
fun CV() {
    Box( modifier = Modifier.size(200.dp) ) {
        AndroidView(factory = {
            CustomView(it)
        })
    }
}

但是运行之后发现,整个页面都渲染成了黑色,似乎这个canvas尺寸大小不对。

compose绘制结果.jpg

于是我加上了日志,看看这个canvas是否是大小计算有误,但是!!

日志.jpg 老铁他没毛病,好吧,暂时不清楚问题在哪,可能跟compose绘制节点有关,后边再说吧,先解决问题。

查阅了相关资料后,我找到了Canvas.clip的相关方法,在输入clip之后,弹出的提示框中就能看到。

clip方法列表.jpg

其中比较重要的就是这个Canvas.clipPath()方法。让我们来看下他文档中是怎么说的。

clipPath方法.jpg 嗯嗯,他干啥的就说了一句,可以用特殊的路径修改当前的裁剪。好像是懂了好像又没懂,所以不如实践一下。

在onDraw中,裁剪出一个区域,然后在这个区域中进行绘制。那么这个区域该怎么计算呢? 我们知道View的绘制离不开三个操作,onMeasure,onLayout,和onDraw,其中onLayout方法可以帮助我们知道View在页面中的位置。所以修改下代码。

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

    val path = Path()

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)

        path.reset()
        path.addRect(
            RectF(
                left.toFloat(),
                top.toFloat(),
                right.toFloat(),
                bottom.toFloat()
            ),
            Path.Direction.CCW
        )
    }


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

        canvas.save()
        canvas.clipPath(path)
        canvas.drawColor(Color.BLACK)
        canvas.restore()
    }
}

然后运行一下,看下结果。

clip加了之后compose绘制.jpg

完美解决!!!

题外话 在看clip相关方法时,我还注意到了clipOutPath()方法。

他跟clipPath()方法区别,从名字上就可以看出来,clipPath()裁剪后对内修改

clipPath效果图.jpg

clipOutPath()则是对外修改

clipoutPath效果图.jpg

但是这个方法需要在Android最低版本26之后使用,其实问题不大,看源码就发现他其实都是调用了相同的方法,只是参数不同。

clipOutPath方法.jpg

所以我们直接使用对应参数的clipPath即可。

题外之题外话

在看clip相关方法的时候还注意到了withClip方法,他是一个Kotlin中对Canvas的ktx拓展方法。

withClip方法.jpg 他帮我们写了一些常用的固定句式,让我们更加关注裁剪后对canvas的逻辑实现。不得不说,kotlin的语法糖是真的甜。