许多应用程序需要能够精确控制屏幕上绘制的内容。这可能像在正确的位置在屏幕上放置一个方框或圆圈一样简单,也可能是在许多不同风格中精心排列的图形元素。
使用修饰符和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
,一个Size
对象,指定DrawScope
的当前尺寸。
要绘制某些内容,您可以使用DrawScope
上的许多绘图函数之一。例如,以下代码在屏幕的左上角绘制一个矩形
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }
要了解有关不同绘图修饰符的更多信息,请参阅图形修饰符文档。
坐标系
要在屏幕上绘制某些内容,您需要知道项目的偏移量(x
和y
)和大小。使用DrawScope
上的许多绘图方法,位置和大小由默认参数值提供。默认参数通常将项目定位在画布上的[0, 0]
点,并提供一个默认的size
,该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 px,向上移动 300 px
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
,并使用测量器调用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() )
文本现在在约束中绘制,末尾带有省略号
绘制图像
要使用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 中绘制的更多信息,请查看以下资源
- 图形修饰符 - 了解不同类型的绘制修饰符。
- 画刷 - 了解如何自定义内容的绘制。
- Compose 中的自定义布局和图形 - Android Dev Summit 2022 - 了解如何使用布局和图形在 Compose 中构建自定义 UI。
- JetLagged 示例 - 展示如何绘制自定义图形的 Compose 示例。
为您推荐
- 注意:当 JavaScript 关闭时,将显示链接文本。
- 图形修饰符
- Compose 中的图形
- Jetpack Compose 中的对齐线