Programación Android, Cómo usar múltiples Fragments II

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn23

Cómo usar múltiples Fragments
Cómo usar múltiples Fragments

En el primer post de cómo usar múltiples fragments en Android hicimos que, cada vez que se creaba una nueva transacción para mostrar diferentes fragments a la pantalla, se realizaran los cambios necesarios en el mapa que contenía la colección de commits. Pero hay una forma de realizar estas comprobaciones de una forma centralizada (para que todos los cambios para eliminar commits en este mapa esté en un mismo punto), y utilizaremos esta misma opción para controlar qué debe mostrarse en pantalla.

Un ejemplo para aclarar esto, sobre el ejemplo de 3 columnas que estamos trabajando, imaginemos que queremos mostrar la primera columna cuando no haya ningún fragment en la columna central, la columna central cuando tenga un fragment visible, pero no haya ninguno en la tercera columna y la tercera columna cuando contenga un fragment visible. En este caso cada vez que vayamos a mostrar un nuevo fragment tendremos que comprobar cuál está presente y mostrar la columna que queramos mostrar, y podremos hacerlo en cuanto añadimos el fragment, pero ¿qué pasa si le damos al botón atrás del dispositivo? El fragment ya no está visible, pero no tenemos control sobre esto y por lo tanto se sigue viendo la columna que ahora ya está vacía.

Para evitar esto la clase FragmentManager contiene un listener OnBackStackChangedListener, que contiene un método onBackStackChanged. Este método se lanzará cada vez que cambie la pila de fragments que hay en pantalla, por lo que se lanza tanto cuando se añaden nuevos fragments como cuando se eliminan. Este es el punto en el que tendríamos que codificar nuestra lógica para:

  1. Comprobar el estado de la pantalla para eliminar, en su caso, los objetos que no sean necesarios en nuestro mapa de commits.
  2. Comprobar qué fragment pasa a estar visible para tomar la decisión sobre la columna a mostrar en pantalla.

Implementar OnBackStackChangedListener

Teniendo este concepto claro, pasemos a lo que realmente nos gusta, trabajar con el código. En primer lugar, sobre el código que ya tenemos hecho del tutorial anterior, vamos a eliminar todas las líneas en las que pusimos commits.remove(…) o commits.clear(). Una vez que tengamos el código limpio pasaremos a implementar el listener del FragmentManager. Justo debajo de donde hemos inicializado este objeto (método onCreate() de nuestra Activity) haremos las llamadas a los métodos que se encargarán de hacer lo que necesitemos, de esta manera tendremos un código limpio y fácil de leer después:

fragManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
         // Comprobar el estado de los commits para eliminar commits del mapa.
         checkCommits();
                
        // Comprobar el estado de la pantalla y mostrar la columna debida.
        setLayoutParams();
    }
});

A continuación vamos a crear nuestro método checkCommits, en él tendremos que comprobar si un determinado nivel de commits existe en nuestro mapa, y de ser así comprobamos si alguno de los fragments que pueden estar en este nivel están aún en pantalla. Si no se cumplen ambas condiciones, ese commit ya no debe existir en nuestro mapa:

/**
 * Comprobar el estado de los commits para eliminar commits del mapa.
 */
private void checkCommits() {
    // Comprobar si en el mapa existe el segundo nivel de commits, de ser así comprobar si 
    // alguno de los fragments que se encuentran en este nivel está en pantalla.
    if (commits.get(SECOND_COMMIT) != null && ((fragTop1 == null || !fragTop1.isVisible()) &&
            (fragTop2 == null || !fragTop2.isVisible()) &&
            (fragBottom1 == null || !fragBottom1.isVisible()) &&
            (fragBottom2 == null || !fragBottom2.isVisible()))) {
        // Existe el commit en el mapa, pero no hay fragment en la última columna.
        // Eliminar commit.
        commits.remove(SECOND_COMMIT);
    }

    // Hacer la misma tarea con el primer nivel, pero comprobar primero si existe un commit
    // de nivel superior.
    if (commits.get(SECOND_COMMIT) == null && commits.get(FIRST_COMMIT) != null &&
            ((fragCentral1 == null || !fragCentral1.isVisible()) &&
                    (fragCentral2 == null || !fragCentral2.isVisible()))) {
        // Existe el commit en el mapa, pero no hay fragment en la columna central.
        // Eliminar commit.
        commits.clear();
    }
}

El siguiente paso es crear el método setLayoutParams, que va a comprobar en primer lugar qué orientación tiene el dispositivo, para mostrar todas las columnas estando en landscape y sólo la última columna que contenga un fragment en el caso de estar en portrait. Para poder modificar los tamaños lo primero que tenemos que hacer es instanciar objetos de tipo View, que harán referencia a las columnas que tenemos definidas en el xml, para poder modificar los tamaños y así decidir qué mostrar en pantalla, estos objetos los inicializaremos en el método onCreate de nuestra Activity:

private View leftColumn, centerColumn, rightColumn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //...

        leftColumn = findViewById(R.id.mainFragment);
        centerColumn = findViewById(R.id.flCentral);
        rightColumn = findViewById(R.id.llRight);
        
        //...
    }

Una vez que tenemos estas referencias ya creadas, podremos modificar sus tamaños cuando sea necesario. Esto lo hará nuestro método setLayoutParams:

/**
 * Comprueba que la orientación del dispositivo y los commits realizados para mostrar la
 * columna debida o todas las columnas.
 */
