17

I am working on fragment transaction, and the backstack is like this:

fragA => fragB => fragC => fragD

I would like to return to fragA after back fromn the fragD

fragD => onBackPress => fragA

So, I tried code like:

getChildFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

but it clear all the backstack , how can I keep the first fragment in the backstack? Thanks a lot

user782104
  • 13,233
  • 55
  • 172
  • 312
  • 1
    This answer here:- https://stackoverflow.com/a/28115271/9969285 is the easiest and best workaround! It worked in my case with multiple fragments (can be started in any order after first fragment), and back pressed will always return to first created fragment. Very important point to remember is not to add to backstack while creating first fragment, but add to backstack when adding all other fragments. – Jemshid Jul 01 '18 at 20:08

10 Answers10

24

E.g. you can do following:

  • add fragA without adding it to backStack. So it always be in
    activity, and won't react on back button.
  • when you open fragD you should clear fragment BackStack. So when you press back button from D frag you go back to A.

P.S. There are other ways how to do what you want. It depends...

Community
  • 1
  • 1
Leonidos
  • 10,482
  • 2
  • 28
  • 37
  • 2
    I clear the entire backstack, then add back fragA and fragD . thanks – user782104 Feb 06 '15 at 09:45
  • a lot of people make this mistake, remember you are adding a transaction to the backstack so in general the transaction to add the first fragment in the flow should NOT be added to the backstack (why would you ever want to return to an empty state?) – hmac Jan 22 '21 at 10:44
4

Because the "back stack" has a stack-like behaviour...last-in, first-out...the last fragment you added to the back stack will be popped out of the back stack first. You will need to implement the behaviour you required manually by specifying your own. This is not that hard using the FragmentManager class methods.

If you "tag" your fragments as you add them to the transaction...

 fragmentTransaction.add(new FragmentA(), "FragmentA_Tag");

you can later determine which fragment to show when the back button is pressed...

FragmentA f = fragmentManager.findFragmentByTag("FragmentA_Tag");
if(f != null){
    f.show();
}

How you determine which fragment to show it's entirely up to you. You can keep track of the current visible fragment or you can use the isHidden method of the Fragment class...BTW, I'm talking about native fragments here, not support library's fragment.

Leo
  • 14,625
  • 2
  • 37
  • 55
4

The backstack contains records about transactions, not fragments itself. So you should not add first transaction's record (null -> fragA) to backstack. And all other transaction's record should be added to backstack. In this case, then you preformpopBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); android removed all fragments except fragA, because there isn't any records about how fragA was added.

inverse12
  • 186
  • 1
  • 7
2

Only a few days ago I start learning about fragments in Android. And I faced with this problem too. Here I show my solution and how I resolve this. Please, fix if my code is not right. What we have at this time? Acivity, many fragments and their backstack. We want open every fragment from Drawer menu and clear all other fragments from backstack. But, we must hold only one Home fragment. When we stay on Home fragment and user press Back button app is goeing closing.

Activity.class

protected void onCreate(Bundle savedInstanceState)
{
    ...
    // adding Home fragment without adding transaction into backstack
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.container, HomeFragment.newInstance("args"), null);
    ft.commit();
}

@Override
public void onBackPressed() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        finish();
    }
}

public void addFragmentFromMenu(Fragment fragment){
    String backStateName =  fragment.getClass().getName();
    clearBackStack();
    FragmentManager manager = getSupportFragmentManager();
    if(manager.getBackStackEntryCount()> 0)
    {
        boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);

        if (!fragmentPopped && manager.findFragmentByTag(backStateName) == null) {
            //fragment not in back stack, create it.
            addFragment(fragment, manager, backStateName);
        }
    }
    else // no fragments
    {
        addFragment(fragment, manager, backStateName);
    }
}

public void addFragment(Fragment fragment, FragmentManager manager, String backStateName)
{
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.container, fragment, backStateName);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
}

public void clearBackStack() {
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}

And on click on drawer menu item

@Override
public boolean onNavigationItemSelected(MenuItem item) {

    int id = item.getItemId();

    if (id == R.id.nav_camera) {
        addFragmentFromMenu(CameraFragment.newInstance("cam1", "cam2"));
    } else if (id == R.id.nav_gallery) {
        addFragmentFromMenu(TestFragment.newInstance("test1","test2"));
    }

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}
Trancer
  • 765
  • 2
  • 8
  • 23
1

After looking through many posts, I figured it out this way:

in fragC => fragD method, do two transactions:

1 clear back stacks, fragC => fragA

2 fragA => fragD

But in this way, the original state of fragA may be destroyed.

public static void changeFragCtoD(FragmentManager fm, Fragment fragD){
    fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    FragmentTransaction fragmentTransaction = fm.beginTransaction();
    fragmentTransaction
            .replace(R.id.containerViewId, new fragAClass())
            .commit();

    FragmentTransaction fragmentTransaction = fm.beginTransaction();
    fragmentTransaction
            .replace(R.id.containerViewId, fragD)
            .addToBackStack(fragD.getClass().getName())
            .commit();
}

Now pressing back on fragD goes back to fragA.

Fews
  • 11
  • 1
