如何在TabHost/ViewPager父片段中恢复子片段

3
问题如下所示,第一部分描述了目前已经实现的内容。
我在一个活动中使用多个片段。图的上半部分显示了一个单独的片段,例如一个带有一些随机内容的列表。如果我点击某个地方,它将打开“AboutTheAppFragment”。当我点击“返回”时,它会再次显示“ASingleFragment”。这部分完美地工作,但那只是简单的部分 :)

a busy cat

这是我用来打开新片段的代码片段。
AboutTheAppFragment aboutTheAppFragment = new AboutTheAppFragment();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(mContainer.getId(), aboutTheAppFragment, AboutTheAppFragment.class.getName());
fragmentTransaction.addToBackStack(null);

现在我的问题是

我想显示一个TabHostPagerFragment,它使用TabHost和ViewPager来显示片段(如下图所示),而不是ASingleFragment。假设我有两个片段TabOneFragmentTabTwoFragment,但只有一个在ViewPager中显示。这也很好用,我可以在屏幕上滑动,它会在两个选项卡片段之间切换。然而,如果我点击某个地方打开AboutTheAppFragment并再次点击back,则两个片段TabOneFragmentTabTwoFragment都不显示任何内容。如果我旋转设备,它会重新加载/恢复片段,一切正常。所以我的问题是,如何将TabOneFragmentTabTwoFragment添加到后退堆栈中,以便在单击back按钮时正确显示它们?请注意,当我将TabOneFragment设置为ASingleFragment时,它的工作方式就像在情况1中一样,然后我单击某个地方并再次单击返回。但是,当我使用TabHostPagerFragment时,它不起作用。

a busy cat

以下是TabHostPagerFragment的一些代码片段:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the tabs pager fragment in the container
    View view = inflater.inflate(R.layout.fragment_tabs_pager, container, false);
    mContainer = container;

    // Set up the TabHost
    mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
    mTabHost.setup();

    // Set up the ViewPager
    mViewPager = (ViewPager) view.findViewById(R.id.pager);

    return view;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // Now initialize the TabsAdapter which will be used to manage the tabs
    mTabsAdapter = new TabsAdapter(activity, mTabHost, mViewPager);

    // Finally add the tabs to the TabsAdapter: meal plan for today and meal plan for the week 
    mTabsAdapter.addTab(mTabHost.newTabSpec("TabOne").setIndicator("TabOne", TabOneFragment.class, null);
    mTabsAdapter.addTab(mTabHost.newTabSpec("TabTwo").setIndicator("TabTwo", TabTwoFragment.class, null);

    if (savedInstanceState != null) {
        // NEVER REACHES THIS CASE
        mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
    }
}

我注意到savedInstanceState总是为空。有什么办法可以解决这个问题并将选项卡存储在已保存的实例状态中吗?
我会很感激任何帮助。
最好的问候,迈克尔
编辑:
以下是片段的调用层次结构:
09-25 09:19:25.695: V/TabsPagerParentFragment(1820): onPause
09-25 09:19:25.695: V/TabsPagerParentFragment(1820): onStop
09-25 09:19:25.703: V/TabsPagerParentFragment(1820): onDestroyView
09-25 09:19:25.710: V/AboutTheAppFragment(1820): onAttach
09-25 09:19:25.710: V/AboutTheAppFragment(1820): onCreate: savedInstanceState == null
09-25 09:19:25.710: V/AboutTheAppFragment(1820): onCreateView: savedInstanceState == null
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onViewCreated
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onActivityCreated
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onViewStateRestored
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onStart
09-25 09:19:25.812: V/AboutTheAppFragment(1820): onResume
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onPause
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onStop
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onDestroyView
09-25 09:19:27.304: V/AboutTheAppFragment(1820): onDestroy
09-25 09:19:27.312: V/TabsPagerParentFragment(1820): onCreateView: savedInstanceState == null
09-25 09:19:27.320: V/TabsPagerParentFragment(1820): onViewCreated
09-25 09:19:27.320: V/TabsPagerParentFragment(1820): onActivityCreated
09-25 09:19:27.328: V/TabsPagerParentFragment(1820): onViewStateRestored
09-25 09:19:27.328: V/TabsPagerParentFragment(1820): onStart
09-25 09:19:27.328: V/TabsPagerParentFragment(1820): onResume
2个回答

1
在您的TabHostFragment中,TabOneFragment和TabTwoFragment的构造函数中调用:
   setRetainInstance(true);

您目前没有任何内容在“重建”片段状态。当您将ASingleFragment替换为AboutTheAppFragment时,ASingleFragment将被销毁并遗忘,直到您按下“返回”按钮,此时系统将重新创建视图层次结构,但不会保留它们的内容。调用setRetainInstance(true)将告诉系统保留整个片段及其数据,以便可以恢复。

onActivityCreated()的saveInstanceState在这里没有作用,它是针对父Activity而不是子Fragments。


谢谢您的回复!我尝试了您的方法,但它不起作用。我还仔细检查了所有构造函数是否都被调用了。我在我的帖子中复制了调用层次结构 - 我想知道为什么TabOneFragmentTabTwoFragmentonDestroy方法从未被调用。有什么想法吗?顺便说一下,我还实现了一个pager(显示图片),没有使用TabHost,这个也完美地工作了。我猜这是关于TabHost实现的一些特定问题 :-/ - Michael
似乎TabHost无法正确地保留其状态。如果您已经执行了setRetainInstance(true),那么Fragment不应该被销毁,因此fragment.onCreate()不会被调用。但是它们的视图(tabhost)将被销毁,因此将调用onCreateView()。您可以在onActivityCreated()中将选项卡添加到适配器中,但它会创建一个新的TabAdapter,那么它在哪里分配给viewPager?我倾向于将该代码移动到onCreateView()中,然后调用mViewPager.setAdapter()以确保所有操作都完成。 - NeilS
@NeilS 你是怎么解决的?我在使用viewpager+fragments时也遇到了同样的问题。 - Erum
你是否遇到了与原问题完全相同的问题?你尝试过使用 setRetainInstance(true) 吗? 由于这个问题比较旧,最好创建一个新问题 - 在这里链接它,我会看看能否提供帮助。 - NeilS

0

我在一个片段中有一个复杂的布局,其中包含3个选项卡,这些选项卡会被其他片段替换。我意识到即使按下主页按钮,ViewpagerAdapter也会保留状态。我的问题是来回切换会使子片段的UI视图元素为空并导致崩溃。关键是不要使用new来创建你的ViewPagerAdapter。为适配器添加空值检查对我很有效。另外,请确保根据需要分配setOffscreenPageLimit()。还有,根据我的理解,setRetainInstance(true); 不应该用于具有UI的片段,它是设计用于无界面的片段。

在持有选项卡的片段中:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_tab, container, false);
    tabLayout = (TabLayout) view.findViewById(R.id.tablayout);
    viewPager = (ViewPager) view.findViewById(R.id.viewPager);

    //Important!!! Do not fire the existing adapter!!
    if (viewPagerAdapter == null) {
        viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
        viewPagerAdapter.addFragments(new AFragment(), "A");
        viewPagerAdapter.addFragments(new BFragment(), "B");
        viewPagerAdapter.addFragments(new CFragment(), "C");
    }
    //Allocate retention buffers for three tabs, mandatory
    viewPager.setOffscreenPageLimit(3);
    tabLayout.setupWithViewPager(viewPager);
    viewPager.setAdapter(viewPagerAdapter);

    return view;
}

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