j2emanue
j2emanue

Reputation: 62519

android viewpager with tablayout - how to add other fragments inside of a tab?

so i have the classic viewpager in a tablayout that looks something like this: enter image description here

My viewPagerAdapter class looks like this:

public class HomePagerAdapter extends FragmentPagerAdapter { int mNumOfTabs;

public HomePagerAdapter(FragmentManager fm, int NumOfTabs) {
    super(fm);
    this.mNumOfTabs = NumOfTabs;
}

@Override
public Fragment getItem(int position) {

    switch (position) {
        case 0:
            TabHomeFragment tab1 = new TabHomeFragment();
            return tab1;
        case 1:
            TabShopFragment tab2 = new TabShopFragment();
            return tab2;
        case 2:
            TabMeFragment tab3 = new TabMeFragment();
            return tab3;
        default:
            return null;
    }
}

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

}

So now on one of the tabs i need to add a fragment on top of it. so its a search fragment really. i do a search in the tab bar and i want the back end search results to appear in only ONE of the tabs (the first one). but i want the search results displayed in a fragment called searchFragmentResults and i want it to be laid out on top of the first tab. Then when user hits the back button it will just go back to the original content in the first tab. Is this possible with view pager ?

so visually when i hit the search icon on the first tabs tabBar it should bring up a searchView and when i search for the users query it should bring up a another fragment with the results but only in the tab that it started from. Here is an example: enter image description here

i cant call fragtransaction.replace(somecontentview, somefragment); (or its add method) because i did not add them to a contentview. i let the viewpager do it for me. So how is this achieved ?

Upvotes: 2

Views: 2622

Answers (1)

j2emanue
j2emanue

Reputation: 62519

i figured out how to do this. The tab should be a fragment who's purpose is to only contain other fragments. the idea is based off of this SO. But i had a need to do it for a viewPager. Lets go through the steps. first the viewpager adapter:

    public class HomePagerAdapter extends FragmentStatePagerAdapter {

    //integer to count number of tabs
    int tabCount;
    private Fragment mCurrentFragment;

    private String[] tabTitles = new String[]{"tab0", "tab1", "tab2", "tab3"};

    //Constructor to the class
    public HomePagerAdapter(FragmentManager fm, int tabCount) {
        super(fm);
        this.tabCount = tabCount;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                TabHomeContainerFragment tab1 = new Tab0ContainerFragment();
                return tab1;
            case 1:
                TabShopFragment tab2 = new Tab1ContainerFragment(); 
                return tab2;
            case 2:
                TabMeFragment tab3 = new Tab2ContainerFragment();
                return tab3;
            case 3:
                TabBagFragment tab4 = new Tab3ContainerFragment();
                return tab4;
            default:
                return null;
        }
    }

    //Overriden method getCount to get the number of tabs
    @Override
    public int getCount() {
        return tabCount;
    }

    public Fragment getCurrentFragment() {
        return mCurrentFragment;
    }
    //* this is key to get the current tab to pop the fragments afterwards**/
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        if (getCurrentFragment() != object) {
            mCurrentFragment = ((Fragment) object);
        }
        super.setPrimaryItem(container, position, object);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return tabTitles[position];
    }

Lets go into a container to see how it would look:

add this to your xml for all the containers (along with anything else you want visually but they ALL must have the same id of container_framelayout:

     <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <FrameLayout android:id="@+id/container_framelayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:android="http://schemas.android.com/apk/res/android" />

    </android.support.design.widget.CoordinatorLayout>

you dont have to put it in a coordinatorLayout i just find it fixes some bugs and works well.

In your fragments base class i copied almost the same code from the SO i mentioned above but slight modification if you want to add tag or not:

public void replaceFragment(Fragment fragment, boolean addToBackStack,String tag) {
        FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
        if (addToBackStack) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.container_framelayout, fragment,tag);
        transaction.commit();
        getChildFragmentManager().executePendingTransactions();
    }

    public boolean popFragment() {
        Log.e("test", "pop fragment: " + getChildFragmentManager().getBackStackEntryCount());
        boolean isPop = false;
        if (getChildFragmentManager().getBackStackEntryCount() > 0) {
            isPop = true;
            getChildFragmentManager().popBackStack();
        }
        return isPop;
    }

Now lets look at the activities layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/white"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="fixed"
        app:tabGravity="fill"
        app:tabTextColor="@color/black"
        app:tabSelectedTextColor="@android:color/darker_gray"
        />

Now i'll show you how to set up the tablayout in the activity hosting the tablayout: its standard:

    public class HomePageActivity  implements TabLayout.OnTabSelectedListener {

               private final int NUM_OF_TABS = 4;

               @BindView(R.id.pager)
               public ViewPager viewPager;

               public HomePagerAdapter adapter;

               @BindView(R.id.tabLayout)
               TabLayout tabLayout;

               @NonNull
               @Override
               public HomePagePresenter createPresenter() {
                   return new HomePagePresenter();
               }

               @Override
               protected void onCreate(Bundle savedInstanceState) {
                   super.onCreate(savedInstanceState);
                   setContentView(R.layout.activity_homepage);
                   ButterKnife.bind(this);
                   initView();
               }

               private void initView() {

                   for (int i = 0; i < NUM_OF_TABS; i++)
                       tabLayout.addTab(tabLayout.newTab());

                   adapter = new HomePagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());

                   //Adding adapter to pager
                   viewPager.setAdapter(adapter);

                   tabLayout.addOnTabSelectedListener(this);

                   tabLayout.setupWithViewPager(viewPager);

                   // configure tab icons
                   int[] imageTabResId = {
                           R.drawable.welcome1,
                           R.drawable.welcome2,
                           R.drawable.welcome3,
                           R.drawable.welcome1};

                   for (int i = 0; i < imageTabResId.length; i++) {
                       tabLayout.getTabAt(i).setIcon(imageTabResId[i]);
                   }
               }

/** this is key. we get the current fragment showing and pop it **/
               @Override
               public void onBackPressed() {
                   boolean isPopFragment = false;
                   isPopFragment = ((BaseFragment) adapter.getCurrentFragment()).popFragment();
                   if (!isPopFragment) {
                       finish();
                   }
               }

               @Override
               public void onTabSelected(TabLayout.Tab tab) {
                   viewPager.setCurrentItem(tab.getPosition());
               }

               @Override
               public void onTabUnselected(TabLayout.Tab tab) {

               }

               @Override
               public void onTabReselected(TabLayout.Tab tab) {

               }
           }

I guess the major part is onBackPress getting the current fragment.

Upvotes: 1

Related Questions