从Firestore拉取更新数据后如何保持RecyclerView的原有滚动位置不变
修复RecyclerView更新数据后滚动到顶部的方案
问题根源
你当前每次拉取到Firestore返回的商品列表后,都会重新给RecyclerView设置LayoutManager、重新创建Adapter实例并绑定到RecyclerView,这是触发列表自动滚动到顶部的核心原因。你额外添加的notifyDataSetChanged在这里没有任何作用,因为替换整个Adapter本身就会重置列表的所有状态包括滚动位置。
修复步骤
1. 全局初始化Adapter和LayoutManager
将Adapter、LayoutManager的初始化逻辑移到页面创建阶段(比如Fragment的onViewCreated中),全程只初始化一次:
// 全局保存列表数据和Adapter实例 private lateinit var adapterProducts: MyProductsListAdapter private val productList = arrayListOf<Product>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 初始化逻辑只执行一次 binding.rvMyProductItems.layoutManager = LinearLayoutManager(activity) binding.rvMyProductItems.setHasFixedSize(true) adapterProducts = MyProductsListAdapter(requireActivity(), productList, this@ProductsFragment) binding.rvMyProductItems.adapter = adapterProducts }
2. 改造拉取成功的回调逻辑
不要每次都创建新的Adapter,只更新原有Adapter绑定的数据集即可:
fun successProductsListFromFireStore(productsList: ArrayList<Product>) { hideProgressDialog() if (productsList.size > 0) { binding.rvMyProductItems.visibility = View.VISIBLE binding.tvNoProductsFound.visibility = View.GONE // 更新原有列表的内容 this.productList.clear() this.productList.addAll(productsList) // 通知Adapter刷新,此时不会重置滚动位置 adapterProducts.notifyDataSetChanged() } else { binding.rvMyProductItems.visibility = View.GONE binding.tvNoProductsFound.visibility = View.VISIBLE } }
3. 可选优化(推荐)
用DiffUtil替换全局的notifyDataSetChanged,实现局部刷新:
- 实现
DiffUtil.ItemCallback<Product>对比新旧商品条目差异 - 计算差异后调用
diffResult.dispatchUpdatesTo(adapterProducts)
可以完全避免不必要的条目刷新、消除列表跳动,滚动位置保留的体验更好。
进阶方案
如果你的增删改操作都是单条数据变更,不需要每次拉取全量商品列表,可以直接监听Firestore对应单条数据的变化,收到变更通知后对应调用adapter.notifyItemInserted/notifyItemRemoved/notifyItemChanged,完全不会影响列表的滚动位置,性能也更高。
内容的提问来源于stack exchange,提问作者Codist
相关产品推荐
相关产品推荐

