Jetpack Compose 图形

1,346 阅读4分钟

使用修饰符和 DrawScope 的基本绘制

在 Compose 中绘制自定义内容的核心方法是使用修饰符,例如 [Modifier.drawWithContent]、[Modifier.drawBehind] 和 [Modifier.drawWithCache]。

例如,如需在可组合项后面绘制内容,您可以使用 drawBehind 修饰符开始执行绘制命令:

Spacer(
    modifier = Modifier
        .fillMaxSize()
        .drawBehind {
            // this = DrawScope
        }
)

所有绘制修饰符都会提供 [DrawScope](一个维护自身状态且限定了作用域的绘制环境),可供您为一组图形元素设置参数。DrawScope 提供了几个有用的字段,例如 size(用于指定 DrawScope 的当前尺寸的 Size 对象)。

如需绘制内容,您可以使用 DrawScope 上的众多绘制函数之一。例如,以下代码会在屏幕的左上角绘制一个矩形:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    drawRect(
        color = Color.Magenta,
        size = canvasQuadrantSize
    )
}

坐标系

如需在屏幕上绘制内容,您需要知道相应项目的偏移量(x 和 y)和大小。对于 DrawScope 上的许多绘制方法,位置和尺寸由[默认参数值]提供。默认参数通常会将项目放置在画布的 [0, 0] 点上,并提供填充整个绘制区域的默认 size,如上例所示,您可以看到矩形被放置在左上角。若要调整项目的尺寸和位置,您需要了解 Compose 中的坐标系。

坐标系的原点 ([0,0]) 位于绘制区域最左上角的像素处。x 会随着向右移动而增加,y 则会随着向下移动而增加。

image.png

例如,如果想要绘制一条从画布区域右上角到左下角的对角线,您可以使用 [DrawScope.drawLine()] 函数,并指定具有对应 x 和 y 位置的起始和结束偏移量:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height
    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

基本转换

DrawScope 会提供一些用于更改绘制命令执行位置或方式的转换。

缩放

[DrawScope.scale()]可用于按系数来增加绘制操作的大小。scale() 之类的操作适用于相应 lambda 中的所有绘图操作。例如,以下代码会将 scaleX 的大小增加 10 倍,并将 scaleY 的大小增加 15 倍:

Canvas(modifier = Modifier.fillMaxSize()) {
    scale(scaleX = 10f, scaleY = 15f) {
        drawCircle(Color.Blue, radius = 20.dp.toPx())
    }
}

平移

[DrawScope.translate()] 可用于向上、向下、向左或向右移动绘制操作。例如,以下代码会将绘制操作向右移动 100 像素,同时向上移动 300 像素:

Canvas(modifier = Modifier.fillMaxSize()) {
    translate(left = 100f, top = -300f) {
        drawCircle(Color.Blue, radius = 200.dp.toPx())
    }
}

旋转

[DrawScope.rotate()] 可用于围绕某个轴心点旋转绘制操作。例如,以下代码会将矩形旋转 45 度:

Canvas(modifier = Modifier.fillMaxSize()) {
    rotate(degrees = 45F) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

常用绘制操作

绘制文本

如需在 Compose 中绘制文本,您一般可以使用 Text 可组合项。不过,如果您使用的是 DrawScope,或想通过自定义设置手动绘制文本,则可以使用 [DrawScope.drawText()] 方法。

如需绘制文本,请使用 [rememberTextMeasurer] 创建 [TextMeasurer],然后使用 Measurer 调用 drawText

val textMeasurer = rememberTextMeasurer()

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Hello")
}

测量文本

绘制文本的方式与其他绘制命令略有不同。通常情况下,您会在绘制命令中指定绘制形状/图片所需的尺寸(宽度和高度)。在绘制文本时,您可以通过几个参数来控制所呈现文本的大小,例如字体大小、字体、连字和字母间距。

在 Compose 中,您可以使用 [TextMeasurer] 来获取根据上述因素测量的文本大小。如果您想在文本后面绘制背景,可以通过测量的信息获知文本所占区域的大小:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()),
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

上述代码段会在文本上生成粉色背景:

如果调整约束条件、字体大小或任何会影响测量尺寸的属性,系统就会报告新的尺寸。您可以为 width 和 height 设置固定大小,然后文本会遵循设定的 [TextOverflow]。例如,以下代码会在可组合项区域的 1⁄3 高度和 1⁄3 宽度内呈现文本,并将 TextOverflow 设置为 TextOverflow.Ellipsis

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixed(
                        width = (size.width / 3f).toInt(),
                        height = (size.height / 3f).toInt()
                    ),
                    overflow = TextOverflow.Ellipsis,
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

文本现在按照约束条件绘制,末尾带有省略号:

绘制图片

如需使用 DrawScope 绘制 [ImageBitmap],请使用 ImageBitmap.imageResource() 加载图片,然后调用 drawImage

val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)

Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
    drawImage(dogImage)
})

绘制基本形状

DrawScope 上有许多形状绘制函数。如需绘制形状,请使用其中一个预定义的绘制函数,例如 drawCircle

val purpleColor = Color(0xFFBA68C8)
Canvas(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
    onDraw = {
        drawCircle(purpleColor)
    }
)
CanvasSnippets.kt
API输出
drawCircle()
drawRect()
drawRoundedRect()
drawLine()
drawOval()
drawArc()
drawPoints()

上一篇 Jetpack Compose 图标

下一篇 Jackpack Compose 图片合成