3

The problem is highlighted below, the first part describes what is working so far.

I use multiple fragments with one activity. The upper part of the figure shows a single fragment, e.g. a list with some random content. If I click somewhere it will open the AboutTheAppFragment. When I click back it shows ASingleFragment again. This works perfectly, but that was the easy part :)

a busy cat

Here is the code snipped I used to open the new fragment.

AboutTheAppFragment aboutTheAppFragment = new AboutTheAppFragment();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(mContainer.getId(), aboutTheAppFragment, AboutTheAppFragment.class.getName());
fragmentTransaction.addToBackStack(null);

Now here's my problem

Instead of ASingleFragment I like to display a TabHostPagerFragment which uses a TabHost and a ViewPager to display the fragments (see figure below). Lets say I have the two fragments TabOneFragment and TabTwoFragment, but only one is shown in the ViewPager. This works great too, I can simply slide on the screen and it switches between the two tab fragments. However, if I click somewhere to open the AboutTheAppFragment and click back again, both fragments TabOneFragment and TabTwoFragment do not display anything. If I rotate the device it reloads/restores the fragments again and everything works fine. So my question is, how can I add TabOneFragment and TabTwoFragment to the backstack, that they are shown correctly when I click the back button? Note that it works like in scenario 1 when I set TabOneFragment as ASingleFragment, then I click somewhere and click back again. But it does not work when I use the TabHostPagerFragment.

a busy cat

Here are some code snippets of the 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"));
    }
}

I noticed that savedInstanceState is always null. Any idea how I could fix this and store the tabs in a saved instance state?

I'ld appreciate any help.

Best regards, Michael

EDIT :

Here is the calling hierarchy of the fragments:

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
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Michael
  • 3,982
  • 4
  • 30
  • 46

2 Answers2

1

In your TabHostFragment, TabOneFragment and TabTwoFragment's constructors call:

   setRetainInstance(true);

You currently have nothing that is 'reconstructing' the fragments' state. When you replace ASingleFragment with AboutTheAppFragment ASingleFragment is destroyed and forgotten until you press 'back', at which point the system will recreate the view heirachy but none of their contents. Calling setRetainInstance(true) will tell the system to retain the entire fragment including its data so it can be restored.

onActivityCreated()'s saveInstanceState doesn't come into play here, that's for the parent Activity not the child Fragments.

NeilS
  • 625
  • 7
  • 13
  • Thanks for your reply! I tried your approach, but it doesn't work. I also double checked that all constructors are called. I copied the calling hierarchy in my post - and I'm wondering why the onDestroy methods of `TabOneFragment` and `TabTwoFragment` are never called. Any idea? By the way, I also implemented a pager (to show pictures) without a TabHost and this works perfectly. I guess its some specific about the TabHost implementation :-/ – Michael Sep 25 '13 at 07:48
  • It seems that the TabHost doesn't retain its state properly. If you've done setRetainInstance(true) then the Fragments shouldn't be destroyed so fragment.onCreate() won't be called. But their views (the tabhost) will be destroyed so onCreateView() is called. You add the tabs to the adapter in onActivityCreated() but it creates a new TabAdapter, where does that get assigned to the viewPager? I'd be tempted to move that code into onCreateView() then call mViewPager.setAdapter() just to make sure it's all done together. – NeilS Sep 25 '13 at 12:17
  • @NeilS how u resolved ? i m also facing same issue in viewpager+fragmetns – Erum Aug 20 '15 at 06:53
  • Do you have exactly the same problem as the original question? Did you try `setRetainInstance(true)`? As this question is quite old it might be better to create a new question - link it here and I'll see if I can help. – NeilS Aug 20 '15 at 16:26
0

I have a complex layout with 3 tabs in a fragment, that gets switched out for other fragments. I realized that the ViewpagerAdapter will retain state, even if you press the home button. My problem was switching back and forth would null out the child fragment UI view elements and crash. The key is to not new out your ViewPagerAdapter. Adding the null check for the Adapter worked for me. Also, be sure to allocate setOffscreenPageLimit() for your needs. Also, from what I understand setRetainInstance(true); should not be used for fragments that have UI, it is designed for headless fragments.

In the fragment that holds your Tabs:

@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;
}
Len
  • 542
  • 1
  • 5
  • 11