Menus

Menus são componentes comuns da interface do usuário em diversos tipos de aplicativos. Para fornecer uma experiência familiar e consistente ao usuário, você precisa usar APIs de Menu para apresentar ações de usuário e outras opções nas suas atividades.

Desde o Android 3.0 (API de nível 11), dispositivos Android não precisam mais fornecer um botão de Menu dedicado. Com essa alteração, os aplicativos Android migrarão de uma dependência do painel de menu de seis itens tradicional para fornecer uma barra de aplicativos para apresentar as ações comuns de usuário.

Apesar de o design e a experiência do usuário para alguns dos itens do menu terem passado por mudanças, a semântica para definir um conjunto de ações e opções ainda se baseia em APIs de Menu. Este guia mostra como criar os três tipos fundamentais de menus ou apresentações de ação em todas as versões do Android:

Menu de opções e barra de aplicativos
O menu de opções é a coleção principal de itens de menu para uma atividade. É onde você precisa colocar as ações que têm impacto global no aplicativo, como “Buscar”, “Escrever e-mail” e “Configurações”.

Consulte a seção Criação de um menu de opções.

Modo de ação contextual e menu de contexto
Um menu de contexto é um menu flutuante que aparece quando o usuário realiza um clique longo em um elemento. Ele fornece ações que afetam o conteúdo selecionado ou o frame do contexto.

O modo de ação contextual exibe os itens de ação que afetam o conteúdo selecionado em uma barra no topo da tela e permite que o usuário selecione vários itens.

Consulte a seção Criação de menus contextuais.

Menu pop-up
Um menu pop-up exibe itens em uma lista vertical ancorada à visualização que apresentou o menu. É bom para fornecer ações adicionais relacionadas a conteúdo específico ou opções de fornecimento de uma segunda parte de um comando. As ações em um menu pop-up não afetam diretamente o conteúdo correspondente — é para isso que servem as ações contextuais. Preferivelmente, o menu pop-up serve para ações estendidas que relacionam as regiões de conteúdo na atividade.

Consulte a seção Criação de um menu pop-up.

Definição de um menu em XML

Para todos os tipos de menu, o Android fornece um formato XML padrão para definir os itens de menu. Em vez de criar um menu no código da atividade, você precisa definir um menu e todos os seus itens em um recurso de menu XML. É possível, assim, inflar o recurso do menu (carregá-lo como um objeto Menu) na atividade ou no fragmento.

Usar um recurso de menu é uma boa prática por alguns motivos:

  • É mais fácil para visualizar a estrutura do menu em XML.
  • Ele separa o conteúdo do menu do código comportamental do aplicativo.
  • Ele permite criar configurações alternativas de menu para versões diferentes de plataforma, de tamanhos de tela e de outras configurações aproveitando a biblioteca de recursos do aplicativo.

Para definir o menu, crie um arquivo XML dentro do diretório res/menu/ do projeto e crie o menu com os seguintes elementos:

<menu>
Define um Menu, que é um contêiner para os itens de menu. Um elemento <menu> precisará ser o nó raiz para o arquivo e pode reter um ou mais elementos <item> e <group>.
<item>
Cria um MenuItem, que representa um único item em um menu. Esse elemento pode conter um elemento <menu> aninhado para criar um submenu.
<group>
Um contêiner invisível e opcional para os elementos <item>. Ele permite que você categorize itens de menu para que eles compartilhem propriedades como estado ativo e visibilidade. Para mais informações, consulte a seção Criação de grupos de menu.

Veja um exemplo de menu chamado 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>

O elemento <item> é compatível com vários atributos que você pode usar para definir a aparência ou o comportamento de um item. Os itens no menu acima incluem os seguintes atributos:

android:id
É um código de recurso exclusivo do item. Ele permite que o aplicativo reconheça o item quando o usuário o seleciona.
android:icon
Uma referência a um drawable para usar como o ícone do item.
android:title
É uma referência a uma string para usar como o título do item.
android:showAsAction
Especifica quando e como esse item aparecerá como um item de ação na barra de aplicativos.

Esses são os atributos mais importantes que devem ser usados, mas há vários outros disponíveis. Para informações sobre todos os atributos compatíveis, consulte o documento Recurso de menu.

É possível adicionar um submenu a um item em qualquer menu adicionando um elemento <menu> como filho de um <item>. Os submenus são úteis quando o aplicativo tem várias funções que podem ser organizadas em tópicos, como itens em uma barra de menu do aplicativo do PC (arquivo, editar, visualizar etc.). Por exemplo:

