14

I am fairly new with android fragments so please bear with me.

I have a bunch of fragments using a single activity as host.

In my mind, my fragments are grouped by sections although they are still modular/reusable by code. Consider this desired scenario:

Frag1 -> (Press Next) -> Frag2 -> (Press Next) -> Frag3 -> (Press Back) -> Frag1

After going through a series of fragments, I would like to skip some previous fragments (in this scenario, skip Frag 2) on pressing the back button.

However, in my current code, my problem is that even though it goes back to Frag1, Frag3 does not disappear from the screen. What happens is that both Frag1 and Frag3 becomes visible on top of each other.

Here are my relevant code snippets:

Code snippet for creating Frag1

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    // init the fragment (with a default fragment, not null)
    Fragment fragment = PlaceholderFragment.newInstance(position + 1);
    // Position number from navigation sidebar starts from 0.
    // Since position starts from 0, add 1 to match section number
    // as implemented in {@link #onSectionAttached()}
    switch(position) {
        case 0:
            fragment = PlaceholderFragment.newInstance(position + 1);
            break;
        case 1: // Frag1 case
            fragment = new AddPointsFragment().newInstance(position + 1, "");
            break;
        default:
            break;
    }
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getFragmentManager();
    // clear all fragments from previous section from the back stack
    fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    // replace all currently added fragments in container and replace with the new fragment
    fragmentManager.beginTransaction()
            .replace(R.id.container, fragment)
            .commit();
}

Code snippet for creating Frag2

public void onEnterButtonFragmentInteraction(int sectionNumber, String cardNo) {
    // TODO: Add point for given card number
    int points = 5; //sample points
    AddPointsSuccessFragment addPointsSuccessFragment =
            new AddPointsSuccessFragment().newInstance(sectionNumber, cardNo, points);
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.container, addPointsSuccessFragment)
            .addToBackStack(null)
            .commit();
}

Code snippet for creating Frag3

public void onOkButtonFragmentInteraction(int sectionNumber, String cardNo, int points) {
    RedeemRewardFragment redeemRewardFragment =
                new RedeemRewardFragment().newInstance(sectionNumber, cardNo, points);
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.container, redeemRewardFragment);
        fragmentTransaction.commit();
}

My current workaround for this is by adding .addToBackStack(null) in creating Frag3 and running this code

public void onBackButtonFragmentInteraction() {
    this.onBackPressed(); // simulate pressing of activity back button
    FragmentManager fragmentmanager = getFragmentManager();
    fragmentmanager.popBackStack(); // pop Frag2 from back stack
}

right after calling the onBackPressed() method. Unfortunately, this workaround is ugly because because there is a split-second appearance of Frag2 before going to Frag1.

Nico Dumdum
  • 3,173
  • 2
  • 18
  • 18

3 Answers3

10

So the key to your solution here is this guy:

.addToBackStack(null)

Instead of null, you can pass in a String identifier for that particular transaction -- for instance, the class name of the Fragment is what we use (although that doesn't work if you have multiple instances of the same fragment on the backstack):

.addToBackStack(Fragment1.class.getName())

Then, if you wanted to get back to Fragment1 from Fragment3, just pop using the identifier of the next fragment, and pass the INCLUSIVE flag (which means it will also pop that next fragment that you specified):

getFragmentManager().popBackStack(
        Fragment2.class.getName(), 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

Which will play your animations as expected, but as if the fragments in between were never there. The reason I suggest to use the next fragment is because you probably don't want your first fragment transaction on the back stack.

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • I tried your suggestion, but unfortunately, the screen still shows the Frag2 for a split-second before returning to Frag1. I also added the code snippet for Frag1 for a better perspective. :( – Nico Dumdum Sep 17 '14 at 02:40
  • 2
    After further experimentations, I found out that after implementing your suggestion, I should not call this.onBackPressed() anymore. And now it works "...as if the fragments in between were never there." Thanks! ;) – Nico Dumdum Sep 17 '14 at 03:37
  • I have followed the same but it is not working for me, Can please check what I am doing wrong here http://stackoverflow.com/questions/31118973 – Piyush Agarwal Jun 29 '15 at 19:32
  • 1
    How can I move Frag3→Frag1 by pressed navigation Back button? – Konstantin Konopko Oct 08 '15 at 15:12
  • This approach is not working if there are more than 3 fragments. – Sai Sep 07 '17 at 08:58
  • @KonstantinKonopko You need to write the code with `popBackStack(...)` in the overriden `onBackPressed()` of your activity. – lorenzo-s May 04 '20 at 12:08
2

You could try this, it should work and doesnt give a split-second delay. Its not beautiful code though, and if somebody has a better way, please post.

1.Give a tag to your fragments.

transaction.add(R.id.main_activity_container, FirstFragment, "FirstFragment");
transaction.replace(R.id.main_activity_container, Second, "SECOND");
transaction.replace(R.id.main_activity_container, Third, "THIRD");

2.Modify your onBackPressed() in your frameActivity (activity) that "houses" your fragments.

@Override
public void onBackPressed() {
    // TODO Auto-generated method stub

    int lastStack = getSupportFragmentManager().getBackStackEntryCount();
    try {
        //If the last fragment was named/tagged "three"
        if (getSupportFragmentManager().getFragments().get(lastStack).getTag().equalsIgnoreCase("THIRD")){

            getSupportFragmentManager().popBackStackImmediate();
            getSupportFragmentManager().popBackStackImmediate();

            //Get your first fragment that you loaded in the beginning. 
            Fragment first = getSupportFragmentManager().findFragmentByTag("FirstFragment");
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.main_activity_container, first);
            transaction.commit();
            return;
        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    super.onBackPressed();
}
Rohan Pawar
  • 1,875
  • 22
  • 40
user3711421
  • 1,658
  • 3
  • 20
  • 37
  • Out of appreciation for answering, I tried implementing your suggestion. Although it took me some time to implement, it definitely worked! Although I would still prefer @kcoppock's answer since it required less code changes and less clutter for me. Still, plus points for you for giving me another possible idea! :) – Nico Dumdum Sep 17 '14 at 03:36
  • Yea, kcoppock's answer is probably the best in this case, looks much cleaner. Maybe i will change to a similar solution in my app :P. Though they might work a bit differently? How does it behave if you go: Frag1 -> Frag2 -> Frag3 -> BackBtn than BackBtn again, does it exit or do you go to Frag2 – user3711421 Sep 17 '14 at 13:04
  • If you mean kcoppock's suggestion, then it exits the app since it followed the flow that I desired. Frag1 -> Next -> Frag2 -> Next -> Frag3 -> Back -> Frag1 -> Back -> EXIT – Nico Dumdum Sep 17 '14 at 13:19
0

When you display Frag2 use:

final FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.addToBackStack(FRAG_2);
fragmentTransaction.replace(containerID, frag2, FRAG2_TAG);

and on back press:

@Override
public void onBackPressed() {
   final Frag2 frag2 = (Frag2)getSupportFragmentManager().findFragmentByTag(FRAG2_TAG);
   if (frag2 != null && frag2.isVisible() && getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStackImmediate();
    return;
   }
}

This will prevent from frag2 to be displayed after onBackPressed() called. Avoid using popBackStack() as this will results in frag2 lifecycle trigger (onCreate, onStart, onResume ...)

Ron____
  • 782
  • 1
  • 6
  • 6