使用ListAdapter提交重排相同列表时RecyclerView不保留滚动位置怎么办
问题根因
排序后滚动位置变化主要由2个常见原因导致:
- 你给RecyclerView设置了
layoutAnimation,每次数据集更新时动画会重新触发,导致滚动位置重置 - 提交新列表时没有保存原有滚动位置,DiffUtil计算差异更新后列表会回滚到默认位置
解决步骤
1. 处理布局动画干扰
如果不需要每次列表更新都播放入场动画,直接移除布局文件里的android:layoutAnimation="@anim/layout_fall_down"属性即可。如果需要保留首次加载的动画效果,后续排序更新时临时禁用动画:
// 排序更新前禁用动画 binding.tabSongsRecyclerView.layoutAnimation = null // 提交列表完成后可按需恢复动画
2. 优化DiffUtil逻辑
你当前的areContentsTheSame仅对比trackId,逻辑没问题但如果后续条目展示内容更新会无法触发UI刷新,建议补充所有展示相关字段的对比:
override fun areContentsTheSame(oldItem: TrackInfo, newItem: TrackInfo): Boolean { return oldItem.trackId == newItem.trackId && oldItem.title == newItem.title && oldItem.artist == newItem.artist // 按需添加其他展示用字段 }
3. 提交新列表时保存并恢复滚动位置
在你接收LiveData更新、提交新列表给Adapter的逻辑中,增加滚动位置的保存和恢复:
// 监听歌曲列表变化的逻辑 viewModel.tracksInfo.observe(viewLifecycleOwner) { newList -> val layoutManager = binding.tabSongsRecyclerView.layoutManager as GridLayoutManager // 保存当前滚动位置信息 val firstVisiblePos = layoutManager.findFirstVisibleItemPosition() val offset = if (layoutManager.childCount > 0) layoutManager.getChildAt(0).top else 0 // 提交新列表,完成回调中恢复位置 tabSongsRva.submitList(newList) { layoutManager.scrollToPositionWithOffset(firstVisiblePos, offset) } }
如果需要保证用户当前查看的歌曲始终保留在视野中(而不是固定原来的位置序号),可以改为保存目标歌曲ID,排序后找到对应新位置再滚动:
viewModel.tracksInfo.observe(viewLifecycleOwner) { newList -> val layoutManager = binding.tabSongsRecyclerView.layoutManager as GridLayoutManager val currentFirstPos = layoutManager.findFirstVisibleItemPosition() // 列表为空直接提交 if (tabSongsRva.currentList.isEmpty() || currentFirstPos >= tabSongsRva.currentList.size) { tabSongsRva.submitList(newList) return@observe } // 保存当前最上方可见的歌曲ID val targetTrackId = tabSongsRva.currentList[currentFirstPos].trackId val offset = if (layoutManager.childCount > 0) layoutManager.getChildAt(0).top else 0 tabSongsRva.submitList(newList) { // 找到该歌曲在新排序列表中的位置 val targetPos = newList.indexOfFirst { it.trackId == targetTrackId } if (targetPos != -1) { layoutManager.scrollToPositionWithOffset(targetPos, offset) } } }
注意:如果你的列表更新逻辑是通过Data Binding的自定义BindingAdapter实现的,需要把上述保存恢复位置的逻辑写到BindingAdapter的实现中,不要直接在XML中绑定后直接调用submitList。
内容的提问来源于stack exchange,提问作者Petros Mosoyan
相关产品推荐
相关产品推荐