0

I have done following way. Clear all fragment then add first home fragment

FragmentManager fm = getActivity().getSupportFragmentManager();
                    fm.popBackStack(Constants.TAG_HOME, FragmentManager.POP_BACK_STACK_INCLUSIVE);

                    ((MainActivity) activity).manageFragment(new HomeFragment(), Constants.TAG_HOME);
 //        write down below function in main activity              
     public void manageFragment(Fragment fragment, String tag) {

            FragmentManager fragmentManager = getSupportFragmentManager();
            if (!fragmentManager.popBackStackImmediate(tag, 0)) {

                FragmentTransaction ft = fragmentManager.beginTransaction();
                ft.add(R.id.content_frame, fragment, tag);
                ft.addToBackStack(tag);
                ft.commit();

            }
        }
Rajesh N
  • 6,198
  • 2
  • 47
  • 58
0

1) Add First Fragment using below code

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
        android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
        if (fm.findFragmentById(R.id.fragment_container) != null) {
            ft.hide(fm.findFragmentById(R.id.fragment_container));
        }
        ft.add(R.id.fragment_container, new OneFragment(),OneFragment.class.getCanonicalName())
                .addToBackStack(OneFragment.class.getCanonicalName()).commit();

2) Add Second Fragment From First fragment using below code

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
                    android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
                    if (fm.findFragmentById(R.id.fragment_container) != null) {
                        ft.hide(fm.findFragmentById(R.id.fragment_container));
                    }
                    ft.add(R.id.fragment_container,new TwoFragment(),TwoFragment.class.getCanonicalName())
.addToBackStack(TwoFragment.class.getCanonicalName()).commit();

3) Add Third Fragment From Second fragment using below code

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
                android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
                if (fm.findFragmentById(R.id.fragment_container) != null) {
                    ft.hide(fm.findFragmentById(R.id.fragment_container));
                }
                ft.add(R.id.fragment_container, new ThreeFragment(),ThreeFragment.class.getCanonicalName())
                        .addToBackStack(ThreeFragment.class.getCanonicalName()).commit();

4) Add Forth Fragment From Third fragment using below code

android.support.v4.app.FragmentManager fm = getActivity().getSupportFragmentManager();
                android.support.v4.app.FragmentTransaction ft=fm.beginTransaction();
                if (fm.findFragmentById(R.id.fragment_container) != null) {
                    ft.hide(fm.findFragmentById(R.id.fragment_container));
                }
                ft.add(R.id.fragment_container, new FourFragment(),ThreeFragment.class.getCanonicalName())
                        .addToBackStack(FourFragment.class.getCanonicalName()).commit();

5) onBackPressed() please write below code

@Override
    public void onBackPressed() {
        hideKeyboard(MainActivity.this);
        Fragment currentFragment = this.getSupportFragmentManager().findFragmentById(R.id.fragment_container);

        if (currentFragment.getClass().getName().equalsIgnoreCase(FourFragment.class.getName())) { // Using this code come from third fragment to first fragment
            Fragment f = this.getSupportFragmentManager().findFragmentByTag(TwoFragment.class.getCanonicalName());
            if (f != null) {
                this.getSupportFragmentManager().popBackStackImmediate(f.getClass().getCanonicalName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }
        }else {
            super.onBackPressed();
        }
    }
0

Mark my words, complete FragmentTransactions are added to backstack not just a fragment, means even if you add and remove fragment in a single transaction calling poBackStack() will reverse the complete transaction. Passing a tag in its argument pops all the transaction till the tagged transaction or even pops the tagged transaction (in case FragmentManager.POP_BACK_STACK_INCLUSIVE added to the argument) So it's more about how you added it not how you removing it. See Performing Fragment Transactions

guneetgstar
  • 621
  • 7
  • 15
0

I know it's too late as an answer to your question, but this might be helpful to other people. Since I have been looking for a solution for the past few days... I have solved it out with using for loop, and I think it's the simplest possible way.

First, initialize an integer in this way

int count = getSupportFragmentManager().getBackStackEntryCount();

where getBackStackEntryCount() will count the number of transactions.

And then just before calling the FragmentTransaction method in your fragD add this for loop

for (int i = 0; i < count; i++){
                getSupportFragmentManager().popBackStack();
            }

for loop will do the trick for you and popBackStack() will return you to the fragA

it looks like this

int count = getSupportFragmentManager().getBackStackEntryCount();
            for (int i = 0; i < count; i++){
                getSupportFragmentManager().popBackStack();
            }
            fragmentTransaction.replace(R.id.frame_layout, new FoodFragment(), "Food").addToBackStack(null);
            fragmentTransaction.commit();
Marko Malbasic
  • 149
  • 1
  • 10
0

You an override the onbackpressed (Mainactivity) and using the getBackStackEntryCount() of the fragment manager, you can check if its not equal to 1 and popbackstack on only that condition.

@Override
public void onBackPressed() {
  if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
     if (!(getSupportFragmentManager().getBackStackEntryCount() == 1)) {
      getSupportFragmentManager().popBackStack() ;
    }
  }    
}

Neb King
  • 31
  • 1
  • 2