简体   繁体   中英

Nested fragment in a ViewPager with tabs

We want to make a "facebook-like" navigation.

We have 3 tabs, with a ViewPager, so here we have 3 categorizedViewModel (see under). Each page is a TabRootFragment

public partial class MainView : MvxActionBarActivity, ActionBar.ITabListener

public class MvxFragmentTabPagerAdapter<TFragment> : FragmentPagerAdapter where TFragment : Fragment

private ViewPager pager;

protected override void OnCreate(Bundle bundle)
{
        ...
        this.SupportActionBar.NavigationMode = (int)ActionBarNavigationMode.Tabs;
        ...
        this.fragmentTabPagerAdapter = new MvxFragmentTabPagerAdapter<TabRootFragment>(this.SupportFragmentManager);
        foreach (var categorizedViewModel in this.MainViewModel.CategorizedViewModels)
        {
            this.AddPage(this.fragmentTabPagerAdapter, categorizedViewModel);
        }

        this.pager.Adapter = this.fragmentTabPagerAdapter;
        this.pager.SetOnPageChangeListener(new ViewPageListenerForActionBar(this.SupportActionBar));
}


    private void AddPage(MvxFragmentTabPagerAdapter<TabRootFragment> adapter, CategorizedViewModel categorizedViewModel)
    {
        Mvx.Trace("MainView => AddPage {0}", Enum.GetName(typeof(Category), categorizedViewModel.Category));
        var nestedFragement = new TestView();
        nestedFragement.ViewModel = categorizedViewModel;

        var fragment = new TabRootFragment(nestedFragement);
        adapter.AddFragment(fragment);

        ActionBar.Tab tab = this.SupportActionBar.NewTab();
        tab.SetIcon(CategoryHelper.GetDrawableId(categorizedViewModel.Category));
        tab.SetTabListener(this);
        this.SupportActionBar.AddTab(tab);
    }

    public void OnTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
    {
        Mvx.Trace("MainView => OnTabSelected()");
        this.pager.SetCurrentItem(this.SupportActionBar.SelectedNavigationIndex, false);           
    }

We have for each tab (each fragment of the viewPager), a TabRootFragment which has a framelayout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <FrameLayout
        android:id="@+id/ChildFragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

And therefore, we want to add some TabChildFragments to this TabRootFragment, in order to have 3 separates stacks of fragments ("facebook-like" :)

public class TabRootFragment : Fragment
{
    /// <summary>
    /// The root children
    /// </summary>
    public TabChildFragment rootChildren;

    /// <summary>
    /// Initializes a new instance of the <see cref="TabRootFragment"/> class.
    /// </summary>
    /// <param name="rootChildren">The root children.</param>
    public TabRootFragment(TabChildFragment rootChildren)
    {
        this.rootChildren = rootChildren;
    }

    /// <summary>
    /// Called when [create view].
    /// </summary>
    /// <param name="inflater">The inflater.</param>
    /// <param name="container">The container.</param>
    /// <param name="savedInstanceState">State of the saved instance.</param>
    /// <returns>View.</returns>
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        Mvx.Trace("TabRootFragment => OnCreateView");
        return inflater.Inflate(Resource.Layout.TabRootFragment, container, false);
    }

    /// <summary>
    /// Called when [view created].
    /// </summary>
    /// <param name="view">The view.</param>
    /// <param name="savedInstanceState">State of the saved instance.</param>
    public override void OnViewCreated(View view, Bundle savedInstanceState)
    {
        Mvx.Trace(
            "TabRootFragment => OnViewCreated {0}",
            Enum.GetName(typeof(Category), this.rootChildren.ViewModel.Category));
        base.OnViewCreated(view, savedInstanceState);
        this.ChildFragmentManager.BeginTransaction()
            .Add(Resource.Id.ChildFragment, this.rootChildren)
            .Commit();
    }

All seems to go well until we reach the OnViewCreated of the TabRootFragment . When we hit the fragment manager transaction, we receive a nice Exception:

 Java.Lang.IllegalStateException: Activity has been destroyed

It would seem that the TabRootFragment is not attached to the Activity. But it is set to the pager by the SetCurrentItem in the OnTabSelected method...

How can I check if this fragment is well attached to the activity through ViewPager ?

Donn was right, this is related to this: https://stackoverflow.com/a/15656428/5210 :)

Excepted that my fragment was never detached, and the fix needed to be applied right from the OnAttach method of the TabRootFragment .

Bonus: the Xamarin code to do so:

public class TabRootFragment : MvxFragment
{
    public override void OnAttach(Activity activity)
    {
        Mvx.Trace("TabRootFragment => OnAttach");
        base.OnAttach(activity);
        this.InitChildFragmentManagerState();
    }

    /// <summary>
    /// Initializes the state of the child fragment manager.
    /// </summary>
    private void InitChildFragmentManagerState()
    {
        var classRefProp = typeof(Fragment).GetProperty(
            "class_ref",
            BindingFlags.NonPublic | BindingFlags.Static);

        IntPtr classRef = (IntPtr)classRefProp.GetValue(this);
        var field = JNIEnv.GetFieldID(
            classRef,
            "mChildFragmentManager",
            "Landroid/support/v4/app/FragmentManagerImpl;");

        JNIEnv.SetField(this.Handle, field, IntPtr.Zero);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM