如何使用Jetpack Compose及Compose Multiplatform实现Android 13波浪滑块?
复现Android 13波浪进度条(Jetpack Compose/Compose Multiplatform)

Android 13在快速设置的媒体控件中引入了这款波浪形进度条,下面是在Jetpack Compose及Compose Multiplatform中复现的具体方案:
核心实现思路
通过Canvas自定义绘制波浪路径,结合Compose的动画API实现进度平滑过渡与波浪滚动效果,最终通过裁剪路径控制进度显示范围。
1. 自定义波浪进度条Composable
import androidx.compose.animation.core.* import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.* import androidx.compose.ui.unit.dp @Composable fun WaveProgressBar( progress: Float, // 进度值范围:0f~1f modifier: Modifier = Modifier, waveColor: Color = Color.Blue, backgroundColor: Color = Color.LightGray, barHeight: Float = 24.dp.toPx(), waveCount: Int = 4, waveAmplitude: Float = 8.dp.toPx(), waveSpeed: Float = 100f // 滚动速度(像素/秒) ) { // 进度平滑过渡动画 val animatedProgress by animateFloatAsState( targetValue = progress.coerceIn(0f, 1f), animationSpec = tween(durationMillis = 300), label = "Progress Animation" ) // 波浪无限滚动偏移动画 val waveOffset by animateFloatAsState( targetValue = 1f, animationSpec = infiniteRepeatable( animation = tween( durationMillis = (1000 * (1 / waveSpeed) * barHeight).toInt(), easing = LinearEasing ), repeatMode = RepeatMode.Restart ), label = "Wave Offset Animation" ) Canvas(modifier = modifier) { val canvasWidth = size.width val canvasHeight = size.height.coerceAtLeast(barHeight) val barTop = (canvasHeight - barHeight) / 2 val barBottom = barTop + barHeight // 绘制背景进度条 drawRoundRect( color = backgroundColor, topLeft = Offset(0f, barTop), size = Size(canvasWidth, barHeight), cornerRadius = CornerRadius(barHeight / 2) ) // 构建波浪路径 val wavePath = Path().apply { moveTo(-canvasWidth * waveOffset, barTop) val segmentWidth = canvasWidth / waveCount for (i in 0..waveCount) { val x = -canvasWidth * waveOffset + i * segmentWidth val controlY = if (i % 2 == 0) barTop - waveAmplitude else barTop + waveAmplitude val endY = barTop quadraticBezierTo( x + segmentWidth / 2, controlY, x + segmentWidth, endY ) } // 闭合路径,形成填充区域 lineTo(canvasWidth, barBottom) lineTo(-canvasWidth * waveOffset, barBottom) close() } // 裁剪出已完成进度的显示区域 val clipPath = Path().apply { addRoundRect( RoundRect( left = 0f, top = barTop, right = canvasWidth * animatedProgress, bottom = barBottom, cornerRadius = CornerRadius(barHeight / 2) ) ) } // 绘制带波浪的进度条 drawPath( path = wavePath, color = waveColor, style = Fill, clipPath = clipPath ) } }
2. 使用示例
import androidx.compose.foundation.layout.* import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @Composable fun WaveProgressDemo() { var progress by remember { mutableStateOf(0.3f) } Column( modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { WaveProgressBar( progress = progress, modifier = Modifier .width(300.dp) .height(32.dp), waveColor = Color(0xFF6200EE), backgroundColor = Color(0xFFE0E0E0) ) Spacer(modifier = Modifier.height(16.dp)) Row(spacing = 16.dp) { Button(onClick = { progress = (progress + 0.1f).coerceAtMost(1f) }) { Text("增加进度") } Button(onClick = { progress = (progress - 0.1f).coerceAtLeast(0f) }) { Text("减少进度") } } } }
关键细节说明
- 波浪生成:通过二次贝塞尔曲线
quadraticBezierTo绘制连续波浪,控制波峰波谷的位置和振幅 - 动画控制:使用
animateFloatAsState分别实现进度的平滑过渡,以及波浪的无限滚动效果 - 裁剪逻辑:通过
clipPath限制波浪的显示范围,只展示对应进度的区域 - 跨平台适配:Compose Multiplatform环境下可直接复用上述代码,无需额外修改(确保引入Compose UI依赖)
内容的提问来源于stack exchange,提问作者Mahozad
相关产品推荐
相关产品推荐

