recyclerview 中的 Diffutil,如果添加了新项目,则使其自动滚动
Posted
技术标签:
【中文标题】recyclerview 中的 Diffutil,如果添加了新项目,则使其自动滚动【英文标题】:Diffutil in recycleview, making it autoscroll if a new item is added 【发布时间】:2017-09-13 11:09:12 【问题描述】:如果我们使用DiffUtil.Callback
,然后做
adapter.setItems(itemList);
diff.dispatchUpdatesTo(adapter);
我们如何确保添加的新元素会滚动到新位置。
我有一个案例,我看到 item 消失了,并且在顶部创建了一个新元素作为第一个元素,但不可见。它隐藏在顶部,直到您向下滚动以使其可见。
在使用DiffUtil
之前,我是手动实现的,在我知道我在某个位置(顶部)插入之后,我可以滚动到。
【问题讨论】:
只需添加项目并使用RecyclerView.scrollToPosition(int position)
。在我不得不这样做的时候工作,即使使用 DiffUtil。
【参考方案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
对我来说工作不正常。谢谢!以上是关于recyclerview 中的 Diffutil,如果添加了新项目,则使其自动滚动的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 DiffUtil 更新 RecyclerView 适配器
Android 高性能列表:RecyclerView + DiffUtil