RecyclerView 与 ViewPager 中的 StaggeredGridLayoutManager,返回 fragment 时自动排列项目

RecyclerView with StaggeredGridLayoutManager in ViewPager, arranges items automatically when going back to fragment

我在我的应用程序中使用导航组件,使用 google 高级示例(here)。 我的问题是当回到片段时,他的滚动位置不会丢失 但它会重新排列项目并移动最高的可见项目,以便这些项目的顶部与 recyclerview 的顶部对齐。请看这个:

在进入下一个片段之前:

返回片段后:

这个问题很重要,因为有时点击的项目会下降,直到向下滚动才能看到。 如何防止这种行为?

请考虑:

更新: 您可以从 here

克隆有此问题的项目

当您从一个片段导航到另一个片段时,导航组件的行为是正常的。我的意思是,前一个片段中的 onDestroyView() 方法被执行了,所以这意味着你的视图被破坏了,但片段没有被破坏。请记住片段有 两个生命周期 一个用于片段,另一个用于视图,关于它有一个 video

此外,为了在某些情况下避免这种行为,在问题跟踪器中注册了一些问题,GitHub 问题:

问题是,当您的片段很重时 重新创建 ,不销毁它更容易,只添加一个片段。因此,当您返回时,它不会被重新创建。但是,因为这种行为不是导航组件的一部分。

解决方案

  • 最简单的解决方案是不使用导航组件并使用传统方式工作,正如您所见,这在您的用例中非常有效。

  • 您可以仅针对此用例使用传统方式,而在其他情况下使用导航组件。

  • 您可以在 activity 中扩充此视图。所以你要添加 un activity

  • 但是如果前面的树选项是不行的。您可以尝试以下方法:

    • 如果你正在使用viewModel,你可以使用SaveState。基本上,它可以保存片段中的数据,它就像一个地图数据结构,因此您可以保存列表或回收器视图中的位置。当回到这个片段时,从这个 saveState 对象中获取位置并使用 scrollToPosition 方法来添加真实位置。
    • Recycler 视图有恢复位置的方法。你可以看到它的用例,因为首先你需要数据然后添加真实位置,更多细节你可以访问 this link。当您丢失内存并且需要使用异步数据重新创建回收器视图时,回收器视图的此配置也很有用。

最后,如果你想了解更多关于片段如何与导航组件一起工作,你可以看到这个

当使用 Navigation Component + ViewPager + StaggeredGridLayoutManager 时,在 Fragment 重新创建期间返回了错误的 recyclerView.computeVerticalScrollOffset()

一般来说,支持库中捆绑的所有布局管理器都知道如何保存和恢复滚动位置,但在这种情况下,我们必须为此负责。

class TestFragment : Fragment(R.layout.fragment_test) {

    private val testListAdapter: TestListAdapter by lazy {
        TestListAdapter()
    }

    private var layoutManagerState: Parcelable? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        postListView.apply {
            layoutManager = StaggeredGridLayoutManager(
                2, StaggeredGridLayoutManager.VERTICAL
            ).apply {
                gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
            }
            setHasFixedSize(true)

            adapter = testListAdapter
        }

        testListAdapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT

    }

    override fun onPause() {
        saveLayoutManagerState()
        super.onPause()
    }

    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        restoreLayoutManagerState()
    }

    private fun restoreLayoutManagerState () {
        layoutManagerState?.let { postListView.layoutManager?.onRestoreInstanceState(it) }
    }

    private fun saveLayoutManagerState () {
        layoutManagerState = postListView.layoutManager?.onSaveInstanceState()
    }
}

源代码:https://github.com/dautovicharis/MyStaggeredListSample/tree/q_65539771