Cómo crear una IU flexible

Al diseñar tu app para que admita una amplia variedad de tamaños de pantalla, puedes volver a usar tus fragmentos en diferentes configuraciones de diseño a fin de optimizar la experiencia del usuario en función del espacio de pantalla disponible.

Por ejemplo, en un teléfono celular, sería adecuado mostrar un solo fragmento a la vez en una interfaz de usuario de un solo panel. Por el contrario, te recomendamos colocar los fragmentos uno al lado del otro en una tablet, ya que tiene una pantalla más grande en la que se puede mostrar más información al usuario.

Figura 1: Se muestran dos fragmentos en distintas configuraciones para la misma actividad en diferentes tamaños de pantalla. En una pantalla grande, ambos fragmentos pueden ubicarse uno junto al otro. En cambio, en un teléfono celular, solo puede mostrarse un fragmento a la vez, de modo que uno reemplaza al otro a medida que el usuario navega.

La clase FragmentManager proporciona métodos que te permiten agregar, quitar y reemplazar fragmentos en una actividad durante el tiempo de ejecución a fin de crear una experiencia dinámica.

Para obtener más información sobre cómo implementar fragmentos, consulta los siguientes recursos:

Cómo agregar un fragmento a una actividad durante el tiempo de ejecución

En lugar de definir los fragmentos para una actividad en el archivo de diseño (como se muestra en la lección anterior con el elemento <fragment>), puedes agregar un fragmento a la actividad durante el tiempo de ejecución de esta. Debes hacerlo si piensas cambiar fragmentos durante la vida útil de la actividad.

Para realizar una transacción, como agregar o quitar un fragmento, debes usar el FragmentManager para crear una FragmentTransaction, que proporciona las API para agregar, quitar, reemplazar y realizar otras transacciones de fragmentos.

Si tu actividad permite quitar y reemplazar fragmentos, debes agregar los fragmentos iniciales a la actividad durante su método onCreate().

Una regla importante al trabajar con fragmentos (en especial al agregar fragmentos durante el tiempo de ejecución) es que el diseño de tu actividad debe incluir un contenedor View en el que puedas insertar el fragmento.

El siguiente diseño es una alternativa al que se proporciona en la lección anterior en el que se muestra un solo fragmento a la vez. Para poder reemplazar un fragmento por otro, el diseño de la actividad incluye un FrameLayout vacío que actúa como contenedor del fragmento.

Observa que el nombre del archivo es el mismo que el del archivo de diseño de la lección anterior, pero el directorio de diseño no tiene el calificador large. Por lo tanto, este diseño se usa cuando el tamaño de la pantalla del dispositivo no es grande, ya que no admite ambos fragmentos al mismo tiempo.

res/layout/news_articles.xml:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

En tu actividad, llama a getSupportFragmentManager() para obtener un FragmentManager mediante las API de biblioteca de compatibilidad. Luego debes llamar a beginTransaction() para crear un FragmentTransaction y a add() para agregar un fragmento.

Puedes realizar transacciones de varios fragmentos para la actividad mediante el mismo FragmentTransaction. Cuando estés listo para realizar cambios, deberás llamar a commit().

Por ejemplo, aquí te mostramos cómo agregar un fragmento al diseño anterior:

Kotlin

    import android.os.Bundle
    import android.support.v4.app.FragmentActivity

    class MainActivity : FragmentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.news_articles)

            // Check that the activity is using the layout version with
            // the fragment_container FrameLayout
            if (findViewById(R.id.fragment_container) != null) {

                // However, if we're being restored from a previous state,
                // then we don't need to do anything and should return or else
                // we could end up with overlapping fragments.
                if (savedInstanceState != null) {
                    return;
                }

                // Create a new Fragment to be placed in the activity layout
                val firstFragment = HeadlinesFragment()

                // In case this activity was started with special instructions from an
                // Intent, pass the Intent's extras to the fragment as arguments
                firstFragment.arguments = intent.extras

                // Add the fragment to the 'fragment_container' FrameLayout
                supportFragmentManager.beginTransaction()
                        .add(R.id.fragment_container, firstFragment).commit()
            }
        }
    }
    

Java

    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;

    public class MainActivity extends FragmentActivity {
        @Override
        public void onCreate(Bundle savedInstanceState?) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.news_articles);

            // Check that the activity is using the layout version with
            // the fragment_container FrameLayout
            if (findViewById(R.id.fragment_container) != null) {

                // However, if we're being restored from a previous state,
                // then we don't need to do anything and should return or else
                // we could end up with overlapping fragments.
                if (savedInstanceState != null) {
                    return;
                }

                // Create a new Fragment to be placed in the activity layout
                HeadlinesFragment firstFragment = new HeadlinesFragment();

                // In case this activity was started with special instructions from an
                // Intent, pass the Intent's extras to the fragment as arguments
                firstFragment.setArguments(getIntent().getExtras());

                // Add the fragment to the 'fragment_container' FrameLayout
                getSupportFragmentManager().beginTransaction()
                        .add(R.id.fragment_container, firstFragment).commit();
            }
        }
    }
    

Como el fragmento se agregó al contenedor FrameLayout durante el tiempo de ejecución (en lugar de definirlo en el diseño de la actividad con un elemento <fragment>), la actividad puede quitarlo y reemplazarlo por uno diferente.

Cómo reemplazar un fragmento con otro

El procedimiento para reemplazar un fragmento es similar al que se realiza para agregar uno, pero se necesita el método replace() en lugar de add().

Recuerda que cuando realices transacciones de fragmentos, como reemplazar o quitar uno, se recomienda permitir al usuario que retroceda en la navegación y "deshaga" el cambio. Para permitir que el usuario retroceda en la navegación de las transacciones de fragmentos, debes llamar a addToBackStack() antes de confirmar la FragmentTransaction.

Nota: Cuando quites o reemplaces un fragmento, y agregues la transacción a la pila de retroceso, el fragmento que se quita se detiene (no se destruye). Si el usuario retrocede en su navegación para restaurar el fragmento, este se reinicia. Si no agregas la transacción a la pila de retroceso al quitar o reemplazar un fragmento, este se destruirá.

Ejemplo del reemplazo de un fragmento con otro:

Kotlin

    // Create fragment and give it an argument specifying the article it should show
    val newFragment = ArticleFragment()
    Bundle args = Bundle()
    args.putInt(ArticleFragment.ARG_POSITION, position)
    newFragment.arguments = args

    val transaction = supportFragmentManager.beginTransaction().apply {
      // Replace whatever is in the fragment_container view with this fragment,
      // and add the transaction to the back stack so the user can navigate back
      replace(R.id.fragment_container, newFragment)
      addToBackStack(null)
    }

    // Commit the transaction
    transaction.commit();
    

Java

    // Create fragment and give it an argument specifying the article it should show
    ArticleFragment newFragment = new ArticleFragment();
    Bundle args = new Bundle();
    args.putInt(ArticleFragment.ARG_POSITION, position);
    newFragment.setArguments(args);

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

    // Replace whatever is in the fragment_container view with this fragment,
    // and add the transaction to the back stack so the user can navigate back
    transaction.replace(R.id.fragment_container, newFragment);
    transaction.addToBackStack(null);

    // Commit the transaction
    transaction.commit();
    

En el método addToBackStack() se incluye un parámetro de string opcional que especifica un nombre único para la transacción. Este nombre no es necesario a menos que quieras realizar operaciones de fragmentos avanzadas con las API FragmentManager.BackStackEntry.