¿Es esta la forma correcta de limpiar la pila de fragmentos al dejar una pila profundamente anidada?

130

Estoy usando la biblioteca de compatibilidad de Android para implementar fragmentos y he extendido la muestra de diseño para que un fragmento contenga un botón que dispara otro fragmento.

En el panel de selección a la izquierda tengo 5 elementos seleccionables - A B C D E.

Cada uno carga un fragmento (vía FragmentTransaction:replace) en el panel de detalles:a b c d e

Ahora he extendido el fragmento epara que contenga un botón que carga otro fragmento e1también en el panel de detalles. He hecho esto en eel método onClick del fragmento de la siguiente manera:

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details_frag, newFrag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();

Si hago las siguientes selecciones:

E - e - e1 - D - E

Entonces el fragmento eestá en el panel de detalles. Esto está bien y lo que quiero. Sin embargo, si presiono el backbotón en este punto, no hace nada. Tengo que hacer clic dos veces porque e1todavía está en la pila. Además, después de hacer clic, obtuve una excepción de puntero nulo en onCreateView:

Para 'resolver' este problema, agregué lo siguiente cada vez que A B C D Ese selecciona:

FragmentManager fm = getActivity().getSupportFragmentManager();
for(int i = 0; i < fm.getBackStackEntryCount(); ++i) {
    fm.popBackStack();
}

¿Me pregunto si esta es la solución correcta o si debería estar haciendo algo diferente?

PJL
fuente

Respuestas:

252

Bueno, hay algunas maneras de hacerlo dependiendo del comportamiento previsto, pero este enlace debería brindarle las mejores soluciones y, como es lógico, es de Dianne Hackborn

http://groups.google.com/group/android-developers/browse_thread/thread/d2a5c203dad6ec42

Esencialmente tienes las siguientes opciones

  • Use un nombre para su estado inicial de pila de respaldo y úselo FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • Use FragmentManager.getBackStackEntryCount()/ getBackStackEntryAt().getId() para recuperar el ID de la primera entrada en la pila posterior, y FragmentManager.popBackStack(int id, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) se supone que explota toda la pila de respaldo ... Creo que la documentación para eso es simplemente incorrecta. (En realidad, supongo que simplemente no cubre el caso en el que pasas POP_BACK_STACK_INCLUSIVE),
Joaquín
fuente
¿Qué significa esto? FragmentManager.getBackStackEntryCount () / getBackStackEntryAt (). GetId ()
dannyroa
44
2do trabajó para mí. Lo que significa para borrar toda la pila: getSupportFragmentManager (). PopBackStack (getSupportFragmentManager (). GetBackStackEntryAt (0) .getId (), FragmentManager.POP_BACK_STACK_INCLUSIVE);
NickL
@JorgeGarcia es posible después de popbackstack que acabamos de terminar nuestro fragmento sin reiniciar el anterior.
duggu
Tenga en cuenta que también existe la versión popBackStackImmediate () ya que popBackStack () es asíncrono, lo que significa que la eliminación no se produce en el momento exacto en que llama al método.
Genc
66
Afirma que el grupo está prohibido como spam y no puedo acceder al enlace :( ¿alguien tiene el recurso en otro lugar?
EpicPandaForce
61

La otra solución limpia si no desea mostrar todas las entradas de la pila ...

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_container, fragmentInstance).addToBackStack(null).commit();

Esto limpiará la pila primero y luego cargará un nuevo fragmento, por lo que en cualquier punto tendrá un solo fragmento en la pila.

Pawan Maheshwari
fuente
1
Esto es muy malo: getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);ante un replace (), ya que se carga el llamado Fragmento onCreateView(), onActivityCreated()etc. La restauración y destroing un Fragmento instante es malo. El Fragment puede registrar un receptor, por ejemplo. Esto afecta el rendimiento.
VasileM
@VasileM, ¿podría describir con detalles? ¿En qué casos es malo? ¿Es por el comportamiento asincrónico de FragmentTransaction? ¿Hay solo un mal rendimiento de la pila de fragmentos incorrecta?
CoolMind
27

