He escrito una actividad ficticia que cambia entre dos fragmentos. Cuando pasas de FragmentA a FragmentB, FragmentA se agrega a la pila posterior. Sin embargo, cuando regreso a FragmentA (presionando hacia atrás), se crea un FragmentA totalmente nuevo y se pierde el estado en el que estaba. Tengo la sensación de que busco lo mismo que esta pregunta, pero he incluido un ejemplo de código completo para ayudar a solucionar el problema:
public class FooActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentA());
transaction.commit();
}
public void nextFragment() {
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentB());
transaction.addToBackStack(null);
transaction.commit();
}
public static class FragmentA extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View main = inflater.inflate(R.layout.main, container, false);
main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
((FooActivity) getActivity()).nextFragment();
}
});
return main;
}
@Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save some state!
}
}
public static class FragmentB extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.b, container, false);
}
}
}
Con algunos mensajes de registro agregados:
07-05 14:28:59.722 D/OMG ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG ( 1260): FragmentA.onCreateView
Nunca llama a FragmentA.onSaveInstanceState y crea un nuevo FragmentA cuando devuelve el golpe. Sin embargo, si estoy en FragmentA y bloqueo la pantalla, se llama a FragmentA.onSaveInstanceState. Tan extraño ... ¿me equivoco al esperar que un fragmento agregado a la pila posterior no necesite recreación? Esto es lo que dicen los documentos :
Mientras que si llama a addToBackStack () cuando elimina un fragmento, el fragmento se detiene y se reanudará si el usuario navega hacia atrás.
ListView
. Parece demasiado salto de aro para adjuntar un escucha de desplazamiento y actualizar una variable de instancia.Respuestas:
Si regresa a un fragmento de la pila posterior, no vuelve a crear el fragmento, sino que reutiliza la misma instancia y comienza con
onCreateView()
el ciclo de vida del fragmento, consulte Fragmento del ciclo de vida .Entonces, si desea almacenar el estado, debe usar variables de instancia y no confiar en él
onSaveInstanceState()
.fuente
En comparación con Apple
UINavigationController
yUIViewController
Google, no funciona bien en la arquitectura de software de Android. Y el documento de Android sobreFragment
no ayuda mucho.Cuando ingresa FragmentB desde FragmentA, la instancia de FragmentA existente no se destruye. Cuando presiona Atrás en FragmentB y vuelve a FragmentA, no creamos una nueva instancia de FragmentA. Se
onCreateView()
llamará a la instancia de FragmentA existente .La clave es que no debemos volver a inflar la vista en FragmentA
onCreateView()
, porque estamos usando la instancia de FragmentA existente. Necesitamos guardar y reutilizar rootView.El siguiente código funciona bien. No solo mantiene el estado del fragmento, sino que también reduce la carga de RAM y CPU (porque solo inflamos el diseño si es necesario). No puedo creer que el código y el documento de muestra de Google nunca lo mencionen, sino que siempre inflan el diseño .
Versión 1 (No use la versión 1. Use la versión 2)
------ Actualización el 3 de mayo de 2005: -------
Como se mencionó en los comentarios, a veces
_rootView.getParent()
es nuloonCreateView
, lo que provoca el bloqueo. La versión 2 elimina _rootView en onDestroyView (), como sugirió dell116. Probado en Android 4.0.3, 4.4.4, 5.1.0.Versión 2
¡¡¡ADVERTENCIA!!!
¡Esto es un HACK! Aunque lo estoy usando en mi aplicación, debes probar y leer los comentarios cuidadosamente.
fuente
Supongo que hay una forma alternativa de lograr lo que estás buscando. No digo que sea una solución completa, pero cumplió el propósito en mi caso.
Lo que hice es en lugar de reemplazar el fragmento que acabo de agregar el fragmento de destino. Así que básicamente vas a usar el
add()
método en su lugarreplace()
.¿Qué más hice? Oculto mi fragmento actual y también lo agrego al backstack.
Por lo tanto, se superpone a un nuevo fragmento sobre el fragmento actual sin destruir su vista (verifique que
onDestroyView()
no se llame a su método. Además, agregarlobackstate
me da la ventaja de reanudar el fragmento).Aquí está el código:
El sistema AFAIK solo llama
onCreateView()
si la vista se destruye o no se crea. Pero aquí hemos guardado la vista al no eliminarla de la memoria. Por lo tanto, no creará una nueva vista.Y cuando regrese de Destination Fragment, aparecerá el último
FragmentTransaction
fragmento de eliminación que hará que la vista superior (SourceFragment) aparezca sobre la pantalla.COMENTARIO: Como dije, no es una solución completa ya que no elimina la vista del fragmento de Fuente y, por lo tanto, ocupa más memoria de lo habitual. Pero aún así, cumple el propósito. Además, estamos utilizando un mecanismo totalmente diferente para ocultar la vista en lugar de reemplazarla, lo cual no es tradicional.
Por lo tanto, no se trata realmente de cómo mantiene el estado, sino de cómo mantiene la vista.
fuente
Activity A{Fragment A --> Fragment B}
cuando vuelvo a iniciar la aplicación después de presionar el botón de inicio,onResume()
se llama a ambos fragmentos y, por lo tanto, comienzan su sondeo. ¿Cómo puedo controlar esto?Sugeriría una solución muy simple.
Tome la variable de referencia Ver y configure la vista en OnCreateView. Verifique si la vista ya existe en esta variable, luego devuelva la misma vista.
fuente
if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }
Es apropiado borrar la memoria?null
a lafragmentView
variable en elonDestroy()
método.onDestroyView()
. Este borrado no está ocurriendo para nuestra variable de vista de copia de seguridad (aquífragmentView
) y provocará una pérdida de memoria cuando el fragmento vuelva a apilarse / destruirse. Puede encontrar la misma referencia en [Causas comunes de pérdidas de memoria] ( square.github.io/leakcanary/fundamentals/… ) en la introducción de LeakCanery.Encontré este problema en un Fragmento que contiene un mapa, que tiene demasiados detalles de configuración para guardar / recargar. Mi solución fue básicamente mantener este Fragmento activo todo el tiempo (similar a lo que mencionó @kaushal).
Digamos que tiene el Fragmento A actual y quiere mostrar el Fragmento B. Resumiendo las consecuencias:
Por lo tanto, si desea mantener ambos Fragmentos "guardados", solo alterne usando hide () / show ().
Pros : método fácil y simple para mantener múltiples fragmentos en ejecución
Contras : utiliza mucha más memoria para mantenerlos a todos en funcionamiento. Puede tener problemas, por ejemplo, mostrar muchos mapas de bits grandes
fuente
onSaveInstanceState()
solo se llama si hay un cambio de configuración.Dado que cambia de un fragmento a otro, no hay cambio de configuración, por lo que no hay llamada a
onSaveInstanceState()
allí. ¿Qué estado no se salva? Se puede especificar?Si ingresa algún texto en EditText, se guardará automáticamente. Cualquier elemento de la IU sin ningún ID es el elemento cuyo estado de vista no se guardará.
fuente
onSaveInstanceState()
También se llama cuando el sistema destruye Actividad porque carece de recursos.Aquí, ya que
onSaveInstanceState
en fragment no se llama cuando agrega fragmento en backstack. El ciclo de vida del fragmento en el backstack cuando se restaura comienzaonCreateView
y finalizaonDestroyView
mientrasonSaveInstanceState
se llama entreonDestroyView
yonDestroy
. Mi solución es crear variable de instancia e iniciar enonCreate
. Código de muestra:Y compruébalo en
onActivityCreated
:fuente
fuente
Mi problema era similar pero me vencí sin mantener vivo el fragmento. Suponga que tiene una actividad que tiene 2 fragmentos: F1 y F2. F1 se inicia inicialmente y digamos que contiene información del usuario y luego, bajo alguna condición, F2 aparece al pedirle al usuario que complete el atributo adicional : su número de teléfono. Luego, desea que ese número de teléfono vuelva a F1 y complete el registro, pero se da cuenta de que toda la información del usuario anterior se pierde y no tiene sus datos anteriores. El fragmento se
onSaveInstanceState
vuelve a crear desde cero e incluso si guardó esta información en el paquete vuelve a ser nuloonActivityCreated
.Solución: guarde la información requerida como una variable de instancia en la actividad de llamada. Luego pase esa variable de instancia en su fragmento.
Entonces, siguiendo con mi ejemplo: antes de mostrar F2, guardo los datos del usuario en la variable de instancia utilizando una devolución de llamada. Luego comienzo F2, el usuario completa el número de teléfono y presiona guardar. Uso otra devolución de llamada en la actividad, recopilo esta información y reemplazo mi fragmento F1, esta vez tiene datos de paquete que puedo usar.
Puede encontrar más información sobre devoluciones de llamada aquí: https://developer.android.com/training/basics/fragments/communicating.html
fuente
fuente
Reemplace un fragmento usando el siguiente código:
La actividad de onBackPressed () es:
fuente
fuente
Solución perfecta que encuentra un fragmento antiguo en la pila y lo carga si existe en la pila.
úsalo como
fuente