【问题标题】:RecyclerView scroll to top with AsyncListDiffer not workingRecyclerView 滚动到顶部,AsyncListDiffer 不起作用
【发布时间】:2019-03-20 13:53:21
【问题描述】:

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

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

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

但是我如何告诉RecyclerViewAsyncListDiffer 完成并更新它之后滚动到顶部?

【问题讨论】:

  • 您是否尝试过提交新项目之前滚动?也许这不是您需要的解决方案,但至少可以知道操作失败是因为AsyncListDiffer 仍在运行还是因为其他原因。
  • 我刚试过,没用。我还刚刚注意到,当我对列表进行排序并提交时,RecyclerView 项目没有重新绑定(刷新)。我必须往下拉一点,然后我才能看到已排序的项目。但这可能是另一个问题..
  • 也许其他问题可能会有所帮助stackoverflow.com/questions/41357303/…stackoverflow.com/questions/30845742/… 关于重新绑定,看起来您可能对稳定的 ID 有问题。你能提供你的适配器代码吗?替代。见medium.com/@hanru.yeh/…
  • 底线:要么禁用适配器中的稳定 ID adapter.setHasStableIds(false),或者更好的是,确保它们已启用 adapter.setHasStableIds(true) 并覆盖适配器中的 long getItemId(int position) 以返回 唯一 每个元素的 ID。
  • 很奇怪,但很高兴知道。感谢分享解决方案!

标签: android android-recyclerview android-asynclistdiffer android-diffutils


【解决方案1】:

这里得到了回答:

https://stackoverflow.com/a/55264063/1181261

基本上,如果您以不同的顺序提交相同的列表,它将被忽略。所以首先你需要submit(null)然后提交你重新排序的列表。

【讨论】:

  • 当你完成所有这些回收站视图相关的查询后,在 medium 上发布一些对像我这样的菜鸟非常有用的文章:)
【解决方案2】:

我担心虽然 .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 并使其与 newList “差异化”,从而进行动画更新,而不是与 null “差异化”。

【讨论】:

  • 通过使用这个,列表在排序后不会滚动回顶部,并且需要2-3秒才能更改列表顺序(动画显示)。
【解决方案3】:

如果您查看AsyncListDiffersubmitList 的javadocs,您会注意到第二个参数是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);
});

【讨论】:

  • 这并不总是有效。根据我的经验,commitCallback 通常在 RecyclerView(好吧,它的 layoutManager)有机会实际重新绑定和布局它的视图之前执行,因此对 scrollToPosition(0) 的调用最终没有任何效果。使用 Kotlin,您可以启动协程并在调用 scrollToPosition(0) 之前使用 delay(),但这似乎很老套。
【解决方案4】:

在您的“mRecyclerView.setAdapter(adapter)”代码之后,在活动中的适配器上使用 registerAdapterDataObserver:

adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {

    public void onChanged() {
       mRecyclerView.scrollToPosition(0);
    }

    public void onItemRangeRemoved(int positionStart, int itemCount) {
        // same or similar scroll code
        }
    }
});

然后在活动的 onStop() 中注销观察者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多