Cómo forzar el uso del menú de desbordamiento en dispositivos con botón de menú


159

Me gustaría que todos los elementos del menú que no encajan en la barra de acciones vayan al menú de desbordamiento (al que se accede desde la barra de acción, no desde el botón de menú) incluso en dispositivos que tienen un botón de menú . Esto parece mucho más intuitivo para los usuarios que incluirlos en una lista de menú separada que requiere que el usuario salte de una interacción táctil (pantalla) a una interacción basada en botones simplemente porque el diseño de la barra de acciones no puede caber en la barra.

En el emulador puedo establecer el valor de "Teclas de Atrás / Inicio de Hardware" en "no" y obtener este efecto. He buscado una manera de hacer esto en código para un dispositivo real que tiene un botón de menú pero no puede encontrar uno. ¿Alguien puede ayudarme?

Respuestas:


54

EDITAR: modificado para responder a la situación del botón del menú físico.

Esto es realmente evitado por el diseño. Según la sección de compatibilidad de la Guía de diseño de Android ,

"... el desbordamiento de acción está disponible desde la tecla de hardware del menú. La ventana emergente de acciones resultante ... se muestra en la parte inferior de la pantalla".

Notarás en las capturas de pantalla que los teléfonos con un botón de menú físico no tienen un menú de desbordamiento en la barra de acciones. Esto evita la ambigüedad para el usuario, esencialmente teniendo dos botones disponibles para abrir exactamente el mismo menú.

Para abordar el problema de la coherencia entre dispositivos: en última instancia, es más importante para la experiencia del usuario que su aplicación se comporte de manera consistente con todas las demás aplicaciones en el mismo dispositivo, que que se comporte de manera consistente consigo misma en todos los dispositivos.


1
Alexander: No, he probado todos los valores de showAsAction y varias combinaciones y ninguna de ellas funciona (al menos en el emulador). El menú de desbordamiento en la barra de acción solo aparece si emulo un dispositivo sin un botón de menú. Quiero ver esa elipsis vertical y tener elementos que se desbordan en los dispositivos con un botón de menú. He editado mi pregunta para aclararlo un poco.
PaulP

41
Entremos en esta conversación y analicemos: Sé que el diseño lo previene (leí las pautas de diseño). Pero eso es un tipo de% $ /% # +, creo. Por ejemplo: el usuario está cambiando de un Galaxy Nexus (-> w Overflow) a un Nexus One (w 4.0 / -> no Overflow). Apuesto a que el usuario ya no encontrará los elementos del menú. Por esa razón, quiero el mismo uso para todos los dispositivos. Entonces, estoy terminando con el mismo problema que paulp. ¿No hay alguna solución alternativa disponible?
Sprigg

10
Yo también leí la Guía de diseño primero. Para mí, esta fue una mala elección de diseño en el paquete de soporte. Una mejor estrategia para la transición de los usuarios LEJOS del botón (el objetivo, ¿verdad?) Sería hacer que sea redundante, poner la funcionalidad en la pantalla y en el botón. Tal como están las cosas ahora, el botón no enumera todas las opciones de menú, solo aquellas que no están en la barra de acción, por lo que este diseño no admite una transición suave a la barra de acción ni hace lo que solía hacer antes de agregar una barra de acción (mostrar todo el menú opciones). Sospecho que tienes razón y que no hay una solución simple.
PaulP

18
Google va en contra de esto en sus nuevas aplicaciones de Google +. Tiene un elemento de desbordamiento sin importar el dispositivo ... sin embargo, todavía impiden que los desarrolladores hagan lo mismo y aconsejan que no lo haga, que yo sepa.
Karl

10
Honestamente, he visto demasiados usuarios que no prueban la tecla dura del menú para esperar que lo hagan. Si el diseño dice que cualquier usuario en cualquier teléfono no debería tener un indicador en pantalla de que existen opciones de menú, ese diseño no está bien informado.
Lance Nanek

323

También puedes usar este pequeño truco aquí:

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

Un buen lugar para ponerlo sería el onCreatemétodo de su clase de aplicación.

Obligará a la aplicación a mostrar el menú de desbordamiento. El botón de menú seguirá funcionando, pero abrirá el menú en la esquina superior derecha.

[Editar] Ya que ha aparecido varias veces: este truco solo funciona para el ActionBar nativo introducido en Android 3.0, no ActionBarSherlock. Este último usa su propia lógica interna para decidir si mostrar el menú de desbordamiento. Si usa ABS, todas las plataformas <4.0 son manejadas por ABS y, por lo tanto, están sujetas a su lógica. El truco seguirá funcionando para todos los dispositivos con Android 4.0 o superior (puede ignorar con seguridad Android 3.x, ya que en realidad no hay tabletas con un botón de menú).

Existe un ForceOverflow-Theme especial que forzará el menú en ABS, pero aparentemente se eliminará en futuras versiones debido a complicaciones .


