- Jetpack Compose 【一】入门:拥抱现代 Android UI 开发
- Jetpack Compose 【二】状态管理详解
- Jetpack Compose 【三】附带效应、协程与异步
- Jetpack Compose 【四】动画
- Jetpack Compose【五】 高级布局与绘制技巧
- Jetpack Compose【六】终极:声明式 UI 如何重塑开发者的思维
前言
在 Jetpack Compose 中,灵活的 UI 构建能力允许开发者以直观、声明式的方式创建界面。不仅如此,Compose 还提供了多个强大的 API 以支持自定义绘制和布局。本文将通过几个示例,展示如何在 Compose 中实现常见的自定义绘制与布局需求。
一、 使用 Canvas 自定义绘制
Canvas
是 Compose 中提供的低层次绘图 API,类似于传统的 onDraw()
方法。通过 drawRect()
、drawCircle()
、drawPath()
等方法,你可以绘制各种图形,满足自定义需求。
示例:绘制一个自定义圆形进度条
@Composable
fun CoolProgressBar(
progress: Float, // 进度 0f - 1f
modifier: Modifier = Modifier,
strokeWidth: Float = 20f, // 进度条宽度
startColor: Color = Color(0xFFFF5722), // 渐变起始色
endColor: Color = Color(0xFF2196F3) // 渐变结束色
) {
Canvas(modifier = modifier) {
val size = size.minDimension
val radius = size / 2f
val center = Offset(size / 2f, size / 2f)
// 绘制背景圆环
drawCircle(
color = Color.LightGray.copy(alpha = 0.3f),
radius = radius - strokeWidth / 2,
style = Stroke(width = strokeWidth, cap = StrokeCap.Round)
)
// 计算渐变颜色
val brush = Brush.linearGradient(
colors = listOf(startColor, endColor),
start = Offset(0f, 0f),
end = Offset(size, size)
)
// 计算进度角度
val sweepAngle = progress * 360f
// 绘制进度条
drawArc(
brush = brush,
startAngle = -90f,
sweepAngle = sweepAngle,
useCenter = false,
style = Stroke(width = strokeWidth, cap = StrokeCap.Round)
)
}
}
@Composable
fun CoolProgressBarDemo() {
var progress by remember { mutableFloatStateOf(0.3f) }
// 进度平滑动画
val animatedProgress by animateFloatAsState(
targetValue = progress,
animationSpec = tween(durationMillis = 1000, easing = FastOutSlowInEasing), label = ""
)
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
CoolProgressBar(
progress = animatedProgress,
modifier = Modifier
.size(150.dp)
.clip(CircleShape)
)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = { progress = Random.nextFloat().coerceIn(0.1f, 1f)}) {
Text("随机进度")
通过 Canvas
,我们可以绘制一个带有背景圆环和动态进度的圆形进度条。drawArc()
让进度条根据传入的 progress
绘制。
二、 使用 Layout 自定义布局
Compose 中,Row
、Column
和 Box
可能无法满足所有布局需求。此时,可以使用 Layout
API 创建复杂的自定义布局。Layout
像是传统View中onMeasure()
+onLayout()
的结合。
示例:实现一个流式布局
@Composable
fun FlowLayout(
modifier: Modifier = Modifier,
maxWidth: Dp = 300.dp, // 限制整体宽度
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
// 将 maxWidth 转换为 Px,并与父布局宽度取最小值,确保不超出
val layoutMaxWidth = minOf(maxWidth.roundToPx(), constraints.maxWidth)
// 测量所有子项
val placeables = measurables.map { it.measure(constraints.copy(maxWidth = layoutMaxWidth)) }
var currentX = 0
var currentY = 0
var maxHeightInRow = 0
layout(layoutMaxWidth, constraints.maxHeight) {
placeables.forEach { placeable ->
// 超出 maxWidth 时换行
if (currentX + placeable.width > layoutMaxWidth) {
currentX = 0
currentY += maxHeightInRow
maxHeightInRow = 0
}
placeable.placeRelative(currentX, currentY)
currentX += placeable.width
maxHeightInRow = maxOf(maxHeightInRow, placeable.height)
}
}
}
}
@Composable
fun TestFlowLayout() {
FlowLayout(maxWidth = 320.dp) {
FlowText("MyOwnColumn")
FlowText("places items")
FlowText("vertically.")
FlowText("We've done it by hand!")
FlowText("final")
}
}
@Composable
fun FlowText(text: String) {
Text(
text = text,
modifier = Modifier
.padding(8.dp)
.border(1.dp, Color.Gray)
.padding(8.dp)
)
}
通过 Layout
API,可以控制每个元素的尺寸和位置。这里的布局会将每个标签垂直排列,形成一个简单的标签列表。
三、 使用 Modifier.drawWithContent 定制绘制
如果你需要在现有组件上添加额外的绘制效果(如边框或渐变效果),可以使用 Modifier.drawWithContent
。
示例:为文本添加下划线
@Composable
fun UnderlinedText(text: String) {
Text(
text = text,
modifier = Modifier.drawWithContent {
drawContent() // 先绘制内容
drawLine(
color = Color.Red,
start = Offset(0f, size.height),
end = Offset(size.width, size.height),
strokeWidth = 2f
)
}
)
}
@Preview
@Composable
fun UnderlinedTextPreview() {
UnderlinedText("带下划线的文本")
}
drawWithContent
用来在绘制文本的基础上,为其添加一条红色的下划线。
四、 使用 AndroidView 复用传统 View
通过 AndroidView
,你可以将传统的 View
嵌入 Compose 布局中,复用 XML 布局中已有的自定义视图。
示例:在 Compose 中嵌入 传统View
@Composable
fun WebViewComponent(url: String) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
//WebView、SurfaceView、其它自定义View
}
)
}
五、总结
自定义方式 | 适用场景 | 核心 API |
---|---|---|
Canvas | 自定义图形、控件 | drawCircle() 、drawPath() |
Layout | 自定义布局 | Layout 、MeasureScope |
drawWithContent | 叠加效果 | drawLine() 、drawRect() |
AndroidView | 复用传统 View | AndroidView |
Compose 提供了灵活的自定义扩展能力,可以满足大多数 UI 设计需求。根据实际情况选择合适的自定义方式,能够帮助你轻松实现多种复杂的 UI 效果。