117

How can I get the latest fragment instance added in backstack (if I do not know the fragment tag & id)?

FragmentManager fragManager = activity.getSupportFragmentManager();
FragmentTransaction fragTransacion = fragMgr.beginTransaction();

/****After add , replace fragments 
  (some of the fragments are add to backstack , some are not)***/

//HERE, How can I get the latest added fragment from backstack ??
a.ch.
  • 8,285
  • 5
  • 40
  • 53
Leem.fin
  • 40,781
  • 83
  • 202
  • 354

17 Answers17

164

You can use the getName() method of FragmentManager.BackStackEntry which was introduced in API level 14. This method will return a tag which was the one you used when you added the Fragment to the backstack with addTobackStack(tag).

int index = getActivity().getFragmentManager().getBackStackEntryCount() - 1
FragmentManager.BackStackEntry backEntry = getFragmentManager().getBackStackEntryAt(index);
String tag = backEntry.getName();
Fragment fragment = getFragmentManager().findFragmentByTag(tag);

You need to make sure that you added the fragment to the backstack like this:

fragmentTransaction.addToBackStack(tag);
Ahmad
  • 69,608
  • 17
  • 111
  • 137
Deepak Goel
  • 5,624
  • 6
  • 39
  • 53
  • this is helpful. Irrespective of getting which fragment is added to stack recently, we can get any fragment in stack by name – Braj Jul 19 '13 at 08:38
  • 25
    in order to find fragment by tag it must be added/replaced with same tag. `FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)` or `FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag)` [doc](http://developer.android.com/reference/android/app/FragmentTransaction.html#replace(int,%20android.app.Fragment,%20java.lang.String)) – Saqib Aug 01 '13 at 18:38
  • 3
    The problem with this is if you have a fragment in the back stack twice, you can't retrieve a specific fragment when all you have is the index or backstackentry entity. – Justin May 29 '14 at 15:54
  • Like @Saqib says, will it really work to retrieve the fragment from the back entry tag name? They are not the same, or are they? – Arne Evertsson Aug 15 '14 at 08:18
  • 15
    What's the point of calling it a `stack` if I can't access the fragment via a pop method? – Kenny Worden Mar 20 '15 at 19:45
  • 5
    RTFM: http://developer.android.com/reference/android/app/FragmentManager.BackStackEntry.html#getName() – artkoenig Apr 05 '15 at 10:04
  • It seems if I use the name from the BackStackEntry to findFragmentByTag() using the name returned by the entry it returns NULL which seems like a bug - if I get the fragment list from the fragment manager the fragment is in the list this is on 4.4.2 really annoying. – peterk Sep 19 '15 at 01:21
  • of course if the list is the stack then one could use the last entry but it is not documented in this way. – peterk Sep 19 '15 at 01:22
  • @TeodorKolev Have you used the same tag during add or replace. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag) or FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) – Deepak Goel Aug 01 '16 at 05:36
  • Yes for above to work you have to use same String value for fragment tag and backstack name otherwise it won't work. So this can be taken as a work around but not a proper solution. – eC Droid Aug 08 '17 at 14:13
  • 1
    I don't know why but findFragmentByTag is returning null, even when the debugger clearly shows that the tag is ok. – htafoya Sep 11 '17 at 12:32
48
FragmentManager.findFragmentById(fragmentsContainerId) 

function returns link to top Fragment in backstack. Usage example:

    fragmentManager.addOnBackStackChangedListener(new OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fr = fragmentManager.findFragmentById(R.id.fragmentsContainer);
            if(fr!=null){
                Log.e("fragment=", fr.getClass().getSimpleName());
            }
        }
    });
ashakirov
  • 12,112
  • 6
  • 40
  • 40
  • 2
    This answer works well in my project since I add every fragment except the root fragment to the back stack. But I guess this answer will not work if the latest added fragment wasn't added to the backstack. – Arne Evertsson Aug 15 '14 at 08:15
44

I personnaly tried many of those solutions and ended up with this working solution:

Add this utility method that will be used several times below to get the number of fragments in your backstack:

protected int getFragmentCount() {
    return getSupportFragmentManager().getBackStackEntryCount();
}

