Jetpack Compose数字输入框更新后台字段游标复位异常问题咨询
问题根因
游标跳回输入开头的核心原因是你的状态同步逻辑没有做边界判断:
- 每次用户输入有效整数就会触发ViewModel的
numCycles更新 - 你写的
state.numCycles.observeAsState().run { ... }逻辑会在每次numCycles变化时,直接用整数值转字符串生成全新的TextFieldValue覆盖本地状态,新生成的TextFieldValue默认游标位置在第0位,直接覆盖了用户当前的输入位置与状态,就会出现输入顺序错乱、游标乱跑的问题。
修复方案
核心要做两个调整:
- 只有当外部其他逻辑修改ViewModel数值时才同步到输入框,用户输入触发的ViewModel更新不需要反向同步
- 同步数值到输入框时,要保留合理的游标位置,不要直接生成无状态的新
TextFieldValue
调整后代码
这里给出修改后的TestNumeric实现,其余代码无需改动,注意额外导入TextRange包:import androidx.compose.ui.text.input.TextRange
@Composable fun TestNumeric(state: State) { val numCycles: Int by state.numCycles.observeAsState(0) val numCyclesString = remember { mutableStateOf(TextFieldValue(numCycles.toString())) } // 仅当外部修改numCycles,且和当前输入框的有效数值不一致时,才更新输入框内容 LaunchedEffect(numCycles) { val currentInputVal = StringToInt.tryParse(numCyclesString.value.text) if (currentInputVal != numCycles) { // 更新时把游标放到文本末尾,避免跳转到开头 numCyclesString.value = TextFieldValue( text = numCycles.toString(), selection = TextRange(numCycles.toString().length) ) } } Surface { TextField( value = numCyclesString.value, onValueChange = { numCyclesString.value = it val i = StringToInt.tryParse(it.text) if (i != null) { state.onNewNumCycles(i) } }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) ) } }
如果希望减少频繁更新ViewModel的次数,也可以改成用户失去输入焦点时再校验更新,调整TextField参数即可:
@Composable fun TestNumeric(state: State) { val numCycles: Int by state.numCycles.observeAsState(0) val numCyclesString = remember { mutableStateOf(TextFieldValue(numCycles.toString())) } val focusManager = LocalFocusManager.current LaunchedEffect(numCycles) { val currentInputVal = StringToInt.tryParse(numCyclesString.value.text) if (currentInputVal != numCycles) { numCyclesString.value = TextFieldValue( text = numCycles.toString(), selection = TextRange(numCycles.toString().length) ) } } Surface { TextField( value = numCyclesString.value, onValueChange = { numCyclesString.value = it }, // 失去焦点时校验更新 modifier = Modifier.onFocusChanged { focusState -> if (!focusState.isFocused) { val i = StringToInt.tryParse(numCyclesString.value.text) if (i != null) { state.onNewNumCycles(i) } else { // 输入无效时自动回滚为上次有效数值 numCyclesString.value = TextFieldValue( text = numCycles.toString(), selection = TextRange(numCycles.toString().length) ) } } }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) ) } }
内容的提问来源于stack exchange,提问作者Craig Graham
相关产品推荐
相关产品推荐

