Compose 中,绘制的 ApidrawBehind drawWithContent 或者是使用完成自定义绘制方式 Canvas,接下来就来看看他们是如何使用的

在已有内容上绘制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Preview
@Composable
fun View1() {
    // drawBehind behind:后面
    Text(text = "绘制", modifier = Modifier.drawBehind {
        // 绘制背景
        this.drawRect(Color.Yellow)

        // 绘制线
        this.drawLine(
            Color.Red,
            start = Offset(0f, size.height / 2), // 起始位置
            end = Offset(size.width, size.height / 2) // 结束位置
        )
    })
}

behind 的中文翻译意思是后面,drawBehind 可以理解为,会在已有内容的后面进行绘制,而上述代码中的意思,就是在 text 文字的后面绘制,上述代码中,我们绘制了一个黄色背景,和画了一条线,运行后是这样的

在这里我们注意到一个区别,那就是绘制背景 drawRect 的时候,并没有填入绘制的范围,这又是哪里设置了呢,起始是 drawRect 中默认参数的,而大小的 size 信息,都是 DrawScope 类中提供了,如果是 Android 原生的话,这些都需要自己填写,会麻烦很多,Compose 在这方面遥遥领先

在 DrawScope 的中,也提供了非常多绘制 Api,这些 Api 都大同小异,基本和原生差不多

完全绘制内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Preview
@Composable
fun View2() {
    Box(modifier = Modifier
        .size(50.dp)
        .drawWithContent {
            drawRect(Color.Green) // 绘制在内容的底部
            drawContent() // 绘制原有内容
            // 绘制在内容的上面
            this.drawLine(
                Color.Red,
                start = Offset(0f, size.height / 2),
                end = Offset(size.width, size.height / 2)
            )
        })
}

我们想要完全自定义绘制内容的时候,可以使用 drawWithContent 参数,然后在 drawWithContent 的上面的代码,是在原有绘制内容的底部,在下面部分的代码是绘制在原有内容的上面部分,最后运行代码后是这样的

在 Android 原生中,一般完全绘制内容的时候,我们会使用 Canvas 这个类,比如一般是这样的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class CustomView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawLine()
        canvas.drawRect()
    }
}

其实在 Compose 也有类似这样的使用方式,上面的代码,转化后,是这样的

图片的几何变化, 拉升和缩放,旋转

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Preview
@Composable
fun View3() {
    val image = ImageBitmap.imageResource(id = R.mipmap.ccc)

    Canvas(modifier = Modifier.size(height = image.height.dp, width = image.width.dp)) {
        rotate(degrees = 0f) { // 旋转
            drawImage(image, dstSize = IntSize(size.width.toInt(), size.height.toInt()))
        }
    }
}

绘制图片使用的是 drawImage ,这里和原生的区别是,原生使用的是 drawBitmap ,而且需要设置 rect 等很多参数设置,而在 compose 则是会简单很多,运行后,效果是这样的

一维旋转

如果我们想对上面的图片进行旋转呢,使用到的是 rotate ,相对比原生的话,也会简单很多,如果是原生的话,需要对 canvas 画布进行操作,操作之后,还得将画布还原回来,而在 Compose 中,底层虽然也是使用的 Canvas ,但都是内部进行处理还原了,不用我们手动处理了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class CustomView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    val paint = Paint()

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.save()
        canvas.rotate(90f)
        canvas.drawBitmap()
        canvas.restore()
    }
}

而如果是 Compose 中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Preview
@Composable
fun View3() {
    val image = ImageBitmap.imageResource(id = R.mipmap.ccc)

    Canvas(modifier = Modifier.size(height = image.height.dp, width = image.width.dp)) {
        rotate(degrees = 45f) { // 平面旋转, 或者是 graphicsLayer 也可以做到
            drawImage(image, dstSize = IntSize(size.width.toInt(), size.height.toInt()))
        }
    }
}