19

I have implemented Navigation Drawer which is a subclass of Activity. I have many fragments in my application. My question goes here

Imagine there are 3 fragments :

Fragment_1 : Fragment_2 : Fragment_3

When I start my application, Fragment_1 is loaded When I click on some components on Fragment_1, I'm navigated to Fragment_2 and so on..

So it's like

Fragment_1 > Fragment_2 > Fragment_3

When I press back key from Fragment_2, I'm navigated back to Fragment_1 But when I press back key from Fragment_3, I'm navigated back to Fragment_1 (instead of Fragment_2)

I want something like this in my application on Back Key press

Fragment_1 < Fragment_2 < Fragment_3

I have used Fragment, FragmentManager, FragmentTransaction as follows :

MyFragment fragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();

fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).addToBackStack(null)commit();

and I tried overriding onBackPressed() in my MainActivity :

@Override
public void onBackPressed() {


        getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        int count = getFragmentManager().getBackStackEntryCount();
        if (count == 0)
               super.onBackPressed();
    }
Harsh
  • 644
  • 3
  • 14
  • 27

5 Answers5

36

Update your Activity#onBackPressed() method to:

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}

The reason your implementation doesn't work is because the method FragmentManager#popBackStack() is asynchronous and does not happen right after it is called.

From the documentation:

This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop.

Reference: http://developer.android.com/reference/android/app/FragmentManager.html#popBackStack(java.lang.String,%20int)

Leandro
  • 949
  • 6
  • 8
  • Your code works for some part. Problem is that :: Suppose I navigate as follows >> Fragment_1 > Fragment_2 > Fragment_3 , and then from navigation drawer I select again Fragment_1 and navigate same as below and later on press back key, I find following sequence Fragment_1 < Fragment_2 < Fragment_3 < Fragment_1 < Fragment_2 < Fragment_3 – Harsh Aug 17 '14 at 16:29
  • You are getting this sequence because each `FragmentManager` operation that you are doing is being added to the back stack through `FragmentTransaction#addToBackStack()`. I read again your question and still don't know what you are expecting in this case. Please clarify. – Leandro Aug 17 '14 at 16:45
  • actually your code solved my issue. But how to change following sequence :: Fragment_1 < Fragment_2 < Fragment_3 < Fragment_1 < Fragment_2 < Fragment_3 __to__ Fragment_1 < Fragment_2 < Fragment_3..... Irrespective of the user's repeated action as Fragment_1 > Fragment_2 > Fragment_3. If you are still not getting my point, then other term for my problem is to remove redundancy of same fragments – Harsh Aug 17 '14 at 17:04
  • I guess what you are planning to do is to avoid duplicated entries in the `FragmentManager` and the Back stack, right? If so, you can check if a `Fragment` is already in the stack by setting a TAG when you commit it and them searching for it (with `FragmentManager#findFragmentByTag(String TAG)` and not calling `addToBackStack()` if it doesn't return a null value. Also please mark my response as the answer to the question if it solved your problem. – Leandro Aug 17 '14 at 17:18
  • Can you illustrate with short example ? (code for findFragmentByTag(...) ) – Harsh Aug 17 '14 at 20:05
3

You have to implement your own backstack implementation as explained here

Separate Back Stack for each tab in Android using Fragments

You can call the popFragments() whenever you click the back button in a fragment and call pushFragments() whenever you navigate from one Fragment to other.

in Short,

public void onBackPressed()
{
    FragmentManager fm = getActivity().getSupportFragmentManager();
    fm.popBackStack();
}
Community
  • 1
  • 1
Sam
  • 4,046
  • 8
  • 31
  • 47
2

The tric is in FragmentManager#executePendingTransactions();.

This is what I use for nested fragments as well...:

/**
 * if there is a fragment and the back stack of this fragment is not empty,
 * then emulate 'onBackPressed' behaviour, because in default, it is not working.
 *
 * @param fm the fragment manager to which we will try to dispatch the back pressed event.
 * @return {@code true} if the onBackPressed event was consumed by a child fragment, otherwise
 */
public static boolean dispatchOnBackPressedToFragments(FragmentManager fm) {

    List<Fragment> fragments = fm.getFragments();
    boolean result;
    if (fragments != null && !fragments.isEmpty()) {
        for (Fragment frag : fragments) {
            if (frag != null && frag.isAdded() && frag.getChildFragmentManager() != null) {
                // go to the next level of child fragments.
                result = dispatchOnBackPressedToFragments(frag.getChildFragmentManager());
                if (result) return true;
            }
        }
    }

    // if the back stack is not empty then we pop the last transaction.
    if (fm.getBackStackEntryCount() > 0) {
        fm.popBackStack();
        fm.executePendingTransactions();
        return true;
    }

    return false;
}

and in my onBackPressed :

                if (!FragmentUtils.dispatchOnBackPressedToFragments(fm)) {
                    // if no child fragment consumed the onBackPressed event,
                    // we execute the default behaviour.
                    super.onBackPressed();
                }
ahmed_khan_89
  • 2,755
  • 26
  • 49
0

Use this code on tab change in your main activity to clear the stack.

int count = getFragmentManager().getBackStackEntryCount();
        if(count>0){
            for (int i = 0; i <count; i++) {
                getFragmentManager().popBackStack();
            }
        }

Then on Back pressed of your main activity do this

 int count = getFragmentManager().getBackStackEntryCount();

     if (count == 0) {
         super.onbackpressed();
        }
else {
        getFragmentManager().popBackStack();
    }
 }
Nanda
  • 31
  • 3
0

Here is working and tested code by me, This will help you

 private static final int TIME_INTERVAL = 2000;
private long mBackPressed;
 private void applyExit() {
    if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) {
        finish();
    } else {
         Toast.makeText(this,"Press Again to exit",Toast.LENGTH_LONG).show();
    }
    mBackPressed = System.currentTimeMillis();
}

@Override
public void onBackPressed() {
    fm = getSupportFragmentManager();
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    }
    if (fm.getFragments().size() <= 1) {
        applyExit();
    } else {
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                applyExit();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    return;
                } else {
                    fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    return;
                }
            }
        }
    }
}
Ness Tyagi
  • 2,008
  • 24
  • 18