【问题标题】: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)
}

【问题讨论】:

    标签: android android-recyclerview kotlin android-architecture-components


    【解决方案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_width="match_parent"
            android:layout_height="wrap_content">
                   <android.support.v7.widget.RecyclerView
                        android:id="@+id/rv"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        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。;)
        • 从性能的角度来看,这太糟糕了
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-21
        相关资源
        最近更新 更多