Alex Gittemeier Alex Gittemeier - 6 months ago 86
Android Question

Fragments Behave Weirdly When Using addToBackStack()

An SSCCE for this issue is available on GitHub.

For future readers, the original example is on a branch of the same project, and the fix is available in this diff.

This SSCCE has a ListView and a row of buttons. The buttons are supposed to change the data in the ListView, and the listView rows (when clicked) are supposed to open a new fragment and advance the backstack while staying in the same activity.

If do the following things, it produces the following result:


  1. Open the app.

  2. Tap the ListView. -
    FragmentTransaction.replace(...)
    with
    addToBackStack(true)

  3. Tap any of the buttons. -
    FragmentTransaction.replace(...)
    with
    addToBackStack(false)

  4. Tap the back button.



Result:

Overlapping Fragments

Both fragments become visible, but I only want the first loaded fragment (
ListTwoFragment
in code) to display. Is this how fragments are supposed to work? If so, how can I get the desired effect?

MainActivity.java:

public class MainActivity extends FragmentActivity implements ListTwoFragment.Callbacks,
ListThreeFragment.Callbacks {
public static final String KEY_ARGS = "args";

private String curUri = "";
private String curArgs = "";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

selectContent(false);
}

private void selectContent(boolean addToBackStack) {
Fragment fragment;

if (curUri.isEmpty()) {
// Use default fragment
fragment = new ListTwoFragment();
curUri = ListTwoFragment.class.getName();
}
else {
try {
Class<Fragment> fragmentClass = (Class<Fragment>) Class.forName(curUri);
fragment = fragmentClass.newInstance();
}
catch (Exception e) { // ClassNotFound, IllegalAccess, etc.
return;
}
}

// Configure fragment
Bundle args = new Bundle();
args.putString(KEY_ARGS, curArgs);
fragment.setArguments(args);

attachFragment(fragment, addToBackStack, curUri + ";" + curArgs, R.id.fragment_container);
}

protected void attachFragment(Fragment fragment, boolean addToBackStack, String tag, int replaceId) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(replaceId, fragment, tag);
if (addToBackStack) transaction.addToBackStack(tag);
transaction.commit();
}

@Override
public void onTwoButtonClick(String title) {
curUri = ListTwoFragment.class.getName();
curArgs = title;
selectContent(false);
}

@Override
public void onTwoListClick() {
curUri = ListThreeFragment.class.getName();
curArgs = "";
selectContent(true);
}

@Override
public void onThreeButtonClick(String title) {
curUri = ListThreeFragment.class.getName();
curArgs = title;
selectContent(false);
}
}

Answer

I'm working with Fragments to, and the way I'm doing it: to go forward (add to stack), and backwords (remove from stack) are two different functions

to Add to Stack and change Fragment:

public void changeFragmentAddToStack(Fragment myNewFragment) {
        FragmentTransaction t = getSupportFragmentManager().beginTransaction();
        t.add(R.id.main_fragment, myNewFragment);
        t.addToBackStack(null);
        t.commit();
    }

To go back Stack:

        public void goBackStackMain() {
    FragmentManager man = getSupportFragmentManager();

                if(man.getBackStackEntryCount()>0){ 
                    man.popBackStack(man.getBackStackEntryAt(0).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }
}

And if you want to do both: To go back stack and change fragment:

    public void goBackStackAndReplaceFragment(Fragment myNewFragment) {
FragmentManager man = getSupportFragmentManager();
            if(man.getBackStackEntryCount()>0){ 
                int n = man.getBackStackEntryCount();
                man.popBackStack(man.getBackStackEntryAt(n-1).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }

        FragmentTransaction t = getSupportFragmentManager().beginTransaction();
        t.replace(R.id.main_fragment, myNewFragment);
        t.commit();
    }

I hope to help you !