Jetpack Compose 学习笔记: 尝试自定义一个Compose版的Seekbar(滑动进度条)
效果图
组件的构成
- 一个显示百分比的文本
- 两根重叠的直线: 一根在底部作为总进度, 一根在顶部作为当前进度
- 一个进度锚点: 用于响应滑动以及指示当前进度, 滑动时‘平滑’地变大
第一步的文本可以直接使用Text, 后两步使用Canvas绘制, 但自定义绘制也只用到drawLine和drawCircle, 整个组件并不复杂
直接上代码
@Composable
fun CustomSeekbar(
modifier: Modifier,
onProgressChanged: (progress: Float) -> Unit
) {
// 当前进度,范围0-1之间, 初始为0
var progress by remember { mutableStateOf(0f) }
// bar是否被按下
var barPressed by remember { mutableStateOf(false) }
// 锚点的半径, 根据barPressed的状态'平滑'地改变自身的大小
val radius by animateFloatAsState(if (barPressed) 30f else 20f)
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically
) {
// 进度的文本
Text(text = (progress * 100).toInt().toString(), Modifier.width(30.dp))
Canvas(
modifier = Modifier
.height(30.dp)
.fillMaxWidth()
.weight(1f)
.padding(10.dp)
.pointerInput(Unit) {
detectDragGestures( // 响应滑动事件
onDragStart = { barPressed = true },
onDragCancel = { barPressed = false },
onDragEnd = {
// 滑动结束时, 恢复锚点大小,并回调onProgressChanged函数
barPressed = false
onProgressChanged.invoke(progress)
},
onDrag = { change, dragAmount ->
// 滑动过程中, 实时刷新progress的值(注意左右边界的问题),
// 此值一旦改变, 整个Seekbar就会重组(刷新)
progress = if (change.position.x < 0) {
0f
} else if (change.position.x > size.width) {
1f
} else {
(change.position.x / this.size.width)
}
})
}
.pointerInput(Unit) {
// 响应点击事件, 直接跳到该进度处
detectTapGestures(onTap = {
progress = (it.x / size.width)
barPressed = false
})
},
onDraw = {
// 底部灰色线条
drawLine(
color = Color.Gray.copy(alpha = 0.5f),
start = Offset(0f, size.height / 2),
end = Offset(size.width, size.height / 2),
strokeWidth = 8f
)
// 顶部蓝色线条
drawLine(
color = Color.Blue,
start = Offset(0f, size.height / 2),
end = Offset(size.width * progress, size.height / 2),
strokeWidth = 12f
)
// 锚点
drawCircle(
color = Color.Blue,
radius = radius,
center = Offset(size.width * progress, size.height / 2)
)
})
}
}
@Preview(showBackground = true)
@Composable
fun previewCustomSeekbar() {
// 预览该composable组件
CustomSeekbar(
modifier = Modifier
.fillMaxWidth()
.background(color = Color.Gray.copy(alpha = 0.1f))
.padding(horizontal = 20.dp),
) {
Log.d("TAG", "seekbar: onProgressChanged=$it")
}
}
运行测试
直接复制以上代码, 添加到其他
composable组件内(如column), 即可运行看到效果