在顶部添加新项目后,回收器视图未滚动到顶部,因为尚未发生对列表适配器的更改
Posted
技术标签:
【中文标题】在顶部添加新项目后,回收器视图未滚动到顶部,因为尚未发生对列表适配器的更改【英文标题】:Recycler view not scrolling to the top after adding new item at the top, as changes to the list adapter has not yet occurred 【发布时间】:2019-01-24 03:04:53 【问题描述】:我在我的实时数据开始时获取带有新项目的新列表,然后使用它的数据来更新适配器
viewModel.myLiveData.observe this, Observer myList ->
adapter.submitList(myList)
recyclerView.scrollToPosition(0)
【问题讨论】:
【参考方案1】:submitList
在后台线程上工作,因此总会出现延迟无法解决的竞争条件。幸运的是,当列表计算完成时,我们可以使用RecyclerView.AdapterDataObserver
回调来通知:
yourRecyclerViewAdapter.registerAdapterDataObserver(object: RecyclerView.AdapterDataObserver()
override fun onChanged()
recycler_view_list.scrollToPosition(0)
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int)
recycler_view_list.scrollToPosition(0)
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int)
recycler_view_list.scrollToPosition(0)
override fun onItemRangeInserted(positionStart: Int, itemCount: Int)
recycler_view_list.scrollToPosition(0)
override fun onItemRangeChanged(positionStart: Int, itemCount: Int)
recycler_view_list.scrollToPosition(0)
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?)
recycler_view_list.scrollToPosition(0)
)
viewModel.myLiveData.observe this, Observer myList ->
adapter.submitList(myList)
【讨论】:
很好的解决方案,谢谢!但我不得不跳过onItemRangeInserted()
的第一次调用以避免在旋转时丢失滚动位置。
这是天才,谢谢。有没有更好的解决方案,比如优化的解决方案。
这比其他解决方案效果更好。【参考方案2】:
这样使用
viewModel.myLiveData.observe this, Observer myList ->
adapter.submitList(myList) // Assuming you are notifying adapter by notifydatasetchanged()
recyclerView.post recyclerView.scrollToPosition(0)
这里post
正在给 UI 线程一些时间来用新数据填充 Recycler,然后调用 scrollToPosition
。
【讨论】:
some time -> 帖子告诉 android,将此工作添加到 UI 线程工作队列。这不是“给它一些时间”,这只是您将工作发布到已经在做的事情的列表上的副作用。这就是为什么它可能会这样工作,但要小心,如果数据很长或者提交列表的 diff util 需要更长的时间,这不会总是像你期望的那样运行。 完美!为我解决了。【参考方案3】:你也可以使用
recyclerView.smoothScrollToPosition(0);
viewModel.myLiveData.observe this, Observer myList ->
adapter.submitList(myList)
recyclerView.smoothScrollToPosition(0);
【讨论】:
【参考方案4】:感谢大家的回复。但唯一对我有用的工作是在执行 submitList 后延迟 1 秒。即使不合适,但由于以下原因起作用:
adapter.submitList()
将始终采用相同的执行时间(以毫秒为单位)
我刚刚打过电话
Handler().postDelayed (
recycler.scrollToPosition(0)
, 1000)
【讨论】:
而不是一秒延迟 recyclerView.post recyclerView.scrollToPosition(0) 这会更好。你可以试试这个。此外,每次您的数据更新时,这也不会创建 Handler() 。它将滚动任务排队到同一队列 RecyclerView 用于它的更新。检查我的答案,这样您将节省少量内存(以保存创建的每个处理程序)和计算(创建处理程序对象)也几乎没有明显的滚动延迟。 如果有帮助,别忘了接受和投票。【参考方案5】:如果您要添加平滑滚动,这里是另一个选项。
ListAdapter 类有一个提交回调 (Runnable),它会延迟发布。
public void submitList(@Nullable List<T> list, @Nullable final Runnable commitCallback)
mDiffer.submitList(list, commitCallback);
这就是我更新列表并将平滑滚动到某个位置的方式
viewModel.list.observe(viewLifecycleOwner,
adapter.submitList(it)
recycler.post recyclerJobs.smoothScrollToPosition(0)
)
【讨论】:
【参考方案6】:解决此问题的另一种方法是将 Recyclerview 包装在 Nested ScrollView 中。
<android.support.v4.widget.NestedScrollView
android:id="@+id/nsv_data"
android:layout_
android:layout_>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_
android:layout_
android:nestedScrollingEnabled="false"
tools:listitem="@layout/rv_interview_prep_row"
android:visibility="gone"
android:background="@color/white" />
</android.support.v4.widget.NestedScrollView>
然后在你的 Activity 或 Fragment 中。
viewModel.myLiveData.observe this, Observer myList ->
adapter.submitList(myList)
nestedScrollView.scrollTo(0,0);
【讨论】:
将 RecyclerView 包装在嵌套的滚动视图中几乎总是是个坏主意。例如,其中一个不太为人所知的副作用是 recyclerview 不能再“回收”视图,因为它不知道可用空间的高度(嵌套滚动视图必须膨胀所有视图来确定内容的“高度”是多少)。用 100 行试试这个:没问题。现在试试 1000。;) 从性能的角度来看,这太糟糕了以上是关于在顶部添加新项目后,回收器视图未滚动到顶部,因为尚未发生对列表适配器的更改的主要内容,如果未能解决你的问题,请参考以下文章
UIScrollView + 在滚动屏幕之前将项目固定到顶部
Apple Watch 应用程序在启动应用程序后以编程方式将视图滚动到顶部
在 ConstraintLayout 中选择 Spinner 项目时,滚动视图跳到顶部