obtener el último fragmento en backstack

104

¿Cómo puedo agregar la última instancia de fragmento en backstack (si no conozco la etiqueta y la identificación del fragmento)?

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
fuente

Respuestas:

156

Puede utilizar el getName()método FragmentManager.BackStackEntryque se introdujo en el nivel de API 14. Este método devolverá una etiqueta que era la que utilizó cuando agregó el Fragmento al backstack addTobackStack(tag).

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

Debe asegurarse de haber agregado el fragmento al backstack de esta manera:

fragmentTransaction.addToBackStack(tag);
Deepak Goel
fuente
24
para encontrar un fragmento por etiqueta, debe agregarse / reemplazarse con la misma etiqueta. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)o FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib
3
El problema con esto es que si tiene un fragmento en el backstackentry dos veces, no puede recuperar un fragmento específico cuando todo lo que tiene es el índice o la entidad backstackentry.
Justin
14
¿Cuál es el punto de llamarlo a stacksi no puedo acceder al fragmento a través de un método pop?
Kenny Worden
1
No sé por qué, pero findFragmentByTag devuelve nulo, incluso cuando el depurador muestra claramente que la etiqueta está bien.
htafoya
49
FragmentManager.findFragmentById(fragmentsContainerId) 

La función devuelve el enlace al principio Fragmentde la pila. Ejemplo de uso:

    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
fuente
2
Esta respuesta funciona bien en mi proyecto ya que agrego cada fragmento excepto el fragmento raíz a la pila de actividades. Pero supongo que esta respuesta no funcionará si el último fragmento agregado no se agregó al backstack.
Arne Evertsson
40

Personalmente probé muchas de esas soluciones y terminé con esta solución de trabajo:

Agregue este método de utilidad que se utilizará varias veces a continuación para obtener la cantidad de fragmentos en su backstack:

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

Luego, cuando agregue / reemplace su fragmento usando el método FragmentTransaction, genere una etiqueta única para su fragmento (por ejemplo: usando la cantidad de fragmentos en su pila):

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

Finalmente, puede encontrar cualquiera de sus fragmentos en su backstack con este método:

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

Por lo tanto, puede obtener fácilmente el fragmento superior en su backstack llamando a:

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

¡Espero que esto ayude!

ptitvinou
fuente
10

este método auxiliar obtiene un fragmento de la parte superior de la pila:

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}
roghayeh hosseini
fuente
1
Gracias. La respuesta más elegante. Se agregó aquí para los amantes de Kotlin stackoverflow.com/a/47336504/1761406
Shirane85
7

Kotlin

activity.supportFragmentManager.fragments.last()
El mejor artista
fuente
4
Es posible que deba usarlo .lastOrNull()en caso de que fragmentsesté vacío.
Westy92
6

Hay una lista de fragmentos en fragmentMananger. Tenga en cuenta que eliminar un fragmento no reduce el tamaño de la lista (la entrada del fragmento simplemente se vuelve nula). Por tanto, una solución válida sería:

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
fuente
2
No fiable. Tres razones: 1) Esta es una lista de todos los fragmentos que conoce el administrador de fragmentos, no solo los de la pila. 2) No hay garantía de que el administrador de fragmentos siga agregando nuevos al final de la lista. Claro, lo hará en pruebas simples, pero ¿qué pasa si se elimina un fragmento, dejando un nulo, y luego, en algunas circunstancias, el administrador de fragmentos decide reutilizar esa ranura vacía? No digo que lo haga, pero no hay garantía de que nunca lo haga, bajo ninguna circunstancia. 3) Si usted o algún programador futuro comienza a usar adjuntar / separar para administrar fragmentos, esto no coincidirá con la pila.
ToolmakerSteve
Hola, gracias por su solución, pero no es hermosa y fácil
Muhammad Ali
Dudo que sea una solución funcional. getFragments()devuelve una colección abreviada de fragmentos. Quizás el último sea visible, pero no muchos otros.
CoolMind
5

La respuesta dada por deepak goel no me funciona porque siempre obtengo nulo de entry.getName();

Lo que hago es establecer una etiqueta para el fragmento de esta manera:

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

Donde ft es mi transacción de fragmento y FRAGMENT_TAGes la etiqueta. Luego uso este código para obtener el fragmento:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
Eduardo
fuente
Esto solo funcionará si le da a todos los fragmentos la misma etiqueta, lo cual no es una buena idea si desea encontrar un fragmento específico más adelante.
Shirane85
4

