如果我提交具有不同项目顺序的相同列表,ListAdapter 不会刷新 RecyclerView

ListAdapter not refreshing RecyclerView if I submit the same list with different item order

我正在使用 RecyclerViewListAdapter (which uses AsyncListDiffer 来计算和动画更改列表时的变化)。

问题是,如果我 submit() 一些列表,然后重新排序该列表,然后再次 submit(),没有任何反应。

如何强制 ListAdapter 评估这个新列表,即使它是 "the same"(但顺序已更改)?

新发现:

我检查了 submitList() 的源代码,在开始时有一个检查:

public void submitList(@Nullable final List<T> newList) {
        final int runGeneration = ++this.mMaxScheduledGeneration;
        if (newList != this.mList) {

我认为这是问题所在。但是如何度过呢?我的意思是,开发人员肯定考虑过提交不同的有序列表?

不会调用该函数,因为 ListAdapter 不会将其视为另一个列表,因为它具有所有 相同的项目 只是顺序发生了变化。

@Override
public void submitList(@Nullable final List<T> list) {
super.submitList(list != null ? new ArrayList<>(list) : null);
}

所以要解决这个问题,你需要先用null调用这个函数,然后立即用改变顺序的列表调用。

submitList(null);
submitList(orderChangedList);

当您连续调用这些行时,它违背了 ListAdapter 自动计算和动画列表更改的目的:

submitList(null);
submitList(orderChangedList);

意思是,您只清除了(null)ListAdapter 的currentList,然后提交了(.submitList())一个新的List。因此,在这种情况下不会看到相应的动画,只会刷新整个 RecyclerView。

解决方案是在您的 ListAdapter 中实现 .submitList( List<T> list) 方法,如下所示:

public void submitList(@Nullable List<T> list) {
    mDiffer.submitList(list != null ? new ArrayList<>(list) : null);
}

通过这种方式,您允许 ListAdapter 保留其 currentList 并将其 "diffed" 与 newList 一起使用,从而计算动画,而不是 "diffing" 与 null

注意:但是,当然,如果新列表包含与原始列表相同顺序的相同项目,则不会发生更新动画。

而不是

submitList(mySameOldListThatIModified)

您必须像这样提交新列表:

ArrayList newList = new ArrayList(oldList);
newList.add(somethingNew); // Or sort or do whatever you want
submitList(newList);

API 有点问题。我们希望 ListAdapter 保留列表的副本,但它没有,可能是出于内存原因。当您更改旧列表时,您实际上是在更改 ListAdapter 存储的同一个列表。当 ListAdapter 检查 if (newList != this.mList) newListmList 都指的是同一个列表实例,因此无论您对该列表进行了哪些更改,它都等于自身,并忽略您的更新。

在 kotlin 中,您可以通过以下方式创建新列表:

val newList = oldList.toMutableList() // Unintuitive way to copy a list
newList[0] = newList[0].copy(isFavourite = false) // Do whatever modifications you want
submitList(newList)

请注意,您不能这样做:

newList.first().isFavourite = false

因为这也会更改旧列表中的第一项,而且 ListAdapter 也不会看到旧列表中的第一项与新列表中的第一项之间的区别。我建议您列表中的所有项目都独占 val 属性,以避免此问题。

要添加 Carson 的答案,这是一种更好的方法,您可以在 Kotlin 中保持 submitList 的优点,如下所示:

submitList(oldList.toList().toMutableList().let {
     it[index] = it[index].copy(property = newvalue) // To update a property on an item
     it.add(newItem) // To add a new item
     it.removeAt[index] // To remove an item
     // and so on....
     it
})

只需调用 listAdapter.notifyDataSetChanged()ListAdapter 将根据提交的值重新绘制列表。

我遇到了类似的问题,但不正确的渲染是由 setHasFixedSize(true) 和 android:layout_height="wrap_content" 的组合引起的。第一次为适配器提供了一个空列表,因此高度从未更新并且为 0。无论如何,这解决了我的问题。其他人可能有同样的问题,并会认为是适配器的问题。

android recyclerview ListAdapter 示例,RecyclerView ListAdapter。 ListAdapter 是一个显示列表的 RecyclerView 适配器。这在 RecyclerView 27.1+ 中可用,如果您无法扩展适配器,则相同的功能也存在于 AsyncListDiffer class 中。 ListAdapter 可帮助您使用随时间更改内容的 RecyclerView。 usersList.observe(这个, 列表 -> adapter.submitList(列表)); recyclerView.setAdapter( 适配器); } } class UserAdapter extends ListAdapter

所有功劳都归功于此人: https://www.xspdf.com/help/50031492.html

问题是提交的新列表没有呈现。

androidx.recyclerview:recyclerview:1.2.0-beta02

暂时的解决办法是在新列表提交后平滑滚动到任意位置,我做到顶部。

movieListAdapter.submitList(list) {
    binding.recycler.smoothScrollToPosition(0)
}

如果您启用了 setHasFixedSize(true),请删除此行。

"RecyclerView 如果能提前知道 RecyclerView 的大小不受适配器内容的影响,就可以进行多项优化...."