<?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 o menu em sua atividade, você precisa inflar o recurso do menu (converter o recurso XML em um objeto programável) usando MenuInflater.inflate(). Nas seções a seguir, você verá como inflar um menu para cada tipo de menu.

Criação de um menu de opções

Figura 1. Menu de opções no navegador.

O menu de opções é onde você incluirá ações e outras opções que são relevantes para o contexto de atividade atual, como “Buscar”, “Escrever e-mail” e “Configurações”.

O local onde os itens no menu de opções aparecem na tela depende da versão em que o aplicativo foi desenvolvido:

  • Caso tenha desenvolvido o aplicativo para Android 2.3.x (API de nível 10) ou inferior, os conteúdos do menu de opções aparecerão na parte inferior da tela, quando o usuário pressionar o botão Menu, como mostrado na figura 1. Quando aberto, a primeira parte visível é o menu de ícones, que tem até seis itens de menu. Se o menu incluir mais de seis itens, o Android colocará o sexto item e o restante em um menu flutuante que o usuário poderá abrir selecionando Mais.
  • Se você desenvolveu o aplicativo para Android 3.0 (API de nível 11) ou superior, os itens do menu de opções estão disponíveis na barra de aplicativos. Por padrão, o sistema posiciona todos os itens nas ações adicionais, que o usuário pode revelar com o ícone de ações adicionais no lado direito da barra de aplicativos (ou pressionando o botão Menu no dispositivo, se disponível). Para ativar o acesso rápido a ações importantes, é possível promover alguns itens para aparecerem na barra de aplicativos adicionando android:showAsAction="ifRoom" aos elementos <item> correspondentes (veja a figura 2).

    Para mais informações sobre os itens de ação e outros comportamentos da barra de aplicativos, consulte a aula Adição da barra do aplicativo.

Figura 2. O aplicativo Google Play mostrando um botão de pesquisa e um botão de ações adicionais.

É possível declarar itens para o menu de opções da subclasse Activity ou de uma subclasse Fragment. Se a atividade e os fragmentos declararem itens para o menu de opções, eles estarão combinados na IU. Os itens da atividade aparecem primeiro, seguidos de cada um desses fragmentos na ordem em que são adicionados à atividade. Se necessário, será possível reorganizar os itens do menu com o atributo android:orderInCategory em cada <item> que você precisar mover.

Para especificar o menu de opções para uma atividade, modifique onCreateOptionsMenu() (os fragmentos fornecem o próprio callback de onCreateOptionsMenu()). Neste método, é possível inflar o recurso de menu (definido no XML) em um Menu fornecido no retorno de chamada. Por exemplo:

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;
}

