Compose条件渲染下onValueChange行为异常致崩溃问题排查
问题
我正在开发一款数独游戏及求解器,用LazyVerticalGrid实现9×9网格界面。原本已经实现了OutlinedTextField仅接受[1-9]数字的逻辑,但添加“仅为空单元格(值为0)显示TextField”的条件后,数字校验逻辑失效,输入非数字会导致程序崩溃;给Card添加背景色/内容色的条件判断时也会出现同样问题,移除条件就恢复正常。
错误信息:java.lang.NumberFormatException: For input string: "a"
相关代码:
fun displayPuzzle(answer: Array<Array<IntArray>>) { var list: SnapshotStateList<String> = mutableStateListOf() for (x in answer[0]) list.addAll(x.map { it.toString() }) var columnHeighty by remember { mutableStateOf(0F) } var columnWidthx by remember { mutableStateOf(0f) } var pad = 20.dp LazyVerticalGrid( columns = GridCells.Fixed(9), contentPadding = PaddingValues( start = pad, top = pad, end = pad, bottom = pad ) ) { items(list.size) { index -> Card( shape = RectangleShape, backgroundColor = Color.Red, modifier = Modifier .requiredWidth(83.dp) .fillMaxWidth() .fillMaxHeight() .onGloballyPositioned { coordinates -> columnWidthx = coordinates.size.width.toFloat() columnHeighty = coordinates.size.height.toFloat() }, //backgroundColor = if (list[index].toInt() == 0) Color.Yellow else Color.White , //contentColor = if (list[index].toInt() == 0) Color.Blue else Color.Black , border = BorderStroke(width = 1.dp, color = Color.Black) ) { Text( text = list[index], fontWeight = FontWeight.Bold, fontSize = 30.sp, color = Color(0xFF000000), textAlign = TextAlign.Center, modifier = Modifier .padding(23.dp) .clickable { } ) } // When the if statement is included the program crashes on a non digit entry //if (list[index].toInt() == 0) { val pattern = remember { Regex("[1-9]") } var value by remember { mutableStateOf("") } OutlinedTextField( keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.outlinedTextFieldColors(cursorColor = Color.Transparent), textStyle = TextStyle(color = Color.Red), modifier = Modifier .fillMaxHeight() .padding(vertical = 10.dp, horizontal = 10.dp), value = value, onValueChange = { if (it.isEmpty() || (it.matches(pattern) && (it.length == 1))) value = it list[index] = value} ) //} } } }
原因分析
- 未捕获的类型转换异常:直接用
list[index].toInt()转换字符串,当用户输入非数字字符时,list会存入非法字符串,后续重组执行该转换会抛出NumberFormatException崩溃。添加条件判断时,每次重组都会触发转换,崩溃必现;移除条件时转换不执行,所以不会崩溃,但校验逻辑本身有漏洞。 - 代码块逻辑错误:
onValueChange中的if语句缺少大括号,导致无论输入是否符合规则,list[index] = value都会执行,非法输入会直接存入list,为后续崩溃埋下隐患。 - 状态不同步:TextField的本地
value状态和全局list状态未同步,导致显示值与实际存储值不一致。
修复方案
步骤1:修复校验逻辑代码块
给onValueChange的if语句添加大括号,确保只有符合规则的输入才更新状态:
onValueChange = { if (it.isEmpty() || (it.matches(pattern) && it.length == 1)) { value = it list[index] = value } }
步骤2:使用安全类型转换
将所有list[index].toInt()替换为list[index].toIntOrNull() ?: 0,避免抛出异常:
- 背景色条件修改:
backgroundColor = if (list[index].toIntOrNull() ?: 0 == 0) Color.Yellow else Color.White,
- TextField显示条件修改:
if (list[index].toIntOrNull() ?: 0 == 0) { // TextField代码 }
步骤3:同步TextField初始值
给remember添加index作为key,确保每个item的状态独立,同时用list[index]初始化value:
var value by remember(index) { mutableStateOf(list[index]) }
修改后的完整代码
fun displayPuzzle(answer: Array<Array<IntArray>>) { val list: SnapshotStateList<String> = mutableStateListOf() for (x in answer[0]) list.addAll(x.map { it.toString() }) var columnHeighty by remember { mutableStateOf(0F) } var columnWidthx by remember { mutableStateOf(0f) } val pad = 20.dp LazyVerticalGrid( columns = GridCells.Fixed(9), contentPadding = PaddingValues(start = pad, top = pad, end = pad, bottom = pad) ) { items(list.size) { index -> val cellValue = list[index].toIntOrNull() ?: 0 Card( shape = RectangleShape, backgroundColor = if (cellValue == 0) Color.Yellow else Color.White, contentColor = if (cellValue == 0) Color.Blue else Color.Black, modifier = Modifier .requiredWidth(83.dp) .fillMaxWidth() .fillMaxHeight() .onGloballyPositioned { coordinates -> columnWidthx = coordinates.size.width.toFloat() columnHeighty = coordinates.size.height.toFloat() }, border = BorderStroke(width = 1.dp, color = Color.Black) ) { if (cellValue != 0) { Text( text = list[index], fontWeight = FontWeight.Bold, fontSize = 30.sp, color = Color(0xFF000000), textAlign = TextAlign.Center, modifier = Modifier .padding(23.dp) ) } } if (cellValue == 0) { val pattern = remember { Regex("[1-9]") } var value by remember(index) { mutableStateOf(list[index]) } OutlinedTextField( keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.outlinedTextFieldColors(cursorColor = Color.Transparent), textStyle = TextStyle(color = Color.Red), modifier = Modifier .fillMaxHeight() .padding(vertical = 10.dp, horizontal = 10.dp), value = value, onValueChange = { input -> if (input.isEmpty() || (input.matches(pattern) && input.length == 1)) { value = input list[index] = input } } ) } } } }
额外优化点
- 提取
cellValue局部变量,避免重复执行类型转换 - 仅在非空单元格显示Text组件,避免与TextField重叠
- 用
index作为remember的key,确保每个item状态独立
内容的提问来源于stack exchange,提问作者JD74
相关产品推荐
相关产品推荐

