Menús

Los menús son un componente común de la interfaz de usuario en muchos tipos de aplicaciones. Para proporcionar una experiencia de usuario conocida y uniforme, debes usar las API de Menu a fin de presentar al usuario acciones y otras opciones en las actividades.

A partir de Android 3.0 (nivel de API 11), los dispositivos con Android ya no tienen que proporcionar un botón Menú exclusivo. Con este cambio, las apps de Android dejarán de depender de los paneles de menú tradicionales de 6 elementos y, en su lugar, proporcionarán una barra de app para mostrar las acciones más comunes del usuario.

Aunque haya cambiado el diseño de la experiencia del usuario para algunos elementos de menú, la semántica para definir un conjunto de acciones y opciones sigue basándose en las API de Menu. Esta guía muestra cómo crear tres tipos fundamentales de presentaciones de menús o acciones en todas las versiones de Android.

Menú de opciones y barra de app
El menú de opciones es la colección principal de elementos de menú de una actividad. Es donde debes colocar las acciones que tienen un impacto global en la app, como "Buscar", "Redactar correo electrónico" y "Configuración".

Consulta la sección Cómo crear un menú de opciones.

Menú contextual y modo de acción contextual
Un menú contextual es un menú flotante que aparece cuando el usuario hace un clic largo en un elemento. Proporciona acciones que afectan el contenido seleccionado o el marco contextual.

En el menú de acción contextual, se muestran los elementos de acción que afectan al contenido seleccionado en una barra en la parte superior de la pantalla y se permite al usuario seleccionar varios elementos.

Consulta la sección Cómo crear menús contextuales.

Menú emergente
Un menú emergente muestra una lista de elementos en una lista vertical que está anclada a la vista que invocó el menú. Es adecuado para proporcionar una ampliación de acciones relacionadas con contenido específico o para proporcionar opciones en una segunda parte de un comando. Las acciones en un menú emergente no deben afectar directamente al contenido correspondiente, ya que para eso están las acciones contextuales. En cambio, el menú emergente es para acciones extendidas relacionadas con partes del contenido de la actividad.

Consulta la sección Cómo crear un menú emergente.

Cómo definir un menú en XML

Para todos los tipos de menús, Android proporciona un formato XML estándar que permite definir los elementos de menú. En lugar de incorporar un menú en el código de la actividad, debes definir un menú y todos los elementos en un recurso de menú XML. Luego, puedes agrandar el recurso de menú (cargarlo como un objeto Menu) en la actividad o el fragmento.

El uso del recurso de menú es una práctica recomendada por algunos motivos:

  • Es más fácil visualizar la estructura del menú en XML.
  • Separa el contenido del menú del código de comportamiento de la aplicación.
  • Te permite crear configuraciones alternativas del menú para diferentes versiones de plataforma, tamaños de pantalla y otras configuraciones aprovechando el marco de trabajo de recursos de la app.

Para definir el menú, crea un archivo XML dentro del directorio res/menu/ del proyecto y desarrolla el menú con los siguientes elementos:

<menu>
Define un Menu, que es un contenedor para elementos de menú. Un elemento <menu> debe ser el nodo raíz del archivo y puede tener uno o más elementos <item> y <group>.
<item>
Crea un MenuItem, que representa un único elemento en un menú. Este elemento puede contener un elemento <menu> anidado para crear un submenú.
<group>
Contenedor opcional e invisible para elementos <item>. Te permite categorizar los elementos de menú para que compartan propiedades, como el estado de una actividad o visibilidad. Para obtener más información, consulta la sección Cómo crear grupos del menú.

Aquí presentamos un menú de ejemplo denominado game_menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/new_game"
          android:icon="@drawable/ic_new_game"
          android:title="@string/new_game"
          android:showAsAction="ifRoom"/>
    <item android:id="@+id/help"
          android:icon="@drawable/ic_help"
          android:title="@string/help" />
</menu>

El elemento <item> admite varios atributos que puedes usar para definir la apariencia y el comportamiento de un elemento. Los elementos del menú precedente incluyen estos atributos:

android:id
El ID de un recurso que es exclusivo del elemento y permite que la aplicación lo reconozca cuando el usuario lo selecciona.
android:icon
Referencia a un elemento de diseño para usar como el ícono del elemento.
android:title
Referencia a una string para usar como el título del elemento.
android:showAsAction
Especifica cuándo y cómo el elemento debe aparecer como un elemento de acción en la barra de app.

Estos son los atributos más importantes que debes usar, pero hay muchos más disponibles. Para obtener información sobre todos los atributos admitidos, consulta el documento Recurso de menú.

