Como forçar o uso do menu de estouro em dispositivos com botão de menu

159

Eu gostaria de ter todos os itens de menu que não se encaixam em qualquer lugar ActionBar para o menu de estouro (aquele que é alcançado a partir do não Barra de ação do botão de menu) , mesmo em dispositivos que fazem tem um botão de Menu . Isso parece muito mais intuitivo para os usuários do que jogá-los em uma lista de menus separada que exige que o usuário pule de uma interação de toque (tela) para uma interação baseada em botão simplesmente porque o layout do ActionBar não pode ajustá-los na barra.

No emulador, posso definir o valor "Hardware Back / Home Keys" como "no" e obter esse efeito. Procurei uma maneira de fazer isso no código de um dispositivo real que tenha um botão de menu, mas que não consiga multar um. Alguém pode me ajudar?

PaulP
fonte

Respostas:

54

EDIT: Modificado para responder à situação do botão físico do menu.

Isso é realmente evitado pelo design. De acordo com a seção de compatibilidade do Android Design Guide ,

"... o excesso de ação está disponível na tecla de hardware do menu. O pop-up de ações resultante ... é exibido na parte inferior da tela."

Você notará nas capturas de tela que telefones com um botão de menu físico não possuem um menu de transbordamento na ActionBar. Isso evita ambiguidade para o usuário, tendo essencialmente dois botões disponíveis para abrir exatamente o mesmo menu.

Para resolver o problema de consistência entre dispositivos: em última análise, é mais importante para a experiência do usuário que seu aplicativo se comporte de maneira consistente com todos os outros aplicativos no mesmo dispositivo, do que se comporte de maneira consistente consigo mesmo em todos os dispositivos.

Alexander Lucas
fonte
1
Alexander - Não, eu tentei todos os valores showAsAction e várias combinações e nenhuma delas faz o truque (pelo menos no emulador). O menu de estouro na barra de ação só aparece se eu emular um dispositivo sem um botão de menu. Quero ver as reticências verticais e os itens transbordantes aparecerem nos dispositivos com um botão de menu. Eu editei minha pergunta para tornar isso um pouco mais claro.
22412 PaulP
41
Vamos entrar nessa conversa e discutir: eu sei que ela é impedida pelo design (li as diretrizes de design). Mas isso é%% $ /% # +, eu acho. Por exemplo: o usuário está alternando de um Galaxy Nexus (-> w Estouro) para um Nexus One (w 4.0 / -> sem Estouro). Aposto que o usuário não encontrará mais os itens do menu. Por esse motivo, quero o mesmo uso para todos os dispositivos. Então, eu estou acabando com o mesmo problema que paulp. Não há nenhuma solução alternativa disponível?
Sprigg
10
Eu também li o Guia de Design primeiro. Para mim, essa foi uma má escolha de design no pacote de suporte. Uma estratégia melhor para fazer a transição dos usuários para longe do botão (o objetivo, certo?) Seria torná-lo redundante, colocar a funcionalidade na tela e também no botão. Como está agora, o botão não lista todas as opções de menu, apenas as que não estão na barra de ação; portanto, esse design não suporta uma transição suave para a barra de ação nem faz o que costumava fazer antes de adicionar uma barra de ação (exibir todo o menu escolhas). Eu suspeito que você está certo e que não há uma solução simples.
PaulP
18
O Google vai contra isso em seus novos aplicativos do Google +. Ele possui um item de estouro, independentemente do dispositivo. Ainda assim, eles impedem que os desenvolvedores façam o mesmo e aconselham contra isso, que eu saiba.
314 Karl
10
Honestamente, eu já vi muitos usuários não experimentarem a tecla do menu para esperar que eles o façam. Se o design disser que qualquer usuário em qualquer telefone não deve ter um indicador na tela de que existem opções de menu, esse design é pouco informado.
Lance Nanek 27/10/12
323

Você também pode usar este pequeno truque aqui:

try {
    ViewConfiguration config = ViewConfiguration.get(this);
    Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
    if (menuKeyField != null) {
        menuKeyField.setAccessible(true);
        menuKeyField.setBoolean(config, false);
    }
} catch (Exception ignored) {
}

Um bom lugar para colocá-lo seria o onCreate-Method da sua classe Application.

Isso forçará o aplicativo a mostrar o menu de estouro. O botão de menu ainda funcionará, mas abrirá o menu no canto superior direito.

