Tengo un gran problema con la forma en que parece funcionar el backstack de fragmentos de Android y agradecería cualquier ayuda que se ofrezca.
Imagina que tienes 3 fragmentos
[1] [2] [3]
Quiero que el usuario pueda navegar [1] > [2] > [3]
pero en el camino de regreso (presionando el botón Atrás) [3] > [1]
.
Como me hubiera imaginado, esto se lograría al no llamar addToBackStack(..)
al crear la transacción que trae un fragmento [2]
al titular del fragmento definido en XML.
La realidad de esto parece ser que si no quiero volver [2]
a aparecer cuando el usuario presiona el botón Atrás [3]
, no debo llamar addToBackStack
a la transacción que muestra un fragmento [3]
. Esto parece completamente contra-intuitivo (quizás proveniente del mundo de iOS).
De todos modos, si lo hago de esta manera, cuando salgo [1] > [2]
y presiono hacia atrás, regreso a[1]
lo esperado.
Si voy [1] > [2] > [3]
y luego presiono hacia atrás, salto de nuevo a [1]
(como se esperaba). Ahora el comportamiento extraño ocurre cuando intento saltar de [2]
nuevo [1]
. En primer lugar [3]
se muestra brevemente antes de que aparezca [2]
. Si presiono hacia atrás en este punto, [3]
se muestra, y si presiono hacia atrás una vez más, la aplicación se cierra.
¿Alguien puede ayudarme a entender qué está pasando aquí?
Y aquí está el archivo xml de diseño para mi actividad principal:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/headerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.fragment_test.FragmentControls" >
<!-- Preview: layout=@layout/details -->
</fragment>
<FrameLayout
android:id="@+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
Actualización Este es el código que estoy usando para construir por jerarquía de navegación
Fragment frag;
FragmentTransaction transaction;
//Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack
frag = new Fragment1();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2]
frag = new Fragment3();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//END OF SETUP CODE-------------------------
//NOW:
//Press back once and then issue the following code:
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Now press back again and you end up at fragment [3] not [1]
Muchas gracias
fuente
Respuestas:
Explicación: ¿qué está pasando aquí?
Si tenemos en cuenta que
.replace()
es igual a lo.remove().add()
que sabemos por la documentación:entonces lo que sucede es así (estoy agregando números al fragmento para que quede más claro):
(Aquí todas las cosas engañosas comienzan a suceder)
¡Recuerde que
.addToBackStack()
está guardando solo la transacción, no el fragmento como sí mismo! Así que ahora tenemosfrag3
en el diseño:Solución posible
Considere implementar
FragmentManager.BackStackChangedListener
para observar los cambios en la pila posterior y aplicar su lógica en elonBackStackChanged()
método:FragmentTransaction.addToBackStack(String name);
fuente
FragmentManager.BackStackChangedListener
para observar los cambios en la pila posterior. Monitoree todas sus transacciones con elonBackStackChanged()
método y actúe según sea necesario: por ej. rastrear un recuento de transacciones en BackStack; comprobar transacción particular por nombre (FragmentTransaction addToBackStack (String name)
) etc.¡¡¡Correcto!!! Después de tirar mucho del cabello, finalmente he descubierto cómo hacer que esto funcione correctamente.
¡Parece que el fragmento [3] no se elimina de la vista cuando se presiona hacia atrás, por lo que debe hacerlo manualmente!
En primer lugar, no use replace () sino que use remove y add por separado. Parece que replace () no funciona correctamente.
La siguiente parte de esto es anular el método onKeyDown y eliminar el fragmento actual cada vez que se presiona el botón Atrás.
¡Espero que esto ayude!
fuente
En primer lugar, gracias @Arvis por una explicación reveladora.
Prefiero una solución diferente a la respuesta aceptada aquí para este problema. No me gusta jugar con anular el comportamiento de retroceso más de lo absolutamente necesario y cuando he intentado agregar y eliminar fragmentos por mi cuenta sin que aparezca la pila de retroceso predeterminada al presionar el botón de retroceso, me encontré en el infierno de fragmentos :) Si usted. agregue f2 sobre f1 cuando lo elimine f1 no llamará a ninguno de los métodos de devolución de llamada como onResume, onStart, etc. y eso puede ser muy desafortunado.
De todos modos, así es como lo hago:
Actualmente, solo se muestra el fragmento f1.
f1 -> f2
Nada fuera de lo común aquí. Que en el fragmento f2 este código te lleva a fragmentar f3.
f2 -> f3
No estoy seguro al leer documentos si esto debería funcionar, se dice que este método de transacción emergente es asíncrono, y tal vez una mejor manera sería llamar a popBackStackImmediate (). Pero hasta donde puedo decir en mis dispositivos está funcionando sin problemas.
La alternativa mencionada sería:
Aquí en realidad habrá un breve regreso a f1 antes de pasar a f3, por lo que hay un pequeño problema allí.
Esto es realmente todo lo que tiene que hacer, no es necesario anular el comportamiento de la pila de respaldo ...
fuente
Sé que es una pregunta antigua, pero tengo el mismo problema y lo soluciono así:
Primero, agregue Fragment1 a BackStack con un nombre (por ejemplo, "Frag1"):
Y luego, cada vez que desee volver al Fragmento1 (incluso después de agregar 10 fragmentos encima), simplemente llame a popBackStackImmediate con el nombre:
Espero que ayude a alguien :)
fuente
Después de la respuesta de @Arvis, decidí profundizar aún más y he escrito un artículo técnico sobre esto aquí: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping- debido a backstack-nightmare-in-android /
Para los desarrolladores perezosos de todo. Mi solución consiste en agregar siempre las transacciones al backstack y realizar un extra
FragmentManager.popBackStackImmediate()
cuando sea necesario (automáticamente).El código tiene muy pocas líneas de código y, en mi ejemplo, quería saltar de C a A sin saltar de nuevo a "B" si el usuario no fue más profundo en el backstack (por ejemplo, de C navega a D).
Por lo tanto, el código adjunto funcionaría de la siguiente manera A -> B -> C (atrás) -> A y A -> B -> C -> D (atrás) -> C (atrás) -> B (atrás) -> A
dónde
fueron emitidos de "B" a "C" como en la pregunta.
Ok, Ok aquí está el código :)
fuente
Si está luchando con addToBackStack () y popBackStack (), simplemente use
En su actividad en OnBackPressed (), descubra el segmento por etiqueta y luego haga sus cosas
Para obtener más información https://github.com/DattaHujare/NavigationDrawer , nunca uso addToBackStack () para manejar fragmentos.
fuente
Creo que, cuando leí tu historia, [3] también está en el backstack. Esto explica por qué lo ves parpadear.
La solución sería nunca establecer [3] en la pila.
fuente
s old method issue but now it
está bien. GraciasTuve un problema similar en el que tenía 3 fragmentos consecutivos en el mismo
Activity
[M1.F0] -> [M1.F1] -> [M1.F2] seguido de una llamada a un nuevoActivity
[M2]. Si el usuario presionó un botón en [M2], quería volver a [M1, F1] en lugar de [M1, F2], que es lo que ya hizo el comportamiento de la presión de retroceso.Para lograr esto, elimino [M1, F2], llamo show en [M1, F1], confirmo la transacción y luego agrego [M1, F2] de nuevo llamándolo con hide. Esto eliminó la presión de retroceso adicional que de otro modo se habría quedado atrás.
Hola Después de hacer este código: no puedo ver el valor de Fragment2 al presionar la tecla Atrás. Mi código:
fuente
executePendingTransactions()
,commitNow()
no trabajado (Trabajó en androidx (jetpack).
fuente