Puedes agregar un submenú a un elemento en cualquier menú (salvo en un submenú) agregando un elemento <menu> como campo secundario de un <item>. Los submenús son útiles cuando la aplicación tiene muchas funciones que se pueden organizar en temas, como elementos de una barra de menús de una aplicación para escritorio (Archivo, Editar, Ver, etc.). Por ejemplo:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/file"
          android:title="@string/file" >
        <!-- "file" submenu -->
        <menu>
            <item android:id="@+id/create_new"
                  android:title="@string/create_new" />
            <item android:id="@+id/open"
                  android:title="@string/open" />
        </menu>
    </item>
</menu>

Para usar un submenú en la actividad, tienes que agrandar el recurso de menú (convertir el recurso XML en un objeto programable) con MenuInflater.inflate(). En las siguientes secciones, verás cómo agrandar un menú para cada tipo de menú.

Cómo crear un menú de opciones

Figura 1: Menú de opciones del navegador

El menú de opciones es donde debes incluir las acciones y otras opciones que son relevantes para el contexto de la actividad actual, como "Buscar", "Redactar correo electrónico" y "Ajustes".

Que los elementos de las opciones aparezcan en el menú en la pantalla depende de la versión para la que desarrollaste la aplicación:

  • Si desarrollaste tu aplicación para Android 2.3.x (nivel de API 10) o versiones anteriores, el contenido del menú de opciones aparece en la parte inferior de la pantalla cuando el usuario presiona el botón Menú, como se muestra en la figura 1. Cuando se abre, la primera parte visible es el menú de íconos, que tiene hasta seis elementos de menú. Si el menú incluye más de seis elementos, Android coloca el sexto elemento adelante en el menú ampliado, que se puede abrir seleccionando Más.
  • Si desarrollaste tu aplicación para Android 3.0 (nivel de API 11) y versiones posteriores, los elementos del menú de opciones están disponibles en la barra de app. De forma predeterminada, el sistema dispone todos los elementos en acciones adicionales, que el usuario puede ver con el ícono de acciones adicionales a la derecha de la barra de app (o mediante el botón Menú del dispositivo si está disponible). Para permitir el acceso rápido a acciones importantes, puedes hacer que algunos elementos aparezcan en la barra de app agregando android:showAsAction="ifRoom" a los elementos <item> correspondientes (consulta la figura 2).

    Para obtener más información sobre los elementos de acción y otros comportamientos de la barra de app, consulta la clase de capacitación Cómo agregar una barra de app.

Figura 2. La app de Google Play Películas muestra un botón de búsqueda en el botón de ampliación de acciones.

Puedes declarar elementos para el menú de opciones desde las subclases Activity o Fragment. Si tanto la actividad como los fragmentos declaran elementos para el menú de opciones, se los combina en la IU. Los elementos de la actividad aparecen primero, y después aparecen los elementos de cada fragmento, en el orden en que cada fragmento se agregó a la actividad. Si es necesario, puedes reordenar los elementos de menú con el atributo android:orderInCategory en cada <item> que necesites mover.

Para especificar el menú de opciones para una actividad, anula onCreateOptionsMenu() (los fragmentos proporcionan su propia devolución de llamada onCreateOptionsMenu()). En este método, puedes agrandar el recurso de menú (definido en XML) hacia el Menu proporcionado en la devolución de llamada. Por ejemplo:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater: MenuInflater = menuInflater
    inflater.inflate(R.menu.game_menu, menu)
    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.game_menu, menu);
    return true;
}

También puedes agregar elementos de menú con add() y recuperar elementos con findItem() para revisar las propiedades con las API de MenuItem.

Si desarrollaste tu aplicación para Android 2.3.x y versiones anteriores, el sistema llama a onCreateOptionsMenu() para crear el menú de opciones cuando el usuario abre el menú por primera vez. Si la desarrollaste para Android 3.0 y versiones posteriores, el sistema llama a onCreateOptionsMenu() cuando comienza la actividad, para mostrar los elementos de la barra de app.

Cómo manejar eventos de clic