Também é possível adicionar itens de menu usando {add() e recuperar os itens com findItem() para revisar as propriedades com APIs de MenuItem.

Caso tenha desenvolvido o aplicativo para Android versão 2.3.x ou anterior, o sistema chamará onCreateOptionsMenu() para criar o menu de opções quando o usuário abrir o menu pela primeira vez. Caso tenha desenvolvido para Android versão 3.0 ou posterior, o sistema chamará onCreateOptionsMenu() ao iniciar a atividade para mostrar os itens para a barra de aplicativos.

Processamento de eventos de clique

Quando o usuário seleciona um item para o menu de opções (inclusive os itens de ação na barra de aplicativos), o sistema chama o método onOptionsItemSelected() da atividade. Esse método passa o MenuItem selecionado. É possível identificar o item chamando getItemId(), que retorna o código exclusivo para o item de menu (definido pelo atributo android:id no recurso de menu ou em um número inteiro fornecido ao método add()). É possível combinar esse código com itens de menu conhecidos para realizar a ação adequada. Por exemplo:

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

Quando processar um item de menu corretamente, retorne true. Se não processar o item de menu, você precisará chamar a implementação de superclasse de onOptionsItemSelected() (a implementação padrão retornará falso).

Se a atividade contiver fragmentos, o sistema chamará primeiro onOptionsItemSelected() para a atividade e, em seguida, para cada fragmento (na ordem em que cada fragmento foi adicionado) até que um retorne true ou até chamar todos os fragmentos.

Dica: O Android 3.0 adiciona a possibilidade de definir o comportamento do clique para um item de menu em XML usando o atributo android:onClick. O valor do atributo precisa ser o nome de um método definido pela atividade usando o menu. O método precisa ser público e aceitar um único parâmetro MenuItem — quando o sistema chamar esse método, ele passará o item de menu selecionado. Para mais informações e um exemplo, consulte o documento Recurso de menu.

Dica: Se o aplicativo contiver várias atividades e algumas delas fornecerem o mesmo menu de opções, considere criar uma atividade que não implemente nada exceto os métodos onCreateOptionsMenu() e onOptionsItemSelected(). Em seguida, estenda essa classe para cada atividade que precisa compartilhar o mesmo menu de opções. Dessa maneira, é possível gerenciar um conjunto de códigos para lidar com ações de menu, e cada classe descendente herda os comportamentos do menu. Se quiser adicionar itens de menu a uma das atividades descendentes, modifique onCreateOptionsMenu() nessa atividade. Chame super.onCreateOptionsMenu(menu) para a criação dos itens de menu originais e, em seguida, adicione novos itens de menu com menu.add(). Você também pode modificar o comportamento da superclasse para itens de menu individuais.

Alteração dos itens de menu em ambiente de execução

Depois que o sistema chamar onCreateOptionsMenu(), ele reterá uma instância do Menu que você preencherá e não chamará onCreateOptionsMenu() novamente, a não ser que o menu seja invalidado por algum motivo. No entanto, você precisa usar onCreateOptionsMenu() somente para criar o estado inicial do menu e não para realizar alterações durante o ciclo de vida da atividade.

Caso queira modificar o menu de opções com base em eventos que ocorrem durante o ciclo de vida da atividade, é possível fazê-lo no método onPrepareOptionsMenu(). Esse método passa a você o objeto Menu como ele existe atualmente para que seja possível modificá-lo, como com adição, remoção ou desativação de itens. Os fragmentos também fornecem um callback de onPrepareOptionsMenu().

No Android versão 2.3.x ou posterior, o sistema chamará onPrepareOptionsMenu() sempre que o usuário abrir o menu de opções (pressionar o botão Menu).

No Android versão 3.0 ou posterior, o menu de opções é considerado sempre aberto quando os itens de menu são apresentados na barra de aplicativos. Quando um evento ocorre e você quer realizar uma atualização de menu, chame invalidateOptionsMenu() para pedir que o sistema chame onPrepareOptionsMenu().

Observação: nunca é recomendável alterar os itens no menu de opções com base na View atualmente em foco. Quando estiver no modo de toque (quando o usuário não está usando trackball ou um botão direcional), as visualizações não poderão ter foco. Portanto, o uso do foco como base para modificar os itens no menu de opções não é recomendado. Se quiser fornecer itens de menu sensíveis ao contexto para uma View, use um menu de contexto.

Criação de menus contextuais

Figura 3. Capturas de tela de um menu de contexto flutuante (esquerda) e a barra de ação contextual (direita).

Um menu contextual oferece ações que afetam um item ou frame de contexto específico na IU. É possível fornecer um menu de contexto para qualquer visualização, mas ele é geralmente usado para itens em uma ListView, GridView ou em outras coleções de visualizações em que o usuário pode realizar ações diretas em cada item.

Há duas formas de fornecer ações contextuais:

  • Em um menu de contexto flutuante. Um menu aparece como uma lista flutuante de itens de menu (semelhante a uma caixa de diálogo) quando o usuário realiza um clique longo (toca e mantém pressionado) em uma visualização que declara suporte para um menu de contexto. Os usuários podem realizar uma ação contextual em um item por vez.
  • No modo de ação contextual. Esse modo é uma implementação de sistema de ActionMode que exibe uma barra de ação contextual no topo da tela com itens de ação que afetam os itens selecionados. Quando esse modo está ativo, os usuários podem realizar uma ação em vários itens por vez (se o aplicativo permitir).

Observação: o modo de ação contextual está disponível no Android 3.0 (API de nível 11) e em posteriores e é a técnica preferencial para exibir ações contextuais quando disponível. Se o aplicativo for compatível com versões mais antigas que a 3.0, retorne a um menu de contexto flutuante nesses dispositivos.

Criação de um menu de contexto flutuante

Para fornecer um menu de contexto flutuante:

  1. Registre a View a que o menu de contexto precisa estar associado chamando registerForContextMenu() e passe-o para View.

    Se a atividade usar ListView ou GridView e você quiser que cada item forneça o mesmo menu de contexto, registre todos os itens para um menu de contexto passando ListView ou GridView para registerForContextMenu().

  2. Implemente o método onCreateContextMenu() em sua Activity ou Fragment.

    Quando a visualização registrada receber um evento de clique longo, o sistema chamará o método .onCreateContextMenu() É aqui que você define os itens de menu, geralmente inflando um recurso de menu. Por exemplo:

    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 permite que você infle o menu de contexto de um recurso de menu. Os parâmetros do método de retorno de chamada incluem a View que o usuário selecionou e um objeto ContextMenu.ContextMenuInfo que fornece informações adicionais sobre o item selecionado. Se sua atividade tiver várias visualizações, em que cada uma forneça um menu de contexto diferente, você precisará usar esses parâmetros para determinar qual menu de contexto será inflado.

  3. Implemente onContextItemSelected().

    Quando o usuário selecionar um item de menu, o sistema chamará esse método para que você possa realizar a ação adequada. Por exemplo:

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

    O método getItemId() consulta o código para o item de menu selecionado, que pode ser atribuído a cada item de menu no XML usando o atributo android:id, como exibido na seção Definição de um menu em XML.

    Quando processar um item de menu corretamente, retorne true. Se não lidar com o item de menu, você precisará passá-lo para a implementação de superclasse. Se a atividade incluir fragmentos, ela receberá esse callback primeiro. Ao chamar a superclasse quando não processada, o sistema passará o evento para o respectivo método de callback em cada fragmento, um por vez (na ordem em que cada fragmento foi adicionado), até o retorno de true ou false. A implementação padrão para Activity e android.app.Fragment retorna false. Portanto, você precisa sempre chamar a superclasse quando não processada.

Uso do modo de ação contextual

O modo de ação contextual é uma implementação de sistema de ActionMode que direciona a interação do usuário a efetuar ações contextuais. Quando um usuário ativa esse modo selecionando um item, uma barra de ação contextual aparece na parte superior da tela para apresentar as ações que o usuário pode realizar nos itens selecionados. Enquanto esse modo estiver ativo, o usuário poderá selecionar vários itens (se você permitir), desmarcar itens e continuar a navegar dentro da atividade (de acordo com o que você permitir). O modo de ação é desativado e a barra de ação contextual desaparece quando o usuário desmarca todos os itens, pressiona o botão BACK ou seleciona a ação Done na lateral esquerda da barra.

Observação: a barra de ação contextual não é necessariamente associada à barra de aplicativos. Elas operam de forma independente, apesar de a barra de ação contextual ocupar visualmente a posição da barra de aplicativos.

Para oferecer visualizações que fornecem ações contextuais, você precisará invocar o modo de ação contextual sobre um dos eventos (ou ambos):

  • O usuário realiza um clique longo na visualização.
  • O usuário seleciona uma caixa de seleção ou um componente de IU semelhante dentro da visualização.

Como o aplicativo invoca o modo de ação contextual e define o comportamento para cada ação depende do seu projeto. Há basicamente dois projetos:

  • Para ações contextuais em visualizações arbitrárias individuais
  • Para ações contextuais em lote em grupos de itens em uma ListView ou GridView (permitindo que o usuário selecione vários itens e realize uma ação em todos eles)

As seguintes seções descrevem a configuração necessária para cada cenário.

Ativação do modo de ação contextual para visualizações individuais

Caso queira invocar o modo de ação contextual somente quando o usuário selecionar visualizações específicas, você precisará:

  1. implementar a interface ActionMode.Callback. Nos métodos de callback da interface, é possível especificar as ações da barra de ação contextual, responder aos eventos de clique em itens de ação e processar outros eventos de ciclo de vida do modo de ação;
  2. chamar startActionMode() quando quiser exibir a barra (como quando o usuário realiza cliques longos na visualização).

Por exemplo:

  1. Implemente a interface 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;
        }
    };
    

    Esses callbacks de eventos são quase exatamente iguais aos callbacks do menu de opções, exceto que cada um deles também passa o objeto ActionMode associado ao evento. É possível usar APIs de ActionMode para realizar várias alterações no CAB, como revisar o título e subtítulo com setTitle() e setSubtitle() (útil para indicar quantos itens são selecionados).

    Os exemplos acima definem a variável mActionMode como nula quando o modo de ação é destruído. Na etapa a seguir, você verá como ela é inicializada e como pode ser útil salvar a variável do membro na atividade ou no fragmento.

  2. Chame startActionMode() para ativar o modo de ação contextual quando apropriado, como em resposta a um clique longo em uma 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;
        }
    });
    

    Ao chamar startActionMode(), o sistema retorna o ActionMode criado. Ao salvar isso em uma variável do membro, é possível realizar alterações na barra de ação contextual em resposta a outros eventos. No exemplo acima, ActionMode é usado para garantir que a instância ActionMode não seja recriada se já estiver ativa, verificando se o membro é nulo antes de iniciar o modo de ação.

