Necesito usar Tabs en mi aplicación y, por lo tanto, basé mi código en el ejemplo de FragmentTabs de Android encontrado aquí.. Sin embargo, he cambiado un par de líneas en el código para adaptarlo a mi propio propósito, que es el siguiente.

En el código de Android tal como se indica, cada vez que el usuario cambia la pestaña, se destruye el Fragmento que se pierde de vista y se crea el Fragmento que se ve. Para mis propósitos, esto NO es deseable. Aunque mis fragmentos pueden ser independientes entre sí, reaccionan a los datos en tiempo real que llegan a través de una conexión de red y actúan sobre ellos. El hecho de que el Fragmento esté fuera de la vista NO significa que quiera que deje de funcionar en segundo plano (que, lamentablemente, parece ser la línea de pensamiento en el ejemplo de Android). En consecuencia, modifiqué el código para 'ocultar' y 'mostrar' pestañas en lugar de crearlas y destruirlas.

Para lograr esto, en el código de ejemplo alteré el onTabChanged() en dos lugares como se muestra a continuación:

public void onTabChanged(String tabId) {
        TabInfo newTab = mTabs.get(tabId);
        if (mLastTab != newTab) {
            FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
                    ft.hide(mLastTab.fragment);  //***** 1st Alteration
                    //ft.detach(mLastTab.fragment);
                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mActivity,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
                    ft.show(newTab.fragment);   //************ 2nd Alteration
                    //ft.attach(newTab.fragment);
                }
            }

            mLastTab = newTab;
            ft.commit();
            mActivity.getSupportFragmentManager().executePendingTransactions();
        }
    }

Esto ahora funciona como quiero. El problema ahora surge cuando el dispositivo se reorienta. Tras el primer cambio de orientación, no se muestra el contenido de la pestaña seleccionada anteriormente, es decir, el Fragmento. Esto tiene que ver con el hecho de que estoy 'ocultando' y 'mostrando' pestañas. Sin embargo, lo que es más grave, en la segunda reorientación del dispositivo se lanza una excepción:

E / AndroidRuntime (371): Causado por: java.lang.NullPointerException E / AndroidRuntime (371): en android.support.v4.app.FragmentManagerImpl.findFragmentByTag (FragmentManager.java:1211) E / AndroidRuntime (371): en $ TabManager.addTab (ClassName.java:121)

El método addTab está aquí:

public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
        tabSpec.setContent(new DummyTabFactory(mActivity));
        String tag = tabSpec.getTag();

        TabInfo info = new TabInfo(tag, clss, args);

        // Check to see if we already have a fragment for this tab, probably
        // from a previously saved state.  If so, deactivate it, because our
        // initial state is that a tab isn't shown.
        FragmentManager fm = mActivity.getSupportFragmentManager();
        Log.i("Class Name","addTab() FragmentManager = " + fm.toString());
        Log.i("Class_Name","addTab() tag = " + tag);
        info.fragment = fm.findFragmentByTag(tag); // **** This causes the exception

        /*
         * Commented the following as Fragments are not removed but hidden/shown on tab change.
         */
        if (info.fragment != null && !info.fragment.isDetached()) {
            FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
            ft.detach(info.fragment);
            ft.commit();
        }

        mTabs.put(tag, info);
        mTabHost.addTab(tabSpec);
    }

Más específicamente, la excepción se lanza al info.fragment = fm.findFragmentByTag(tag);. Como se puede ver en el código, he intentado aislar exactamente qué objeto es nulo, es decir, ¿es el FragmentManager o la etiqueta, pero estos no se registran como nulos.

No entiendo completamente el ejemplo de Android y funciona bien si adjunto y separo fragmentos en lugar de ocultarlos y mostrarlos. ¿Alguien puede explicar cuál es el problema?

0
D-Dᴙum 25 may. 2012 a las 15:41
Me parece que si está comprobando que savedInstanceState != null antes de hacer cualquier cosa, todo debería restaurarse tal como estaba antes del cambio de orientación ...
 – 
Barak
25 may. 2012 a las 16:29
Me pregunto si es algo en el código del propio FragmentManager.
 – 
D-Dᴙum
25 may. 2012 a las 16:59
¿Podrías publicar onCreate para tu actividad que contenga las pestañas?
 – 
Barak
25 may. 2012 a las 17:16
El método onCreate () es el mismo que en el código de ejemplo para el que vinculé anteriormente, excepto que usé mis propios Fragmentos en su lugar. Pero todavía falla de la misma manera si uso mis propios Fragmentos o los que se dan en el ejemplo.
 – 
D-Dᴙum
25 may. 2012 a las 17:57
¿Anula el método onSavedInstanceState como también lo hace el tutorial?
 – 
Barak
25 may. 2012 a las 18:33

1 respuesta

La mejor respuesta

Según los documentos de Android sobre cambios en tiempo de ejecución, cuando cambia la orientación su actividad se reinicia. Eso significa que FragmentManager guarda su estado en un paquete y luego lo vuelve a cargar.

Todas las pestañas que se han examinado se agregan, incluso si no son visibles, por lo que se guardan en la lista de fragmentos agregados de FragmentManager. Sin embargo, TabManager no almacena nada, simplemente se crea de nuevo, y sospecho que es esta falta de coincidencia entre el nuevo TabManager y el FragmentManager restaurado lo que le afecta.

Recomiendo leer con detenimiento ese documento y algunas opciones muy deliberadas sobre guardar / restaurar pestañas.

El resto de esto es pura especulación. Parece que ni Fragment.instantiate ni ft.add verifican si el nuevo fragmento duplica al antiguo. Eso podría conducir a un escenario en el que TabManager contiene una referencia a un fragmento y FragmentManager contiene dos instancias de ese fragmento.

Puede ser suficiente borrar el FragmentManager de las pestañas que no están visibles actualmente en onCreate, o para verificar la existencia del fragmento antes de crear una nueva instancia.

1
Iain 25 may. 2012 a las 17:24