Cómo generar una IU flexible

Cuando designas tu aplicación para que admita una amplia variedad de tamaños de pantalla, puedes reutilizar 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 para una interfaz de usuario de un solo panel. Por el contrario, te recomendamos disponer los fragmentos uno al lado del otro en una tablet, ya que tiene un mayor tamaño de pantalla que permite mostrar más información al usuario.

Figura 1: Dos fragmentos exhibidos en distintas configuraciones para la misma actividad en diferentes tamaños de pantalla En una pantalla grande, ambos fragmentos pueden ubicarse uno al lado del otro, pero en un teléfono celular, solo puede exhibirse un fragmento a la vez, de modo que los fragmentos se reemplazan unos a otros 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 elementos Fragment, consulta los siguientes recursos.

Cómo agregar un elemento Fragment a un Activity durante el tiempo de ejecución

En lugar de definir los fragmentos de 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. Esto es necesario si piensas cambiar fragmentos durante la vida útil de la actividad.

Para realizar una transacción, como agregar o quitar un fragmento, debes usar FragmentManager para crear una FragmentTransaction, que proporciona 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 el método onCreate() de la actividad.

Una regla importante al trabajar con fragmentos, especialmente 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 muestra en la lección anterior y que exhibe un solo fragmento a la vez. Para poder reemplazar un fragmento con otro, el diseño de la actividad debe incluir un FrameLayout vacío que actúe como el contenedor del fragmento.

Ten en cuenta 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 que este diseño se utiliza cuando la pantalla del dispositivo es más pequeña que 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, invoca a getSupportFragmentManager() para obtener un FragmentManager mediante el uso de las API de la Biblioteca de compatibilidad. Luego, invoca a beginTransaction() para crear una FragmentTransaction y llama a add() para agregar un fragmento.

Puedes realizar múltiples transacciones de fragmentos para la actividad mediante el mismo objeto FragmentTransaction. Cuando estés listo para realizar los cambios, debes invocar 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();
        }
    }
}

Debido a que el fragmento se agregó al contenedor de 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 quitar el fragmento y reemplazarlo con uno diferente.

Cómo reemplazar un fragmento con otro

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

Recuerda que cuando realizas transacciones con fragmentos, como reemplazar o quitar uno, se recomienda permitirle al usuario retroceder en la navegación y "deshacer" el cambio. Para permitirle al usuario retroceder en la navegación de las transacciones con fragmentos, debes invocar a addToBackStack() antes de confirmar la FragmentTransaction.

Nota: Cuando quitas o reemplazas un fragmento y agregas la transacción a la pila de retroceso, el fragmento que se está quitando se detiene (no se destruye). Si el usuario retrocede en su navegación para restaurar el fragmento, este se reiniciará. Si no agregas la transacción a la pila de retroceso, se destruirá el fragmento cuando se quite o reemplace.

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();

El método addToBackStack() incluye un parámetro de cadena opcional que especifica un nombre único para la transacción. El nombre no es necesario, a menos que pienses realizar operaciones avanzadas con fragmentos mediante el uso de las API de FragmentManager.BackStackEntry.