Cuando el usuario selecciona un elemento del menú de opciones (incluidos los elementos de acción de la barra de app), el sistema llama al método onOptionsItemSelected() de la actividad. Este método pasa el MenuItem seleccionado. Puedes identificar el elemento si llamas a getItemId(), que muestra el ID único del elemento de menú (definido por el atributo android:id en el recurso de menú o con un valor entero proporcionado al método add()). Puedes hacer coincidir este ID con elementos de menú conocidos para realizar la acción correspondiente. Por ejemplo:

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // Handle item selection
    return when (item.itemId) {
        R.id.new_game -> {
            newGame()
            true
        }
        R.id.help -> {
            showHelp()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

Cuando controles correctamente un elemento de menú, muestra true. Si no controlas el elemento de menú, debes llamar a la implementación de la superclase de onOptionsItemSelected() (la implementación predeterminada muestra un valor falso).

Si la actividad incluye fragmentos, el sistema llama primero a onOptionsItemSelected() para la actividad y, a continuación, para cada fragmento (en el orden en que se agregaron) hasta que uno muestre true o se llamen todos los fragmentos.

Sugerencia: Android 3.0 incluye la capacidad de definir el comportamiento haciendo clic en un elemento de menú en XML, mediante el atributo android:onClick. El valor del atributo debe ser el nombre de un método definido por la actividad que usa el menú. El método debe ser público y aceptar un único parámetro MenuItem. Cuando el sistema llama a este método, pasa el elemento de menú seleccionado. Para obtener más información y ver un ejemplo, consulta el documento Recurso de menú.

Sugerencia: Si la aplicación contiene varias actividades y algunas de ellas proporcionan el mismo menú de opciones, considera crear una actividad que solo implemente los métodos onCreateOptionsMenu() y onOptionsItemSelected(). Luego, extiende esta clase por cada actividad que deba compartir el mismo menú de opciones. De esta forma, puedes administrar un conjunto de código para controlar acciones de menú, y cada clase descendiente hereda los comportamientos de menús. Si quieres agregar elementos de menú a una de las actividades descendientes, anula onCreateOptionsMenu() en esa actividad. Llama a super.onCreateOptionsMenu(menu) para que se creen los elementos originales del menú y, luego, agrega elementos nuevos con menu.add(). También puedes invalidar el comportamiento de la superclase para elementos del menú individuales.

Cambio de elementos del menú en tiempo de ejecución

Después de que el sistema llama a onCreateOptionsMenu(), retiene una instancia del Menu que tú completas y no llamará a onCreateOptionsMenu() nuevamente, a menos que se inicie el menú por otro motivo. Sin embargo, debes usar onCreateOptionsMenu() solamente para crear el estado inicial del menú, y no para hacer cambios durante el ciclo de vida de la actividad.

Si deseas modificar el menú de opciones a partir de eventos que tengan lugar durante el ciclo de vida de la actividad, puedes hacerlo en el método onPrepareOptionsMenu(). Este método te pasa el objeto Menu como existe en ese momento para que puedas modificarlo, por ejemplo, agregando, quitando o inhabilitando elementos. Los fragmentos también proporcionan una devolución de llamada de onPrepareOptionsMenu().

En Android 2.3.x y versiones anteriores, el sistema llama a onPrepareOptionsMenu() cada vez que el usuario abre el menú de opciones (presiona el botón Menú).

En Android 3.0 y versiones posteriores, se considera que el menú de opciones está siempre abierto cuando los elementos del menú se presentan en la barra de app. Cuando se produce un evento y deseas realizar una actualización del menú, debes llamar a invalidateOptionsMenu() para solicitar que el sistema llame a onPrepareOptionsMenu().

Nota: Nunca debes cambiar elementos del menú de opciones conforme la View actual en foco. Cuando está en el modo táctil (cuando el usuario no usa una bola de seguimiento o un controlador de dirección), las vistas no pueden estar en el foco, por lo que nunca debes usar el foco como base para la modificación de elementos del menú de opciones. Si deseas proporcionar elementos del menú que dependan del contexto de View, usa un menú contextual.

Cómo crear menús contextuales

Figura 3: Capturas de pantalla de un menú contextual flotante (izquierda) y la barra de acciones contextuales (derecha)

Un menú contextual ofrece acciones que afectan a un elemento o un marco contextual específicos en la IU. Puedes proporcionar un menú contextual para cualquier vista, aunque se usan con mayor frecuencia para elementos en ListView, GridView u otras colecciones de vistas en las cuales el usuario puede realizar acciones directamente en cada elemento.

Existen dos maneras de proporcionar acciones contextuales:

  • En un menú contextual flotante. Un menú aparece como una lista flotante de elementos del menú (similar a un cuadro de diálogo) cuando el usuario realiza un clic largo (presiona y mantiene presionado) en una vista que declara admitir un menú contextual. Los usuarios pueden realizar una acción contextual en un elemento por vez.
  • En el modo de acción contextual. Este modo es una implementación del sistema del ActionMode que muestra una barra de acciones contextuales en la parte superior de la pantalla con elementos de acción que afectan a los elementos seleccionados. Cuando este modo está activo, los usuarios pueden realizar una acción en varios elementos a la vez (si la app lo permite).

Nota: El modo de acción contextual está disponible en Android 3.0 (nivel de API 11) y versiones posteriores, y es la técnica preferida para mostrar acciones contextuales cuando están disponibles. Si tu app admite versiones anteriores a la versión 3.0, debes volver al menú contextual flotante en esos dispositivos.

Cómo crear un menú contextual flotante

Para crear un menú contextual flotante:

  1. Registra la View con la que se debe asociar el menú contextual llamando a registerForContextMenu() y pásalo a la View.

    Si la actividad usa una ListView o GridView, y deseas que cada elemento proporcione el mismo menú contextual, registra todos los elementos de un menú contextual pasando ListView o GridView a registerForContextMenu().

  2. Implementa el método onCreateContextMenu() en tu Activity o Fragment.

    Cuando la vista registrada recibe un evento de clic largo, el sistema llama al método onCreateContextMenu(). Aquí es donde defines los elementos del menú, por lo general, agrandando un recurso de menú. Por ejemplo:

    Kotlin

    override fun onCreateContextMenu(menu: ContextMenu, v: View,
                            menuInfo: ContextMenu.ContextMenuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo)
        val inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.context_menu, menu)
    }
    

    Java

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
    }
    

    MenuInflater te permite agrandar el menú contextual desde un recurso de menú. Los parámetros del método de devolución de llamada incluyen la View que el usuario seleccionó y el objeto ContextMenu.ContextMenuInfo que proporciona información adicional sobre el elemento seleccionado. Si la actividad tiene varias vistas, cada una de las cuales proporciona un menú contextual diferente, podrías usar estos parámetros para determinar qué menú contextual debes agrandar.

  3. Implementa onContextItemSelected().

    Cuando un usuario selecciona un elemento de menú, el sistema llama a este método para que puedas realizar la acción apropiada. Por ejemplo:

    Kotlin

    override fun onContextItemSelected(item: MenuItem): Boolean {
        val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
        return when (item.itemId) {
            R.id.edit -> {
                editNote(info.id)
                true
            }
            R.id.delete -> {
                deleteNote(info.id)
                true
            }
            else -> super.onContextItemSelected(item)
        }
    }
    

    Java

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        switch (item.getItemId()) {
            case R.id.edit:
                editNote(info.id);
                return true;
            case R.id.delete:
                deleteNote(info.id);
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }
    

    El método getItemId() consulta el ID del elemento de menú seleccionado, que debes asignar a cada elemento de menú en XML con el atributo android:id, como se muestra en la sección Cómo definir un menú en XML.

    Cuando controles correctamente un elemento de menú, muestra true. Si no manejas el elemento de menú, debes pasarlo a la implementación de la superclase. Si la actividad incluye fragmentos, recibe la devolución de llamada primero. Al llamar a una superclase cuando no se maneja el elemento, el sistema pasa el evento al método de devolución de llamada correspondiente en cada fragmento, uno por vez (en el orden en el que se agregó cada fragmento) hasta que se muestra true o false. (La implementación predeterminada para Activity y android.app.Fragment muestra false, de modo que siempre debes llamar a la superclase cuando no se maneja un elemento).

