Introducción a Android · INS Marianao

Menús y Toolbar
en Android

Todo el tema ordenado paso a paso: de dibujar la barra en el layout a responder a los clics del menú. Con el código listo para copiar y simuladores para entenderlo.

6pasos
15trozos de código
4simuladores
20preguntas quiz
11:43▾◢▮
Toolbar♥⋮
Paso 01 · Layout

La Toolbar

Las Activity tienen una barra arriba, justo debajo de la barra de estado, llamada barra de aplicación. La dibujamos con el widget Toolbar en el fichero layout de la Activity.

Anatomía
① Barra de estado
② Toolbar
③ Contenido
<androidx.appcompat.widget.Toolbar
    android:id="@+id/my_toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:elevation="4dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    style="@style/Widget.MaterialComponents.Toolbar.Primary"/>

Qué hace cada atributo

android:id="@+id/my_toolbar"Id para encontrarla luego desde Kotlin con findViewById.
layout_width="match_parent"Ocupa todo el ancho de la pantalla.
layout_height="?attr/actionBarSize"Altura estándar de barra de acción (la define el tema).
android:elevation="4dp"Sombra para que "flote" sobre el contenido.
app:layout_constraint…Anclada arriba, izquierda y derecha del padre (ConstraintLayout).
style="…Toolbar.Primary"Estilo Material: fondo colorPrimary y texto colorOnPrimary.
i
Namespace app:

Los atributos app: necesitan xmlns:app="http://schemas.android.com/apk/res-auto" en la raíz del layout (Android Studio lo añade solo).

Paso 02 · Colores

Definir un tema

El tema de la app está en dos ficheros bajo res/values/themes: uno para el modo claro y otro para el oscuro.

▾ res
  ▸ drawable
  ▸ layout
  ▸ menu
  ▸ mipmap
  ▾ values
     colors.xml
     strings.xml
    ▾ themes (2)
       themes.xml
       themes.xml (night)
☀️

themes.xml

Tema claro (light) · res/values/themes.xml

🌙

themes.xml (night)

Tema oscuro · res/values-night/themes.xml

i
¿Por qué aparecen juntos?

La vista Android de Android Studio agrupa los dos en una carpeta virtual themes. En disco están en carpetas distintas.

Contenido del fichero de tema

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.NOM_DEL_TEMA_DE_LA_NOSTRA_APP" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>
🏷️

Nombre del tema

Theme.NOM_DEL_TEMA… cambia según el nombre del proyecto.

🧩

<style>

Un fichero puede tener varios temas, uno por etiqueta <style>.

🎯

<item>

Define el color de un elemento concreto del tema.

Los atributos de color

colorPrimaryColor principal de la marca. Por defecto, el fondo de la Toolbar.
colorPrimary · colorSecondaryRepresentan los colores de tu marca.
colorPrimaryVariant · colorSecondaryVariantVariantes más claras u oscuras de los colores de marca (la primaria tiñe la barra de estado).
android:colorBackgroundColor de fondo de las ventanas de la app.
colorErrorPara errores o advertencias.
colorOn… (colorOnPrimary…)Pintan el primer plano (texto, iconos) encima de otros colores. Necesitan contraste suficiente (accesibilidad).

Más detalles en material.io.

Cambiar los colores a verde

1
Crear los colores en res/values/colors.xml
2
Sustituir los purple por green en los dos themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="green_200">#A5D6A7</color>
    <color name="green_500">#4CAF50</color>
    <color name="green_700">#388E3C</color>
    ...
</resources>
<item name="colorPrimary">@color/green_500</item>
<item name="colorPrimaryVariant">@color/green_700</item>
<item name="colorPrimary">@color/green_200</item>
<item name="colorPrimaryVariant">@color/green_700</item>
✓
¿Por qué green_200 en modo noche?

En tema oscuro se usan tonos más claros para que no deslumbren sobre fondo negro.

Playground

¿Qué pinta cada atributo?

2:22▾◢▮
Toolbar
Paso 03 · Kotlin

Activar la Toolbar

