限制OutlinedTextField输入为两位数字的自定义键盘问题
问题:自定义键盘下Jetpack Compose数字输入位数限制失效
我使用Jetpack Compose的OutlinedTextField实现用户数字输入功能,要求限制为两位数字。但使用自定义键盘时,正则表达式限制失效,可输入多位数字;启用系统软键盘时,该限制可正常生效。
相关代码如下:
val pattern = Regex("^\d{1,2}$") OutlinedTextField( enabled = false, colors = TextFieldDefaults.outlinedTextFieldColors(disabledTextColor = Color.Black, cursorColor = Color.Transparent), shape = RectangleShape, value = viewModel.text.largestDigit.value, onValueChange = { if (it.isEmpty() || it.matches(pattern)) viewModel.text.largestDigit.value = it }, modifier = Modifier .width(IntrinsicSize.Min) .padding(horizontal = 1.dp), textStyle = TextStyle(fontSize = 25.sp, textAlign = TextAlign.Center), label = { Text( text = "Enter maximum number", modifier = Modifier.fillMaxWidth(), fontSize = 15.sp, textAlign = TextAlign.Center ) } ) @Composable fun Keyboard(onKeyPressed: (String) -> Unit) { val keys = listOf("1", "2", "3", "4", "5", "6", "7", "8", "9" , "<-", "0", "Enter" ) LazyVerticalGrid( modifier = Modifier .fillMaxWidth() .padding(bottom = 30.dp) .border(BorderStroke(1.dp, Color.Black)), columns = GridCells.Fixed(3), state = LazyGridState(), verticalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center ) { items(12) { index -> ElevatedCard( modifier = Modifier .fillMaxWidth() .wrapContentWidth() .wrapContentHeight() .border(BorderStroke(1.dp, Color.Black)) .clickable( onClick = { onKeyPressed(keys[index]) }, ), shape = RectangleShape, colors = CardDefaults.elevatedCardColors(containerColor = Color.Green), elevation = CardDefaults.cardElevation(), ) { Text( text = keys[index], modifier = Modifier .fillMaxWidth() .requiredHeight(50.dp), fontSize = 30.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center ) } } } } Keyboard(onKeyPressed = { key -> when (key) { "<-" -> {viewModel.text.largestDigit.value = viewModel.text.largestDigit.value.dropLast(1)} "Enter" -> { TODO() } else -> {viewModel.text.largestDigit.value = viewModel.text.largestDigit.value + key[viewModel.answer.index.value]} } })
问题原因
- 系统键盘输入会触发OutlinedTextField的
onValueChange回调,这里的正则校验逻辑会生效,从而限制输入长度; - 自定义键盘是直接修改ViewModel中的
largestDigit值,完全绕过了OutlinedTextField的校验逻辑,导致正则限制根本没被执行。
解决思路
方案1:在自定义键盘逻辑中直接添加长度校验
在自定义键盘的按键处理代码里,先判断当前输入长度是否已达2位,再决定是否添加新字符:
Keyboard(onKeyPressed = { key -> when (key) { "<-" -> { viewModel.text.largestDigit.value = viewModel.text.largestDigit.value.dropLast(1) } "Enter" -> { TODO() } else -> { val currentText = viewModel.text.largestDigit.value // 限制最多两位数字 if (currentText.length < 2) { // 原代码中key[viewModel.answer.index.value]疑似笔误,自定义键盘的key本身是单个数字字符串,直接拼接即可 viewModel.text.largestDigit.value = currentText + key } } } })
方案2:统一校验逻辑到ViewModel层
把输入校验逻辑抽离到ViewModel中,不管是系统键盘还是自定义键盘,都通过ViewModel的方法更新输入值,确保校验逻辑统一:
- 在ViewModel中添加校验更新方法:
fun updateLargestDigit(newValue: String) { // 注意:Kotlin中反斜杠需要转义,修正正则写法 val pattern = Regex("^\\d{1,2}$") if (newValue.isEmpty() || newValue.matches(pattern)) { text.largestDigit.value = newValue } }
- 修改OutlinedTextField的
onValueChange:
OutlinedTextField( // 其他参数保持不变 onValueChange = { viewModel.updateLargestDigit(it) }, )
- 自定义键盘的按键处理也调用该方法:
Keyboard(onKeyPressed = { key -> when (key) { "<-" -> { viewModel.updateLargestDigit(viewModel.text.largestDigit.value.dropLast(1)) } "Enter" -> { TODO() } else -> { val currentText = viewModel.text.largestDigit.value viewModel.updateLargestDigit(currentText + key) } } })
方案3:修正正则表达式(可选)
原正则Regex("^\d{1,2}$")在Kotlin中需要转义反斜杠,正确写法应为Regex("^\\d{1,2}$"),虽然系统键盘下能生效,但修正后可避免潜在的正则匹配问题。
内容的提问来源于stack exchange,提问作者JD74
相关产品推荐
相关产品推荐