Cómo usar el modo de acción contextual

El modo de acción contextual es una implementación del sistema de ActionMode que centra la interacción del usuario en la realización de acciones contextuales. Cuando un usuario habilita este modo seleccionando un elemento, aparece una barra de acciones contextuales en la parte superior de la pantalla para presentar las acciones que el usuario puede realizar en los elementos seleccionados en ese momento. Cuando este modo está habilitado, el usuario puede seleccionar varios elementos (si lo permites), anular la selección de elementos y seguir navegando dentro de la actividad (tanto como lo permitas). El modo de acción está inhabilitado y la barra de acciones contextuales desaparece cuando el usuario anula la selección de elementos, presiona el botón ATRÁS o selecciona la acción Listo en el lado izquierdo de la barra.

Nota: La barra de acciones contextuales no está necesariamente asociada con la barra de app. Estas barras funcionan de manera independiente, aunque la barra de acciones contextuales ocupa visualmente la posición de la barra de app.

En las vistas que proporcionan acciones contextuales, usualmente, debes invocar el modo de acción contextual cuando se produce uno de estos dos eventos (o los dos):

  • El usuario realiza un clic largo en la vista.
  • El usuario selecciona una casilla de verificación o un componente similar de la IU en la vista.

La manera en que la aplicación invoca el modo de acción contextual y define el comportamiento de cada acción depende de su diseño. Básicamente, hay dos diseños:

  • Para acciones contextuales en vistas individuales arbitrarias.
  • Para acciones contextuales por lotes de grupos de elementos en una ListView o una GridView (que permiten al usuario seleccionar varios elementos y realizar una acción en todos ellos).

