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

设置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:27:33