trabajar - Gestión del ciclo de vida de Android de fragmentos dentro de ViewPager y FragmentPagerAdapter
java android fragment (3)
He estado luchando por descubrir cuál es el manejo correcto de los Fragmentos dentro de una FragmentActivity
con un ViewPager
. Antes de entrar en detalles, un resumen rápido del problema que estoy enfrentando es el siguiente:
Tengo una FragmentActivity
con un ViewPager
. El ViewPager
usa un FragmentPagerAdapter
personalizado, pero muy simple. Cada Fragment
dentro de ViewPager
compone de una ExpandableListView
. También tengo un botón de la barra de acción llamado "Refrescar". Por ahora, supongamos que ViewPager
tiene solo un Fragment
. Se crea la actividad y se Fragment
la ExpandableListView
del Fragment
(hasta ahora todo bien). Cuando se hace clic en el botón Actualizar, el método de manejo dentro de FragmentActivity
itera sobre la lista de Fragments
que están asignados a FragmentPagerAdapter y llama a refresh()
en cada Fragment
para poblar su ListView. Sin embargo, cuando la orientación del dispositivo cambia (por ejemplo, de vertical a horizontal), la actividad se recrea y también lo son los fragmentos. Al hacer clic en el botón Actualizar ahora se iterará sobre los Fragments
no inicializados.
Sé que estoy siendo bastante vago, especialmente sin código de muestra, pero por favor tengan paciencia conmigo. He rastreado las llamadas al problema y al método de la siguiente manera desde el inicio de la aplicación / actividad:
-
FragmentActivity.onCreate()
-
FragmentActivity.setContentView()
-
FragmentActivity.createPagerFragments()
<- esto crea una ArrayList de Fragments y los asigna a un nuevo FragmentPagerAdapter que a su vez está asignado a ViewPager. -
Fragment.onAttach()
-
Fragment.onCreate()
<- nada especial aquí, simplemente llamando al súper método. -
Fragment.onCreateView()
<- nada especial aquí tampoco, solo inflando el diseño -
Fragment.onActivityCreated()
<- nada aquí tampoco. - << Todo bien, la orientación cambia aquí >>
-
FragmentActivity.onCreate()
-
Fragment.onAttach()
-
Fragment.onCreate()
-
FragmentActivity.setContentView()
-
FragmentActivity.createPagerFragments()
-
Fragment.onCreateView()
-
Fragment.onActivityCreated()
- << Botón Actualizar pulsado >>
-
FragmentActivity.refresh()
<- itera sobre los Fragmentos recién creados desde el # 13 (¡no estos por Android!). - << Crash: NullPointerException para mExpandableListView en Fragment. >>
Entonces, el problema, como lo veo, es el siguiente: cuando Android vuelve a crear FragmentActivity
y sus Views
después de un cambio de orientación de pantalla (llamadas # 9-15 anteriores), crea nuevos objetos Fragment
con su estado restaurado a lo que el los originales fueron Sin embargo, estos parecen estar completamente gestionados por el FragmentManager
, y no por el FragmentPagerAdapter
. Por el contrario, cuando FragmentPagerAdapter
se vuelve a crear junto con los Fragments
en el método onCreate
la actividad (consulte la llamada n. ° 13) los Fragments
que se asignan al adaptador nunca tienen sus métodos Fragment.onCreate()
o Fragment.onCreateView()
llamados en todas. Entonces cuando se llama al método refresh () (ver # 17) el método itera sobre estos Fragments
que no han sido inicializados. Por lo tanto, cuando intentan llenar el ExpandableListView
, la variable de instancia de la vista es NULL
. Esto es de esperar ya que la variable de instancia solo se asigna en el método Fragment.onCreateView()
que nunca recibe llamadas en estos Fragments
.
Así que mi pregunta es: ¿cómo se hace una reutilización adecuada de los Fragments
recreados (por Android) después de que la orientación de la pantalla ha cambiado para evitar la creación de nuevos que no se inicialicen? Necesito tener una referencia válida para ellos, con el fin de llamar a su método de actualización () que los rellena según demanda. Idealmente, también deberían asignarse al FragmentPagerAdapter
.
Espero haber sido claro al describir el problema, y la razón por la que no proporcioné código de muestra es porque el problema (como se puede ver) no proviene del código en sí, sino de una recreación de Fragmentos bastante incorrecta (seemigly) en lugar de volver a usar. Pero si es necesario, puedo darte un código de muestra, simplemente por este camino sería más claro.
¡Gracias!
¿Qué le parece agregar esto a la etiqueta de actividad en su manifiesto?
android:configChanges="orientation"
o esto para API 13 o superior
android:configChanges="orientation|screenSize"
por lo que no volverá a crear tus fragmentos cuando cambie de orientación.
Es mucho para leer, pero después de leer solo la introducción y la pregunta y tener experiencia con FragmentStatePagerAdapter, que es similar a FragmentPagerAdapter, puedo decirle que:
Después de la rotación, su adaptador AUTOMÁTICAMENTE adjuntará fragmentos viejos. Parece que, aunque se recrea el adaptador de creación de actividades, FragmentManager, que es global y recrea la actividad de conservación de instancias, detectará que el nuevo FragmentStatePagerAdapter se combina con el mismo ViewPager y solicita los mismos Fragmentos y simplemente los recuperará de BackStack de Fragment.
Usted como diseñador de Fragments puede observar este comportamiento al continuar la invocación de Fragment.onAttach () y Fragment.onDetach (). Cuando onAttach () ocurre, o bien se crea su Fragmento o se reutiliza después de la rotación. Debería poder distinguir que Fragment se rotó con el uso de callback onRestoreRnstanceState ().
Verá en sus registros muchos onCreate () y otros registros de estados simultáneamente, porque FragmentStatePagerAdapter siempre busca / crea min 3 Fragments (excepto si establece que solo son 2 o 1), por lo que también después de la rotación de la pantalla se volverán a unir tres fragmentos de pila posterior.
Espero que haya ayudado.
Creo que esta pregunta sobre cómo recuperar el fragmento actual de un ViewPager lo ayudará. Como ya se señaló, los fragmentos son administrados por el Fragment (State) PagerAdapter y NOT Activity o Fragment''s lifecycle.
- La primera vez que se crea la actividad, el método
getItem
devuelve losgetItem
. Este método se llama solo una vez por fragmento, incluso si la actividad se vuelve a crear. - Tiempos posteriores, los fragmentos son devueltos por el método
instantiateItem
. Lo más probable es que este sea el lugar donde necesite obtener sus fragmentos y llamar a sus métodos de actualización.