Jetpack Compose LazyColumn内非顶部TextField失焦如何解决
LazyColumn 内嵌靠后位置TextField 自动失焦解决方案
问题根因
该问题由LazyColumn惰性回收机制和软键盘弹出的布局变化共同触发:
- 软键盘弹出时会大幅压缩LazyColumn的可用可视高度,位置靠后的TextField会被判定为非可视项,直接被回收移出组合树
- 被回收的控件会直接丢失焦点状态,等布局重算完成、TextField重新被组合加载时,已经不再持有焦点,软键盘会自动收起
- 示例代码中把输入框状态定义在LazyColumn的item作用域内,item回收时状态绑定关系会被重置,会进一步加剧失焦问题
可落地修复方案
按以下步骤调整即可实现稳定输入:
- 把输入框相关的状态、协程作用域等实例挪到LazyColumn外部定义,不要放在item作用域中,避免item回收时实例被重建
- 给LazyColumn添加
imePadding()修饰符,让列表正确感知软键盘高度变化,不要直接把边缘item判定为可回收项 - 给承载TextField的item设置唯一稳定key,避免LazyColumn错误复用/回收该输入框item
- 给输入框添加焦点监听,获取焦点时自动把输入框item滚动到完全可视的位置,避免被软键盘遮挡触发回收
修复后完整代码
val listState = rememberLazyListState() // 状态提前到列表外部定义 var text by remember { mutableStateOf("") } val coroutineScope = rememberCoroutineScope() LazyColumn( modifier = Modifier .padding(horizontal = 16.dp) // 适配软键盘高度 .imePadding(), state = listState, ) { items(7) { QuickStart() } // 给输入框item设置唯一稳定key item(key = "search_text_field") { val focusRequester = remember { FocusRequester() } TextField( modifier = Modifier .focusRequester(focusRequester) .onFocusChanged { state -> if (state.isFocused) { coroutineScope.launch { // 获焦时滚动到输入框位置,确保完全可见 listState.animateScrollToItem(index = 7) } } }, value = TextFieldValue(text = text), onValueChange = { text = it.text } ) } }
补充说明
- 如果列表长度是动态变化的,不要硬编码滚动索引,可以遍历
listState.layoutInfo.visibleItemsInfo找到key为search_text_field的item的实际索引再执行滚动 - 如果使用的Compose BOM版本低于2023.03,需要在对应Activity的
onCreate方法中添加window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE),确保软键盘弹出时布局能正确触发重算 - 不要在LazyColumn的item作用域内定义任何和输入框焦点、输入内容相关的状态,否则item一旦被回收再重组,所有状态都会重置,必然导致焦点丢失
内容的提问来源于stack exchange,提问作者Stefan Todorovski
相关产品推荐
相关产品推荐

