OK, I finally figured this out:
As suggested above, first I changed the fragment creation to be done programatically and had them added to the child fragment manager, like so:
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, each time I switch tabs, an extra instance of Lhs/RhsFragment would be created, but I noticed that the old Lhs/RhsFragment's OnCreateView would also get called. So after each tab switch, there would be one more call to OnCreateView. Switch tabs 10 times = 11 calls to OnCreateView. This is obviously wrong.
Looking at the source code for FragmentTabHost, I can see that it simply detaches and re-attaches the tab's content fragment when switching tabs. It seems the parent Fragment's ChildFragmentManager is keeping the child fragments around and automatically recreating their views when the parent fragment is re-attached.
So, I moved the creation of fragments to OnCreate, and only if we're not loading from 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 the additional views and switching tab's basically worked now.
The next question was saving and restoring view state. In the child fragments I need to save and restore the currently selected item. Originally I had something like this (this is the child fragment's 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 with this is that the tab host doesn't call OnSaveInstanceState when switching tabs. Rather the child fragment is kept alive and it's _selection variable can be just left alone.
So I moved the code to manage selection 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 it all seems to be working perfectly, both when switching tabs and changing orientation.