RecyclerView 滚动到顶部,AsyncListDiffer 不起作用
RecyclerView scroll to top with AsyncListDiffer not working
我正在使用 RecyclerView
和 AsyncListDiffer
(计算新旧项目之间的差异并在后台线程中设置动画)。
我有一个按钮可以对列表进行排序。在我对它进行排序并使用 mDiffer.submitList(items);
将其重新设置为 RecyclerView
之后,我还调用了 recyclerView.scrollToPosition(0)
或 (smoothScrollToPosition(0)
),但它没有任何效果。
我认为这种行为是预料之中的,因为 AsyncListDiffer
可能在调用 scrollToPosition(0)
时仍在计算差异,因此它没有任何效果。此外,默认情况下 AsyncListDiffer
不会滚动回顶部,而是让 RecyclerView 保持相同状态。
但是如何让 RecyclerView
在 AsyncListDiffer
完成并更新后滚动到顶部?
这里得到了回答:
基本上,如果您以不同的顺序提交相同的列表,它将被忽略。所以首先你需要submit(null)
然后提交你重新排序的列表。
我担心虽然 .submitList(null)
可能对您有用,但它只会刷新您的整个 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
。
如果您查看 AsyncListDiffer
的 submitList
的 javadoc,您会注意到第二个参数是 Runnable
,它在项目提交到适配器后执行:
/**
* Pass a new List to the AdapterHelper. Adapter updates will be computed on a background
* thread.
* <p>
* If a List is already present, a diff will be computed asynchronously on a background thread.
* When the diff is computed, it will be applied (dispatched to the {@link ListUpdateCallback}),
* and the new List will be swapped in.
* <p>
* The commit callback can be used to know when the List is committed, but note that it
* may not be executed. If List B is submitted immediately after List A, and is
* committed directly, the callback associated with List A will not be run.
*
* @param newList The new List.
* @param commitCallback Optional runnable that is executed when the List is committed, if
* it is committed.
*/
所以你想要的是这个(顺便说一句,这是在 Kotlin 中):
adapter.submitList(items) {
// This will run after items have been set in the adapter
recyclerView.scrollToPosition(0)
}
或 Java
adapter.submitList(items, () -> {
recyclerView.scrollToPosition(0);
});
在 activity 中的适配器上使用 registerAdapterDataObserver,就在您的“mRecyclerView.setAdapter(适配器)”代码之后:
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
public void onChanged() {
mRecyclerView.scrollToPosition(0);
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// same or similar scroll code
}
}
});
然后在activity.
的onStop()中注销观察者
我正在使用 RecyclerView
和 AsyncListDiffer
(计算新旧项目之间的差异并在后台线程中设置动画)。
我有一个按钮可以对列表进行排序。在我对它进行排序并使用 mDiffer.submitList(items);
将其重新设置为 RecyclerView
之后,我还调用了 recyclerView.scrollToPosition(0)
或 (smoothScrollToPosition(0)
),但它没有任何效果。
我认为这种行为是预料之中的,因为 AsyncListDiffer
可能在调用 scrollToPosition(0)
时仍在计算差异,因此它没有任何效果。此外,默认情况下 AsyncListDiffer
不会滚动回顶部,而是让 RecyclerView 保持相同状态。
但是如何让 RecyclerView
在 AsyncListDiffer
完成并更新后滚动到顶部?
这里得到了回答:
基本上,如果您以不同的顺序提交相同的列表,它将被忽略。所以首先你需要submit(null)
然后提交你重新排序的列表。
我担心虽然 .submitList(null)
可能对您有用,但它只会刷新您的整个 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
。
如果您查看 AsyncListDiffer
的 submitList
的 javadoc,您会注意到第二个参数是 Runnable
,它在项目提交到适配器后执行:
/**
* Pass a new List to the AdapterHelper. Adapter updates will be computed on a background
* thread.
* <p>
* If a List is already present, a diff will be computed asynchronously on a background thread.
* When the diff is computed, it will be applied (dispatched to the {@link ListUpdateCallback}),
* and the new List will be swapped in.
* <p>
* The commit callback can be used to know when the List is committed, but note that it
* may not be executed. If List B is submitted immediately after List A, and is
* committed directly, the callback associated with List A will not be run.
*
* @param newList The new List.
* @param commitCallback Optional runnable that is executed when the List is committed, if
* it is committed.
*/
所以你想要的是这个(顺便说一句,这是在 Kotlin 中):
adapter.submitList(items) {
// This will run after items have been set in the adapter
recyclerView.scrollToPosition(0)
}
或 Java
adapter.submitList(items, () -> {
recyclerView.scrollToPosition(0);
});
在 activity 中的适配器上使用 registerAdapterDataObserver,就在您的“mRecyclerView.setAdapter(适配器)”代码之后:
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
public void onChanged() {
mRecyclerView.scrollToPosition(0);
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// same or similar scroll code
}
}
});
然后在activity.
的onStop()中注销观察者