最近在使用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)
}
那么在实际运行后会显示
于是我打算在compose中也这么写,先用AndroidView套个壳,然后加上外部加上尺寸限制。
@Composable
fun CV() {
Box( modifier = Modifier.size(200.dp) ) {
AndroidView(factory = {
CustomView(it)
})
}
}
但是运行之后发现,整个页面都渲染成了黑色,似乎这个canvas尺寸大小不对。
于是我加上了日志,看看这个canvas是否是大小计算有误,但是!!
老铁他没毛病,好吧,暂时不清楚问题在哪,可能跟compose绘制节点有关,后边再说吧,先解决问题。
查阅了相关资料后,我找到了Canvas.clip的相关方法,在输入clip之后,弹出的提示框中就能看到。
其中比较重要的就是这个Canvas.clipPath()方法。让我们来看下他文档中是怎么说的。
嗯嗯,他干啥的就说了一句,可以用特殊的路径修改当前的裁剪。好像是懂了好像又没懂,所以不如实践一下。
在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相关方法时,我还注意到了clipOutPath()方法。
他跟clipPath()方法区别,从名字上就可以看出来,clipPath()裁剪后对内修改
clipOutPath()则是对外修改
但是这个方法需要在Android最低版本26之后使用,其实问题不大,看源码就发现他其实都是调用了相同的方法,只是参数不同。
所以我们直接使用对应参数的clipPath即可。
题外之题外话
在看clip相关方法的时候还注意到了withClip方法,他是一个Kotlin中对Canvas的ktx拓展方法。
他帮我们写了一些常用的固定句式,让我们更加关注裁剪后对canvas的逻辑实现。不得不说,kotlin的语法糖是真的甜。