Cómo trabajar con la barra de la app

La barra de la app superior proporciona un lugar coherente en la parte superior de la ventana de la app para mostrar información y acciones desde la pantalla actual.

Ejemplo de una barra de la app superior
Figura 1: Ejemplo de una barra de la app superior.

La propiedad de la barra de la app varía según las necesidades de tu app. Cuando usas fragmentos, la barra de la app se puede implementar como una ActionBar que pertenece a la actividad del host o a una barra de herramientas dentro del diseño de tu fragmento.

Si todas tus pantallas usan la misma barra de la app que siempre está en la parte superior y abarca el ancho de la pantalla, usa una barra de acciones proporcionada por el tema y alojada por la actividad. El uso de barras de la app temáticas ayuda a mantener una apariencia coherente y brinda un lugar para alojar menús de opciones y un botón Arriba.

Usa una barra de herramientas alojada por el fragmento si deseas tener más control sobre el tamaño, la ubicación y la animación de la barra de la app en varias pantallas. Por ejemplo, quizás necesites una barra de la app que se contraiga o que solo use la mitad del ancho de la pantalla y que esté centrada verticalmente.

En diferentes situaciones, se requieren enfoques distintos para cuestiones como aumentar menús y responder a la interacción del usuario. Comprender los diferentes enfoques y usar el mejor para tu app te ahorra tiempo y ayuda a garantizar que tu app funcione correctamente.

Los ejemplos de este tema hacen referencia a un ExampleFragment que contiene un perfil editable. El fragmento aumenta el siguiente menú definido por XML en la barra de la app:

<!-- sample_menu.xml -->
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

El menú contiene dos opciones: una para navegar a una pantalla del perfil y otra para guardar los cambios de perfil que se realicen.

Barra de la app perteneciente a una actividad

La barra de la app suele ser propiedad de la actividad del host. Cuando esto ocurre, los fragmentos pueden interactuar con la barra de la app anulando los métodos del framework que son llamados durante la creación del fragmento.

Cómo registrarse con la actividad

Debes informar al sistema que el fragmento de la barra de la app participa en la propagación del menú de opciones. Para ello, llama a setHasOptionsMenu(true) en el método onCreate(Bundle) de tu fragmento, como se muestra en el siguiente ejemplo:

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

public class ExampleFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

setHasOptionsMenu(true) le indica al sistema que tu fragmento desea recibir devoluciones de llamada relacionadas con el menú. Cuando ocurre un evento relacionado con el menú, como un clic, primero se llama al método de control de eventos en la actividad antes de que se lo llame en el fragmento.

Sin embargo, no te bases en este orden en la lógica de tu aplicación. Si la misma actividad aloja varios fragmentos, cada uno puede proporcionar opciones de menú, en cuyo caso el orden de devolución de llamada depende del orden en el que se agregan los fragmentos.

Cómo aumentar el menú

Para combinar tu menú con el menú de opciones de la barra de la app, anula onCreateOptionsMenu() en tu fragmento. Ese método recibe el menú actual de la barra de la app y un MenuInflater como parámetros. Usa el amplificador de menú a fin de crear una instancia del menú del fragmento y, luego, combinarla con el menú actual, como se muestra en el siguiente ejemplo:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

En la Figura 2, se muestra el menú actualizado.

El menú de opciones ahora contiene tu fragmento de menú
Figura 2: El menú de opciones ahora contiene tu fragmento de menú.

Cómo controlar eventos de clic

Cada actividad y fragmento que participa en el menú de opciones puede responder a toques. El elemento onOptionsItemSelected() del fragmento recibe el elemento de menú seleccionado como un parámetro y muestra un valor booleano para indicar si se consume el toque. Una vez que una actividad o un fragmento muestre true desde onOptionsItemSelected(), ningún otro fragmento participante recibirá la devolución de llamada.

