当内置组件无法满足设计需求时,你需要使用 Canvas 进行自定义绘制。Compose 的 Canvas API 提供了强大而简洁的绘制能力,让你可以创建任意复杂的图形和动画效果。
一、Canvas 基础
Canvas Composable
@Composable
fun SimpleCanvas() {
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
) {
// this: DrawScope
// size: Size - 画布尺寸
// center: Offset - 画布中心点
drawCircle(
color = Color.Blue,
radius = 100f,
center = center
)
}
}
二、基础图形绘制
绘制矩形
Canvas(modifier = Modifier.size(300.dp)) {
// 填充矩形
drawRect(
color = Color.Blue,
topLeft = Offset(20f, 20f),
size = Size(200f, 100f)
)
// 描边矩形
drawRect(
color = Color.Red,
topLeft = Offset(20f, 140f),
size = Size(200f, 100f),
style = Stroke(width = 4f)
)
// 圆角矩形
drawRoundRect(
color = Color.Green,
cornerRadius = CornerRadius(16f)
)
}
绘制圆形和弧形
Canvas(modifier = Modifier.size(300.dp)) {
// 圆形
drawCircle(
color = Color.Blue,
radius = 80f,
center = Offset(100f, 100f)
)
// 弧线
drawArc(
color = Color.Red,
startAngle = 0f,
sweepAngle = 270f,
useCenter = false,
style = Stroke(width = 8f, cap = StrokeCap.Round)
)
}
三、Path 路径绘制
Path 是自定义绘制的核心,可以创建任意复杂的形状。
基本 Path 操作
Canvas(modifier = Modifier.size(300.dp)) {
val path = Path().apply {
moveTo(50f, 200f) // 移动到起点
lineTo(150f, 50f) // 直线到下一点
lineTo(250f, 200f)
close() // 闭合路径
}
drawPath(path = path, color = Color.Blue)
}
贝塞尔曲线
val path = Path().apply {
moveTo(50f, 200f)
// 二次贝塞尔曲线
quadraticBezierTo(
x1 = 150f, y1 = 0f, // 控制点
x2 = 250f, y2 = 200f // 终点
)
}
val cubicPath = Path().apply {
moveTo(50f, 350f)
// 三次贝塞尔曲线
cubicTo(
x1 = 100f, y1 = 200f, // 控制点1
x2 = 200f, y2 = 500f, // 控制点2
x3 = 250f, y3 = 350f // 终点
)
}
四、渐变与画刷
Canvas(modifier = Modifier.size(300.dp)) {
// 线性渐变
val linearGradient = Brush.linearGradient(
colors = listOf(Color.Red, Color.Yellow, Color.Green),
start = Offset.Zero,
end = Offset(size.width, size.height)
)
drawRect(brush = linearGradient)
// 径向渐变
val radialGradient = Brush.radialGradient(
colors = listOf(Color.Yellow, Color.Transparent),
center = center,
radius = size.minDimension / 2
)
drawCircle(brush = radialGradient)
}
五、实战:环形进度条
@Composable
fun CircularProgressIndicator(
progress: Float, // 0f - 1f
strokeWidth: Dp = 8.dp,
trackColor: Color = Color.LightGray,
progressColor: Color = Color.Blue
) {
Canvas(modifier = Modifier.size(100.dp)) {
val stroke = strokeWidth.toPx()
val radius = (size.minDimension - stroke) / 2
// 绘制轨道
drawCircle(
color = trackColor,
radius = radius,
style = Stroke(width = stroke)
)
// 绘制进度
drawArc(
color = progressColor,
startAngle = -90f,
sweepAngle = 360f * progress,
useCenter = false,
style = Stroke(width = stroke, cap = StrokeCap.Round)
)
}
}
六、Canvas 动画
@Composable
fun RotatingArc() {
val infiniteTransition = rememberInfiniteTransition(label = "rotation")
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing)
),
label = "rotation"
)
Canvas(modifier = Modifier.size(100.dp)) {
rotate(rotation) {
drawArc(
color = Color.Blue,
startAngle = 0f,
sweepAngle = 270f,
useCenter = false,
style = Stroke(width = 8f, cap = StrokeCap.Round)
)
}
}
}
七、性能优化
使用 drawWithCache
@Composable
fun CachedDrawing() {
Box(
modifier = Modifier
.size(200.dp)
.drawWithCache {
// 这里的计算只在尺寸变化时执行
val path = Path().apply { ... }
val brush = Brush.linearGradient(...)
onDrawBehind {
// 这里每帧执行,但使用缓存的对象
drawPath(path, brush)
}
}
)
}
💡 性能建议
避免在 Canvas lambda 中创建对象(Path、Brush 等),使用 remember 或 drawWithCache 缓存复杂计算。
总结
- Canvas Composable:提供 DrawScope,是自定义绘制的入口
- 基础图形:drawRect、drawCircle、drawLine、drawArc
- Path:创建任意复杂形状,支持贝塞尔曲线
- Brush:线性、径向、扫描渐变
- 动画:结合 animate*AsState 创建动态效果
- 性能:使用 drawWithCache 缓存复杂计算