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

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}
                )
            //}
        }
    }
}
原因分析
  1. 未捕获的类型转换异常:直接用list[index].toInt()转换字符串,当用户输入非数字字符时,list会存入非法字符串,后续重组执行该转换会抛出NumberFormatException崩溃。添加条件判断时,每次重组都会触发转换,崩溃必现;移除条件时转换不执行,所以不会崩溃,但校验逻辑本身有漏洞。
  2. 代码块逻辑错误:onValueChange中的if语句缺少大括号,导致无论输入是否符合规则,list[index] = value都会执行,非法输入会直接存入list,为后续崩溃埋下隐患。
  3. 状态不同步: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:46:14