Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Fragmento se inicializa dos veces al recargar la actividad con pestañas cuando cambia la orientación

Tengo un problema al recargar una actividad con pestañas y fragmentos cuando cambio la orientación de mi dispositivo.

Aquí está la situación:

Tengo una actividad que tiene 3 pestañas en la barra de acción. Cada ficha carga un fragmento diferente en un FrameLayout en la vista principal. Todo funciona bien si no cambio la orientación del dispositivo. Pero cuando lo hago, Android intenta inicializar el fragmento actualmente seleccionado dos veces que produce el siguiente error:

 E/AndroidRuntime(2022): Caused by: android.view.InflateException: Binary XML file line #39: Error inflating class fragment 

Esta es la secuencia de pasos que producen el error:

  1. Cargo la actividad, selecciona la pestaña 2 y cambia la orientación del dispositivo.
  2. Android destruye la actividad y la instancia del fragmento cargado por la pestaña nr 2 (de ahora en adelante, 'Fragmento 2'). A continuación, procede a crear nuevos casos de la actividad y el fragmento.
  3. Dentro de Activity.onCreate() agrego la primera pestaña a la barra de acción. Cuando hago eso, esta pestaña se selecciona automáticamente. Puede representar un problema en el futuro, pero ahora no me importa. onTabSelected se llama y se crea y carga una nueva instancia del primer fragmento (ver código abajo).
  4. Añado todas las otras pestañas sin que se active ningún evento, lo cual está bien.
  5. Llamo ActionBar.selectTab(myTab) para seleccionar Tab nr 2.
  6. onTabUnselected() se llama para la primera pestaña, y luego onTabSelected() para la segunda pestaña. Esta secuencia reemplaza al fragmento actual por una instancia del Fragmento 2 (ver código abajo).
  7. A continuación, Fragment.onCreateView() se llama en la instancia del fragmento 2 y el diseño del fragmento se infla.
  8. Aquí está el problema. Android onCreate() y luego onCreateView() en la instancia de fragmentos ONCE AGAIN, que produce la excepción cuando intento inflar (una segunda vez) el diseño.

Obviamente el problema es que Android está inicializando el fragmento dos veces, pero no sé por qué.

Traté de no seleccionar la segunda pestaña cuando reaload la actividad, pero el segundo fragmento se inicializa de todos modos y no se muestra (ya que no seleccionó su pestaña).

He encontrado esta pregunta: Android Fragments recreado en el cambio de orientación

El usuario pide básicamente lo mismo que yo, pero no me gusta la respuesta elegida (es sólo un workaroud). Debe haber alguna manera de conseguir este trabajo sin el android:configChanges truco.

En caso de que no esté claro, lo que quiero saber es si prevenir la recreación del fragmento o evitar la doble inicialización de la misma. Sería bueno saber por qué está sucediendo esto también. :PAG