Para que la barra aparezca al ejecutar, en onCreate la buscamos por su id y la convertimos en la barra de acción de la Activity.

val myToolbar: Toolbar = findViewById(R.id.my_toolbar)
setSupportActionBar(myToolbar)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val myToolbar: Toolbar = findViewById(R.id.my_toolbar)
    setSupportActionBar(myToolbar)
}
2:22▾◢▮
Toolbar

Resultado esperado

🔎

findViewById(R.id.my_toolbar)

Obtiene la Toolbar del layout a partir de su id.

🪄

setSupportActionBar(...)

La convierte en la barra de acción: ya puede mostrar título y menú.

!
Import correcto

Usa androidx.appcompat.widget.Toolbar (no android.widget.Toolbar). La Activity debe heredar de AppCompatActivity.

!
Error típico

Si el tema ya trae ActionBar (…DarkActionBar) y llamas a setSupportActionBar, puede petar con "This Activity already has an action bar supplied by the window decor". Solución: tema padre …NoActionBar (ver paso 06).

Paso 04 · res/menu

Añadir acciones: el recurso de menú

Las acciones de la Toolbar se definen en un recurso de menú: un fichero .xml dentro de res/menu.

Etiquetas

<menu> OBLIGATORIO

Tiene que ser el nodo raíz. Contiene <item> o <group>.

<item>

Un elemento del menú. Puede contener un <menu> → submenú. Debe ser hijo de <menu> o <group>.

<group>

Agrupa varios <item> para compartir propiedades (visibilidad, activación…).

<menu>                <!-- raíz obligatoria -->
    <item />
    <group>
        <item />
        <item />
    </group>
    <item>
        <menu>        <!-- submenú -->
            <item />
        </menu>
    </item>
</menu>

Atributos de <item>

android:idId del recurso, para referenciarlo (p. ej. en onOptionsItemSelected).
android:titleTítulo de la opción.
android:titleCondensedTítulo corto para cuando el normal es demasiado largo.
android:iconImagen usada como icono del elemento.
android:onClickMétodo que se ejecuta al pulsar. Público en la Activity y con un MenuItem como único parámetro.
showAsActionCuándo y cómo aparece como botón de acción en la barra (en la práctica app:showAsAction).
fun onFavoriteClick(item: MenuItem) {
    Toast.makeText(this, "Favorito", Toast.LENGTH_SHORT).show()
}

Valores de showAsAction

ifRoom
App♥ ⋮

En la barra solo si hay espacio. Si no, al menú ⋮.

withText
App♥ FAV ⋮

Muestra también el título. Se combina: ifRoom|withText.

never
App⋮

Nunca en la barra. Siempre en el menú ⋮.

always
App♥ 🔍 ⇪ ⚙

Siempre en la barra. Evitar salvo lo esencial: se pueden superponer.

Simulador

Juega con showAsAction y el ancho de pantalla

11:43▾◢▮
Ancho de pantalla
my_toolbar_menu.xml · en vivoxml
Paso 05 · Menú completo

Creación del menú

Creamos el fichero de menú, lo inflamos en la Activity y gestionamos qué pasa al pulsar cada opción.

1
Clic derecho en res

New → Android Resource File

2
Nombre y tipo

File name: my_toolbar_menu · Resource type: Menu

3
Añadir los items

En res/menu/my_toolbar_menu.xml

4
Inflar el menú

onCreateOptionsMenu

5
Gestionar los clics

onOptionsItemSelected

New Resource File
File name:my_toolbar_menu Resource type:Menu ▾ Root element:menu Directory name:menu
🖼
El icono

ic_baseline_favorite_24: descárgalo de fonts.google.com/icons o créalo con Vector Asset de Android Studio.

Los ficheros del menú

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_favorite"
        app:showAsAction="ifRoom"
        android:icon="@drawable/ic_baseline_favorite_24"
        android:title="@string/action_favorite" />

    <!-- Settings, should always be in the overflow -->
    <item
        android:id="@+id/action_settings"
        app:showAsAction="never"
        android:title="@string/action_settings" />

</menu>
<string name="action_favorite">Favorite</string>
<string name="action_settings">Settings</string>
!
No olvides los strings

