如何禁止Jetpack Compose BasicTextField获取焦点时自动滚到顶部
问题根因
BasicTextField获取焦点唤起软键盘时,会触发默认的bringIntoView可视区域调整逻辑,加上软键盘弹出带来的WindowInsets变化触发布局重算,绑定的垂直滚动状态会被默认逻辑重置到顶部初始位置,不会保留用户之前手动滚动的偏移量。
实现方案
方案1:偏移量记忆恢复(最小改动,不影响原有输入交互)
- 给绑定在BasicTextField上的滚动状态、焦点请求器提前做实例持有
- 在主动调用
requestFocus()前,先记录当前滚动状态的偏移值 - 等待焦点请求完成、软键盘弹出、布局重排结束后,将滚动位置恢复为之前记录的偏移值
示例代码:
val scrollState = rememberScrollState() val focusRequester = remember { FocusRequester() } val scope = rememberCoroutineScope() // 绑定到触发焦点的点击事件等业务逻辑中 scope.launch { // 先记录当前滚动位置 val savedOffset = scrollState.value // 请求焦点唤起软键盘 focusRequester.requestFocus() // 等待下一帧布局绘制完成,避免滚动值被重算逻辑覆盖 awaitFrame() // 恢复到原滚动位置 scrollState.scrollTo(savedOffset) } // 组件配置 BasicTextField( value = inputText, onValueChange = { inputText = it }, modifier = Modifier .fillMaxWidth() .height(200.dp) .verticalScroll(scrollState) .focusRequester(focusRequester) )方案2:禁用默认自动滚动逻辑(适合不需要光标自动滚到可视区域的场景)
通过CompositionLocal替换全局的BringIntoViewSpec实现,拦截默认的焦点触发自动滚动行为,从根源避免滚动位置被篡改:CompositionLocalProvider( LocalBringIntoViewSpec provides object : BringIntoViewSpec { override fun calculateRectForParent(bounds: Rect, size: IntSize, position: Offset): Rect { // 直接返回原始边界,不触发任何自动滚动调整 return bounds } } ) { BasicTextField( value = inputText, onValueChange = { inputText = it }, modifier = Modifier .fillMaxWidth() .height(200.dp) .verticalScroll(scrollState) .focusRequester(focusRequester) ) }
注意:如果使用方案2,输入过程中光标移出可视区域时不会自动滚动调整位置,更适合只读大文本+手动触发焦点编辑的场景;常规可输入场景优先选择方案1,不会破坏原有的输入体验。
内容的提问来源于stack exchange,提问作者KongDa
相关产品推荐
相关产品推荐