Las siguientes secciones describen la configuración necesaria para cada escenario.

Cómo habilitar el modo de acción contextual para vistas individuales

Si deseas invocar el modo de acción contextual solo cuando el usuario selecciona vistas específicas, debes hacer lo siguiente:

  1. Implementa la interfaz ActionMode.Callback. En los métodos de devolución de llamada, puedes especificar las acciones de la barra de acciones contextuales, responder a eventos de clic en elementos de acción y manejar otros eventos del ciclo de vida para el modo de acción.
  2. Llama a startActionMode() cuando desees mostrar la barra (como cuando el usuario hace clics largos en la vista).

Por ejemplo:

  1. Implementa la interfaz ActionMode.Callback:

    Kotlin

    private val actionModeCallback = object : ActionMode.Callback {
        // Called when the action mode is created; startActionMode() was called
        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Inflate a menu resource providing context menu items
            val inflater: MenuInflater = mode.menuInflater
            inflater.inflate(R.menu.context_menu, menu)
            return true
        }
    
        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
            return false // Return false if nothing is done
        }
    
        // Called when the user selects a contextual menu item
        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
            return when (item.itemId) {
                R.id.menu_share -> {
                    shareCurrentItem()
                    mode.finish() // Action picked, so close the CAB
                    true
                }
                else -> false
            }
        }
    
        // Called when the user exits the action mode
        override fun onDestroyActionMode(mode: ActionMode) {
            actionMode = null
        }
    }
    

    Java

    private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
    
        // Called when the action mode is created; startActionMode() was called
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate a menu resource providing context menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
            return true;
        }
    
        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false; // Return false if nothing is done
        }
    
        // Called when the user selects a contextual menu item
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.menu_share:
                    shareCurrentItem();
                    mode.finish(); // Action picked, so close the CAB
                    return true;
                default:
                    return false;
            }
        }
    
        // Called when the user exits the action mode
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            actionMode = null;
        }
    };
    

    Ten en cuenta que estas devoluciones de llamada de evento son casi exactamente las mismas que las del menú de opciones, excepto que estas también pasan el objeto ActionMode asociado al evento. Puedes usar las API de ActionMode para realizar varios cambios a la CAB, como revisar el título y el subtítulo con setTitle() y setSubtitle() (útil para indicar cuántos elementos se seleccionaron).

    Además, ten en cuenta que en el ejemplo anterior se fija la variable mActionMode en null cuando se destruye el modo de acción. En el siguiente paso, verás cómo se inicializa y cómo guardar la variable de miembro en la actividad o el fragmento puede ser útil.

  2. Llama a startActionMode() para habilitar el modo de acción contextual cuando corresponda; por ejemplo, en respuesta a un clic largo en una View:

    Kotlin

    someView.setOnLongClickListener { view ->
        // Called when the user long-clicks on someView
        when (actionMode) {
            null -> {
                // Start the CAB using the ActionMode.Callback defined above
                actionMode = activity?.startActionMode(actionModeCallback)
                view.isSelected = true
                true
            }
            else -> false
        }
    }
    

    Java

    someView.setOnLongClickListener(new View.OnLongClickListener() {
        // Called when the user long-clicks on someView
        public boolean onLongClick(View view) {
            if (actionMode != null) {
                return false;
            }
    
            // Start the CAB using the ActionMode.Callback defined above
            actionMode = getActivity().startActionMode(actionModeCallback);
            view.setSelected(true);
            return true;
        }
    });
    

    Cuando llamas a startActionMode(), el sistema ejecuta el ActionMode creado. Guardando esto en una variable de miembro, puedes realizar cambios en la barra de acciones contextuales ante otros eventos. En el ejemplo precedente, el ActionMode se usa para garantizar que la instancia del ActionMode no sea recreada si ya está activa, controlando si el miembro es null antes de iniciar el modo de acción.

Cómo habilitar acciones contextuales por lotes en una ListView o GridView

Si tienes una colección de elementos en una ListView o GridView (u otra extensión de AbsListView) y deseas permitir que los usuarios realicen acciones por lotes, debes hacer lo siguiente:

Por ejemplo:

Kotlin