Aquí está el código relevante:

 public class MyActivity extends Activity implements ActionBar.TabListener { private static final String TAG_FRAGMENT_1 = "frag1"; private static final String TAG_FRAGMENT_2 = "frag2"; private static final String TAG_FRAGMENT_3 = "frag3"; Fragment frag1; Fragment frag2; Fragment frag3; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // my_layout contains a FragmentLayout inside setContentView(R.layout.my_layout); // Get a reference to the fragments created automatically by Android // when reloading the activity FragmentManager fm = getFragmentManager(); this.frag1 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_1); this.frag2 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_2); this.frag3 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_3) ActionBar actionBar = getActionBar(); // snip... // This triggers onTabSelected for the first tab actionBar.addTab(actionBar.newTab() .setText("Tab1").setTabListener(this) .setTag(MyActivity.TAG_FRAGMENT_1)); actionBar.addTab(actionBar.newTab() .setText("Tab2").setTabListener(this) .setTag(MyActivity.TAG_FRAGMENT_2)); actionBar.addTab(actionBar.newTab() .setText("Tab3").setTabListener(this) .setTag(MyActivity.TAG_FRAGMENT_3)); Tab t = null; // here I get a reference to the tab that must be selected // snip... // This triggers onTabUnselected/onTabSelected ab.selectTab(t); } @Override protected void onDestroy() { // Not sure if this is necessary this.frag1 = null; this.frag2 = null; this.frag3 = null; super.onDestroy(); } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString()); if (curFrag == null) { curFrag = createFragmentInstanceForTag(tab.getTag().toString()); if(curFrag == null) { // snip... return; } } ft.replace(R.id.fragment_container, curFrag, tab.getTag().toString()); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString()); if (curFrag == null) { // snip... return; } ft.remove(curFrag); } private Fragment getFragmentInstanceForTag(String tag) { // Returns this.frag1, this.frag2 or this.frag3 // depending on which tag was passed as parameter } private Fragment createFragmentInstanceForTag(String tag) { // Returns a new instance of the fragment requested by tag // and assigns it to this.frag1, this.frag2 or this.frag3 } } 

El código para el Fragmento es irrelevante, simplemente devuelve una vista inflada en el método onCreateView() .

8 Solutions collect form web for “Fragmento se inicializa dos veces al recargar la actividad con pestañas cuando cambia la orientación”

Tengo una respuesta simple para eso:

Simplemente agregue setRetainInstance(true); A la onAttach(Activity activity) del onAttach(Activity activity) oa onActivityCreated(Bundle savedInstanceState) . Estos dos son call-backs en la clase Fragmento.

Así que, básicamente, lo que setRetainInstance(true) hace es: Mantiene el estado de su fragmento tal cual es, cuando pasa:

  • onPause();
  • onStop();

Mantiene la instancia del Fragmento sin importar lo que pase la Actividad. El problema con él podría ser, si hay demasiados fragmentos, puede poner una tensión en el sistema.

Espero eso ayude.

 @Override public void onAttach(Activity activity) { super.onAttach(activity); setRetainInstance(true); } 

Abra para la corrección como siempre. Saludos, Edward Quijote.

Parece que, cuando se gira la pantalla y la aplicación se reinicia, se está recreando cada fragmento llamando al constructor por defecto para la clase del fragmento.

He encontrado el mismo problema y utilicé la solución siguiente:

En el fragmento onCreateView inicio de:

 if (mView != null) { // Log.w(TAG, "Fragment initialized again"); ((ViewGroup) mView.getParent()).removeView(mView); return mView; } // normal onCreateView mView = inflater.inflate(R.layout...) 

Creo que esta es una manera a toda prueba de evitar la re-inflación de la vista de la raíz del fragmento:

 private WeakReference<View> mRootView; private LayoutInflater mInflater; /** * inflate the fragment layout , or use a previous one if already stored <br/> * WARNING: do not use in any function other than onCreateView * */ private View inflateRootView() { View rootView = mRootView == null ? null : mRootView.get(); if (rootView != null) { final ViewParent parent = rootView.getParent(); if (parent != null && parent instanceof ViewGroup) ((ViewGroup) parent).removeView(rootView); return rootView; } rootView = mFadingHelper.createView(mInflater); mRootView = new WeakReference<View>(rootView); return rootView; } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { mInflater=inflater!=null?inflater:LayoutInflater.from(getActivity()); final View view = inflateRootView(); ... //update your data on the views if needed } 

Add android:configChanges="orientation|screenSize" en el archivo de manifiesto

Para proteger la actividad recreate, intenta agregar configChanges en tu etiqueta Activity (en manifiesto), como:

 android:configChanges="keyboardHidden|orientation|screenSize" 

Mi código era un poco diferente, pero creo que nuestro problema es el mismo.

En el onTabSelected no utilice reemplazar, uso add cuando es la primera vez que crear el fragmento y adjuntar si no lo es. En el onTabUnselected utilizo detach.

El problema es que cuando la vista es destruida, mi Fragmento fue unido al FragmentManager y nunca destruido. Para resolver que implementé en el onSaveInstanceBundle para separar el fragmento del FragmentManager .

El código era algo así:

 FragmentTransition ft = getSupportFragmentManager().begin(); ft.detach(myFragment); ft.commit(); 

En el primer intento puse ese código en el onDestroy , pero consigo una excepción que me dice que no podría hacerlo después del onSaveInstanceBundle , así que moví el código al onSaveInstanceBundle y todo trabajó.

Lo siento, pero el lugar donde trabajo no me permite poner el código aquí en StackOverflow. Esto es lo que recuerdo del código. Siéntase libre de editar la respuesta para agregar el código.

Creo que se enfrentan a lo que me enfrenté. Tenía un descargador de hilos para json que se inicia en onCreate() , cada vez que cambié la orientación del hilo se llama y la descarga se dispara. onSaveInstance() esto usando onSaveInstance() y onRestoreInstance() para pasar la respuesta de json en una lista, en la combinación de comprobar si la lista no está vacía, así que la transferencia adicional no es necesaria.

Espero que esto te dé una pista.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.