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.
- No puede convertir implícitamente tipo 'System.IO.Stream' a 'Java.IO.InputStream'
- Cómo implementar CountDownTimer Clase en Xamarin C # Android?
- Cómo instalar dos actividades como una aplicación en Xamarin Android C #
- ¿Cómo evitar que la aplicación de Android se desinstale durante la implementación con Xamarin Studio?
- Java.lang.IllegalArgumentException: ya agregado Lokio / AsyncTimeout
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:
- Es el anterior la forma correcta de hacer esto?
- Si no, ¿cómo debo hacerlo?
- 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?
- ¿Por qué MonoDroid no puede encontrar mis ensamblajes?
- Objeto refrence no establecido en una instancia de un objeto Cuando creo nueva aplicación de Android (Visual Studio)
- Xamarin.Forms MissingMethodException: 'Android.Support.V4.Widget.DrawerLayout.AddDrawerListener' no encontrado
- Cómo configurar onclick oyente en xamarin?
- Targeting / Developing para múltiples plataformas móviles con un solo lenguaje de programación (C #)? ¿Coste-beneficio?
- ¿Cómo se compara Xamarin para Android (Mono para Android) con el desarrollo nativo de Android?
- HAXM de Android SDK desactivado después de instalar Hyper V
- ¿Cómo puedo resolver, "La aplicación en la que se basa este tipo de proyecto no se encontró." Error?
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.