val listView: ListView = getListView()
with(listView) {
    choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
    setMultiChoiceModeListener(object : AbsListView.MultiChoiceModeListener {
        override fun onItemCheckedStateChanged(mode: ActionMode, position: Int,
                                               id: Long, checked: Boolean) {
            // Here you can do something when items are selected/de-selected,
            // such as update the title in the CAB
        }

        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
            // Respond to clicks on the actions in the CAB
            return when (item.itemId) {
                R.id.menu_delete -> {
                    deleteSelectedItems()
                    mode.finish() // Action picked, so close the CAB
                    true
                }
                else -> false
            }
        }

        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Inflate the menu for the CAB
            val menuInflater: MenuInflater = mode.menuInflater
            menuInflater.inflate(R.menu.context, menu)
            return true
        }

        override fun onDestroyActionMode(mode: ActionMode) {
            // Here you can make any necessary updates to the activity when
            // the CAB is removed. By default, selected items are deselected/unchecked.
        }

        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Here you can perform updates to the CAB due to
            // an <code><a href="/reference/android/view/ActionMode.html#invalidate()">invalidate()</a></code> request
            return false
        }
    })
}

Java

ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position,
                                          long id, boolean checked) {
        // Here you can do something when items are selected/de-selected,
        // such as update the title in the CAB
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        // Respond to clicks on the actions in the CAB
        switch (item.getItemId()) {
            case R.id.menu_delete:
                deleteSelectedItems();
                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        // Inflate the menu for the CAB
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context, menu);
        return true;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        // Here you can make any necessary updates to the activity when
        // the CAB is removed. By default, selected items are deselected/unchecked.
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        // Here you can perform updates to the CAB due to
        // an <code><a href="/reference/android/view/ActionMode.html#invalidate()">invalidate()</a></code> request
        return false;
    }
});

Eso es todo. Ahora, cuando un usuario selecciona un elemento con un clic largo, el sistema llama al método onCreateActionMode() y muestra la barra de acciones contextuales con las acciones especificadas. Mientras la barra de acciones contextuales está visible, los usuarios pueden seleccionar elementos adicionales.

En los casos en los que las acciones contextuales proporcionan elementos de acción comunes, puedes agregar una casilla de verificación o un elemento de IU similar que permita a los usuarios seleccionar elementos, ya que es posible que no descubran el comportamiento del clic largo. Cuando un usuario activa la casilla de verificación, puedes invocar el modo de acción contextual estableciendo el elemento de lista correspondiente al estado activado con setItemChecked().

Cómo crear un menú emergente

Figura 4: Menú contextual en la app de Gmail anclado al botón de acciones adicionales en la parte superior derecha

Un PopupMenu es un menú modal anclado a una View. Aparece debajo de la vista anclada si hay espacio o, de lo contrario, sobre esta. Es útil para lo siguiente:

  • Proporcionar un menú de estilo de ampliación para las acciones que se relacionan con contenido específico (como los encabezados de correo electrónico de Gmail, que se muestran en la figura 4).

    Nota: Esto no es lo mismo que un menú contextual, que generalmente se usa para acciones que afectan el contenido seleccionado. Para las acciones que afectan el contenido seleccionado, usa el modo de acción contextual o un menú contextual.

  • Proporcionar la segunda parte de una oración de comandos (como un botón marcado "Agregar" que produce un menú emergente con diferentes opciones "Agregar").
  • Proporcionar un menú desplegable similar a Spinner que no retiene una selección persistente.

Nota: PopupMenu está disponible con el nivel de API 11 y niveles superiores.

Si defines el menú en XML, puedes hacer esto para mostrar el menú emergente:

  1. Crea una instancia de PopupMenu con su constructor, que toma el Context de la aplicación actual y la View a la que debe anclarse el menú.
  2. Usa MenuInflater para inflar el recurso de menú hacia el objeto Menu devuelto por PopupMenu.getMenu().
  3. Llama a PopupMenu.show().

Por ejemplo, aquí se presenta un botón con el atributo android:onClick que muestra un menú emergente:

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_overflow_holo_dark"
    android:contentDescription="@string/descr_overflow_button"
    android:onClick="showPopup" />

La actividad puede mostrar el menú emergente de la siguiente manera:

Kotlin

fun showPopup(v: View) {
    val popup = PopupMenu(this, v)
    val inflater: MenuInflater = popup.menuInflater
    inflater.inflate(R.menu.actions, popup.menu)
    popup.show()
}

Java

public void showPopup(View v) {
    PopupMenu popup = new PopupMenu(this, v);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.actions, popup.getMenu());
    popup.show();
}

