10

I'm fairly new to Android development and now running in to weird behaviour.

  • I have an empty FrameLayout to be the container of a fragment.
  • If user press a button for the first time, generate new Fragment and put inside container.
  • If user press a button later and there is existing fragment inside container, replace existing one with newly generated one.
  • If user press back button, pop away the fragment inside container.

Here is my code

public void showFragment(View v) {
    FragmentA f = new FragmentA();

    FragmentManager fm = getSupportFragmentManager();
    String tag = f.getFragmentTag(); // instance method of a to get a tag

    FragmentTransaction ft = fm.beginTransaction();
    ft.setCustomAnimations(R.anim.slide_in_top, 0, 0, R.anim.slide_out_top);            
    ft.replace(R.id.container, f, tag);
    ft.addToBackStack(tag);
    ft.commit();
}

@Override
public void onBackPressed() {
    FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 0) {
        fm.popBackStack();
    } else {
        super.onBackPressed();
    }
}

When user press button for the first time, it behave like what I expected, add new fragment to container. But, the second time user press button while container still contains a fragment, instead of replacing, it add new one on top of existing one. So, 2 fragments inside container, 2 back press to remove all fragment.

I found that if I remove the line

ft.addToBackStack();

And rework the onBackPress() method like following, it works again like I expected (1 fragment in container at a time)

basically, manually remove fragment instead of popFromBackStack method

private FragmentA currentFragment = null; // to hold the reference to exising fragment, if any.

@Override
public void onBackPressed() {
    if (currentFragment != null) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.setCustomAnimations(0, R.anim.slide_out_top);
        ft.remove(currentFragment);
        ft.commit();

        currentFragment = null;
    } else {
        super.onBackPressed();
    }
}

So, my question are

  • replace and addToBackStack is not working together?
  • or, did I do something wrong?

Appreciate all comments and suggestions.

Tar_Tw45
  • 3,122
  • 6
  • 35
  • 58
  • I think this is the reason: "If you do not call addToBackStack() when you perform a transaction that removes a fragment, then that fragment is destroyed when the transaction is committed and the user cannot navigate back to it. Whereas, if you do call addToBackStack() when removing a fragment, then the fragment is stopped and will be resumed if the user navigates back." When you use addToBackStack(), previous fragments are stopped and retained. See: https://developer.android.com/guide/components/fragments.html#Transactions – Roger Huang Feb 23 '17 at 08:40

2 Answers2

13

addToBackstack creates a snapshot of your fragments state. Which means when you press the back button, you are actually reverting to the last state that addToBackstack was called on.

In your case, you add a Fragment. The back button would remove this added fragment. When you call replace, and add to backstack again, you now have two states on the backstack (1. when you had the first fragment added, 2. when you had no fragments added). If you the back button to remove the current fragment, the don't use addToBackstack. Only use addToBackstack when you want to preserve the state of fragments in a view.

Spidy
  • 39,723
  • 15
  • 65
  • 83
  • Hi Spidy, thanks for your comment. I'm still quite confusing but can I say, in my case, I shouldn't use addToBackStack? – Tar_Tw45 Dec 19 '13 at 05:20
  • 1
    Yes, it sounds like you don't need addToBackstack for your use case – Spidy Dec 19 '13 at 06:17
  • 3
    @Spidy on the Android developer site https://developer.android.com/training/implementing-navigation/temporal.html#back-fragments they say that you should use addToBackStack for providing proper back navigation when navigating between fragments. What are your arguments not to use them? And 2: "In your case, you add a Fragment. The back button would remove this added fragment. When you call replace, and add to backstack again, you now have two states on the backstack". Where in his code does he add a fragment? I only see replace() – Rule Jun 23 '15 at 07:07
-2

For those, who are still looking for solution.

In the main Activity class (which is hosting the fragments)just override onBackPressed().

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

There is no onBackPressed() method in fragment, and this method is just for the activity. So,when we press the back key, the default behaviour of activity is shown, which is

you will either go to previous activity(if there is any) or the app will exit.

Now we need to override this method to tell the activity that when we press the back key, if there are any fragments in back stack, pop them out (and this is when the addToBackStack() comes into picture). Otherwise follow the default behaviour.

find more details here here

KhogaEslam
  • 2,528
  • 1
  • 20
  • 21