在顶部添加新项目后,回收器视图未滚动到顶部,因为尚未发生对列表适配器的更改

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 项目时,滚动视图跳到顶部

React-Native 使用 Flatlist 滚动到顶部

iOS滚动视图禁用自动滚动到顶部