En el nivel de API 14 y en niveles superiores, puedes combinar las dos líneas que agrandan el menú con PopupMenu.inflate().

El menú se cierra cuando el usuario selecciona un elemento o toca fuera del área del menú. Puedes recibir el evento de cierre con PopupMenu.OnDismissListener.

Cómo manejar eventos de clic

Para realizar una acción cuando el usuario selecciona un elemento de menú, debes implementar la interfaz de PopupMenu.OnMenuItemClickListener y registrarla en el PopupMenu llamando a setOnMenuItemclickListener(). Cuando el usuario selecciona un elemento, el sistema llama a la devolución de llamada onMenuItemClick() en la interfaz.

Por ejemplo:

Kotlin

fun showMenu(v: View) {
    PopupMenu(this, v).apply {
        // MainActivity implements OnMenuItemClickListener
        setOnMenuItemClickListener(this@MainActivity)
        inflate(R.menu.actions)
        show()
    }
}

override fun onMenuItemClick(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.archive -> {
            archive(item)
            true
        }
        R.id.delete -> {
            delete(item)
            true
        }
        else -> false
    }
}

Java

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);

    // This activity implements OnMenuItemClickListener
    popup.setOnMenuItemClickListener(this);
    popup.inflate(R.menu.actions);
    popup.show();
}

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.archive:
            archive(item);
            return true;
        case R.id.delete:
            delete(item);
            return true;
        default:
            return false;
    }
}

Cómo crear grupos de menú

Un grupo de menú es una colección de elementos de menú que comparten ciertas características. Con un grupo, puedes hacer lo siguiente:

Puedes crear un grupo anidando elementos <item> en un elemento <group> en el recurso de menú o especificando un ID de grupo con el método add().

Aquí presentamos un recurso de menú que incluye un grupo:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/menu_save"
          android:title="@string/menu_save" />
    <!-- menu group -->
    <group android:id="@+id/group_delete">
        <item android:id="@+id/menu_archive"
              android:title="@string/menu_archive" />
        <item android:id="@+id/menu_delete"
              android:title="@string/menu_delete" />
    </group>
</menu>

Los elementos que están en el grupo aparecen en el mismo nivel que el primer elemento, los tres elementos en el menú son elementos hermanos. Sin embargo, puedes modificar los rasgos de los dos elementos en el grupo haciendo referencia al ID del grupo y usando los métodos antes indicados. El sistema nunca separará los elementos agrupados. Por ejemplo, si declaras android:showAsAction="ifRoom" para cada elemento, ambos aparecerán en la barra de acciones o en las acciones adicionales.

Cómo usar elementos de menú que se pueden activar

Figura 5: Captura de pantalla de un submenú con elementos que pueden marcarse

Un menú puede ser una interfaz útil para activar y desactivar opciones, usar una casilla de verificación para opciones independientes o botones de selección para grupos de opciones mutuamente exclusivas. En la figura 5, se muestra un submenú con elementos que se pueden activar mediante botones de selección.

Nota: Los elementos del menú en el menú de íconos (del menú de opciones) no pueden mostrar una casilla de verificación ni un botón de selección. Si decides que los elementos en el menú de íconos se pueden activar, debes indicar el estado activado manualmente cambiando el ícono o el texto cada vez que el estado cambie.

Puedes definir el comportamiento de activación para elementos individuales del menú con el atributo android:checkable del elemento <item> o para todo un grupo con el atributo android:checkableBehavior del elemento <group>. Por ejemplo, todos los elementos de este grupo del menú se pueden activar con un botón de selección:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item android:id="@+id/red"
              android:title="@string/red" />
        <item android:id="@+id/blue"
              android:title="@string/blue" />
    </group>
</menu>

El atributo android:checkableBehavior acepta lo siguiente:

single
Solo un elemento del grupo se puede activar (botones de selección).
all
Todos los elementos se pueden activar (casillas de verificación).
none
No se puede activar ningún elemento

Puedes aplicar un estado activado predeterminado a un elemento con el atributo android:checked del elemento <item> y cambiarlo en el código con el método setChecked().

