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

使用ListAdapter提交重排相同列表时RecyclerView不保留滚动位置怎么办

问题根因

排序后滚动位置变化主要由2个常见原因导致:

  1. 你给RecyclerView设置了layoutAnimation,每次数据集更新时动画会重新触发,导致滚动位置重置
  2. 提交新列表时没有保存原有滚动位置,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 16:24:05