Search code examples
androidandroid-fragmentsback-stackfragmentmanager

How to squash fragments on the backstack which match by their fragment tag?


The layout of my Android tablet app consists of a list of items and a details view. When a list item is selected the associated content is displayed in the details view.

+--------+-------------+ 
| Item 1 |             |
+--------+    Item     |
| Item 2 |   details   |
+--------+             |
| Item 3 |             |
+--------+-------------+

The details view is a Fragment which is programmatically inflated into a FrameLayout placeholder:

<FrameLayout
    android:id="@+id/detail_fragment_placeholder"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

Here is the Fragment operation:

getSupportFragmentManager()
    .beginTransaction()
    .replace(containerViewId, fragment, fragmentTag)
    .addToBackStack(backStackStateName)
    .commit();

Multiple instances [Dx] of the DetailsFragment are added to the backstack when the user selects one item after another.

                [D3]
        [D2]    [D2]
[D1] -> [D1] -> [D1]

Therefore, the user needs to press the BACK button multiple times to pop the instances from the backstack to empty the details view.

How can I replace an existing instance [Dx] of DetailsFragment on the backstack when the fragmentTag of the existing fragment matches the fragmentTag of a new fragment?

[D1] -> [D2] -> [D3]

Solution

  • I'm not sure I understand your question correctly. If you want to replace multiple fragments with some tag from the top of the backstack with a single fragment with the same tag then you can use the following approach.

    Instead of using tags for identifying fragments set different backstack names for fragments of different types. You can still use a fragment tag but it will not help in solving this particular problem. Then remove fragments from the backstack manually one by one until there's a fragment with a different backstack name on the top or no fragments left.

      public void addFragment(final int containerViewId, final Fragment fragment,
          final String backStackName, final boolean replace) {
        final FragmentManager fragmentManager = getSupportFragmentManager();
    
        if (replace) {
          while (fragmentManager.getBackStackEntryCount() > 0) {
            final int last = fragmentManager.getBackStackEntryCount() - 1;
            final FragmentManager.BackStackEntry entry = 
                fragmentManager.getBackStackEntryAt(last);
            if (!TextUtils.equals(entry.getName(), backStackName)) {
              break;
            }
    
            fragmentManager.popBackStackImmediate();
          }
        }
    
        fragmentManager
            .beginTransaction()
            .replace(containerViewId, fragment)
            .addToBackStack(backStackName)
            .commit();
        fragmentManager.executePendingTransactions();
      }
    

    Now if you make the following calls your backstack will contain just fragment1 and fragment4.

    addFragment(R.id.container, fragment1, "D2", false);
    addFragment(R.id.container, fragment2, "D1", false);
    addFragment(R.id.container, fragment3, "D1", false);
    addFragment(R.id.container, fragment4, "D1", true);
    

    UPDATE:

    In this particular case the following code was enough:

    getSupportFragmentManager().popBackStack(
        backStackStateName, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    getSupportFragmentManager()
        .beginTransaction()
        .replace(containerViewId, fragment, fragmentTag)
        .addToBackStack(backStackStateName)
        .commit();
    

    https://github.com/tuxmobil/CampFahrplan/pull/148