Solo tomé la respuesta (correcta) de @roghayeh hosseini y la hice en Kotlin para aquellos aquí en 2017 :)

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

* Esto debe llamarse desde dentro de una actividad.

Disfruta :)

Shirane85
fuente
3

puede utilizar getBackStackEntryAt () . Para saber cuántas entradas tiene la actividad en el backstack, puede usar getBackStackEntryCount ()

int lastFragmentCount = getBackStackEntryCount() - 1;
Cinta negra
fuente
11
Pero, ¿cómo puedo conseguir el último fragmento en backstack? PopBackStackEntryAt () solo devuelve una instancia de BackStackEntry, NO un fragmento
Leem.fin
sí, tiene razón, pero cada BackStackEntry contiene una identificación que puede recuperar con getId (). puede usar esta identificación para recuperar el fragmento
Blackbelt
1
Para obtener el último fragmento: getBackStackEntryCount () - 1
An-droid
3
Esta respuesta es incorrecta, veo que las entradas de backstack tienen una identificación de 0, por lo que no puedo recuperar el fragmento por identificación.
Justin
1
Esto es antiguo, pero para cualquiera que pasee por aquí: la pila de actividades no contiene fragmentos, sino transacciones de fragmentos, por eso esta respuesta es incorrecta
maciekjanusz
3

Agregaré algo a la respuesta de Deepak Goel ya que muchas personas, incluido yo, obtuvieron un valor nulo al usar su método. Aparentemente, para que la etiqueta funcione cuando agrega un fragmento al backstack, debería hacerlo así:

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

Necesita agregar la misma etiqueta dos veces.

Hubiera comentado pero no tengo 50 reputación.

Cosmin Vacaru
fuente
tienes 50 ahora :)
davidbilla
2

Mantener su propia pila de vuelta: myBackStack. Como Addfragmento del FragmentManager, agréguelo también myBackStack. En onBackStackChanged()pop a partir de myBackStackcuando su longitud es mayor que getBackStackEntryCount.

Arne Evertsson
fuente
2

Parece que algo ha cambiado para mejor, porque el código a continuación funciona perfectamente para mí, pero no lo encontré en las respuestas ya proporcionadas.

Kotlin:

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

Java:

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

Si lo usa addToBackStack(), puede usar el siguiente código.

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

Akbar Kazimov
fuente
1

En realidad, no se ha agregado el último fragmento a la pila porque puede agregar varios o fragmentos a la pila en una sola transacción o simplemente eliminar fragmentos sin agregar uno nuevo.

Si realmente desea tener una pila de fragmentos y poder acceder a un fragmento por su índice en la pila, será mejor que tenga una capa de abstracción sobre FragmentManagery su backstack. Así es como puede hacerlo:

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

Ahora, en lugar de agregar y quitar fragmentos llamando FragmentManagerdirectamente, se debe utilizar push(), replace()y pop()los métodos de FragmentStackManager. Y podrá acceder al fragmento superior simplemente llamando stack.get(stack.size() - 1).

Pero si te gustan los trucos, tengo otras formas de hacer cosas similares. Lo único que tengo que mencionar es que estos trucos funcionarán solo con fragmentos de soporte.

El primer truco es simplemente agregar todos los fragmentos activos al administrador de fragmentos. Si solo reemplaza los fragmentos uno por uno y los saca de la pila, este método devolverá el fragmento superior:

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

El segundo enfoque es event more hacky y le permite obtener todos los fragmentos agregados en la última transacción para la que addToBackStackse ha llamado:

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

Tenga en cuenta que en este caso debe poner esta clase en el android.support.v4.apppaquete.

Miguel
fuente
0

O simplemente puede agregar una etiqueta al agregar fragmentos correspondientes a su contenido y usar un campo String estático simple (también puede guardarlo en el paquete de instancia de actividad en el método onSaveInstanceState (Bundle)) para contener la última etiqueta de fragmento agregada y obtener este fragmento porTag () en cualquier momento que necesite ...

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

La respuesta más alta (Deepak Goel) no funcionó bien para mí. De alguna manera, la etiqueta no se agregó correctamente.

Terminé enviando el ID del fragmento a través del flujo (usando intenciones) y recuperándolo directamente del administrador de fragmentos.

htafoya
fuente