设置adjustResize后如何阻止RecyclerView随软键盘弹出滚动
解决软键盘弹出时RecyclerView自动滚动的问题
这个场景在聊天类App里确实很头疼——明明设置了adjustResize|stateAlwaysHidden让布局自适应软键盘,但RecyclerView总是跟着往上滚,破坏了用户的浏览体验。你试过的LayoutManager#onSaveInstanceState()没起作用,大概率是因为保存时机不对,毕竟软键盘弹出不属于Activity生命周期变化,这个方法不会被触发。下面给你几个针对性的解决方案:
方案一:监听布局变化,手动保存并恢复滚动位置
核心思路是通过ViewTreeObserver监听布局高度变化,判断软键盘是否弹出,在弹出前精准记录RecyclerView的滚动位置,弹出后再手动恢复。
代码示例(Kotlin):
class ChatActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private var lastVisiblePosition = 0 private var lastVisibleOffset = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_chat) recyclerView = findViewById(R.id.recyclerView) // 初始化RecyclerView的Adapter、LayoutManager等... // 监听布局变化,判断软键盘状态 recyclerView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { val rect = Rect() window.decorView.getWindowVisibleDisplayFrame(rect) val screenHeight = window.decorView.rootView.height val keyboardHeight = screenHeight - rect.bottom if (keyboardHeight > screenHeight * 0.15) { // 软键盘弹出,恢复之前记录的滚动位置 (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(lastVisiblePosition, lastVisibleOffset) } else { // 软键盘收起,记录当前滚动位置 val layoutManager = recyclerView.layoutManager as LinearLayoutManager val firstVisibleItem = layoutManager.findFirstVisibleItemPosition() val firstVisibleView = layoutManager.findViewByPosition(firstVisibleItem) firstVisibleView?.let { lastVisibleOffset = it.top - recyclerView.paddingTop lastVisiblePosition = firstVisibleItem } } } }) } }
方案二:自定义LayoutManager,禁止自动滚动行为
默认的LinearLayoutManager在布局大小变化时会自动调整滚动位置,我们可以通过自定义LayoutManager来覆盖这个行为,只在需要的时候才允许滚动调整。
代码示例(Kotlin):
class NoAutoScrollLinearLayoutManager(context: Context) : LinearLayoutManager(context) { private var allowAutoScroll = true // 提供外部控制开关 fun setAllowAutoScroll(allow: Boolean) { allowAutoScroll = allow } override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { // 保存当前滚动状态 val firstVisiblePos = findFirstVisibleItemPosition() val firstView = findViewByPosition(firstVisiblePos) val offset = firstView?.top ?: 0 super.onLayoutChildren(recycler, state) // 如果禁止自动滚动,恢复之前的位置 if (!allowAutoScroll) { scrollToPositionWithOffset(firstVisiblePos, offset) } } }
然后在Activity中使用这个自定义LayoutManager,结合软键盘监听控制滚动开关:
val layoutManager = NoAutoScrollLinearLayoutManager(this) recyclerView.layoutManager = layoutManager // 在布局监听的逻辑中: if (keyboardHeight > screenHeight * 0.15) { layoutManager.setAllowAutoScroll(false) } else { layoutManager.setAllowAutoScroll(true) }
方案三:延迟恢复滚动位置
有时候软键盘弹出后布局需要一点时间完成重绘,直接恢复位置可能不生效。可以用postDelayed给布局重绘留足时间:
// 在软键盘弹出的判断逻辑里: recyclerView.postDelayed({ (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(lastVisiblePosition, lastVisibleOffset) }, 100)
这个延迟时间不用太长,100ms左右足够布局完成重绘。
内容的提问来源于stack exchange,提问作者kaneda
相关产品推荐
相关产品推荐