Ativação de ações contextuais agrupadas em ListView ou GridView

Se tiver uma coleção de itens em uma ListView ou GridView (ou outra extensão de AbsListView) e quiser permitir que os usuários realizem ações em lote, você precisará:

Por exemplo:

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

Pronto! Agora, quando o usuário selecionar um item com um clique longo, o sistema chamará o método onCreateActionMode() e exibirá a barra de ação contextual com as ações especificadas. Enquanto a barra de ação contextual estiver visível, os usuários poderão selecionar itens adicionais.

Em alguns casos em que as ações contextuais fornecem itens de ação comuns, você pode querer adicionar uma caixa de seleção ou um elemento de IU semelhante que permite que os usuários selecionem itens, pois eles podem não descobrir o comportamento do clique longo. Quando um usuário seleciona a caixa de seleção, é possível invocar o modo de ação contextual definindo o respectivo item de lista para o estado marcado com setItemChecked().

Criação de um menu pop-up

Figura 4. Um menu pop-up no aplicativo do Gmail ancorado ao botão adicional no canto superior direito.

O PopupMenu é um menu modal ancorado a uma View. Ele aparece sob a visualização de âncora, se tiver espaço, ou sobre a visualização. Ele serve para:

  • fornecer um menu de estilo de estouro para ações que se relacionam com o conteúdo específico (como cabeçalhos de e-mail do Gmail, exibidos na figura 4);

    Observação: isso não é igual ao menu de contexto, que geralmente é usado para ações que afetam o conteúdo selecionado. Para ações que afetam o conteúdo selecionado, use o modo de ação contextual ou o menu de contexto flutuante.

  • fornecer uma segunda parte de uma sentença de comando (como um botão marcado como “Add” que produz um menu pop-up com opções diferentes de “Add”);
  • fornecer uma lista suspensa semelhante a Spinner que não retenha uma seleção persistente.

