Search code examples
androidandroid-fragmentsandroid-activityandroid-tablayout

Should I use a nested fragment for tabs in a fragment?


I have a Main Activity which uses an AHBottomNavigationView for a menu at the bottom of the screen. When a different menu item is clicked, it creates a new fragment corresponding to that menu item with logic like so (condensed switch statement for the simplicity of this question):

fragmentManager.beginTransaction().replace(R.id.content_id, New TheFragmentForTheTabClicked).commit();

Where content_id is the ID of the Main Activity's ConstraintLayout.

Within the fragment for my first navigation menu item, there are two more tabs (using TabLayout), which replace the screen space with another fragment. This is done with a FragmentPagerAdapter, which is set onto a ViewPager, so tapping each tab changes the sub fragment. So at this point, there is a fragment nested in a fragment nested in a class. Here is what it generally is:

 Main Activity  
 |
 +-- Fragment 1 (selected from AHBottomNavigationView)
 |   |
 |   +-- Sub-Fragment 1 (selected by clicking the first tab in Fragment 1)
 |   |
 |   +-- Sub-Fragment 2 (selected by clicking the second tab in Fragment 1)
 |
 +-- Fragment 2 (selected from AHBottomNavigationView)
 |
 +-- Fragment 3 (selected from AHBottomNavigationView)
 |
 +-- Fragment 4 (selected from AHBottomNavigationView)

So my question is this: Is the way I am doing this correct, and if not, what would a better way be? Also, I'm finding that When I tab to Fragment 1 the first time, the swiping and tapping between the two tabs works fine, however if I tap a different bottom navigation menu item (i.e. Fragment 3) and then go back, I get the following 2 issues:

  1. The content in either of the subfragments is not shown
  2. Swiping between the two tabs no longer works. Instead of one motion moving to the different tab, I have to pull across the screen entirely because the indicator gets "stuck" part way between two tabs.

If there is any more information that I can provide, please let me know and I will.

Fragment1.java:

package com.mypackage.mypackage;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * A simple {@link Fragment} subclass.
 */
public class Fragment1 extends Fragment {

    private FragmentActivity mContext;

    public Fragment1() {
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_1, container, false);

    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);

        // Find the view pager that will allow the user to swipe between fragments
        ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewpager);

        // Create an adapter that knows which fragment should be shown on each page
        // using getFragmentManager() will work too
        Fragment1PagerAdapter adapter = new Fragment1PagerAdapter(mContext.getSupportFragmentManager(), mContext);

        // Set the adapter onto the view pager
        viewPager.setAdapter(adapter);

        TabLayout tabLayout = (TabLayout) getView().findViewById(R.id.sliding_tabs);
        tabLayout.setupWithViewPager(viewPager);
    }

    /**
     * Override to set context.  This context is used for getSupportFragmentManager in onCreateView
     * @param activity
     */
    @Override
    public void onAttach(Activity activity) {
        mContext=(FragmentActivity) activity;
        super.onAttach(activity);
    }

}

fragment_1.xml

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.design.widget.TabLayout
            android:id="@+id/sliding_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabBackground="@color/fragment1TabBackground"
            app:tabIndicatorColor="@color/fragment1TabIndicatorColor"/>

        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1"/>

    </LinearLayout>


</android.support.constraint.ConstraintLayout>

Fragment1PagerAdapter.java

package com.mypackage.mypackage;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.content.Context;


public class Fragment1PagerAdapter extends FragmentPagerAdapter {

    private Context context;

    public Fragment1PagerAdapter(FragmentManager fm, Context mContext){
        super(fm);
        context = mContext;

    }

    @Override
    public Fragment getItem(int position){
        if (position == 0){
            return new SubFragment1();
        }
        else{
            return new SubFragment2();
        }
    }

    @Override
    public int getCount() {return 2;}

    @Override
    public CharSequence getPageTitle(int position) {

        switch(position){
            case 0:
                return context.getResources().getString(R.string.sub_fragment_1_page_title);
            case 1:
                return context.getResources().getString(R.string.sub_fragment_2_page_title);
            default:
                return null;
        }
    }
}

Solution

  • When nesting Fragments inside Fragment with ViewPager and swipe feature as FragmentManager which needs to be provided to Adapter recommended is to use: getChildFragmentManager() instead of getSupportFragmentManager() or getFragmentManager(). Because both are actually related to Activities instead of getChildFragmentManager(), as documentation says, is related to Fragment:

    Return a private FragmentManager for placing and managing Fragments inside of this Fragment.