简体   繁体   English

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

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

I am using a RecyclerView with ListAdapter (which uses AsyncListDiffer to calculate and animate changes when list is replaced).我正在使用带有ListAdapterRecyclerView (它使用AsyncListDiffer来计算和动画替换列表时的更改)。

The problem is that if I submit() some list, then re-order that list, and submit() again, nothing happens.问题是,如果我submit()一些列表,然后重新排序该列表,然后再次submit() ,什么也没有发生。

How do I force ListAdapter to evaluate this new list, even though it is "the same" (but order has changed)?我如何强制ListAdapter评估这个新列表,即使它是“相同的”(但顺序已更改)?

New findings:新发现:

I checked source code of submitList() and in the beggining there is a check:我检查了submitList() 的源代码,并在开始时进行了检查:

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

I think this is the problem.我认为这就是问题所在。 But how to get past it?但是如何度过呢? I mean, surely the developers thought about submiting a different ordered list?我的意思是,开发人员肯定考虑过提交不同的有序列表吗?

Instead of代替

submitList(mySameOldListThatIModified)

You have to submit a new list like this:您必须提交这样的新列表:

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

It's kind of a problem with the API. API有点问题。 We would expect ListAdapter to keep a copy of the list, but it doesn't, probably for memory reasons.我们希望 ListAdapter 保留列表的副本,但它没有,可能是出于内存原因。 When you change your old list, you are actually changing the same list that ListAdapter has stored.当您更改旧列表时,您实际上是在更改 ListAdapter 存储的相同列表。 When ListAdapter checks if (newList != this.mList) both newList and mList are referring to the same list instance, so no matter what you have changed on that list, it will equal itself, and ignore your update.当 ListAdapter 检查if (newList != this.mList) newListmList是否都指的是同一个列表实例时,因此无论您对该列表进行了哪些更改,它都将等于自身,并忽略您的更新。

In kotlin you can create a new list via:在 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)

Note that you cannot do this:请注意,您不能这样做:

newList.first().isFavourite = false

because that will also change the first item in your old list, and again ListAdapter won't see a difference between the first item in your old list and the first item in your new list.因为这也将更改旧列表中的第一项,再次ListAdapter不会看到旧列表中的第一项与新列表中的第一项之间的差异。 I would recommend that all items in your list have val properties exclusively, to avoid this problem.我建议您列表中的所有项目都只具有val属性,以避免出现此问题。

It defeats ListAdapter 's purpose for automatically calculating and animating list changes when you call these lines consecutively:当您连续调用这些行时,它会破坏ListAdapter自动计算和动画列表更改的目的:

submitList(null);
submitList(orderChangedList);

Meaning, you only cleared ( null ) the ListAdapter's currentList and then submitted ( .submitList() ) a new List.意思是,您只清除了 ( null ) ListAdapter 的 currentList ,然后提交了 ( .submitList() ) 一个新列表。 Thus, no corresponding animation will be seen in this case but only a refresh of the entire RecyclerView.因此,在这种情况下不会看到相应的动画,而只会刷新整个 RecyclerView。

Solution is to implement the .submitList( List<T> list) method inside your ListAdapter as follows:解决方案是在.submitList( List<T> list)实现.submitList( List<T> list)方法,如下所示:

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

This way you allow the ListAdapter to retain its currentList and have it "diffed" with the newList, thereby the calculated animations, as opposed to "diffing" with a null .通过这种方式,您允许 ListAdapter 保留其 currentList 并使其与 newList “差异化”,从而计算动画,而不是使用null “差异化”。

Note: However, no update animation will happen, if, of course, the newList contains the same items in the same order as the originalList.注意:当然,如果 newList 包含与 originalList 相同顺序的相同项,则不会发生更新动画。

To add to Carson's answer, which is a better way to do it, you can maintain the benefits of submitList as follows in Kotlin:要添加到 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
})

The issue is that the new list submitted is not rendered.问题是提交的新列表没有呈现。

androidx.recyclerview:recyclerview:1.2.0-beta02

The temporary solution is to smooth-scroll to any position after the new list committed, I do to the top.临时解决方案是在新列表提交后平滑滚动到任何位置,我做到顶部。

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

If you have enable setHasFixedSize(true) , remove this line.如果您启用了setHasFixedSize(true) ,请删除此行。

"RecyclerView can perform several optimizations if it can know in advance that RecyclerView's size is not affected by the adapter contents...." “如果 RecyclerView 可以提前知道 RecyclerView 的大小不受适配器内容的影响,它可以执行多次优化......”

That function will not get called because ListAdapter won't consider it as another list, since it has all the same items only the order got changed.该函数不会被调用,因为ListAdapter不会将其视为另一个列表,因为它具有所有相同的项目,只是顺序发生了变化。

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

So to solve this, you need to call this function with null first, then immediately call with the order changed list.所以要解决这个问题,你需要先用null调用这个函数,然后立即用顺序改变的列表调用。

submitList(null);
submitList(orderChangedList);

I had a similar problem but the incorrect rending was caused by a combination of setHasFixedSize(true) and android:layout_height="wrap_content".我有一个类似的问题,但不正确的渲染是由 setHasFixedSize(true) 和 android:layout_height="wrap_content" 的组合引起的。 For the first time the adapter was supplied with an empty list so the height never got updated and was 0. Anyway, this resoved my issue.第一次为适配器提供了一个空列表,因此高度从未更新并且为 0。无论如何,这解决了我的问题。 Someone else might have the same problem and will think it is problem in the adapter.其他人可能有同样的问题,并会认为这是适配器的问题。

android recyclerview listadapter example, RecyclerView ListAdapter. android recyclerview listadapter 示例,RecyclerView ListAdapter。 ListAdapter is a RecyclerView adapter that displays a list. ListAdapter 是一个显示列表的 RecyclerView 适配器。 This is available in RecyclerView 27.1+, and the same function also exists in the AsyncListDiffer class if you cannot extend the adapter.这在 RecyclerView 27.1+ 中可用,如果您无法扩展适配器,则 AsyncListDiffer 类中也存在相同的功能。 ListAdapter helps you to work with RecyclerViews that change the content over time. ListAdapter 可帮助您使用随时间更改内容的 RecyclerViews。 usersList.observe(this, list -> adapter.submitList(list)); usersList.observe(this, list -> adapter.submitList(list)); recyclerView.setAdapter(​adapter); recyclerView.setAdapter( adapter); } } class UserAdapter extends ListAdapter<User,类 UserAdapter 扩展 ListAdapter<User,

all credits to this man: https://www.xspdf.com/help/50031492.html此人的所有功劳: https : //www.xspdf.com/help/50031492.html

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM