使用ViewPager2的FragmentStateAdapter时,与之关联的Fragment已经不存在。

22

我正在我的项目中使用ViewPager2。我需要在一个包含ViewPager2的片段内使用嵌套片段。在导航到非嵌套片段时,一切正常工作。

但是,在第一次导航嵌套片段后,应用程序会因下面解释的错误而崩溃。

包含嵌套片段的片段的OnCreateView方法如下:

View view = inflater.inflate(R.layout.orders_fragment, null);

ViewPager2 viewPager = view.findViewById(R.id.childViewPager);

TabLayout tabs = view.findViewById(R.id.tabs);

SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager(),getLifecycle());

viewPager.setAdapter(sectionsPagerAdapter);

TabLayoutMediator.TabConfigurationStrategy tabConfigurationStrategy = (tab, position) -> {

    String[] order_activity_tabs = getResources().getStringArray(R.array.situations);

    for (int i=0; i<order_activity_tabs.length; i++) {

        if(i==position)
            tab.setText(order_activity_tabs[i]);

    }
};

TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabs, viewPager, tabConfigurationStrategy);
tabLayoutMediator.attach();

return view;
当我返回包含嵌套片段的片段时,它会崩溃。
 java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id 4fbe17b8-5e22-4e07-a543-4a79445ad39c
        at androidx.fragment.app.FragmentManagerImpl.getFragment(FragmentManagerImpl.java:365)
        at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:549)
        at androidx.viewpager2.widget.ViewPager2.restorePendingState(ViewPager2.java:350)
        at androidx.viewpager2.widget.ViewPager2.dispatchRestoreInstanceState(ViewPager2.java:375)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4045)
        at android.view.View.restoreHierarchyState(View.java:20253)
        at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:548)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)

对于FragmentStatePagerAdapter有解决方案,但是对于FragmentStateAdapter没有。因为我无法重写FragmentStateAdapter中的方法,所以实现这些解决方案是不可能的。

感谢您阅读此内容。任何帮助将不胜感激。


在访问片段之前,请先检查它是否存在于片段堆栈中。 - Hemant Parmar
谢谢回复。但我不确定如何做到这一点? - Eren Tüfekçi
6个回答

25

使用具有FragmentStateAdapter的viewpager2与FragmentStatePagerAdapter具有类似的行为。

实际上,有两种解决这个问题的方法。

第一种方法是将setSaveEnabled()设置为false到我们的viewpager2中。

viewpager2.setSaveEnabled(false)
另一种方法是在适配器中覆盖restoreState并返回null。 查看: https://developer.android.com/reference/androidx/viewpager2/adapter/FragmentStateAdapter 重要提示: 请注意,这不应该是禁用状态的推荐方法,这对于某些用例可能有效,但对于生产应用程序,请考虑保存片段的状态或从像viewmodel这样的状态持有者重新发送状态到UI。

3
很遗憾,restoreState被声明为final。 - JE42
13
  1. 如果你这样做,应用将无法保存并重新创建上一个片段的状态。
  2. 正如@JE42所说,“restoreState”被声明为final。
- extmkv
我认为这更多是用于调试目的,而不是替换片段的保存状态。 - Gastón Saillén
在产品版本中不建议这样做,如果将其设置为false,则不会保存此视图的状态,这意味着当您从另一个屏幕返回到具有视图页面的片段时,它将丢失其先前的数据。 - imansdn
我完全同意@imansdn的观点。我认为这更像是一个快速解决方案,但正如你所说,如果我们想保留状态,它永远不应该用于生产应用程序。我认为对于某些用例,我们可以自己改变状态,因为如果框架让我们改变状态,我看不到任何目的。 - Gastón Saillén

2

关于viewpager2从recyclerview分离的问题存在一些问题 https://issuetracker.google.com/issues/154751401

据我所知,有两种解决方法:

  1. 在你的xml viewpager2组件中添加这行代码。

    android:saveEnabled="false"
    

但是上面的代码不会恢复当前实例,每次弹出时片段都会重新创建。

推荐的解决方案是在您的片段中添加以下行:
override fun onDestroyView() {
   super.onDestroyView()
   binding.pager.adapter = null
}

你能解释一下为什么推荐的解决方案有效吗? - Bugs Happen

0
你所需要做的就是在布局文件中设置你的ViewPager2,使用以下属性。
android:saveEnabled="true"

1
应该将值设置为“false”,而不是“true”吧? - Yazid

0

在 FragmentStateAdapter 中的片段参数中尝试调用 requireActivity()。

private inner class ScreenSlidePagerAdapter(fa: FavoriteFragment) : FragmentStateAdapter(fa.requireActivity())

-2

我之前也遇到了同样的问题。原因是当包含的 Fragment 被重新创建时,它的 childFragmentManager 也会被重新创建。而 FragmentStateAdapter 在传递的 fragmentmanager 中查找要恢复状态的 fragment。

我的解决方案是使用 activity 的 fragmentManager 而不是 fragment 的 fragmentManager,例如使用带有 activity 参数的构造函数。

class MyAdapter(f: Fragment) : FragmentStateAdapter(f.activity!!)

或者在使用 FragmentStateAdapter(FragmentManager, Lifecycle) 构造函数时,请确保传递 parentFragmentManager不是 childFragmentManager


-3
我的问题通过实现新的导航组件得到了解决。它本身处理片段转换。任何遇到这个问题的人都可以更改他们的导航方法。

请问您能否详细解释一下您的解决方案? - imansdn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接