Gracias a la respuesta de Joachim , finalmente uso el código para borrar toda la entrada de la pila.

// In your FragmentActivity use getSupprotFragmentManager() to get the FragmentManager.

// Clear all back stack.
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {

    // Get the back stack fragment id.
    int backStackId = getSupportFragmentManager().getBackStackEntryAt(i).getId();

    getSupportFragmentManager().popBackStack(backStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

} /* end of for */
Jay Parker
fuente
18
¿Por qué usar un bucle aquí? Para mí, FragmentManager.popBackStack (nulo, FragmentManager.POP_BACK_STACK_INCLUSIVE) borró todas las entradas de back stack ...
Marek
3
@Marek, porque a veces no se deben eliminar todos los fragmentos, un bucle es adecuado para esto.
CoolMind
1
@CoolMind, eso todavía no tiene sentido para mí. popBackStack(backStackId, INCLUSIVE)hará aparecer todos los fragmentos (estados) de nuevo a backStackId, por lo que una vez que aparezca el más bajo ique va a aparecer, todos los más altos deberían aparecer al mismo tiempo. Entonces, ¿cuál es el punto de un bucle?
LarsH
@LarsH, tienes razón. Ver stackoverflow.com/a/59158254/2914140 . Para hacer estallar otros fragmentos en la etiqueta requerida, primero debemos agregar el fragmento con addToBackStack ( etiqueta ).
CoolMind
7

He investigado mucho para limpiar Backstack, y finalmente veo Transaction BackStack y su administración . Aquí está la solución que mejor funcionó para mí.

 // CLEAR BACK STACK.
    private void clearBackStack() {
        final FragmentManager fragmentManager = getSupportFragmentManager();
        while (fragmentManager.getBackStackEntryCount() != 0) {
            fragmentManager.popBackStackImmediate();
        }
    }

El método anterior recorre todas las transacciones en el backstack y las elimina inmediatamente de una en una.

Nota: el código anterior a veces no funciona y me enfrento a ANR debido a este código, así que no intente esto.

El siguiente método de actualización elimina todo el envío de ese "nombre" del backstack.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack("name",FragmentManager.POP_BACK_STACK_INCLUSIVE);
  • nombre Si no es nulo, este es el nombre de un estado anterior anterior para buscar; si se encuentra, se mostrarán todos los estados hasta ese estado. los
  • El indicador POP_BACK_STACK_INCLUSIVE se puede usar para controlar si el estado en sí mismo aparece. Si es nulo, solo aparece el estado superior.
Lokesh
fuente
No veo por qué los eliminarías uno a la vez. ¿Hay alguna razón por la que eligió esta solución en lugar de las otras soluciones? ¿Hay alguna razón para eliminarlos uno a la vez en lugar de todos a la vez?
miva2
no hay ningún método para eliminar el backstack a la vez, consulte developer.android.com/reference/android/app/…
Lokesh el
3

Estoy usando un código similar a los que usan el ciclo while pero llamo al recuento de entradas en cada ciclo ... así que supongo que es algo más lento

FragmentManager manager = getFragmentManager();
while (manager.getBackStackEntryCount() > 0){
        manager.popBackStackImmediate();
    }
Jorge Lozano
fuente
0

Como está escrito en Cómo hacer estallar un fragmento de backstack y por LarsH aquí, podemos hacer estallar varios fragmentos de arriba hacia abajo a una etiqueta específica ( junto con el fragmento etiquetado ) usando este método:

fragmentManager?.popBackStack ("frag", FragmentManager.POP_BACK_STACK_INCLUSIVE);

Sustituye "frag" con la etiqueta de tu fragmento. Recuerde que primero debemos agregar el fragmento al backstack con:

fragmentTransaction.addToBackStack("frag")

Si agregamos fragmentos con addToBackStack(null), no haremos estallar fragmentos de esa manera.

CoolMind
fuente
-4
    // pop back stack all the way
    final FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
    int entryCount = fm.getBackStackEntryCount(); 
    while (entryCount-- > 0) {
        fm.popBackStack();
    }
farid_z
fuente