Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ViewPageAdapter with a separate backstack for each tab

I have an Android App with a single activity. The activity contains a SlidingTabLayout with a ViewPageAdapter as in this example. Each tab contains a respective root fragment which I then replace with other fragments as the user uses that screen. A visual representation is shown below:

enter image description here

With my current implementation I do the following: go to tab1, the ProductListFragment immediately loads by replacing the ProductRootFragment with the ProductListFragment. I click on a product and it replaces the ProductListFragment with the ProductDetailFragment. Now I switch to tab2 and Tab2ListFragment immediately loads by replacing Tab2RootFragment. I click on an item and the Tab2DetailFragment replaces the Tab2ListFragment. I now return to Tab1 and, as I expect, ProductDetailFragment is still displayed. However, if I now click the back button, it doesn't take me back to ProductListFragment as I expect. Instead it pops off Tab2DetailFragment from the backstack.

I want to implement a separate backstack for each "tab" fragment. There are several posts about this already but they all seem to be outdated and some suggest very elaborate solutions. (See here, here and here...)

It seems that in the latest versions of the SDK Android can automatically handle this backstack hierarchy without the need to manually implement some kind of backstack by using getChildFragmentManager rather than getSupportFragmentManager in my transactions where I replace the fragments. I have, however, not been able to get this to work. (I intuitively feel that the correct solution lies along the lines of letting Android handle this as it feels like manually overriding the back button and implementing your own stack is an elaborate hack.)

Can someone please explain how to properly implement the expected behavior where a separate backstack is maintained for each tab? Or perhaps link to a simple, well-designed and up-to-date example of how to achieve the desired behavior?

Additional information about my code: In my MainActivity.java, I construct the ViewPagerAdapter using getSupportFragmentManager().

ViewPagerAdapter.java

@Override
public Fragment getItem(int position) {
    switch(position){
        case 0: return new ProductRootFragment();
        ...
    }
    ...
}

ProductRootFragment.java

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

    FragmentTransaction transaction = getFragmentManager()
            .beginTransaction();
    transaction.replace(R.id.product_root_container, new ProductListFragment());
    transaction.commit();
    return view;
}

ProductListFragment.java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View layout = inflater.inflate(R.layout.product_list_fragment, container, false);
    view = (ProductListView) layout;
    return view;
}
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    ...
    ProductDetailFragment newFragment = new ProductDetailFragment();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.product_root_container, newFragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

ProductDetailFragment.java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    view = (ProductDetailView) inflater.inflate(R.layout.product_detail_fragment, container, false);
    return view;
}
like image 574
Stanley Avatar asked Aug 19 '15 09:08

Stanley


People also ask

What is the difference between ViewPager and ViewPager 2?

ViewPager2 is an improved version of the ViewPager library that offers enhanced functionality and addresses common difficulties with using ViewPager . If your app already uses ViewPager , read this page to learn more about migrating to ViewPager2 .

What is ViewPager adapter in Android?

ViewPager in Android allows the user to flip left and right through pages of data. In our android ViewPager application we'll implement a ViewPager that swipes through three views with different images and texts.

How many methods does the PageTransformer of a ViewPager expose?

PageTransformer interface and supply it to the view pager. The interface exposes a single method, transformPage() . At each point in the screen's transition, this method is called once for each visible page (generally there's only one visible page) and for adjacent pages just off the screen.


1 Answers

I know its late to answer this question, but maybe someone still need it. For achieve this you must use getChildFragmentManager() instead of getFragmentManager() in your root fragments

For example I have a activity with ViewPager and TabLayout and I add RootFragment1 and RootFragment2 to ViewPager and setup TabLayout with ViewPager. Now in my RootFragment1 for replace my Tab1Fragment1 I use this code:

final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.replace(R.id.RootFrame1, Tab1Fragment1.newInstance(), "MyTag");
ft.commit();

And need the method to popup fragment from backstack likes this:

public boolean popBackStack() {
    if(getChildFragmentManager().getBackStackEntryCount() > 0) {
        getChildFragmentManager().popBackStackImmediate();
        return true;
    } else
        return false;
}

And for example I have a button in the Tab1Fragment1 that replace fragment Tab1Fragment2 with current. The button onClickListener must look likes this:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        final FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.RootFrame1, Tab1Fragment2.newInstance(), "MyTag");
        ft.addToBackStack(null);
        ft.commit();
    }
});

I do same with second fragment (RootFragment2).

After that, now I only need to override onBackPressed() of my MainActivity likes below:

@Override
public void onBackPressed() {
    if (viewPager.getCurrentItem() == 0) {
        RootFragment1 fragment = ((RootFragment1) sectionsPagerAdapter.getItem(0));
        if (!fragment.popBackStack())
            super.onBackPressed();
    } else if (viewPager.getCurrentItem() == 1) {
        RootFragment2 fragment = ((RootFragment2) sectionsPagerAdapter.getItem(1));
        if (!fragment.popBackStack())
            super.onBackPressed();
    } else
        super.onBackPressed();
}

That I check if each tab contains fragment then I popup from its backstack else call super.onBackPressed() to do default action.

like image 133
SiSa Avatar answered Nov 14 '22 02:11

SiSa