obtenir le dernier fragment dans backstack

104

Comment puis-je obtenir la dernière instance de fragment ajoutée dans Backstack (si je ne connais pas la balise et l'ID de fragment)?

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 ??
Leem.fin
la source

Réponses:

156

Vous pouvez utiliser la getName()méthode FragmentManager.BackStackEntryqui a été introduite au niveau d'API 14. Cette méthode renverra une balise qui était celle que vous avez utilisée lorsque vous avez ajouté le fragment à la pile d'arrière-plan avec addTobackStack(tag).

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

Vous devez vous assurer que vous avez ajouté le fragment à la backstack comme ceci:

fragmentTransaction.addToBackStack(tag);
Deepak Goel
la source
24
afin de trouver fragment par tag, il doit être ajouté / remplacé par le même tag. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)or FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib
3
Le problème avec cela est que si vous avez un fragment dans la pile arrière deux fois, vous ne pouvez pas récupérer un fragment spécifique lorsque tout ce que vous avez est l'index ou l'entité backstackentry.
Justin
14
Quel est l'intérêt de l'appeler un stacksi je ne peux pas accéder au fragment via une méthode pop?
Kenny Worden
1
Je ne sais pas pourquoi mais findFragmentByTag renvoie null, même lorsque le débogueur montre clairement que la balise est correcte.
htafoya
49
FragmentManager.findFragmentById(fragmentsContainerId) 

La fonction retourne le lien vers le haut Fragmentde la pile d'arrière-plan. Exemple d'utilisation:

    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
la source
2
Cette réponse fonctionne bien dans mon projet puisque j'ajoute chaque fragment à l'exception du fragment racine à la pile arrière. Mais je suppose que cette réponse ne fonctionnera pas si le dernier fragment ajouté n'a pas été ajouté à la backstack.
Arne Evertsson
40

J'ai personnellement essayé plusieurs de ces solutions et je me suis retrouvé avec cette solution fonctionnelle:

Ajoutez cette méthode utilitaire qui sera utilisée plusieurs fois ci-dessous pour obtenir le nombre de fragments dans votre backstack:

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

Ensuite, lorsque vous ajoutez / remplacez votre fragment en utilisant la méthode FragmentTransaction, générez une balise unique à votre fragment (par exemple: en utilisant le nombre de fragments dans votre pile):

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

Enfin, vous pouvez trouver l'un de vos fragments dans votre backstack avec cette méthode:

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

Par conséquent, la récupération du fragment supérieur de votre backstack peut être facilement réalisée en appelant:

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

J'espère que cela t'aides!

ptitvinou
la source
10

cette méthode d'assistance récupère un fragment du haut de la pile:

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}
roghayeh hosseini
la source
1
Merci. La réponse la plus élégante. Ajouté ici pour les amateurs de Kotlin stackoverflow.com/a/47336504/1761406
Shirane85
7

Kotlin

activity.supportFragmentManager.fragments.last()
Le meilleur artiste
la source
4
Vous devrez peut-être utiliser .lastOrNull()au cas où fragmentsest vide.
Westy92
6

Il existe une liste de fragments dans le fragmentMananger. Sachez que la suppression d'un fragment ne fait pas diminuer la taille de la liste (l'entrée de fragment devient simplement nulle). Par conséquent, une solution valable serait:

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
la source
2
Pas fiable. Trois raisons: 1) Ceci est une liste de tous les fragments que le gestionnaire de fragments connaît, pas seulement ceux de la pile. 2) Il n'y a aucune garantie que le gestionnaire de fragments continuera à en ajouter de nouveaux à la fin de la liste. Bien sûr, il le fera dans des tests simples, mais que se passe-t-il si un fragment est supprimé, laissant un null, puis, dans certaines circonstances, le gestionnaire de fragments décide de réutiliser cet emplacement vide? Ne pas dire que oui, mais il n'y a aucune garantie que ce ne sera jamais le cas, en aucune circonstance. 3) Si vous ou un futur programmeur commencez à utiliser attacher / détacher pour gérer les fragments, cela ne correspondra pas à la pile.
ToolmakerSteve
Salut, merci pour votre solution mais ce n'est pas beau et facile
Muhammad Ali
Je doute que ce soit une solution de travail. getFragments()renvoie une collection raccourcie de fragments. Peut-être que le dernier est visible, mais pas beaucoup d'autres.
CoolMind
5

La réponse donnée par deepak goel ne fonctionne pas pour moi parce que j'obtiens toujours null entry.getName();

Ce que je fais est de définir une balise sur le fragment de cette façon:

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

Où ft est ma transaction de fragment et FRAGMENT_TAGest la balise. Ensuite, j'utilise ce code pour obtenir le fragment:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
Eduardo
la source
Cela ne fonctionnera que si vous donnez à tous les fragments la même balise, ce qui n'est pas une bonne idée si vous voulez trouver un fragment spécifique plus tard.
Shirane85
4

