Android FragmentTab node and fragments inside fragments

I have an application with this hierarchy:

FragmentTabHost (Main Activity) - Fragment (tab 1 content - splitter view) - Fragment (lhs, list) - Framment (rhs, content view) - Fragment (tab 2 content) - Fragment (tab 2 content) 

All kinds of fragments are inflated from resources.

When the application starts, everything appears and looks normal. When I switch from the first tab to another tab and vice versa, I get inflation exceptions trying to recreate tabs of the 1st kind.

Digging a little deeper, this is what happens:

  • At the first boot, bloating the separator view causes two child fragments to be added to the fragment manager.
  • When switching from the first tab, this view is destroyed, but its child fragments remain in the fragment manager
  • When you switch to the first tab, the view is overestimated, and since the old child fragments are still in the fragment manager, an exception is thrown when creating new child fragments (by inflation).

I worked on this by removing child fragments from the fragment manager (I use Mono), and now I can switch tabs without exception.

 public override void OnDestroyView() { var ft = FragmentManager.BeginTransaction(); ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ListFragment)); ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ContentFragment)); ft.Commit(); base.OnDestroyView(); } 

So, I have a few questions:

  • Is this done right?
  • If not, how do I do this?
  • In any case, how to save the state of the instance instance in all this so that I do not lose the display state when switching tabs?
+5
android android-tabhost android-fragments android-nested-fragment xamarin.android
source share
2 answers

I'm not sure how to do this in Mono, but to add child fragments to another fragment, you cannot use the FragmentManager for Activity . Instead, you should use the ChildFragmentManager to host the Fragment :

http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager () http://developer.android.com/reference/android/support/v4/app/Fragment.html#getChildFragmentManager ()

The main FragmentManager Activity processes your tabs.
ChildFragmentManager of tab1 handles split views.

+2
source share

OK, I finally figured this out:

As suggested above, first I changed the creation of the fragment, which will be executed programmatically, and whether they added it to the manager of the child fragments, for example:

 public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) { var view = inflater.Inflate(Resource.Layout.MyView, viewGroup, false); // Add fragments to the child fragment manager // DONT DO THIS, SEE BELOW var tx = ChildFragmentManager.BeginTransaction(); tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); tx.Commit(); return view; } 

As expected, every time I switch tabs, an additional instance of Lhs / RhsFragment will be created, but I noticed that the old LHs / RhsFragment OnCreateView will also be called. Therefore, after each tab switch in OnCreateView there will be another call. Tab switching 10 times = 11 OnCreateView calls. This is obviously wrong.

After looking at the source code for FragmentTabHost, I can see that it just detaches and reattaches a fragment of the contents of the tab when switching tabs. It seems that the parent Fragment ChildFragmentManager supports child fragments and automatically recreates their views when the parent fragment is reattached.

So, I moved the creation of fragments to OnCreate and only if we do not boot from the saved state:

 public override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); if (savedInstanceState == null) { var tx = ChildFragmentManager.BeginTransaction(); tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); tx.Commit(); } } public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) { // Don't instatiate child fragments here return inflater.Inflate(Resource.Layout.MyView, viewGroup, false); } 

This fixed the creation of additional views and switching tabs, mostly working now.

The next question is saving and restoring the state of a view. In child fragments, I need to save and restore the selected item. I originally had something like this (this is a child fragment of OnCreateView)

 public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) { var view = inflater.Inflate(Resource.Layout.CentresList, container, false); // ... other code ommitted ... // DONT DO THIS, SEE BELOW if (savedInstance != null) { // Restore selection _selection = savedInstance.GetString(KEY_SELECTION); } else { // Select first item _selection =_items[0]; } return view; } 

The problem is that on the tab, the host does not call OnSaveInstanceState when switching tabs. Rather, the child fragment is kept alive, and the _selection variable can simply be left alone.

So, I moved the selection control code to OnCreate:

 public override void OnCreate(Bundle savedInstance) { base.OnCreate(savedInstance); if (savedInstance != null) { // Restore Selection _selection = savedInstance.GetString(BK_SELECTION); } else { // Select first item _selection = _items[0]; } } public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) { // Don't restore/init _selection here return inflater.Inflate(Resource.Layout.CentresList, container, false); } 

Now everything works fine, both when switching tabs, and when changing orientation.

+2
source share

All Articles