Observação: PopupMenu está disponível com a API de nível 11 ou posteriores.

Se definir o menu em XML, abaixo é exposto o modo de exibir o menu pop-up:

  1. Instancie um PopupMenu com seu construtor, que usa o Context do aplicativo atual e a View em que o menu precisa estar ancorado.
  2. Use MenuInflater para inflar o recurso de menu no objeto Menu retornado por PopupMenu.getMenu().
  3. Chame PopupMenu.show().

Por exemplo, a seguir há um botão com o atributo android:onClick que exibe um menu pop-up:

<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" />

A atividade pode então exibir o menu pop-up desta forma:

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

Em APIs de nível 14 ou posterior, é possível combinar as duas linhas que inflam o menu com PopupMenu.inflate().

O menu é dispensado quando o usuário seleciona um item ou toca fora da área do menu. Para ouvir o evento de dispensa, use PopupMenu.OnDismissListener.

Processamento de eventos de clique

Para realizar uma ação quando o usuário seleciona um item de menu, você precisará implementar a interface PopupMenu.OnMenuItemClickListener e registrá-la com o PopupMenu chamando setOnMenuItemclickListener(). Quando o usuário seleciona um item, o sistema chama o callback onMenuItemClick() na interface.

Por exemplo:

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;
    }
}

Criação de grupos de menu

Um grupo de menu é uma coleção de itens de menu que compartilham certas peculiaridades. Com um grupo, é possível:

É possível criar um grupo aninhando elementos <item> dentro de um elemento <group> no recurso de menu ou especificando um código de grupo com o método add().

Veja este exemplo de recurso de menu que inclui um 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>

Os itens que estão no grupo aparecem no mesmo nível que o primeiro item — todos os três itens no menu são irmãos. No entanto, é possível modificar as peculiaridades dos dois itens no grupo mencionando o código do grupo e usando os métodos listados acima. O sistema também nunca separará os itens agrupados. Por exemplo: se você declarar android:showAsAction="ifRoom" para cada item, eles aparecerão na barra de ação ou nas ações adicionais.

Uso de itens de menu marcáveis

Figura 5. Captura de tela de um submenu com itens marcáveis.

Um menu como uma interface pode ser útil para ativar e desativar as opções, usar uma caixa de seleção para opções independentes ou botões de opção para grupos de opções mutuamente exclusivas. A figura 5 mostra um submenu com itens marcáveis com botões de opção.