En tu implementación de onOptionsItemSelected(), usa una declaración switch en el itemId del elemento de menú. Si el elemento seleccionado es tuyo, controla el toque de forma adecuada y muestra true para indicar que se gestionó el evento de clic. Si el elemento seleccionado no es tuyo, llama a la implementación de super. De forma predeterminada, la implementación de super muestra false para permitir que continúe el procesamiento del menú.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // Navigate to settings screen.
                true
            }
            R.id.action_done -> {
                // Save profile changes.
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // Navigate to settings screen.
                return true;
            }
            case R.id.action_done: {
                // Save profile changes.
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

Cómo modificar el menú de forma dinámica

Coloca la lógica para ocultar o mostrar un botón o cambiar el ícono en onPrepareOptionsMenu(). Se llama a este método justo antes de que se muestre el menú.

Siguiendo con el ejemplo anterior, el botón Guardar debe ser invisible hasta que el usuario comience a editar y debería desaparecer después de que el usuario efectúe la operación de guardar. Si agregas esta lógica a onPrepareOptionsMenu(), el menú se presenta de forma correcta:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

Cuando necesites actualizar el menú, como cuando un usuario presiona el botón Editar para editar la información del perfil, llama a invalidateOptionsMenu() en la actividad del host para solicitar que el sistema llame a onCreateOptionsMenu(). Luego de realizar la invalidación, puedes hacer las actualizaciones en onCreateOptionsMenu(). Una vez que el menú aumente, el sistema llamará a onPrepareOptionsMenu() y lo actualizará a los efectos de reflejar el estado actual del fragmento.

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

Barra de la app perteneciente a un fragmento

Si la mayoría de las pantallas de tu app no necesita una barra, o bien si una necesita una diferente, puedes agregar una Toolbar a tu diseño del fragmento. Si bien puedes agregar una Toolbar en cualquier lugar de la jerarquía de vistas del fragmento, en general, debes mantenerla en la parte superior de la pantalla. Para usar la Toolbar en el fragmento, proporciona un ID y obtén una referencia a ella en tu fragmento, como lo harías con cualquier otra vista. También puedes animar la barra de herramientas con comportamientos CoordinatorLayout.

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

Cuando usas una barra de la app perteneciente a un fragmento, Google recomienda que uses directamente las APIs de Toolbar. No uses setSupportActionBar() ni las APIs de menú de Fragment, que son adecuadas solo para las barras de la app pertenecientes a una actividad.

Cómo aumentar el menú

El método de conveniencia inflateMenu(int) de Toolbar toma el ID de un recurso de menú como parámetro. Para aumentar un recurso de menú XML en tu barra de herramientas, pasa el resId a este método, como se muestra en el siguiente ejemplo:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

Para aumentar otro recurso de menú XML, llama de nuevo al método con el resId del menú nuevo. Los nuevos elementos de menú se agregan a este y los elementos de menú existentes no se modifican ni se quitan.

Si deseas reemplazar el conjunto de menús existente, borra el menú antes de llamar a inflateMenu(int) con el ID del menú nuevo, como se muestra en el siguiente ejemplo:

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

Cómo controlar eventos de clic

Puedes pasar un OnMenuItemClickListener directamente a la barra de herramientas mediante el método setOnMenuItemClickListener(). Este objeto de escucha se invoca cuando el usuario selecciona un elemento de menú de los botones de acción que se presentan al final de la barra de herramientas o del menú ampliado asociado. El MenuItem seleccionado se pasa al método onMenuItemClick() del objeto de escucha y se puede usar para consumir la acción, como se muestra en el siguiente ejemplo:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen.
                    true
                }
                R.id.action_done -> {
                    // Save profile changes.
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen.
                    return true;
                case R.id.action_done:
                    // Save profile changes.
                    return true;
                default:
                    return false;
            }
        });
    }
}

Cómo modificar el menú de forma dinámica

Cuando la barra de la app es propiedad de tu fragmento, puedes modificar la Toolbar en el tiempo de ejecución de la misma manera que lo harías para cualquier otra vista.

Siguiendo con el ejemplo anterior, la opción Guardar del menú debe ser invisible hasta que el usuario comience a editar y debería desaparecer nuevamente cuando se la presione:

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

Si está presente, el botón de navegación aparecerá al comienzo de la barra de herramientas. Este botón será visible si estableces un ícono de navegación en la barra de herramientas. También puedes configurar un onClickListener() específico de la navegación, al que se llama cada vez que el usuario hace clic en el botón de navegación, como se muestra en el siguiente ejemplo:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere.
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere.
            }
        });
    }
}