我正在使用RecyclerView显示从数据库中检索为Java对象的项目列表。对象中的字段之一是数据库中项目的ID,因此可以对其执行进一步的操作。我的areContentsTheSame实现比较对象中的各个字段,以确定项目的内容是否已更改。

问题在于,有时在刷新数据源时,项的ID会更改而可见内容不会更改(即,从数据库中删除相同的项,然后再次添加它们,从而在此过程中更改其ID)。在这种情况下,我当前的areContentsTheSame实现返回true。但是,当areContentsTheSame返回true时,不会重新绑定(bind)RecyclerView View (对于onBindViewHolder返回areContentsTheSame的项目,不会调用true),因此 View 仍然持有对包含旧ID的旧对象的引用,从而在以下情况下导致错误:尝试对该项目执行某项操作(例如用户点击它以显示它)。

我尝试在ID更改时使areContentsTheSame返回false,即使没有发生可见的更改也迫使 View 重新绑定(bind),但这会导致显示“项目更改”动画,即使该项目没有明显更改也是如此。我想要的是一种在不让areContentsTheSame返回false的情况下强制重新绑定(bind) View 的方法,或者是让areContentsTheSame返回true并触发重新绑定(bind)而不显示动画的方法。

最佳答案

实现所需目标的最佳方法是在getChangePayload()实现中重写 DiffUtil.Callback 方法。

因此,请确保当数据库ID更改时,您的areContentsTheSame()方法返回false,然后实现getChangePayload()来检查这种情况并返回非null值。也许是这样的:

@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
    if (onlyDatabaseIdChanged(oldItemPosition, newItemPosition)) {
        return Boolean.FALSE;
    } else {
        return null;
    }
}
从此方法返回什么对象都没有关系,只要在您不希望播放默认的更改动画的情况下它不为null。
有关为何有效的更多详细信息,请参见以下问题的答案:https://stackoverflow.com/a/47355363/8298909

关于Android DiffUtil.ItemCallback areContentsTheSame具有不同的ID,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50474945/

10-10 20:06