Observação: os itens de menu no menu de ícones (do menu de opções) não podem exibir uma caixa de seleção ou um botão de opção. Caso escolha tornar marcáveis os itens no menu de ícones, você precisará indicar manualmente o estado marcado arrastando o ícone e/ou digitando sempre que o estado for alterado.

É possível definir o comportamento marcável para itens individuais de menu usando o atributo android:checkable no elemento <item> ou para um grupo inteiro com o atributo android:checkableBehavior no elemento <group>. Por exemplo, todos os itens neste grupo de menu são marcáveis com um botão de opção:

<?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>

O atributo android:checkableBehavior aceita:

single
Somente um item do grupo pode ser marcado (botões de opção)
all
Todos os itens podem ser marcados (caixas de seleção)
none
Nenhum item é marcável

Para aplicar um estado marcado padrão a um item, pode-se usar o atributo android:checked no elemento <item> e alterar seu código com o método setChecked().

Quando um item marcável é selecionado, o sistema chama o respectivo método de retorno de chamada do item selecionado (como onOptionsItemSelected()). É aqui que você precisa definir o estado da caixa de seleção, porque a caixa de seleção ou o botão de opção não altera seu estado automaticamente. É possível consultar o estado do item (como ele era antes de o usuário selecioná-lo) com isChecked() e, em seguida, definir o estado marcado com setChecked(). Por exemplo:

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

Caso você não defina o estado marcado desta maneira, o estado visível do item (a caixa de seleção ou o botão de opção) não se alterará quando o usuário selecioná-lo. Quando o estado é definido, a atividade preserva o estado marcado do item para que, quando o usuário abrir o menu posteriormente, o estado marcado definido esteja visível.

Observação: os itens de menu marcáveis servem para serem usados somente em uma base por sessão e não são salvos quando o aplicativo é destruído. Caso tenha configurações de aplicativo que gostaria de salvar para o usuário, você precisa armazenar os dados usando as preferências compartilhadas.

Adição de itens de menu com base em um intent

Às vezes, você precisará que um item de menu inicie uma atividade usando um Intent (seja uma atividade no seu ou em outro aplicativo). Quando você sabe qual intent quer usar e tem um item de menu específico que precisa iniciar o intent, é possível executá-lo com startActivity() durante o método de retorno de chamada selecionado no item (como o callback onOptionsItemSelected()).

No entanto, caso não tenha certeza de que o dispositivo do usuário contém um aplicativo que lida com o intent, adicionar um item que o invoca resulta em um item de menu que não funciona, porque o intent pode não se resolver em uma atividade. Para resolver isso, o Android permite que você adicione itens de menu dinamicamente ao seu menu quando encontra atividades no dispositivo que lidam com o intent.

Para adicionar itens de menu com base nas atividades disponíveis que aceitam um intent, faça o seguinte:

  1. Defina o intent com a categoria CATEGORY_ALTERNATIVE e/ou CATEGORY_SELECTED_ALTERNATIVE, além de quaisquer outros requisitos.
  2. Chame Menu.addIntentOptions(). O Android procura um aplicativo que possa realizar o intent e adiciona-o ao seu menu.

Se não houver nenhum aplicativo instalado que satisfaça o intent, nenhum item de menu será adicionado.

Observação: CATEGORY_SELECTED_ALTERNATIVE é usado para lidar com o elemento atualmente selecionado na tela. Portanto, ele deve ser usado somente ao criar um menu em onCreateContextMenu().

Por exemplo:

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;
}

Para cada atividade encontrada que fornece um filtro de intent correspondente ao intent definido, um item de menu é adicionado, usando o valor no android:label do filtro de intent como o título do item e o ícone do aplicativo como o ícone do item de menu. O método addIntentOptions() retorna o número de itens de menu adicionados.

Observação: ao chamar addIntentOptions(), ele modifica todos os itens de menu no grupo do menu especificado no primeiro argumento.

Permissão para a atividade ser adicionada a outros menus

Você pode também oferecer os serviços da sua atividade para outros aplicativos, para que o aplicativo possa ser incluído no menu de outros (revertendo as funções descritas acima).

Para ser incluído nos menus de outros aplicativos, você precisa definir um filtro de intent como normalmente faz, mas certificando-se de incluir os valores CATEGORY_ALTERNATIVE e/ou CATEGORY_SELECTED_ALTERNATIVE para a categoria do filtro de intent. Por exemplo:

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

Leia mais sobre a criação de filtros de intent no documento Intents e filtros de intents.