Android - 如何使RecyclerView的ListAdapter停止闪烁?

5

我有一个使用ListAdapter (版本1.1.0)的recycler view适配器:

class InnerEpisodeFragmentAdapter(
    private val actCtx: Context,
) : ListAdapter<Episode, InnerEpisodeFragmentAdapter.MViewHolder>(COMPARATOR) {
...

RecyclerView是由来自Room数据库中的Episode表的Kotlin Flow提供数据的:

vm.episodesFlow().asLiveData().observe(viewLifecycleOwner) { episodes ->
  episodes.let { adapter.submitList(it) }
}

@Query("SELECT * FROM Episode ORDER BY pubDate DESC")
fun episodesFlow(): Flow<List<Episode>>

每当 Episode 表中的元组发生变化时,都会发出一系列新的剧集,并更新回收视图。这个功能可以很好地运行,但每次更新时都会出现令人不爽的闪烁,这会给用户带来不好的体验。
当流向外部发出新的值时,如何避免这种闪烁?在我的应用程序的先前版本中,我使用了像 notifyDataSetChanged() 或 notifyItemChanged() 这样的函数,它们从未出现过闪烁。我知道我仍然可以尝试使用这些函数,但如果我不能避免使用上面展示的 Kotlin 流时的闪烁,我会非常失望。谢谢。

流程本身不应该是问题,也许您的项目比较器存在问题,无法识别未更改的项目。 - Pawel
你能提供一个屏幕录像吗? - Sam Chen
1
请发布您的“COMPARATOR”代码。 - Amirhosein
请见下文。 - u2gilles
你找到这个问题的答案了吗?还是改变比较器解决了你的问题? - alena_fox_spb
3个回答

5

您的比较器

areItemsThesame() 实现错误。您正在比较引用,其中旧对象和新对象可能不是相同的对象。相反,您应该使用某种 uuid 来比较两者,例如主键。如果对象不同,则会返回 false,因此 areContentsTheSame 将永远不会被调用;否则,如果对象相同,则没有必要比较内容,因为 oldObject 和 newObject 都指向同一个引用。

对于由移动设备书写而导致的错误,我们感到抱歉。


1
我的问题是关于在更新列表时抑制闪烁,而不是是否应该更新列表。我的比较器按预期工作。我只是无法停止看到回收视图闪烁。 - u2gilles
2
我猜因为将列表中的每个项目作为引用进行比较是导致闪烁的原因。在areItemsTheSame中,比较应该基于uuId而不是引用(===)或equals(==)。 - Sheikh Zakir Ahmad
明白了。我已经用 return oldItem.id == newItem.id 替换了 areItemsTheSame(),现在只有更新的列表项在闪烁。虽然我更希望在不闪烁列表项的情况下更新项目中的文本视图,但这也是可以接受的。谢谢。 - u2gilles

2
如果任何人在实现了areContentsTheSame和areItemsTheSame后仍然遇到问题,请将Recyclerview的animator设置为null,像这样:
yourRecyclerView.itemAnimator = null

希望能有所帮助


1

======== 请求额外信息 ======

我的比较器很好,正是我想要的。例如,我希望在每个回收视图项中实时更新isPlayed。而且确实如此。但整个列表会在一瞬间闪烁。回收视图会消失一小段时间,然后重新出现,并显示更新的信息。

companion object {
    private val COMPARATOR = object : DiffUtil.ItemCallback<Episode>() {
        override fun areItemsTheSame(oldItem: Episode, newItem: Episode): Boolean {
            return oldItem === newItem
        }

        override fun areContentsTheSame(oldItem: Episode, newItem: Episode): Boolean {
            return (oldItem.id == newItem.id)
                    && (oldItem.isOnDisk == newItem.isOnDisk)
                    && (oldItem.downloadId == newItem.downloadId)
                    && (oldItem.isPlayed == newItem.isPlayed)
                    && (oldItem.isDeleted == newItem.isDeleted)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接