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

Jetpack Compose中如何实现带冒号的时间输入键盘?

在Jetpack Compose中实现带冒号的时间输入TextField的几种方式

方法1:自定义VisualTransformation + 输入逻辑处理

通过自定义VisualTransformation自动插入冒号,同时在onValueChange里控制输入合法性(仅允许数字、限制长度、处理冒号的插入与删除),确保输入始终符合HH:mm格式。

示例代码:

@Composable
fun TimeInputTextField(
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier
) {
    TextField(
        value = value,
        onValueChange = { newText ->
            // 过滤所有非数字字符
            val filteredDigits = newText.filter { it.isDigit() }
            // 根据输入长度格式化文本
            val formattedTime = when (filteredDigits.length) {
                1 -> filteredDigits
                2 -> "$filteredDigits:"
                in 3..4 -> "${filteredDigits.substring(0,2)}:${filteredDigits.substring(2)}"
                else -> filteredDigits.take(4).let { "${it.substring(0,2)}:${it.substring(2)}" }
            }
            onValueChange(formattedTime)
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        visualTransformation = VisualTransformation { text ->
            val rawDigits = text.text.filter { it.isDigit() }
            val displayText = when (rawDigits.length) {
                1 -> rawDigits
                2 -> "$rawDigits:"
                in 3..4 -> "${rawDigits.substring(0,2)}:${rawDigits.substring(2)}"
                else -> rawDigits.take(4).let { "${it.substring(0,2)}:${it.substring(2)}" }
            }
            TransformedText(AnnotatedString(displayText), OffsetMapping.Identity)
        },
        singleLine = true,
        maxLines = 1,
        modifier = modifier
    )
}

方法2:自定义InputFilter控制格式

通过InputFilter直接拦截输入,限制长度、过滤非数字并自动插入冒号,无需在onValueChange里重复处理逻辑。

示例代码:

class TimeInputFilter : InputFilter {
    override fun filter(
        source: CharSequence?,
        start: Int,
        end: Int,
        dest: Spanned?,
        dstart: Int,
        dend: Int
    ): CharSequence? {
        val newInput = (dest?.toString() ?: "").replaceRange(dstart, dend, source ?: "")
        val digitsOnly = newInput.filter { it.isDigit() }
        
        val formatted = when (digitsOnly.length) {
            1 -> digitsOnly
            2 -> "$digitsOnly:"
            in 3..4 -> "${digitsOnly.substring(0,2)}:${digitsOnly.substring(2)}"
            else -> digitsOnly.take(4).let { "${it.substring(0,2)}:${it.substring(2)}" }
        }
        
        return if (formatted != newInput) formatted else null
    }
}

@Composable
fun TimeInputWithFilter() {
    var time by remember { mutableStateOf("") }
    TextField(
        value = time,
        onValueChange = { time = it },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        singleLine = true,
        maxLines = 1,
        filters = arrayOf(TimeInputFilter())
    )
}

方法3:结合TimePicker弹窗(替代手动输入)

如果允许用户通过弹窗选择时间而非手动输入,用Compose自带的TimePickerDialog更省心,还能避免输入非法值:

示例代码:

@Composable
fun TimePickerTextField() {
    var time by remember { mutableStateOf("") }
    var showPicker by remember { mutableStateOf(false) }

    TextField(
        value = time,
        onValueChange = {}, // 禁用手动输入
        enabled = false,
        trailingIcon = {
            IconButton(onClick = { showPicker = true }) {
                Icon(Icons.Default.Schedule, contentDescription = "选择时间")
            }
        }
    )

    if (showPicker) {
        val initialTime = LocalTime.now()
        var selectedTime by remember { mutableStateOf(initialTime) }

        TimePickerDialog(
            onDismissRequest = { showPicker = false },
            confirmButton = {
                TextButton(onClick = {
                    time = "${selectedTime.hour.toString().padStart(2, '0')}:${selectedTime.minute.toString().padStart(2, '0')}"
                    showPicker = false
                }) {
                    Text("确认")
                }
            },
            dismissButton = {
                TextButton(onClick = { showPicker = false }) {
                    Text("取消")
                }
            },
            timePicker = {
                TimePicker(state = rememberTimePickerState(
                    initialHour = initialTime.hour,
                    initialMinute = initialTime.minute
                ).apply {
                    selectedTime = LocalTime.of(hour, minute)
                })
            }
        )
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:02:56