tutoriales studio proyectos programacion guia desarrollo avanzado aplicaciones android tabs android-fragments device-orientation

programacion - proyectos android studio pdf



El fragmento se inicializa dos veces al volver a cargar la actividad con pestaƱas cuando cambia la orientaciĆ³n (8)

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 pestaña carga un fragmento diferente en un FrameLayout en la vista principal. Todo funciona bien si no cambio la orientación del dispositivo. Pero cuando hago eso, Android intenta inicializar el fragmento seleccionado actualmente dos veces, lo que produce el siguiente error:

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

Aquí está la secuencia de pasos que producen el error:

  1. Cargo la actividad, selecciono la pestaña nº 2 y cambio 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''). Luego se procede a crear nuevas instancias 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 recibe una llamada y se crea y carga una nueva instancia del primer fragmento (consulte el código a continuación).
  4. Agrego todas las demás pestañas sin que se active ningún evento, lo cual está bien.
  5. Llamo a 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 el fragmento actual de una instancia de Fragmento 2 (ver código a continuación).
  7. A continuación, se llama a Fragment.onCreateView() en la instancia del Fragmento 2 y el diseño del fragmento se infla.
  8. Aquí está el problema. Android llama onCreate() y luego onCreateView() en la instancia del fragmento UNA VEZ MÁS, lo 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é.

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

Encontré esta pregunta: Fragmentos de Android recreados en el cambio de orientación.

El usuario pregunta básicamente lo mismo que yo, pero no me gusta la respuesta elegida (solo es un trabajo fuerte). Debe haber alguna forma de hacer que esto funcione sin el truco de android:configChanges .

En caso de que no quede claro, quiero saber cómo prevenir la recreación del fragmento o evitar la doble inicialización. 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 la onCreateView() método onCreateView() .


Creo que estás enfrentando lo que yo enfrenté. Tuve un descargador de subprocesos para json que comienza en onCreate() , cada vez que cambié la orientación, se llama al subproceso y se dispara la descarga. onSaveInstance() esto usando onSaveInstance() y onRestoreInstance() para pasar la respuesta json en una lista, en combinación con la verificación de si la lista no está vacía, por lo que no es necesaria la descarga adicional.

Espero que esto te dé una pista.


Creo que esta es una manera infalible de evitar volver a inflar la vista 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 }


He encontrado el mismo problema y he usado la siguiente solución:

en el comienzo de onCreateView del fragmento:

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


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

En el onTabSelected no onTabSelected reemplazar, uso agregar cuando es la primera vez que crea el fragmento y adjunto si no lo es. En el onTabUnselected utilizo separar.

El problema es que cuando se destruye la vista, mi Fragmento se adjuntó al FragmentManager y nunca se destruyó. Para resolver eso implementé en 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 onDestroy , pero recibí una excepción que me decía que no podía hacerlo después de onSaveInstanceBundle , así que moví el código a onSaveInstanceBundle y todo funcionó.

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


Para proteger la recreación de la actividad, intente agregar configChanges en su etiqueta de Actividad (en manifiesto), como:

android:configChanges="keyboardHidden|orientation|screenSize"


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


Tengo una respuesta simple para eso:

Solo agrega setRetainInstance(true); al Fragmento onAttach(Activity activity) o onActivityCreated(Bundle savedInstanceState) . Estos dos son devoluciones de llamada en la clase de fragmento.

Básicamente, lo que hace setRetainInstance(true) es: Mantiene el estado de su fragmento tal como está, cuando pasa por:

  • onPause();
  • onStop();

Mantiene la instancia del Fragmento sin importar lo que pase la Actividad. El problema con esto 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); }

Abierto para corrección como siempre. Saludos, Edward Quijote.


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