[Edit] Desde que surgiu várias vezes agora: Este hack funciona apenas para o ActionBar nativo introduzido no Android 3.0, não para o ActionBarSherlock. O último usa sua própria lógica interna para decidir se o menu de estouro será exibido. Se você usa o ABS, todas as plataformas <4.0 são tratadas pelo ABS e, portanto, sujeitas à sua lógica. O hack ainda funcionará para todos os dispositivos com Android 4.0 ou superior (você pode ignorar com segurança o Android 3.x, pois não há realmente tablets por aí com um botão de menu).

Existe um tema especial ForceOverflow que forçará o menu no ABS, mas aparentemente será removido em versões futuras devido a complicações .

Timo Ohr
fonte
4
Eu olhei através do código fonte. Tesouro de recursos não documentados :) Apenas verifique se você tem um substituto viável para coisas assim.
Timo Ohr
4
Muito obrigado! Isso é realmente ótimo. Queria colocar revisão, relatório de erros e compartilhar ações no excesso, mas não estava aparecendo no Nexus S. Os usuários nem sequer clicam no botão Menu. Com o estouro, os usuários veem que ações extras estão disponíveis.
Yuriy Kulikov 23/02
4
@ Ewoks Estou muito atrasado no comentário aqui, mas tentei isso com a versão mais recente do ActionBarCompat, e funcionou.
jacobhyphenated
3
Isso funciona no Samsung Galaxy S3 e S4, mas não no LG G2 (suspeito de que eles estejam codificando a verificação para mostrar o botão de menu de estouro)
dvd
18
Lindo! Se o Eclipse fornecer 6 importações diferentes para você escolher, Fieldescolha esta java.lang.reflect.Field;)
Eugene van der Merwe
35

Eu uso para contornar isso definindo meu menu como este (também com o ícone ActionBarSherlock usado no meu exemplo):

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/abs__ic_menu_moreoverflow_normal_holo_light"
        android:orderInCategory="11111"
        android:showAsAction="always">
        <menu>
            <item
                android:id="@+id/menu_overflow_item1"
                android:showAsAction="never"
                android:title="@string/overflow_item1_title"/>
            <item
                android:id="@+id/menu_overflow_item2"
                android:showAsAction="never"
                android:title="@string/overflow_item2_title"/>
        </menu>
    </item>

</menu>

Admito que isso possa exigir "gerenciamento de estouro" manual em seu xml, mas achei esta solução útil.

Você também pode forçar o dispositivo a usar o botão HW para abrir o menu de estouro, em sua atividade:

private Menu mainMenu;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // TODO: init menu here...
    // then:
    mainMenu=menu;
    return true;
}

@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            if (mainMenu !=null) {
                mainMenu.performIdentifierAction(R.id.menu_overflow, 0);
            }
    }

    return super.onKeyUp(keycode, e);
}

:-)

Berťák
fonte
2
como usar o botão HW para abrir o menu de estouro?
bladefury
1
Veja minha resposta atualizada para abrir o menu de estouro com o botão HW. :) #
303
11

Se você estiver usando a barra de ação da biblioteca de suporte ( android.support.v7.app.ActionBar), use o seguinte:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yorapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/icon"
        yourapp:showAsAction="always"
        android:title="">
        <menu>
            <item
                android:id="@+id/item1"
                android:title="item1"/>
            <item
                android:id="@+id/item2"
                android:title="item2"/>
        </menu>
    </item>

</menu>
um jogador justo
fonte
Ao usar a biblioteca de suporte implementei isso também e funciona ok em ambos pré e pós 3,0 dispositivos
leafcutter
7

Esse tipo de método é impedido pelo Android Developers Design System, mas eu encontrei uma maneira de passar:

Adicione isso ao seu arquivo de menu XML:

<item android:id="@+id/pick_action_provider"
    android:showAsAction="always"
    android:title="More"
    android:icon="@drawable/ic_action_overflow"
    android:actionProviderClass="com.example.AppPickActionProvider" />

Em seguida, crie uma classe chamada 'AppPickActionProvider' e copie o seguinte código para ela:

    package com.example;

import android.content.Context;
import android.util.Log;
import android.view.ActionProvider;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.SubMenu;
import android.view.View;