Cuando se selecciona un elemento que se puede activar, el sistema llama al método de devolución de llamada correspondiente del elemento seleccionado (como onOptionsItemSelected()). Aquí es donde debes establecer el estado de la casilla de verificación porque las casillas de verificación y los botones de selección no cambian de estado automáticamente. Puedes consultar el estado actual de un elemento (como estaba antes de que el usuario lo seleccionara) con isChecked() y, a continuación, establecer el estado activado con setChecked(). Por ejemplo:

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.vibrate, R.id.dont_vibrate -> {
            item.isChecked = !item.isChecked
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.vibrate:
        case R.id.dont_vibrate:
            if (item.isChecked()) item.setChecked(false);
            else item.setChecked(true);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

Si no estableces el estado activado de esta manera, el estado visible del elemento (la casilla de verificación o el botón de selección) no cambiará cuando el usuario lo seleccione. Si estableces el estado, la actividad preserva el estado activado del elemento de manera que, cuando el usuario abra el menú más tarde, el estado activado que estableció sea visible.

Nota: Los elementos del menú que se pueden activar están previstos para usarse solo por sesión y no se guardan después de la destrucción de la aplicación. Si tienes una configuración de la aplicación que desearías guardar para el usuario, debes almacenar los datos con preferencias compartidas.

Cómo agregar elementos de menú basados en una intent

A veces, desearás que un elemento de menú inicie una actividad con una Intent (ya sea una actividad en tu aplicación o en otra). Cuando conozcas la intent que deseas usar y tengas un elemento de menú específico que deba iniciarla, podrás ejecutarla con startActivity() durante el método de devolución de llamada correspondiente del elemento seleccionado (como onOptionsItemSelected()).

Sin embargo, si no estás seguro de que el dispositivo del usuario contenga una aplicación que maneje la intent, la adición de un elemento de menú que la invoque puede dar como resultado un elemento de menú que no funcione, ya que la intent puede no resolverse en una actividad. Para solucionar esto, Android te permite agregar dinámicamente elementos de menú al menú cuando encuentra actividades en el dispositivo que manejan la intent.

Para agregar elementos de menú en función de actividades disponibles que aceptan una intent:

  1. Define una intent con la categoría CATEGORY_ALTERNATIVE o CATEGORY_SELECTED_ALTERNATIVE, además de otros requisitos.
  2. Llama a Menu.addIntentOptions(). Android busca aplicaciones que puedan realizar la intent y las agrega al menú.

Si no hay aplicaciones instaladas que satisfagan la intent, no se agrega ningún elemento de menú.

Nota: CATEGORY_SELECTED_ALTERNATIVE se usa para manejar el elemento seleccionado actualmente en la pantalla. Por lo tanto, solo debe usarse cuando se cree un menú en onCreateContextMenu().

Por ejemplo:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    val intent = Intent(null, dataUri).apply {
        addCategory(Intent.CATEGORY_ALTERNATIVE)
    }

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items will be added
            0,                  // Unique item ID (none)
            0,                  // Order for the items (none)
            this.componentName, // The current activity name
            null,               // Specific items to place first (none)
            intent,             // Intent created above that describes our requirements
            0,                  // Additional flags to control items (none)
            null)               // Array of MenuItems that correlate to specific items (none)

    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu){
    super.onCreateOptionsMenu(menu);

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    Intent intent = new Intent(null, dataUri);
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
         R.id.intent_group,  // Menu group to which new items will be added
         0,      // Unique item ID (none)
         0,      // Order for the items (none)
         this.getComponentName(),   // The current activity name
         null,   // Specific items to place first (none)
         intent, // Intent created above that describes our requirements
         0,      // Additional flags to control items (none)
         null);  // Array of MenuItems that correlate to specific items (none)

    return true;
}

Por cada actividad encontrada que proporciona un filtro de intents que coincida con la intent definida, se agrega un elemento de menú, y se usa el valor de android:label del filtro de intents como título del elemento de menú y el ícono de la aplicación como ícono del elemento en cuestión. El método addIntentOptions() muestra la cantidad de elementos de menú agregados.

Nota: Cuando llamas al objeto addIntentOptions(), este anula todos los elementos del menú del grupo del menú especificado en el primer argumento.

Cómo permitir que la actividad se agregue a otros menús

También puedes ofrecer los servicios de tu actividad a otras aplicaciones, por lo que tu aplicación se puede incluir en el menú de otros (se revierten los roles previamente descritos).

Para que se incluya en los menús de las aplicaciones, debes definir un filtro de intents de la manera usual, pero asegúrate de incluir los valores CATEGORY_ALTERNATIVE o CATEGORY_SELECTED_ALTERNATIVE para la categoría del filtro de intents. Por ejemplo:

<intent-filter label="@string/resize_image">
    ...
    <category android:name="android.intent.category.ALTERNATIVE" />
    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    ...
</intent-filter>

Obtén más información sobre cómo crear filtros de intents en el documento Intents y filtros de intents.