44
Miré a través del código fuente. Treasure Grove de características indocumentadas :) Solo asegúrese de tener una alternativa viable para cosas como esa.
Timo Ohr

44
¡Muchas gracias! Esto es realmente genial Quería poner la revisión, el informe de errores y compartir acciones en el desbordamiento, pero no se mostraba en Nexus S. Los usuarios ni siquiera harán clic en el botón Menú. Con el desbordamiento, los usuarios ven que hay acciones adicionales disponibles.
Yuriy Kulikov

44
@Ewoks Llegué bastante tarde al comentario aquí, pero acabo de probar esto con la versión más reciente de ActionBarCompat, y funcionó.
jacobhyphenated

3
Esto funciona en el Samsung Galaxy S3 y S4, pero no en el LG G2 (sospecha que están codificando la verificación para mostrar el botón del menú de desbordamiento)
DVD

18
¡Hermoso! Si Eclipse le ofrece 6 importaciones diferentes para elegir, Fieldelija esta java.lang.reflect.Field;)
Eugene van der Merwe

35

Solía ​​solucionarlo definiendo mi menú de esta manera (también con el icono ActionBarSherlock usado en mi ejemplo)

<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 esto puede requerir una "gestión de desbordamiento" manual en su xml, pero esta solución me pareció útil.

También puede forzar al dispositivo a usar el botón HW para abrir el menú de desbordamiento, en su actividad:

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

:-)


2
¿Cómo usar el botón HW para abrir el menú de desbordamiento?
bladefury

1
Vea mi respuesta actualizada para abrir el menú de desbordamiento con el botón HW. :)
Berťák

11

Si está utilizando la barra de acción de la biblioteca de soporte ( android.support.v7.app.ActionBar), use lo siguiente:

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

Cuando utilicé la biblioteca de soporte, también implementé esto y funciona bien en los dispositivos pre y post 3.0
leafcutter

7

El sistema de diseño de desarrolladores de Android previene este tipo de método, pero encontré una forma de pasarlo:

Agregue esto a su archivo de menú 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" />

A continuación, cree una clase llamada 'AppPickActionProvider' y copie el siguiente código:

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

¿Cuál es la diferencia entre usar este método y solo usar un submenú, como se muestra aquí: stackoverflow.com/a/14634780/878126 ?
Desarrollador de Android el

5

Bueno, creo que Alexander Lucas ha proporcionado (desafortunadamente) la respuesta correcta, así que la marco como la "correcta". La respuesta alternativa que agrego aquí es simplemente señalar a los lectores nuevos a esta publicación en el blog de Desarrolladores de Android como una discusión bastante completa del tema con algunas sugerencias específicas sobre cómo lidiar con su código al pasar del nivel 11 anterior a la nueva barra de acción.

Todavía creo que fue un error de diseño no hacer que el botón de menú se comportara como un botón redundante de "Desbordamiento de acción" en dispositivos habilitados para botón de menú como una mejor manera de hacer la transición de la experiencia del usuario, pero su agua debajo del puente en este punto.


Estoy completamente de acuerdo con usted sobre el error de diseño, ¡y esto me parece frustrante!
Paul Hunnisett

1
Estoy de acuerdo con usted en esto, si no fuera por el hecho de que Google mismo comenzó a violar esa regla en su reciente aplicación de Google+. Y con toda la razón. El botón de menú no es heredado, incluso los dispositivos nuevos como el SGS3 lo tienen. Está aquí para quedarse, por desgracia. Y obstaculiza seriamente la usabilidad.
Timo Ohr

4

No estoy seguro de si esto es lo que está buscando, pero creé un submenú dentro del menú de la barra de acciones y configuré su icono para que coincida con el icono del menú de desbordamiento. Aunque no tendrá elementos enviados automáticamente (es decir, debe elegir lo que siempre está visible y lo que siempre está desbordado), me parece que este enfoque puede ayudarlo.


2

En la aplicación de Gmail que viene con ICS preinstalado, el botón de menú se deshabilita cuando tiene varios elementos seleccionados. El menú de desbordamiento está "forzado" a activarse mediante el uso del botón de desbordamiento en lugar del botón de menú físico. Hay una biblioteca de terceros llamada ActionBarSherlock que le permite "forzar" el menú de desbordamiento. Pero esto solo funcionará en el nivel API 14 o inferior (pre-ICS)



1

Lo siento si este problema está muerto.

Esto es lo que hice para resolver el error. Fui a diseños y creé dos que contenían barras de herramientas. Uno era un diseño para sdk versión 8 y el otro era para sdk versión 21. En la versión 8, usé android.support.v7.widget.Toolbar mientras usaba android.widget.Toolbar en el diseño sdk 21.

Luego inflo la barra de herramientas en mi actividad. Compruebo el SDK para ver si tenía 21 o más. Luego inflo el diseño correspondiente. Esto obliga al botón de hardware a mapearse en la barra de herramientas que realmente diseñó.


0

Para cualquiera que use el nuevo 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);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.