Android FragmentTab host y Fragmentos dentro de Fragmentos

Tengo una aplicación con jerarquía como esta:

FragmentTabHost (Main Activity) - Fragment (tab 1 content - splitter view) - Fragment (lhs, list) - Framment (rhs, content view) - Fragment (tab 2 content) - Fragment (tab 2 content) 

Todas las vistas de los fragmentos están siendo infladas de los recursos.

Cuando la aplicación comienza todo aparece y se ve bien. Cuando cambio de la primera pestaña a otra pestaña y vuelvo otra vez me inflame excepciones tratando de recrear las vistas de la pestaña 1.

Excavando un poco más, esto es lo que está sucediendo:

  • En la primera carga, inflar la vista de divisor hace que sus dos fragmentos secundarios se agreguen al administrador de fragmentos.
  • Al cambiar de la primera pestaña, su vista se destruye, pero sus fragmentos de niño se dejan en el gestor de fragmentos
  • Al cambiar de nuevo a la primera pestaña, la vista se vuelve a inflar y puesto que los fragmentos secundarios antiguos están todavía en el gestor de fragmentos, se genera una excepción cuando los nuevos fragmentos secundarios son instanciados (por inflación)

He trabajado en torno a esto mediante la eliminación de los fragmentos de niños del gestor de fragmentos (estoy usando Mono) y ahora puedo cambiar las pestañas sin la excepción.

 public override void OnDestroyView() { var ft = FragmentManager.BeginTransaction(); ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ListFragment)); ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ContentFragment)); ft.Commit(); base.OnDestroyView(); } 

Así que tengo algunas preguntas:

  1. Es el anterior la forma correcta de hacer esto?
  2. Si no, ¿cómo debo hacerlo?
  3. De cualquier manera, ¿cómo se guarda el estado de la instancia en todo esto para que no pierda el estado de vista al cambiar las pestañas?

No estoy seguro de cómo hacer esto en Mono, pero para agregar fragmentos de niño a otro fragmento, no puede utilizar el FragmentManager de la Activity . En su lugar, tiene que utilizar el ChildFragmentManager del Fragment alojamiento:

http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager () http://developer.android.com/reference/android/support/v4/app/Fragment.html#getChildFragmentManager ()

El FragmentManager principal de la Activity maneja las pestañas.
El ChildFragmentManager de tab1 maneja las vistas divididas.

OK, finalmente me di cuenta de esto:

Como se sugirió anteriormente, primero cambié la creación de fragmentos para que se hiciera mediante programación y los tuviera agregados al gestor de fragmentos secundarios, así:

 public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) { var view = inflater.Inflate(Resource.Layout.MyView, viewGroup, false); // Add fragments to the child fragment manager // DONT DO THIS, SEE BELOW var tx = ChildFragmentManager.BeginTransaction(); tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); tx.Commit(); return view; } 

Como era de esperar, cada vez que cambiar las pestañas, una instancia extra de Lhs / RhsFragment se crearía, pero me di cuenta de que el viejo Lhs / RhsFragment OnCreateView también sería llamado. Así que después de cada conmutador de tabulación, habría una llamada más a OnCreateView. Cambie las pestañas 10 veces = 11 llamadas a OnCreateView. Esto es obviamente incorrecto.

Mirando el código fuente de FragmentTabHost, puedo ver que simplemente se separa y vuelve a conectar el fragmento de contenido de la pestaña al cambiar las pestañas. Parece que el ChildFragmentManager del Fragmento de padre mantiene los fragmentos secundarios alrededor y automáticamente vuelve a crear sus vistas cuando se vuelve a conectar el fragmento padre.

Así que, moví la creación de fragmentos a OnCreate, y sólo si no estamos cargando desde el estado guardado:

 public override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); if (savedInstanceState == null) { var tx = ChildFragmentManager.BeginTransaction(); tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); tx.Commit(); } } public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) { // Don't instatiate child fragments here return inflater.Inflate(Resource.Layout.MyView, viewGroup, false); } 

Esto fijó la creación de las vistas adicionales y la pestaña de conmutación funcionó básicamente ahora.

La siguiente pregunta fue guardar y restaurar el estado de vista. En los fragmentos secundarios necesito guardar y restaurar el elemento actualmente seleccionado. Originalmente tenía algo como esto (este es el OnCreateView del fragmento de niño)

 public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) { var view = inflater.Inflate(Resource.Layout.CentresList, container, false); // ... other code ommitted ... // DONT DO THIS, SEE BELOW if (savedInstance != null) { // Restore selection _selection = savedInstance.GetString(KEY_SELECTION); } else { // Select first item _selection =_items[0]; } return view; } 

El problema con esto es que el host de tabulación no llama OnSaveInstanceState al cambiar tabulaciones. Más bien, el fragmento de niño se mantiene vivo y su variable de selección se puede dejar solo.

Así que moví el código para gestionar la selección a OnCreate:

 public override void OnCreate(Bundle savedInstance) { base.OnCreate(savedInstance); if (savedInstance != null) { // Restore Selection _selection = savedInstance.GetString(BK_SELECTION); } else { // Select first item _selection = _items[0]; } } public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) { // Don't restore/init _selection here return inflater.Inflate(Resource.Layout.CentresList, container, false); } 

Ahora todo parece estar funcionando perfectamente, tanto al cambiar las pestañas como al cambiar la orientación.

  • Async / esperar malas prácticas bajo Android?
  • App Crash instantáneamente sin excepción o registro (parece como xamarin / mono error)
  • Añadir Google Maps API v2 en Mono para Android
  • Monodroid - Manejo Haga clic en eventos dentro de las filas ListAdapter
  • InflateException con TextInputLayout y AlertDialog
  • MonoDroid VS Java en el desarrollo de Android?
  • Cómo cambiar el color por defecto cuando el interruptor está "on" (azul a verde) en formas xamarin. En defecto del ios es GReen pero en androide es azul
  • Pasando variables de ViewModel a otro View (MVVMCross)
  • Circular ImageView en Xamarin
  • ¿Cómo agregar una línea horizontal 1px encima de la vista de la imagen en un diseño relativo?
  • Mono Droid onClick evento no encontrado
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.