RecyclerView 滚动到顶部,AsyncListDiffer 不起作用

RecyclerView scroll to top with AsyncListDiffer not working

我正在使用 RecyclerViewAsyncListDiffer(计算新旧项目之间的差异并在后台线程中设置动画)。

我有一个按钮可以对列表进行排序。在我对它进行排序并使用 mDiffer.submitList(items); 将其重新设置为 RecyclerView 之后,我还调用了 recyclerView.scrollToPosition(0) 或 (smoothScrollToPosition(0)),但它没有任何效果。

我认为这种行为是预料之中的,因为 AsyncListDiffer 可能在调用 scrollToPosition(0) 时仍在计算差异,因此它没有任何效果。此外,默认情况下 AsyncListDiffer 不会滚动回顶部,而是让 RecyclerView 保持相同状态。

但是如何让 RecyclerViewAsyncListDiffer 完成并更新后滚动到顶部?

这里得到了回答:

基本上,如果您以不同的顺序提交相同的列表,它将被忽略。所以首先你需要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

如果您查看 AsyncListDiffersubmitList 的 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()中注销观察者