许多应用需要能够精确控制屏幕上绘制的内容。这可能小到在屏幕上恰到好处地放置一个方框或圆形,也可能是在许多不同样式中精心安排图形元素。
使用修饰符和 DrawScope 进行基本绘制
在 Compose 中绘制自定义内容的核心方法是使用修饰符,例如 Modifier.drawWithContent、Modifier.drawBehind 和 Modifier.drawWithCache。
例如,要在您的可组合项后面绘制内容,您可以使用 drawBehind 修饰符来开始执行绘制命令
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
如果您只需要一个可绘制的可组合项,您可以使用 Canvas 可组合项。Canvas 可组合项是对 Modifier.drawBehind 的便捷封装。您可以像放置任何其他 Compose UI 元素一样,在布局中放置 Canvas。在 Canvas 中,您可以精确控制元素的样式和位置来绘制它们。
所有绘制修饰符都公开一个 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 值增大。
例如,如果您想从画布区域的右上角到左下角绘制一条对角线,您可以使用 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 ) } }
rotate() 将旋转应用于当前绘制范围,这将使矩形旋转 45 度。内嵌
使用 DrawScope.inset() 调整当前 DrawScope 的默认参数,更改绘制边界并相应地平移绘制内容。
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
此代码有效地为绘制命令添加了内边距。
多重转换
要对您的绘制内容应用多重转换,请使用 DrawScope.withTransform() 函数,该函数创建并应用一个组合了所有所需更改的单一转换。使用 withTransform() 比对单个转换进行嵌套调用更高效,因为所有转换都在一个操作中一起执行,而不是 Compose 需要计算和保存每个嵌套转换。
例如,以下代码对矩形同时应用平移和旋转。
Canvas(modifier = Modifier.fillMaxSize()) { withTransform({ translate(left = size.width / 5F) rotate(degrees = 45F) }) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
withTransform 同时应用旋转和平移,旋转矩形并将其向左移动。常见绘制操作
绘制文本
在 Compose 中绘制文本,通常可以使用 Text 可组合项。但是,如果您在 DrawScope 中或想手动自定义绘制文本,可以使用 DrawScope.drawText() 方法。
要绘制文本,请使用 rememberTextMeasurer 创建一个 TextMeasurer,然后使用该测量器调用 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。例如,以下代码将文本渲染为可组合区域高度的 ⅓ 和宽度的 ⅓,并将 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() )
文本现在在约束中绘制,末尾带有省略号。
TextOverflow.Ellipsis 在测量文本时具有固定约束。绘制图像
要使用 DrawScope 绘制 ImageBitmap,请使用 ImageBitmap.imageResource() 加载图像,然后调用 drawImage。
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })
ImageBitmap。绘制基本形状
DrawScope 上有许多形状绘制函数。要绘制形状,请使用其中一个预定义的绘制函数,例如 drawCircle。
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
输出 |
|
|
|
|
|
|
|
|
|
|
|
|
|
绘制路径
路径是一系列数学指令,执行后会生成一个图形。DrawScope 可以使用 DrawScope.drawPath() 方法绘制路径。
例如,假设您想绘制一个三角形。您可以使用绘图区域的大小,通过 lineTo() 和 moveTo() 等函数生成一条路径。然后,使用此新创建的路径调用 drawPath() 以获得一个三角形。
Spacer( modifier = Modifier .drawWithCache { val path = Path() path.moveTo(0f, 0f) path.lineTo(size.width / 2f, size.height / 2f) path.lineTo(size.width, 0f) path.close() onDrawBehind { drawPath(path, Color.Magenta, style = Stroke(width = 10f)) } } .fillMaxSize() )
Path。访问 Canvas 对象
使用 DrawScope,您无法直接访问 Canvas 对象。您可以使用 DrawScope.drawIntoCanvas() 获取对 Canvas 对象本身的访问权限,您可以在其上调用函数。
例如,如果您有一个自定义的 Drawable 想绘制到画布上,您可以访问画布并调用 Drawable#draw(),传入 Canvas 对象。
val drawable = ShapeDrawable(OvalShape()) Spacer( modifier = Modifier .drawWithContent { drawIntoCanvas { canvas -> drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt()) drawable.draw(canvas.nativeCanvas) } } .fillMaxSize() )
Drawable。了解详情
有关 Compose 中绘制的更多信息,请查看以下资源:
- 图形修饰符 - 了解不同类型的绘制修饰符。
- 画笔 - 了解如何自定义内容的绘制。
- Compose 中的自定义布局和图形 - Android 开发者峰会 2022 - 了解如何在 Compose 中使用布局和图形构建自定义 UI。
- JetLagged 示例 - 显示如何绘制自定义图表的 Compose 示例。
为您推荐
- 注意:当 JavaScript 关闭时显示链接文本
- 图形修饰符
- Compose 中的图形
- Jetpack Compose 中的对齐线