8

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;
}
Community
  • 1
  • 1
Stanley
  • 5,261
  • 9
  • 38
  • 55

3 Answers3

3

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.

SiSa
  • 2,594
  • 1
  • 15
  • 33
0

you are getting this problem because every time you replace the fragment you add that fragment into back stack. Backstack is maintained for fragment depends on the activity in which fragment is added. so every time you do addToBackStack() it maintain in same activity. and when you are trying to use your scenario last added fragment that is at the top of the backstack is Tab2DetailFragment. to solve this problem you need to clear the backstack every time when are switching to other tab. remember you need to clear all fragment from backstack not activity.

Umesh Saraswat
  • 560
  • 1
  • 8
  • 22
  • This is not really helpful, nor is it an answer. I understand WHY the problem is occurring - it is because I keep on adding more fragments on top of the single stack and the latest fragment I add is always on top. When I click the back button they are then popped off the stack in the order in which they were added, regardless of which "tab" it was added from. The question is: how to implement a separate stack for each of the tabs so that it works correctly. – Stanley Aug 19 '15 at 12:07
  • 1
    See the links to older instances of this question to which I refer. Even though there is technically only one backstack, these examples show that it is indeed possible to simulate behavior as if there were a different backstacks. The problem is that these examples are outdated and don't properly explain proper use of getChildFragmentManager which seems to be the correct way to simulate this behavior. My question asks for a solution to get this functionality working. – Stanley Aug 19 '15 at 13:25
  • I'm having the exact same problem. when switching to a tab that causes the first root fragment to be destroyed (because I am using FragmentStateAdapter) I get a crash when I press back button since the view is not found any more. I don't know why this is not implemented by the SDK. – Kumait Mar 29 '16 at 21:37
0

You can use below code;

@Override
public void onBackPressed() {

    //get current tab index.
    if (viewPager.getCurrentItem() == 1) {
        viewPager.setCurrentItem(0);
    } else if (viewPager.getCurrentItem() == 2) {
        viewPager.setCurrentItem(0);
    } else if (viewPager.getCurrentItem() == 3) {
        viewPager.setCurrentItem(0);
    } else {
        super.onBackPressed();
    }
}
Vishal Vaishnav
  • 3,346
  • 3
  • 26
  • 57