Then, when you add/replace your fragment using FragmentTransaction method, generate a unique tag to your fragment (e.g.: by using the number of fragments in your stack):

getSupportFragmentManager().beginTransaction().add(yourContainerId, yourFragment, Integer.toString(getFragmentCount()));

Finally, you can find any of your fragments in your backstack with this method:

private Fragment getFragmentAt(int index) {
    return getFragmentCount() > 0 ? getSupportFragmentManager().findFragmentByTag(Integer.toString(index)) : null;
}

Therefore, fetching the top fragment in your backstack can be easily achieved by calling:

protected Fragment getCurrentFragment() {
    return getFragmentAt(getFragmentCount() - 1);
}

Hope this helps!

Stephen Vinouze
  • 1,815
  • 1
  • 17
  • 28
22

Kotlin

// In activities
activity.supportFragmentManager.fragments.lastOrNull()

// In fragments
fragment.childFragmentManager.fragments.lastOrNull()
Pavel Shorokhov
  • 4,485
  • 1
  • 35
  • 44
The Finest Artist
  • 3,150
  • 29
  • 34
14

this helper method get fragment from top of stack:

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}
Jorgesys
  • 124,308
  • 23
  • 334
  • 268
roghayeh hosseini
  • 676
  • 1
  • 8
  • 21
  • 1
    Thanks. The most elegant answer. Added it here for Kotlin lovers https://stackoverflow.com/a/47336504/1761406 – Shirane85 Nov 16 '17 at 18:14
6

There is a list of fragments in the fragmentMananger. Be aware that removing a fragment, does not make the list size decrease (the fragment entry just turn to null). Therefore, a valid solution would be:

public Fragment getTopFragment() {
 List<Fragment> fragentList = fragmentManager.getFragments();
 Fragment top = null;
  for (int i = fragentList.size() -1; i>=0 ; i--) {
   top = (Fragment) fragentList.get(i);
     if (top != null) {
       return top;
     }
   }
 return top;
}
Erez
  • 87
  • 1
  • 4
  • 2
    Not reliable. Three reasons: 1) This is a list of all fragments the fragment manager knows about, not just those on the stack. 2) There is no guarantee that the fragment manager will keep adding new ones at the end of the list. Sure, it will do so in simple tests, but what if a fragment is removed, leaving a null, and then under some circumstances the fragment manager decides to reuse that empty slot? Not saying it does, but there is no guarantee it never will, under any circumstances. 3) If you or some future programmer starts using attach/detach to manage fragments, this won't match stack. – ToolmakerSteve Sep 20 '16 at 11:45
  • Hi, thanks for your solution but it is not beautiful and easy – Muhammad Ali Oct 17 '18 at 08:16
  • I doubt it is a working solutuin. `getFragments()` returns a shortened collection of fragments. Maybe the last one is visible, but not many others. – CoolMind Dec 24 '19 at 14:50
5

The answer given by deepak goel does not work for me because I always get null from entry.getName();

What I do is to set a Tag to the fragment this way:

ft.add(R.id.fragment_container, fragmentIn, FRAGMENT_TAG);

Where ft is my fragment transaction and FRAGMENT_TAG is the tag. Then I use this code to get the fragment:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
Ziem
  • 6,579
  • 8
  • 53
  • 86
Eduardo
  • 214
  • 3
  • 9
  • This will only work if you give all the fragment the same tag, which is not a good idea if you want to find a specific fragment later. – Shirane85 Feb 23 '17 at 05:34
5

Just took @roghayeh hosseini (correct) answer and made it in Kotlin for those here in 2017 :)

fun getTopFragment(): Fragment? {
    supportFragmentManager.run {
        return when (backStackEntryCount) {
            0 -> null
            else -> findFragmentByTag(getBackStackEntryAt(backStackEntryCount - 1).name)
        }
    }
}

*This should be called from inside an Activity.

Enjoy :)

Shirane85
  • 2,255
  • 1
  • 26
  • 38
4

Looks like something has changed for the better, because code below works perfectly for me, but I didn't find it in already provided answers.

Kotlin:

supportFragmentManager.fragments[supportFragmentManager.fragments.size - 1]

Java:

getSupportFragmentManager().getFragments()
.get(getSupportFragmentManager().getFragments().size() - 1)
3

I will add something to Deepak Goel's answer since a lot of people, me included, were getting a null by using his method. Apparently to make the tag work when you add a fragment to the backstack you should be doing it like this:

getSupportFragmentManager.beginTransaction().replace(R.id.container_id,FragmentName,TAG_NAME).addToBackStack(TAG_NAME).commit();

You need to add the same tag twice.

I would have commented but i don't have 50 reputation.

Cosmin Vacaru
  • 239
  • 2
  • 12
3

you can use getBackStackEntryAt(). In order to know how many entry the activity holds in the backstack you can use getBackStackEntryCount()

int lastFragmentCount = getBackStackEntryCount() - 1;
Ziem
  • 6,579
  • 8
  • 53
  • 86
Blackbelt
  • 156,034
  • 29
  • 297
  • 305
  • 14
    But how can I get the last fragment in backstack?? The popBackStackEntryAt() only returns an BackStackEntry instance, NOT fragment – Leem.fin Mar 14 '12 at 12:53
  • yes you are right, but every BackStackEntry holds and id which you can retrive with getId(). you can use this Id in order to retrieve the fragment – Blackbelt Mar 14 '12 at 12:55
  • 1
    To get the last fragment : getBackStackEntryCount() - 1 – An-droid Apr 08 '13 at 09:12
  • But then in order to get that fragment by id (getId()) you have to maintain your own stacks independent of the Android back stack). –  Mar 05 '14 at 17:28
  • 3
    This answer is wrong, I'm seeing the backstack entries have an id of 0, so can't retrieve the fragment by id. – Justin May 29 '14 at 15:53
  • @Justin the documentations says *Return the unique identifier for the entry. This is the only representation of the entry that will persist across activity instances.* – Blackbelt May 29 '14 at 15:57
  • 2
    This is old, but for anybody that wanders here: back stack does not hold fragments, but fragment transactions, that's why this answer is wrong – maciekjanusz May 31 '16 at 16:31
  • 1) I think you mean `getBackStackEntryAt` rather than `popBackStackEntryAt`? 2) Despite the negative comments, getId() is useful for most cases, where there is only a single fragment of each type. Then use `fragmentManager.findFragmentById( id )`. Limitation is if a fragment type is inflated multiple times, only one of them can be found. In that case, use `tags` (names): http://stackoverflow.com/a/28046389/199364 – ToolmakerSteve Sep 20 '16 at 11:07
  • @ToolmakerSteve, the answer is 4 years old – Blackbelt Sep 20 '16 at 11:10
2

Keep your own back stack: myBackStack. As you Add a fragment to the FragmentManager, also add it to myBackStack. In onBackStackChanged() pop from myBackStack when its length is greater than getBackStackEntryCount.

Arne Evertsson
  • 19,693
  • 20
  • 69
  • 84
2

If you use addToBackStack(), you can use following code.

List<Fragment> fragments = fragmentManager.getFragments(); activeFragment = fragments.get(fragments.size() - 1);

1

Actually there's no latest fragment added to the stack because you can add several or fragments to the stack in a single transaction or just remove fragments without adding a new one.

If you really want to have a stack of fragments and to be able to access a fragment by its index in the stack, you'd better have an abstraction layer over the FragmentManager and its backstack. Here's how you can do it:

public class FragmentStackManager {
  private final FragmentManager fragmentManager;
  private final int containerId;

  private final List<Fragment> fragments = new ArrayList<>();

  public FragmentStackManager(final FragmentManager fragmentManager,
      final int containerId) {
    this.fragmentManager = fragmentManager;
    this.containerId = containerId;
  }

  public Parcelable saveState() {
    final Bundle state = new Bundle(fragments.size());
    for (int i = 0, count = fragments.size(); i < count; ++i) {
      fragmentManager.putFragment(state, Integer.toString(i), fragments.get(i));
    }
    return state;
  }