Los títulos usan @string/…: hay que crearlos en strings.xml o no compila.

El código de la Activity

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.my_toolbar_menu, menu)
    return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.action_favorite -> {
            Toast.makeText(this, "You have selected Favorite", Toast.LENGTH_SHORT).show()
            true
        }
        R.id.action_settings -> {
            Toast.makeText(this, "You have selected Settings", Toast.LENGTH_SHORT).show()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}
supportActionBar?.hide()   // ocultar la Toolbar
supportActionBar?.show()   // mostrarla otra vez
📥

inflate()

menuInflater.inflate convierte el XML en MenuItems. return true = se muestra.

🎯

item.itemId

El android:id del item pulsado. Devolvemos true si lo hemos gestionado.

↩️

else → super

Si no es nuestro, lo gestiona el padre. El ?. de supportActionBar es porque puede ser null.

Pruébalo

Igual que en el emulador

11:44▾◢▮
Toolbar
Settings

Pulsa ♥ o ⋮ → Settings en el móvil y mira qué se ejecuta:

El usuario pulsa una opción
onOptionsItemSelected(item)
when (item.itemId)
Toast.makeText(...).show() → true
Paso 06 · Estilo propio

Personalización de la Toolbar

Podemos definir temas distintos al principal de la app solo para la Toolbar y para su menú desplegable.

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.NOM_DEL_TEMA_DE_LA_NOSTRA_APP" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- The App Theme -->
        ...
    </style>
    <style name="Theme.Custom.Toolbar" parent="ThemeOverlay.MaterialComponents.Dark">
        <item name="android:background">@color/customGray</item>
        <item name="android:textColor">@color/black</item>
        <item name="android:textColorPrimary">@color/black</item>
        <item name="android:textColorSecondary">@color/black</item>
    </style>
    <style name="Theme.Custom.Toolbar.Popup" parent="ThemeOverlay.MaterialComponents.Dark">
        <item name="colorSurface">@color/customGray</item>
        <item name="android:background">@color/customGray</item>
        <item name="android:textColor">@color/black</item>
    </style>
</resources>
<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:elevation="4dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:theme="@style/Theme.Custom.Toolbar"
    app:popupTheme="@style/Theme.Custom.Toolbar.Popup"/>
🎨

android:theme

Tema de la Toolbar en sí: fondo, título, iconos.

📋

app:popupTheme

Tema de su menú desplegable (overflow ⋮).

i
Fíjate en el tema base

Ahora el padre es …DayNight.NoActionBar: la barra la pone nuestra Toolbar, no el sistema. Y customGray hay que definirlo en colors.xml.

Comparar

Tema por defecto vs personalizado

12:00▾◢▮
Toolbar♥⋮
Settings

Toolbar con colorPrimary y menú con el tema por defecto.

Resumen

Chuleta rápida

Todo el tema en una pantalla, para repasar antes del examen.

01 · Dibujar
res/layout/activity_main.xml
<androidx.appcompat.widget.Toolbar/>
02 · Colores
themes.xml + values-night
colorPrimary colorPrimaryVariant colorOnPrimary
02 · Definir colores
res/values/colors.xml
<color name="green_500">
03 · Activar
onCreate
setSupportActionBar(myToolbar)
04 · Acciones
res/menu/my_toolbar_menu.xml
<menu><item app:showAsAction/>
05 · Mostrar menú
onCreateOptionsMenu
menuInflater.inflate(R.menu.x, menu)
05 · Clics
onOptionsItemSelected
when (item.itemId) {…}
05 · Ocultar
Activity
supportActionBar?.hide() show()
06 · Tema propio
Toolbar XML
android:theme app:popupTheme
showAsAction en 1 línea

always siempre · ifRoom si cabe · never al ⋮ · withText + título

Etiquetas del menú

<menu> raíz obligatoria · <item> opción / submenú · <group> agrupa

Practica

Quiz tipo Kahoot

20 preguntas en orden aleatorio. 20 segundos cada una: cuanto más rápido aciertes, más puntos.