public class AppPickActionProvider extends ActionProvider implements
        OnMenuItemClickListener {

    static final int LIST_LENGTH = 3;

    Context mContext;

    public AppPickActionProvider(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public View onCreateActionView() {
        Log.d(this.getClass().getSimpleName(), "onCreateActionView");

        return null;
    }

    @Override
    public boolean onPerformDefaultAction() {
        Log.d(this.getClass().getSimpleName(), "onPerformDefaultAction");

        return super.onPerformDefaultAction();
    }

    @Override
    public boolean hasSubMenu() {
        Log.d(this.getClass().getSimpleName(), "hasSubMenu");

        return true;
    }

    @Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        Log.d(this.getClass().getSimpleName(), "onPrepareSubMenu");

        subMenu.clear();

        subMenu.add(0, 1, 1, "Item1")
        .setIcon(R.drawable.ic_action_home).setOnMenuItemClickListener(this);

        subMenu.add(0, 2, 1, "Item2")
            .setIcon(R.drawable.ic_action_downloads).setOnMenuItemClickListener(this);
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch(item.getItemId())
        {
            case 1:

                // What will happen when the user presses the first menu item ( 'Item1' )

                break;
            case 2:

                // What will happen when the user presses the second menu item ( 'Item2' )

                break;

        }

        return true;
    }
}
Eli Revah
fonte
qual é a diferença entre usar esse método e usar apenas um submenu, como mostrado aqui: stackoverflow.com/a/14634780/878126 ?
desenvolvedor android
5

Bem, acho que Alexander Lucas forneceu a (infelizmente) resposta correta, por isso estou marcando-a como a "correta". A resposta alternativa que estou adicionando aqui é simplesmente apontar novos leitores para esta postagem no blog Android Developers como uma discussão bastante completa do tópico com algumas sugestões específicas sobre como lidar com seu código ao fazer a transição do nível anterior 11 para a nova barra de ação.

Eu ainda acredito que foi um erro de design não fazer com que o botão de menu se comporte como um botão redundante "Action Overflow" nos dispositivos habilitados para botões de menu como uma maneira melhor de fazer a transição da experiência do usuário, mas a água sob a ponte neste momento.

PaulP
fonte
Concordo plenamente com você sobre o erro de design - e estou achando isso frustrante!
Paul Hunnisett
1
Eu concordo com você sobre isso, se não fosse pelo fato de o próprio Google ter começado a violar essa regra em seu aplicativo G + recente. E com razão. O botão de menu não é herdado, até mesmo novos dispositivos como o SGS3 o possuem. Está aqui para ficar, infelizmente. E isso dificulta seriamente a usabilidade.
Timo Ohr
4

Não tenho certeza se é isso que você está procurando, mas criei um Submenu no Menu da ActionBar e configurei seu ícone para corresponder ao ícone do Menu Estouro. Embora ele não tenha itens enviados automaticamente, (ou seja, você precisa escolher o que é sempre visível e o que sempre excede), parece-me que essa abordagem pode ajudá-lo.

Chris
fonte
2

No aplicativo do Gmail que vem com o ICS pré-instalado, o botão de menu é desativado quando você tem vários itens selecionados. O menu de estouro aqui é "forçado" a ser acionado pelo uso do botão de estouro em vez do botão físico do menu. Existe uma biblioteca de terceiros chamada ActionBarSherlock que permite "forçar" o menu de estouro. Mas isso funcionará apenas no nível da API 14 ou inferior (pré-ICS)

borislemke
fonte
2

Se você usa a Barra de ferramentas , pode mostrar o estouro em todas as versões e dispositivos. Tentei em alguns dispositivos 2.x, funciona.

TeeTracker
fonte
1

Desculpe se este problema está morto.

Aqui está o que eu fiz para resolver o erro. Fui para layouts e criei dois contendo barras de ferramentas. Um era um layout para a versão 8 do sdk e o outro era para a versão 21. do sdk. Na versão 8, usei a barra de ferramentas android.support.v7.widget.Toolbar enquanto usei a barra de ferramentas android.widget.Toolbar no layout da sdk 21.

Então eu inflo a barra de ferramentas na minha atividade. Verifico o sdk para ver se tinha 21 anos ou mais. Depois, inflo o layout correspondente. Isso força o botão de hardware a mapear na barra de ferramentas que você realmente criou.

Dolandlod
fonte
0

Para quem usa o novo Toolbar:

private Toolbar mToolbar;

@Override
protected void onCreate(Bundle bundle) {
    super.onCreate(bundle);

    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);

    ...
}


@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            mToolbar.showOverflowMenu();
            return true;
        }

    return super.onKeyUp(keycode, e);
}
Maksim Ivanov
fonte