ViewPager中带有StaggeredGridLayoutManager的RecyclerView,返回fragment时自动排列item
Posted
技术标签:
【中文标题】ViewPager中带有StaggeredGridLayoutManager的RecyclerView,返回fragment时自动排列item【英文标题】:RecyclerView with StaggeredGridLayoutManager in ViewPager, arranges items automatically when going back to fragment 【发布时间】:2021-04-08 21:27:20 【问题描述】:我在我的应用程序中使用导航组件,使用谷歌高级示例 (here)。 我的问题是当返回片段时,t他的滚动位置不会丢失,但它会重新排列项目并移动最高可见项目,以便这些项目的顶部与 recyclerview 的顶部对齐。请看这个:
在进入下一个片段之前:
然后返回片段:
这个问题很重要,因为有时单击的项目会向下滚动并且直到向下滚动才能看到。 如何防止这种行为?
请考虑:
如果使用导航组件更改片段,则会存在此问题。如果使用supportFragmentManager.beginTransaction()
启动片段或启动另一个活动,然后转到该片段,则可以。但是如果我使用导航组件导航到另一个片段,则存在此问题。(可能是因为重新创建片段)
如果在 ViewPager 中使用片段也会存在这个问题。即 recyclerView 位于使用 ViewPagerAdapter 处理的片段中,而 viewPager 位于使用 Navigation 组件打开的 HomeFragment 中。如果 recyclerView 在 HomeFragment 中就没有问题。
LinearLayoutManager 没问题。仅适用于 StaggeredGridLayoutManager。
使用 ViewPager2 和 FragmentStatePagerAdapter 没有区别
我试图阻止片段的重新创建(通过这个solution)但没有解决。
更新: 您可以从 here
克隆有此问题的项目【问题讨论】:
我不能花时间在项目设置上,但如果你能提供一个我可以 fork 和运行的功能演示,那么我可以提供帮助。 @HarisDautović 感谢您的回复。你可以从这个链接得到:github.com/thawri1/MyStaggeredListSample 【参考方案1】:当使用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
【讨论】:
谢谢。它在示例项目中工作。但不是在我的真实项目中!事实上,我已经在我的真实应用程序中尝试过你的解决方案,但它并没有解决问题。在你回答之后,我又试了一次。但我没有看到任何变化。我还将几乎整个示例文件复制到实际项目中,但也没有看到变化。之后,我将所有真实的应用程序依赖项复制到示例应用程序。现在问题返回到示例项目!经过这么多尝试和错误,我意识到问题出在 Paging 3 库(刚刚导入但未使用)! 依赖是:实现“androidx.paging:paging-runtime-ktx:3.0.0-alpha11” 我猜从分页中排除某些东西可以解决问题但我不知道是什么 最新稳定版本不会发生这种情况 -> 实现“androidx.paging:paging-runtime-ktx:2.1.2”。 我知道。但版本 3 和 2 非常不同。我的应用程序几乎可以发布了。也许我必须为第 3 页创建新问题。【参考方案2】:当您从一个片段导航到另一个片段时,导航组件的行为是正常的。我的意思是,前一个片段中的onDestroyView()
方法被执行,所以这意味着你的视图被破坏了,但片段没有被破坏。记住片段有两个生命周期,一个用于片段,另一个用于视图,有一个 video 关于它。
此外,在问题跟踪器中注册了一些问题,以避免在某些情况下出现这种行为以及 GitHub 问题:
https://issuetracker.google.com/issues/127932815 https://github.com/android/architecture-components-samples/issues/530问题是当你有一个很重的片段时重新创建,更容易不破坏它,只添加一个片段。因此,当您返回时,它不会被重新创建。但是,因为这种行为不是导航组件的一部分。
解决方案
最简单的解决方案是不使用导航组件并使用传统方式,因为您可以看到这在您的用例中非常有效。
您可以只针对这个用例使用传统方式,而针对其他情况使用导航组件。
您可以在活动中扩充此视图。所以你正在添加联合国活动
但是如果前面的树选项是不可能的。您可以尝试以下方法:
如果你正在使用 viewModel,你可以使用SaveState。基本上,它可以保存片段中的数据,它就像一个地图数据结构,因此您可以保存列表或回收器视图中的位置。当回到这个片段时,从这个 saveState 对象中获取位置,然后使用scrollToPosition
方法来添加真实的位置。
回收站视图具有恢复位置的方法。您可以查看其用例,因为首先您需要数据,然后添加实际位置,有关更多详细信息,您可以访问this link。当您丢失内存并且需要使用异步数据重新创建回收器视图时,此回收器视图配置也很有用。
最后,如果你想了解更多关于 fragment 是如何与导航组件一起工作的,你可以看到这个link
【讨论】:
其实滚动位置并没有丢失。只是将最顶部的项目对齐到 recyclerview 的顶部 我认为它与顶部对齐,因为。它正在再次添加数据。所以就像你第一次打开那个片段 关键是这种行为只存在于viewpager中。 任何有数据的组件都会发生这种情况。您可以看到问题跟踪器和 GitHub 页面以上是关于ViewPager中带有StaggeredGridLayoutManager的RecyclerView,返回fragment时自动排列item的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI ScrollView 不计算自定义 StaggeredGrid 高度
Kotlin Compose 自定义布局 StaggeredGrid
ViewPager + FragmentStatePagerAdapter + 方向改变
CollapsingToolbar, Viewpager 有 recyclerview 和 NestedScrollview