Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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]
like image 625
JJD Avatar asked Jan 22 '16 18:01

JJD


People also ask

How do you resume an existing Backstack fragment?

Since you want only one back stack entry per Fragment , make the back state name the Fragment's class name (via getClass(). getName() ). Then when replacing a Fragment , use the popBackStackImmediate() method. If it returns true, it means there is an instance of the Fragment in the back stack.

How do you identify a fragment tag?

findFragmentById(R. id. fragment_container); Alternatively, you can assign a unique tag to a fragment and get a reference using findFragmentByTag() .

What is fragment tag used for?

Fragment tags can be used to avoid recreating a Fragment on Activity orientation change.


2 Answers

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

like image 149
Michael Avatar answered Sep 22 '22 23:09

Michael


You could use the tag and handle it in onBackPressed(), but I think it would be a cleaner solution to handle it while constructing the back stack. Selectively add to the back stack for each FragmentTransaction, and only add to the back stack if it's the first instance of the DetailsFragment.

Here is a simple example that prevents any given Fragment from being added to the back stack twice in a row:

public void replaceFragment(Fragment frag) {
    FragmentManager fm = getSupportFragmentManager();

    if (fm != null){
        FragmentTransaction t = fm.beginTransaction();
        //you could also use containerViewId in place of R.id.detail_fragment_placeholder
        Fragment currentFrag = fm.findFragmentById(R.id.detail_fragment_placeholder);
        if (currentFrag != null && currentFrag.getClass().equals(frag.getClass())) {
            t.replace(R.id.detail_fragment_placeholder, frag).commit();
        } else {
            t.replace(R.id.detail_fragment_placeholder, frag).addToBackStack(null).commit();
        }
    }
}
like image 21
Daniel Nugent Avatar answered Sep 24 '22 23:09

Daniel Nugent