许多应用都需要能够精确控制屏幕上所绘制的内容。这种控制有可能只是将方框或圆形放到屏幕上的正确位置,也有可能是精心布置许多不同样式的图形元素。
使用修饰符和 DrawScope
的基本绘制
在 Compose 中绘制自定义内容的核心方法是使用修饰符,例如 Modifier.drawWithContent
、Modifier.drawBehind
和 Modifier.drawWithCache
。
例如,如需在可组合项后面绘制内容,您可以使用 drawBehind
修饰符开始执行绘制命令:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
如果您只需使用可组合项进行绘制,可使用 Canvas
可组合项。Canvas
可组合项是一个包裹 Modifier.drawBehind
的便利封装容器。在布局中放置 Canvas
的方式与放置其他 Compose UI 元素相同。在 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 ) } }
边衬区
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 ) } }
常用绘制操作
绘制文本
如需在 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) } )
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() )
访问 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() )
了解更多内容
如需详细了解如何在 Compose 中绘制,请参阅以下资源:
- 图形修饰符 - 了解不同类型的绘制修饰符。
- Brush - 了解如何自定义内容的绘制方式。
- Compose 中的自定义布局和图形 - 2022 年 Android 开发者峰会:了解如何在 Compose 中使用布局和图形构建自定义界面。
- JetLagged 示例:显示了如何绘制自定义图表的 Compose 示例。
为您推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- 图形修饰符
- Compose 中的图形
- Jetpack Compose 中的对齐行