You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中实现随时间变化的环形进度效果?

实现Jetpack Compose环形番茄钟进度条

我开发了一款番茄钟(Pomodoro)应用,想要实现一个随时间值变化的环形样式(不需要外圈圆点)。尝试用Jetpack Compose的Canvas和drawArc实现,但没成功。参考效果图为:浅灰色的完整环形底色,上方有一段彩色的进度环,进度环两端为圆角样式。

以下是我的测试代码(角度为演示用随机值):

Canvas(modifier.offset(y = 10.dp)) {
    drawArc(
        color = inactiveBarColor,
        startAngle = 90f,      
        sweepAngle = 90f,       
        useCenter = false,
        size = Size(size.width.toFloat(), size.height.toFloat()),
        style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)
   )
   drawArc(
        color = activeBarColor,
        startAngle = 0f,      
        sweepAngle = 90f,
        useCenter = false,
        size = Size(size.width.toFloat(), size.height.toFloat()),
        style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)
   )
}

问题分析与修复方案

你的代码存在几个关键问题:

  • 背景环仅绘制了90°,应该是完整的360°环形
  • 两个环形的起始角度不一致,导致错位
  • 未处理画布边界,可能出现环形圆角超出显示范围的情况

修复后的完整代码:

@Composable
fun PomodoroRing(
    progress: Float, // 进度值,范围0f-1f,0为无进度,1为满进度
    strokeWidth: Dp = 16.dp,
    inactiveColor: Color = Color.LightGray,
    activeColor: Color = Color(0xFF4CAF50)
) {
    Canvas(
        modifier = Modifier
            .size(200.dp)
            .padding(strokeWidth / 2) // 避免圆角超出画布
    ) {
        val canvasSize = size.minDimension
        val strokeWidthPx = strokeWidth.toPx()
        val arcSize = Size(canvasSize, canvasSize)
        val startAngle = 270f // 以12点钟方向作为环形起始点

        // 绘制背景环形
        drawArc(
            color = inactiveColor,
            startAngle = startAngle,
            sweepAngle = 360f,
            useCenter = false,
            size = arcSize,
            style = Stroke(strokeWidthPx, cap = StrokeCap.Round)
        )

        // 绘制动态进度环形
        val progressSweepAngle = progress * 360f
        drawArc(
            color = activeColor,
            startAngle = startAngle,
            sweepAngle = progressSweepAngle,
            useCenter = false,
            size = arcSize,
            style = Stroke(strokeWidthPx, cap = StrokeCap.Round)
        )
    }
}

// 组件使用示例
@Composable
fun PomodoroScreen() {
    var progress by remember { mutableStateOf(0.3f) } // 模拟30%的进度

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        PomodoroRing(progress = progress)
        Spacer(modifier = Modifier.height(24.dp))
        Button(onClick = { progress = (progress + 0.1f).coerceAtMost(1f) }) {
            Text("增加进度")
        }
    }
}

核心优化点

  • 统一起始角度:设置为270°对应12点钟方向,符合用户对环形进度的视觉习惯
  • 完整背景环:背景环绘制360°,确保底色是完整的环形
  • 画布内边距:通过padding(strokeWidth / 2)防止环形的圆角部分被画布裁剪
  • 进度动态映射:将0-1的进度值直接映射为0-360°的扫过角度,实现随时间变化的效果
  • 正圆形保证:使用size.minDimension确保环形始终为正圆,不受画布宽高影响

内容的提问来源于stack exchange,提问作者oolyvi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 00:13:13