سؤال

I have a number of pages/fragments listed in my navigation drawer, the user is likely to switch between these frequently and I want them in the backstack so that they can navigate back, but I only want one instance of each fragment in the backstack so that the user doesn't not have to press back an insane number of times to exit the app. I can't figure out how to effectively 'reorder' the backstack' without pages getting removed.

Currently when I change page I was using this code to change the fragment and make sure it's only in the back stack once

  if (mFragMgr == null) {
      mFragMgr = getSupportFragmentManager();
  }

  String backStateName = fragmentDescriptor.name();
  boolean fragmentPopped = mFragMgr.popBackStackImmediate(backStateName, 0);
  if (!fragmentPopped){
      mFragMgr.beginTransaction()
      .remove((Fragment) mFragment)
      .replace(R.id.content_frame, (Fragment) mFragment)
      .addToBackStack(backStateName)
      .commit();
  }

I use this code in onBackPressed

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

This works but it means it removes pages I don't want removed. Example:

When my user visits 6 pages in the order A > B > C > D > E > C because I'm doing a remove I expected the following stack:

                            [E]    [C]
                     [D]    [D]    [E]
              [C]    [C]    [C]    [D]
       [B]    [B]    [B]    [B]    [B]
[A] -> [A] -> [A] -> [A] -> [A] -> [A]

But what I actually get is the following - it pops everything up to the element that matches the name, this is regardless of whether I include the ".remove((Fragment) mFragment)" or not - (I've already realised now that remove isn't affecting the backstack, so no need to point that out):

                            [E]
                     [D]    [D]
              [C]    [C]    [C]    [C]
       [B]    [B]    [B]    [B]    [B]
[A] -> [A] -> [A] -> [A] -> [A] -> [A]

If I don't use a name and instead use null when adding to the backstack I get the following:

                                   [C]
                            [E]    [E]   
                     [D]    [D]    [D]    
              [C]    [C]    [C]    [C]   
       [B]    [B]    [B]    [B]    [B]  
[A] -> [A] -> [A] -> [A] -> [A] -> [A] 

How can I get the behaviour I expect? Is it possible at all or am I going to need to record the changes myself and skip the backstack altogether?

هل كانت مفيدة؟

المحلول

There is no API to do this, but becuse I was wondering the same thing last week,
I took it as an exercise an implemented it myself.

My method allows you to remove a Fragment from anywhere in the backstack and this is achieved by using refrection to modify the internal variables that store the backstack information in FragmentManagerImpl and BackStackRecord.

There are few important variables:

  • mBackStack - stores the BackStackRecords that hold the Fragment info, the previous ones, animations, etc
  • mActive - all added Fragments
  • mAvailBackStackIndices - stores the indices that can be used by the newly inserted records, i.e. indices of null values in mActive
  • and other

Tested with 150+ Fragments and couldn't notice any leaks, but only monitored the heap in DDMS and did not perform any other memory analysis.
So because someting may be broken, explore the code, re-test it, see if anyone provides a better answer, and rethink if you really need to do this in your project.

I uploaded the code as a gist on GitHub, hope it helps.

نصائح أخرى

When you want to reuse the fragment before it, just first hide all the fragments and show the desired fragment at a time. By doing this you can reuse your fragments without removing them and without reloading the fragments. But be sure before to implement it you have to add the fragment to backstack.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top