Je viens de prendre la réponse @roghayeh hosseini (correcte) et de l'avoir fait à Kotlin pour ceux qui sont ici en 2017 :)

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

* Cela doit être appelé depuis l'intérieur d'une activité.

Prendre plaisir :)

Shirane85
la source
3

vous pouvez utiliser getBackStackEntryAt () . Afin de savoir combien d'entrées l'activité détient dans la backstack, vous pouvez utiliser getBackStackEntryCount ()

int lastFragmentCount = getBackStackEntryCount() - 1;
Ceinture noire
la source
11
Mais comment puis-je obtenir le dernier fragment en backstack ?? Le popBackStackEntryAt () renvoie uniquement une instance de BackStackEntry, PAS un fragment
Leem.fin
oui vous avez raison, mais chaque BackStackEntry contient un identifiant que vous pouvez récupérer avec getId (). vous pouvez utiliser cet identifiant pour récupérer le fragment
Blackbelt
1
Pour obtenir le dernier fragment: getBackStackEntryCount () - 1
An-droid
3
Cette réponse est fausse, je vois que les entrées de backstack ont ​​un id de 0, donc je ne peux pas récupérer le fragment par id.
Justin
1
C'est vieux, mais pour tous ceux qui errent ici: la pile arrière ne contient pas de fragments, mais des transactions fragmentées, c'est pourquoi cette réponse est fausse
maciekjanusz
3

J'ajouterai quelque chose à la réponse de Deepak Goel car beaucoup de gens, moi y compris, obtenaient une valeur nulle en utilisant sa méthode. Apparemment, pour que la balise fonctionne lorsque vous ajoutez un fragment à la backstack, vous devriez le faire comme ceci:

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

Vous devez ajouter deux fois la même balise.

J'aurais commenté mais je n'ai pas 50 réputation.

Cosmin Vacaru
la source
vous en avez 50 maintenant :)
davidbilla
2

Gardez votre dos pile: myBackStack. En tant que Addfragment du FragmentManager, ajoutez-le également à myBackStack. En onBackStackChanged()pop à partir de myBackStackquand sa longueur est supérieure à getBackStackEntryCount.

Arne Evertsson
la source
2

On dirait que quelque chose a changé pour le mieux, car le code ci-dessous fonctionne parfaitement pour moi, mais je ne l'ai pas trouvé dans les réponses déjà fournies.

Kotlin:

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

Java:

getSupportFragmentManager().getFragments()
.get(getSupportFragmentManager().getFragments().size() - 1)
Vasily Kravchenko
la source
2

Si vous utilisez addToBackStack(), vous pouvez utiliser le code suivant.

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

Akbar Kazimov
la source
1

En fait, il n'y a pas de dernier fragment ajouté à la pile car vous pouvez en ajouter plusieurs ou fragments à la pile en une seule transaction ou simplement supprimer des fragments sans en ajouter un nouveau.

Si vous voulez vraiment avoir une pile de fragments et pouvoir accéder à un fragment par son index dans la pile, vous feriez mieux d'avoir une couche d'abstraction sur le FragmentManageret sa backstack. Voici comment vous pouvez le faire:

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);
  }
}

Maintenant , au lieu d'ajouter et de supprimer des fragments en appelant FragmentManagerdirectement, vous devez utiliser push(), replace()et les pop()méthodes de FragmentStackManager. Et vous pourrez accéder au fragment le plus haut en appelant simplement stack.get(stack.size() - 1).

Mais si vous aimez les hacks, j'ai d'autres moyens de faire des choses similaires. La seule chose que je dois mentionner est que ces hacks ne fonctionneront qu'avec des fragments de support.

Le premier hack consiste simplement à ajouter tous les fragments actifs au gestionnaire de fragments. Si vous remplacez simplement les fragments un par un et sortez le de la pile, cette méthode renverra le fragment le plus haut:

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;
  }
}

La deuxième approche est un événement plus hacky et vous permet d'obtenir tous les fragments ajoutés dans la dernière transaction pour laquelle addToBackStacka été appelée:

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;
  }
}

Veuillez noter que dans ce cas, vous devez mettre cette classe dans le android.support.v4.apppackage.

Michael
la source
0

Ou vous pouvez simplement ajouter une balise lors de l'ajout de fragments correspondant à leur contenu et utiliser un champ de chaîne statique simple (vous pouvez également l'enregistrer dans le bundle d'instances d'activité dans la méthode onSaveInstanceState (Bundle)) pour contenir la dernière balise de fragment ajoutée et obtenir ce fragment byTag () à tout moment dont vous avez besoin ...

Евгений Шевченко
la source
0

La réponse la plus élevée (Deepak Goel) n'a pas bien fonctionné pour moi. D'une manière ou d'une autre, la balise n'a pas été ajoutée correctement.

J'ai fini par envoyer simplement l'ID du fragment à travers le flux (en utilisant les intentions) et le récupérer directement à partir du gestionnaire de fragments.

htafoya
la source