RecyclerView可见时获取Item数据,onAttachedToRecyclerView中layoutManager为空如何解决
问题原因
onAttachedToRecyclerView 回调的触发时机是Adapter被关联到RecyclerView的瞬间,如果你的代码执行顺序是「先设置Adapter,再给RecyclerView设置LayoutManager」,或者LayoutManager还没完成初始化绑定,此时调用recyclerView.layoutManager就会返回null。
修复方案
方案1:调整初始化顺序+将滚动监听移到业务层(最推荐)
不要把滚动监听的逻辑耦合在Adapter内部,在你初始化RecyclerView的Activity/Fragment中按正确顺序执行操作:
- 先给RecyclerView设置LayoutManager
- 再给RecyclerView设置Adapter
- 最后添加滚动监听,此时LayoutManager必然非空
示例代码:
// 你的Activity/Fragment中的初始化逻辑 val recyclerView = findViewById<RecyclerView>(R.id.recycler_view) // 第一步:先设置LayoutManager val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) recyclerView.layoutManager = layoutManager // 第二步:设置Adapter val adapter = ProductAdapter() recyclerView.adapter = adapter // 第三步:添加滚动监听 recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) val firstVisibleItemPositions = layoutManager.findFirstVisibleItemPositions(null) val firstVisibleItemPosition = firstVisibleItemPositions[0] if (firstVisibleItemPosition > -1) { val item = adapter.getItem(firstVisibleItemPosition) // 处理你的业务逻辑 } } })
方案2:Adapter内部延迟获取LayoutManager
如果你确实需要把逻辑放在Adapter内部,可以通过post任务延迟获取,等RecyclerView完成初始化后再执行逻辑,修改后的代码如下:
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) // post任务到RecyclerView的消息队列,等布局初始化完成后再执行 recyclerView.post { val manager = recyclerView.layoutManager Log.i("ProductAdapter","$manager") if (manager is StaggeredGridLayoutManager) { val slm: StaggeredGridLayoutManager = manager recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) val firstVisibleItemPositions = slm.findFirstVisibleItemPositions(null) val firstVisibleItemPosition = firstVisibleItemPositions[0] if (firstVisibleItemPosition > -1) { // 注意:你原代码此处多了一个多余的右括号,已经移除 val item = getItem(firstVisibleItemPosition) // 处理你的业务逻辑 } } }) }else{ Log.e("ProductAdapter","error") } } }
额外优化提示
- 如果你需要获取所有在屏幕可见区域的item数据,不要只取第一个可见位置,可以配合
findLastVisibleItemPositions拿到最后一个可见位置,遍历区间内的所有position获取对应数据 - 滚动事件触发非常频繁,如果你的业务逻辑有耗时操作,建议添加防抖逻辑避免重复执行
内容的提问来源于stack exchange,提问作者Willt42
相关产品推荐
相关产品推荐

