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

如何在Jetpack Compose中实现HH:MM:SS倒计时并在LazyColumn显示

Jetpack Compose 简易倒计时实现

核心逻辑拆分

  • 状态管理:用Compose内置的状态持有类存储输入值、剩余时长、运行标记,绑定页面生命周期的协程作用域跑计时任务,避免内存泄漏。
  • 输入处理:三个独立输入框分别接收小时、分钟、秒参数,做输入过滤,仅允许数字输入,同时做范围限制:小时上限23,分钟、秒上限59,拦截非法值。
  • 计时实现:启动时将输入的三类时间参数统一转换为总秒数,在协程中以1秒为间隔逐次扣减剩余时长,直到剩余时长为0时自动停止计时、更新运行状态。
  • 格式转换:剩余总秒数通过除法、取模运算拆分出时、分、秒三段数值,用字符串格式化补前导零,直接输出HH:MM:SS格式的展示文本。
  • 列表承载:输入模块、操作按钮、倒计时展示内容全部作为LazyColumn的列表项插入即可,Compose状态机制会自动追踪依赖,仅更新时间文本对应的节点,不会触发整个列表重绘。

可直接运行的核心代码

@Composable
fun CountDownTimerScreen() {
    // 状态声明
    var hourInput by remember { mutableStateOf("") }
    var minuteInput by remember { mutableStateOf("") }
    var secondInput by remember { mutableStateOf("") }
    var remainSeconds by remember { mutableLongStateOf(0L) }
    var isTimerRunning by remember { mutableStateOf(false) }
    val coroutineScope = rememberCoroutineScope()

    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 16.dp, vertical = 24.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 时间输入区域
        item {
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                // 小时输入框
                OutlinedTextField(
                    value = hourInput,
                    onValueChange = { input ->
                        val filtered = input.filter { it.isDigit() }
                        val numVal = filtered.toIntOrNull() ?: 0
                        hourInput = if (numVal > 23) "23" else filtered
                    },
                    label = { Text("小时") },
                    modifier = Modifier.weight(1f),
                    singleLine = true
                )
                // 分钟输入框
                OutlinedTextField(
                    value = minuteInput,
                    onValueChange = { input ->
                        val filtered = input.filter { it.isDigit() }
                        val numVal = filtered.toIntOrNull() ?: 0
                        minuteInput = if (numVal > 59) "59" else filtered
                    },
                    label = { Text("分钟") },
                    modifier = Modifier.weight(1f),
                    singleLine = true
                )
                // 秒输入框
                OutlinedTextField(
                    value = secondInput,
                    onValueChange = { input ->
                        val filtered = input.filter { it.isDigit() }
                        val numVal = filtered.toIntOrNull() ?: 0
                        secondInput = if (numVal > 59) "59" else filtered
                    },
                    label = { Text("秒") },
                    modifier = Modifier.weight(1f),
                    singleLine = true
                )
            }
        }

        // 启动按钮
        item {
            Button(
                onClick = {
                    if (isTimerRunning) return@Button
                    // 计算总时长
                    val totalSec = (hourInput.toLongOrNull() ?: 0) * 3600 +
                            (minuteInput.toLongOrNull() ?: 0) * 60 +
                            (secondInput.toLongOrNull() ?: 0)
                    if (totalSec <= 0) return@Button
                    remainSeconds = totalSec
                    isTimerRunning = true
                    // 启动计时协程
                    coroutineScope.launch {
                        while (remainSeconds > 0 && isTimerRunning) {
                            delay(1000)
                            remainSeconds--
                        }
                        isTimerRunning = false
                    }
                },
                modifier = Modifier.fillMaxWidth(),
                enabled = !isTimerRunning
            ) {
                Text(if (isTimerRunning) "倒计时进行中" else "启动倒计时")
            }
        }

        // 倒计时展示
        item {
            val displayH = remainSeconds / 3600
            val displayM = (remainSeconds % 3600) / 60
            val displayS = remainSeconds % 60
            val formatTime = String.format("%02d:%02d:%02d", displayH, displayM, displayS)
            Text(
                text = formatTime,
                style = MaterialTheme.typography.displayLarge,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 32.dp)
                    .wrapContentWidth(Alignment.CenterHorizontally)
            )
        }
    }
}

实现注意事项

  • 不要自定义全局协程作用域跑计时任务,rememberCoroutineScope()返回的作用域和当前Composable生命周期绑定,页面退出时会自动取消协程,不会产生内存泄漏。
  • 如果需要扩展暂停、重置功能,只需要新增对应操作逻辑:暂停时将isTimerRunning设为false即可中断协程循环,重置时将remainSeconds置0、清空输入值、更新运行状态即可。
  • 如果需要更顺滑的毫秒级倒计时效果,可将总时长单位换为毫秒,delay时长改为100ms,格式化时取整到秒即可,不会有明显性能损耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:24:12