private void setLayoutParams() {
    // Estando en portrait muestra la última columna que contenga un fragment.
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        //Si existe un commit en este nivel es que se está mostrando el fragment en la
        // columna de la derecha, por lo tanto se ocultan las demás y se muestra esta.
        if (commits.get(SECOND_COMMIT) != null) {
            // Se establece el ancho de la primera columna a 0.
            LinearLayout.LayoutParams leftColParams =
                    (LinearLayout.LayoutParams) leftColumn.getLayoutParams();
            leftColParams.width = 0;
            leftColParams.weight = 0;
            leftColumn.setLayoutParams(leftColParams);

            // Se establece el ancho de la segunda columna a 0.
            LinearLayout.LayoutParams centerColParams =
                    (LinearLayout.LayoutParams) centerColumn.getLayoutParams();
            centerColParams.width = 0;
            centerColParams.weight = 0;
            centerColumn.setLayoutParams(centerColParams);

            // Se establece el ancho de la última columna al ancho de pantalla.
            LinearLayout.LayoutParams rightColParams =
                    (LinearLayout.LayoutParams) rightColumn.getLayoutParams();
            rightColParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
            rightColParams.weight = 1;
            rightColumn.setLayoutParams(rightColParams);

        } else if (commits.get(FIRST_COMMIT) != null) {
            // Se establece el ancho de la primera columna a 0.
            LinearLayout.LayoutParams leftColParams =
                    (LinearLayout.LayoutParams) leftColumn.getLayoutParams();
            leftColParams.width = 0;
            leftColParams.weight = 0;
            leftColumn.setLayoutParams(leftColParams);

            // Se establece el ancho de la segunda columna al ancho de pantalla.
            LinearLayout.LayoutParams centerColParams =
                    (LinearLayout.LayoutParams) centerColumn.getLayoutParams();
            centerColParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
            centerColParams.weight = 1;
            centerColumn.setLayoutParams(centerColParams);

            // Se establece el ancho de la tercera columna a 0.
            LinearLayout.LayoutParams rightColParams =
                    (LinearLayout.LayoutParams) rightColumn.getLayoutParams();
            rightColParams.width = 0;
            rightColParams.weight = 0;
            rightColumn.setLayoutParams(rightColParams);

        } else {
            // Se establece el ancho de la primera columna al ancho de pantalla.
            LinearLayout.LayoutParams leftColParams =
                    (LinearLayout.LayoutParams) leftColumn.getLayoutParams();
            leftColParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
            leftColParams.weight = 1;
            leftColumn.setLayoutParams(leftColParams);

            // Se establece el ancho de la segunda columna a 0.
            LinearLayout.LayoutParams centerColParams =
                    (LinearLayout.LayoutParams) centerColumn.getLayoutParams();
            centerColParams.width = 0;
            centerColParams.weight = 0;
            centerColumn.setLayoutParams(centerColParams);

            // Se establece el ancho de la tercera columna a 0.
            LinearLayout.LayoutParams rightColParams =
                    (LinearLayout.LayoutParams) rightColumn.getLayoutParams();
            rightColParams.width = 0;
            rightColParams.weight = 0;
            rightColumn.setLayoutParams(rightColParams);
        }
    } else {
        // El se establece el peso de cada columna a 1 para que todas ocupen el mismo
        // espacio en pantalla.
        LinearLayout.LayoutParams leftColParams =
                (LinearLayout.LayoutParams) leftColumn.getLayoutParams();
        leftColParams.width = 0;
        leftColParams.weight = 1;
        leftColumn.setLayoutParams(leftColParams);

        LinearLayout.LayoutParams centerColParams =
                (LinearLayout.LayoutParams) centerColumn.getLayoutParams();
        centerColParams.width = 0;
        centerColParams.weight = 1;
        centerColumn.setLayoutParams(centerColParams);

        LinearLayout.LayoutParams rightColParams =
                (LinearLayout.LayoutParams) rightColumn.getLayoutParams();
        rightColParams.width = 0;
        rightColParams.weight = 1;
        rightColumn.setLayoutParams(rightColParams);
    }
}

Y por último tenemos que recoger el evento que lanza el sistema cuando cambia de orientación la pantalla (haremos una pequeña configuración después en el archivo manifest.xml), para de esta manera poder volver a mostrar todas las columnas o sólo la columna debida, en función de la nueva orientación. Para hacer esto sobreescribiremos el método onConfigurationChanged, lanzado por el sistema al cambiar la configuración de la orientación de la pantalla:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    // Siempre que cambie la configuración del dispositivo establecemos el layout de nuevo.
    // Este evento se lanzará cuando el dispositivo cambie de orientación.
    setLayoutParams();
    super.onConfigurationChanged(newConfig);
}

Ahora en el archivo manifest.xml hacemos una modificación para impedir que nuestra Activity se reinicie cada vez que gire la pantalla, y de esta manera perdamos los valores de nuestras variables, al volver a lanzarse el método onCreate de la Activity. La modificación será añadir el atributo android:configChanges=”orientation|screenSize” a la Activity, que implica que no haya un reinicio de nuestra Activity cuando haya un cambio de orientación ni cuando haya cambio de tamaño de pantalla. Esta segunda opción se añade porque algunas versiones de Android, al cambiar la orientación también hace un cambio de tamaño de pantalla:

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize"
    android:label="@string/app_name">

Y con esto, si lanzamos nuestra aplicación, podremos ver que en landscape siempre se mostrarán todas las columnas, mientras que en portrait se mostrará sólo una de las columnas, que será la última que contenga algún fragment.

Descargas

Puedes acceder a nuestro repositorio para bajar el proyecto aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn23
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

Deja un comentario