Reputation: 4000
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 :)
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);
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.
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
Upvotes: 3
Views: 3597
Reputation: 542
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;
}
Upvotes: 0
Reputation: 635
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.
Upvotes: 1