在 Jetpack Compose 中绘制自定义图形,最核心的方式是使用 Canvas 可组合项 (Composable)。Canvas 提供了一个二维绘图环境,你可以在其中使用各种绘制命令来创建你想要的任何视觉效果。
以下是如何在 Compose 中进行自定义绘制的步骤和关键概念:
1. 使用 Canvas Composable:
Canvas 是一个 Composable 函数,它接收一个 modifier 来定义其尺寸(非常重要,否则画布没有大小),以及一个 onDraw lambda 函数。所有的绘制操作都在这个 onDraw lambda 的 DrawScope 上下文中执行。
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color // 导入 Color
import androidx.compose.ui.graphics.drawscope.DrawScope // 导入 DrawScope
import androidx.compose.ui.tooling.preview.Preview
@Composable
fun MyCustomDrawing() {
Canvas(
modifier = Modifier.fillMaxSize() // 给 Canvas 指定大小,这里填满父布局
) {
// 所有的绘制代码都写在这里
// 'this' 指向 DrawScope
drawRect(color = Color.Blue) // 示例:绘制一个填满画布的蓝色矩形
}
}
@Preview(showBackground = true)
@Composable
fun PreviewMyCustomDrawing() {
MyCustomDrawing()
}
2. 理解 DrawScope:
onDraw lambda 的接收者类型是 DrawScope。它提供了:
-
绘图函数: 如
drawLine,drawRect,drawCircle,drawOval,drawArc,drawPath,drawImage,drawText等。 -
画布信息: 最重要的是
size属性,它是一个Size对象,包含画布的width和height。你可以基于这个size来进行相对定位和尺寸计算。 -
变换操作: 如
translate,rotate,scale,inset等,可以改变绘制坐标系。
3. 常用的绘制函数 (DrawScope 内):
-
绘制线条:
-
drawLine( color = Color.Red, start = Offset(x = 0f, y = 0f), // 起点坐标 (x, y) end = Offset(x = size.width, y = size.height), // 终点坐标 strokeWidth = 5f // 线条宽度 ) -
绘制矩形:
-
drawRect( color = Color.Green, topLeft = Offset(x = size.width / 4, y = size.height / 4), // 左上角坐标 size = Size(width = size.width / 2, height = size.height / 2) // 矩形尺寸 // style = Fill // 默认是填充 (Fill) ) // 绘制描边矩形 drawRect( color = Color.Black, topLeft = Offset(x = 10f, y = 10f), size = Size(width = 100f, height = 50f), style = androidx.compose.ui.graphics.drawscope.Stroke(width = 3f) // 使用 Stroke 进行描边 ) -
绘制圆形:
-
drawCircle( color = Color.Magenta, radius = size.minDimension / 4, // 半径,取宽高最小值的一半 center = center // center 是 DrawScope 提供的画布中心 Offset // style = Fill // 默认填充 ) -
绘制椭圆:
-
drawOval( color = Color.Yellow, topLeft = Offset(x = 50f, y = 200f), size = Size(width = 200f, height = 100f) // 定义椭圆的外切矩形 ) -
绘制圆弧:
-
drawArc( color = Color.Cyan, startAngle = 0f, // 起始角度 (0度是3点钟方向) sweepAngle = 180f, // 扫过的角度 (顺时针) useCenter = true, // 是否连接到圆心,形成扇形 (true) 或弓形 (false) topLeft = Offset(x = size.width * 0.1f, y = size.height * 0.6f), size = Size(width = size.width * 0.8f, height = size.width * 0.8f) // 圆弧所在椭圆的外切矩形 )
4. 使用 Path 绘制复杂图形:
对于不规则或复杂的形状,使用 Path。Path 对象允许你定义一系列的线段、曲线等来构建形状。
import androidx.compose.ui.graphics.Path
// ... 在 Canvas 的 onDraw 中 ...
val path = Path().apply {
moveTo(x = size.width / 2, y = 0f) // 移动到起点
lineTo(x = size.width, y = size.height / 2) // 画线到...
lineTo(x = size.width / 2, y = size.height)
lineTo(x = 0f, y = size.height / 2)
close() // 闭合路径 (连接起点和终点)
}
drawPath(
path = path,
color = Color.DarkGray
)
Path 支持 moveTo, lineTo, quadraticBezierTo (二次贝塞尔曲线), cubicTo (三次贝塞尔曲线), addRect, addOval 等多种命令。
5. 使用 Brush (画笔) 实现渐变等效果:
除了纯色 (Color),你还可以使用 Brush 来填充或描边,创建渐变效果。
import androidx.compose.ui.graphics.Brush
// ... 在 Canvas 的 onDraw 中 ...
val gradientBrush = Brush.linearGradient(
colors = listOf(Color.Red, Color.Blue),
start = Offset.Zero, // 渐变起始点
end = Offset(x = size.width, y = size.height) // 渐变结束点
)
drawCircle(
brush = gradientBrush, // 使用画笔代替纯色
radius = size.minDimension / 3,
center = center
)
val radialBrush = Brush.radialGradient(
colors = listOf(Color.Yellow, Color.Transparent),
center = center,
radius = size.minDimension / 5
)
drawRect(brush = radialBrush)
还有 Brush.radialGradient (径向渐变) 和 Brush.sweepGradient (扫描渐变)。
6. 结合状态和交互:
- 响应状态变化: 如果你的绘图依赖于某些状态(例如,滑块位置、按钮点击次数),将这些状态用
remember { mutableStateOf(...) }或通过 ViewModel 管理。当状态改变时,Compose 会触发重组 (recomposition),Canvas的onDraw会重新执行,使用新的状态值进行绘制。 - 添加交互: 你可以在
Canvas上应用Modifier.pointerInput来检测触摸事件(点击、拖动等),然后更新状态,从而动态地改变绘制内容。
示例:绘制一个简单的同心圆
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun ConcentricCircles() {
Canvas(modifier = Modifier.size(200.dp)) { // 固定大小的 Canvas
val canvasWidth = size.width
val canvasHeight = size.height
val center = Offset(x = canvasWidth / 2, y = canvasHeight / 2)
val strokeWidth = 10f
// 外圆 (红色描边)
drawCircle(
color = Color.Red,
center = center,
radius = size.minDimension / 2 - strokeWidth / 2, // 考虑描边宽度
style = Stroke(width = strokeWidth)
)
// 中圆 (蓝色描边)
drawCircle(
color = Color.Blue,
center = center,
radius = size.minDimension / 3 - strokeWidth / 2,
style = Stroke(width = strokeWidth)
)
// 内圆 (绿色填充)
drawCircle(
color = Color.Green,
center = center,
radius = size.minDimension / 6
// style = Fill // 默认填充
)
}
}
@Preview(showBackground = true)
@Composable
fun PreviewConcentricCircles() {
ConcentricCircles()
}
总结:
使用 Canvas 可组合项是 Jetpack Compose 中进行自定义图形绘制的主要方式。通过理解 DrawScope 提供的绘图函数、画布信息以及结合 Path 和 Brush,你可以创建出丰富多样的自定义视觉效果。记住,始终要为 Canvas 提供一个 Modifier 来确定其尺寸。