  public void restoreState(final Parcelable state) {
    if (state instanceof Bundle) {
      final Bundle bundle = (Bundle) state;
      int index = 0;
      while (true) {
        final Fragment fragment =
            fragmentManager.getFragment(bundle, Integer.toString(index));
        if (fragment == null) {
          break;
        }

        fragments.add(fragment);
        index += 1;
      }
    }
  }

  public void replace(final Fragment fragment) {
    fragmentManager.popBackStackImmediate(
        null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    fragmentManager.beginTransaction()
        .replace(containerId, fragment)
        .addToBackStack(null)
        .commit();
    fragmentManager.executePendingTransactions();

    fragments.clear();
    fragments.add(fragment);
  }

  public void push(final Fragment fragment) {
    fragmentManager
        .beginTransaction()
        .replace(containerId, fragment)
        .addToBackStack(null)
        .commit();
    fragmentManager.executePendingTransactions();

    fragments.add(fragment);
  }

  public boolean pop() {
    if (isEmpty()) {
      return false;
    }

    fragmentManager.popBackStackImmediate();

    fragments.remove(fragments.size() - 1);
    return true;
  }

  public boolean isEmpty() {
    return fragments.isEmpty();
  }

  public int size() {
    return fragments.size();
  }

  public Fragment getFragment(final int index) {
    return fragments.get(index);
  }
}

Now instead of adding and removing fragments by calling FragmentManager directly, you should use push(), replace(), and pop() methods of FragmentStackManager. And you will be able to access the topmost fragment by just calling stack.get(stack.size() - 1).

But if you like hacks, I have to other ways of doing similar things. The only thing I have to mention is that these hacks will work only with support fragments.

The first hack is just to get all active fragments added to the fragment manager. If you just replace fragments one by one and pop the from the stack this method will return the topmost fragment:

public class BackStackHelper {
  public static List<Fragment> getTopFragments(
      final FragmentManager fragmentManager) {
    final List<Fragment> fragments = fragmentManager.getFragments();
    final List<Fragment> topFragments = new ArrayList<>();

    for (final Fragment fragment : fragments) {
      if (fragment != null && fragment.isResumed()) {
        topFragments.add(fragment);
      }
    }

    return topFragments;
  }
}

The second approach is event more hacky and allows you to get all fragments added in the last transaction for which addToBackStack has been called:

package android.support.v4.app;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BackStackHelper {
  public static List<Fragment> getTopFragments(
      final FragmentManager fragmentManager) {
    if (fragmentManager.getBackStackEntryCount() == 0) {
      return Collections.emptyList();
    }

    final List<Fragment> fragments = new ArrayList<>();

    final int count = fragmentManager.getBackStackEntryCount();
    final BackStackRecord record =
        (BackStackRecord) fragmentManager.getBackStackEntryAt(count - 1);
    BackStackRecord.Op op = record.mHead;
    while (op != null) {
      switch (op.cmd) {
        case BackStackRecord.OP_ADD:
        case BackStackRecord.OP_REPLACE:
        case BackStackRecord.OP_SHOW:
        case BackStackRecord.OP_ATTACH:
          fragments.add(op.fragment);
      }
      op = op.next;
    }

    return fragments;
  }
}

Please notice that in this case you have to put this class into android.support.v4.app package.

Michael
  • 53,859
  • 22
  • 133
  • 139
0

Or you may just add a tag when adding fragments corresponding to their content and use simple static String field (also you may save it in activity instance bundle in onSaveInstanceState(Bundle) method) to hold last added fragment tag and get this fragment byTag() at any time you need...

0

The highest (Deepak Goel) answer didn't work well for me. Somehow the tag wasn't added properly.

I ended up just sending the ID of the fragment through the flow (using intents) and retrieving it directly from fragment manager.

htafoya
  • 18,261
  • 11
  • 80
  • 104
0

Kotlin Developers can use this to get the current fragment:

        supportFragmentManager.addOnBackStackChangedListener {
        val myFragment = supportFragmentManager.fragments.last()

        if (null != myFragment && myFragment is HomeFragment) {
            //HomeFragment is visible or currently loaded
        } else {
            //your code
        }
    }
Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82