【问题标题】:Diffutil in recycleview, making it autoscroll if a new item is addedrecyclerview 中的 Diffutil,如果添加了新项目,则使其自动滚动
【发布时间】:2017-09-13 11:09:12
【问题描述】:

如果我们使用DiffUtil.Callback,然后做

adapter.setItems(itemList);
diff.dispatchUpdatesTo(adapter);

我们如何确保添加的新元素会滚动到新位置。

我有一个案例,我看到 item 消失了,并且在顶部创建了一个新元素作为第一个元素,但不可见。它隐藏在顶部,直到您向下滚动以使其可见。 在使用DiffUtil 之前,我是手动实现的,在我知道我在某个位置(顶部)插入之后,我可以滚动到。

【问题讨论】:

  • 只需添加项目并使用RecyclerView.scrollToPosition(int position)。在我不得不这样做的时候工作,即使使用 DiffUtil。

标签: android android-recyclerview


【解决方案1】:

您也可以使用dispatchUpdatesTo(ListUpdateCallback) 方法。

所以你可以只实现一个ListUpdateCallback,它会为你提供插入的第一个元素

class MyCallback implements ListUpdateCallback {
    int firstInsert = -1;
    Adapter adapter = null;
    void bind(Adapter adapter) {
        this.adapter = adapter;
    }
    public void onChanged(int position, int count, Object payload) {
        adapter.notifyItemRangeChanged(position, count, payload);
    }
    public void onInserted(int position, int count) {
        if (firstInsert == -1 || firstInsert > position) {
            firstInsert = position;
        }
        adapter.notifyItemRangeInserted(position, count);
    }
    public void onMoved(int fromPosition, int toPosition) {
        adapter.notifyItemMoved(fromPosition, toPosition);
    }
    public void onRemoved(int position, int count) {
        adapter.notifyItemRangeRemoved(position, count);
    }
}

然后手动滚动RecyclerView

myCallback.bind(adapter)
adapter.setItems(itemList);
diff.dispatchUpdatesTo(myCallback);
recycler.smoothScrollToPosition(myCallback.firstInsert);

【讨论】:

  • 注意:即使开始之前不可见,这也会滚动到第一个位置。为了获得更好的用户体验,您应该尝试保留用户当前的滚动位置,并且只有在更新之前开始可见时才滚动到开始。
  • 感谢分享!
  • @LorneLaliberte 您能否提供代码示例以供您提出建议?我认为这将是最好的解决方案
  • @okset 我已经做到了。我对这个问题的回答包含将完成我的评论建议的代码。
【解决方案2】:

有一种简单的方法可以做到这一点,如果项目被插入到可视区域之外,它还可以保留用户的滚动位置:

import android.os.Parcelable;

Parcelable recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
// apply diff result here (dispatch updates to the adapter)
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);              

使用这种方法,如果将新项目插入到用户可以看到的位置,它们就会变得可见 - 但如果将项目插入到视图之外,则用户的观点会被保留。

【讨论】:

  • 谢谢!它可以工作,只需在更新项目之前保存实例状态并在更新后立即恢复
  • 谢谢。我将其用作某些边缘情况的解决方法。一个关于不需要的自动滚动的跟踪器被打开 - issuetracker.google.com/issues/70149059
  • 这应该是公认的答案。效果很好!非常感谢!
  • 看来这是唯一可行的解​​决方案。但为什么?我喜欢新的 ListAdapter,但有时它会很痛苦……是因为 DiffUtil 内容比较是错误的吗?
【解决方案3】:

我使用RecyclerView.AdapterDataObserver 检测何时添加了新项目,然后滚动到我的列表顶部;

adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        super.onItemRangeInserted(positionStart, itemCount);
        recycleView.smoothScrollToPosition(0);
    }
});

【讨论】:

  • 如果您希望在添加新项目时始终滚动到顶部,这很有效。谢谢@gregdev
  • 我最终使用了这个和onItemRangeRemoved,因为我可以在更改范围时始终滚动到顶部。 onItemRangeChanged 对我来说工作不正常。谢谢!
猜你喜欢
  • 2017-05-05
  • 2020-06-04
  • 1970-01-01
  • 1970-01-01
  • 2017-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多