Comment forcer l'utilisation du menu de débordement sur les appareils avec le bouton de menu

159

Je voudrais avoir tous les éléments de menu qui ne rentrent pas dans le go ActionBar dans le menu de trop - plein (celui qui est atteint de la barre d' action non sur le bouton de menu) , même sur des appareils qui faire un bouton Menu . Cela semble beaucoup plus intuitif pour les utilisateurs que de les jeter dans une liste de menus séparée qui oblige l'utilisateur à passer d'une interaction tactile (écran) à une interaction basée sur un bouton simplement parce que la disposition de l'ActionBar ne peut pas les adapter à la barre.

Sur l'émulateur, je peux définir la valeur "Hardware Back / Home Keys" sur "no" et obtenir cet effet. J'ai cherché un moyen de le faire dans le code pour un appareil réel qui a un bouton de menu mais ne peut pas en régler un. Quelqu'un peut-il m'aider?

PaulP
la source

Réponses:

54

EDIT: Modifié pour répondre à la situation du bouton de menu physique.

Ceci est en fait empêché par la conception. Selon la section de compatibilité du guide de conception Android ,

"... le débordement d'action est disponible à partir de la touche matérielle du menu. La fenêtre contextuelle des actions qui en résulte ... s'affiche en bas de l'écran."

Vous remarquerez dans les captures d'écran que les téléphones dotés d'un bouton de menu physique n'ont pas de menu de débordement dans l'ActionBar. Cela évite toute ambiguïté pour l'utilisateur, ayant essentiellement deux boutons disponibles pour ouvrir exactement le même menu.

Pour résoudre le problème de la cohérence entre les appareils: en fin de compte, il est plus important pour l'expérience utilisateur que votre application se comporte de manière cohérente avec toutes les autres applications sur le même appareil, que de se comporter de manière cohérente avec elle-même sur tous les appareils.

Alexandre Lucas
la source
1
Alexander - Non, j'ai essayé toutes les valeurs de showAsAction et plusieurs combinaisons et aucune d'elles ne fait l'affaire (du moins sur l'émulateur). Le menu de débordement de la barre d'actions n'apparaît que si j'émule un appareil sans bouton de menu. Je veux voir ces points de suspension verticaux et voir les éléments débordants apparaître sur les appareils avec un bouton de menu. J'ai modifié ma question pour que cela soit un peu plus clair.
PaulP
41
Entrons dans cette conversation et discutons: je sais que c'est empêché par la conception (j'ai lu les directives de conception) Mais c'est une sorte de% $ /% # +, je pense. Par exemple: l'utilisateur passe d'un Galaxy Nexus (-> w Overflow) à un Nexus One (w 4.0 / -> no Overflow). Je parie que l'utilisateur ne trouvera plus les éléments du menu. Pour cette raison, je souhaite la même utilisation pour tous les appareils. Donc, je me retrouve avec le même problème que paulp. N'y a-t-il pas de solution de contournement propre disponible?
Sprigg
10
Moi aussi, j'ai lu le Guide de conception en premier. Pour moi, c'était un mauvais choix de conception dans le package de support. Une meilleure stratégie pour écarter les utilisateurs du bouton (l'objectif, non?) Serait de le rendre redondant, de mettre la fonctionnalité à l'écran ainsi que dans le bouton. Dans l'état actuel des choses, le bouton ne répertorie pas tous les choix de menu, seulement ceux qui ne sont pas sur la barre d'actions, donc cette conception ne prend pas en charge une transition en douceur vers la barre d'actions ni ne fait ce qu'elle faisait avant d'ajouter une barre d'actions (afficher tout le menu les choix). Je soupçonne que vous avez raison et qu'il n'y a pas de solution de contournement simple.
PaulP
18
Google va à l'encontre de cela dans ses nouvelles applications Google +. Il a un élément de débordement quel que soit l'appareil .. pourtant, ils empêchent toujours les développeurs de faire de même et des conseils contre lui, à ma connaissance.
Karl
10
Honnêtement, j'ai vu beaucoup trop d'utilisateurs ne pas essayer la touche dure du menu pour s'attendre à ce qu'ils le fassent. Si la conception indique qu'un utilisateur de n'importe quel téléphone ne devrait pas avoir d'indicateur à l'écran indiquant que les options de menu existent, cette conception est mal informée.
Lance Nanek
323

Vous pouvez également utiliser ce petit hack ici:

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

Le bon endroit pour le mettre serait la onCreate-Méthode de votre classe Application.

Cela forcera l'application à afficher le menu de débordement. Le bouton de menu fonctionnera toujours, mais il ouvrira le menu dans le coin supérieur droit.

[Modifier] Depuis qu'il est venu plusieurs fois maintenant: Ce hack ne fonctionne que pour l'ActionBar native introduite dans Android 3.0, pas ActionBarSherlock. Ce dernier utilise sa propre logique interne pour décider d'afficher ou non le menu de débordement. Si vous utilisez l'ABS, toutes les plateformes <4.0 sont gérées par l'ABS et sont donc soumises à sa logique. Le hack fonctionnera toujours pour tous les appareils équipés d'Android 4.0 ou supérieur (vous pouvez ignorer en toute sécurité Android 3.x, car il n'y a pas vraiment de tablettes avec un bouton de menu).

Il existe un thème spécial ForceOverflow qui forcera le menu en ABS, mais apparemment, il sera supprimé dans les versions futures en raison de complications .

Timo Ohr
la source
4
J'ai parcouru le code source. Treasure Grove de fonctionnalités non documentées :) Assurez-vous simplement d'avoir une solution de secours réalisable pour des choses comme ça.
Timo Ohr
4
Merci beaucoup! C'est vraiment génial. Je voulais mettre un examen, un rapport de bogue et des actions de partage dans le débordement, mais cela ne s'affichait pas sur Nexus S.Les utilisateurs ne cliquent même pas sur le bouton Menu. Avec le débordement, les utilisateurs voient que des actions supplémentaires sont disponibles.
Yuriy Kulikov
4
@Ewoks Je suis assez en retard sur le commentaire ici, mais je viens de l'essayer avec la version la plus récente d'ActionBarCompat, et cela a fonctionné.
jacobhyphenated
3
Cela fonctionne sur le Samsung Galaxy S3 et S4, mais pas sur le LG G2 (soupçonnez qu'ils codent en dur la vérification pour afficher le bouton de menu de débordement)
DVD
18
Belle! Si Eclipse vous donne le choix entre 6 importations différentes, Fieldchoisissez celle-ci java.lang.reflect.Field;)
Eugene van der Merwe
35

J'utilise pour contourner le problème en définissant mon menu comme celui-ci (également avec l'icône ActionBarSherlock utilisée dans mon exemple):

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

J'admets que cela peut nécessiter une "gestion de débordement" manuelle dans votre xml, mais j'ai trouvé cette solution utile.

Vous pouvez également forcer l'appareil à utiliser le bouton HW pour ouvrir le menu de débordement, dans votre activité:

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
la source
2
comment utiliser le bouton HW pour ouvrir le menu de débordement?
bladefury
1
Voir ma réponse mise à jour pour ouvrir le menu de débordement avec le bouton HW. :)
Berťák
11

Si vous utilisez la barre d'action de la bibliothèque de support ( android.support.v7.app.ActionBar), utilisez ce qui suit:

<?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>
un joueur juste
la source
Lors de l'utilisation de la bibliothèque de support, j'ai également implémenté cela et cela fonctionne bien sur les appareils pré et post 3.0
leafcutter
7

Ce type de méthode est empêché par le système de conception Android Developers, mais j'ai trouvé un moyen de le faire passer:

Ajoutez ceci à votre fichier 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" />

Ensuite, créez une classe nommée 'AppPickActionProvider' et copiez-y le code suivant:

    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
la source
quelle est la différence entre utiliser cette méthode et simplement utiliser un sous-menu, comme indiqué ici: stackoverflow.com/a/14634780/878126 ?
développeur android
5

Eh bien, je pense qu'Alexander Lucas a fourni la réponse (malheureusement) correcte, donc je la marque comme "correcte". La réponse alternative que j'ajoute ici est simplement de diriger tous les nouveaux lecteurs vers cet article du blog Android Developers en tant que discussion plutôt complète du sujet avec quelques suggestions spécifiques sur la façon de gérer votre code lors de la transition du pré-niveau 11 à la nouvelle barre d'action.

Je crois toujours que c'était une erreur de conception que le bouton de menu ne se comporte pas comme un bouton "Action Overflow" redondant dans les appareils activés par le bouton de menu comme un meilleur moyen de faire la transition de l'expérience utilisateur, mais son eau sous le pont à ce stade.

PaulP
la source
Je suis entièrement d'accord avec vous sur l'erreur de conception - et je trouve cela frustrant!
Paul Hunnisett
1
Je serais d'accord avec vous sur ce point, si ce n'était du fait que Google lui-même a commencé à enfreindre cette règle dans sa récente application G +. Et à juste titre. Le bouton de menu n'est pas hérité, même les nouveaux appareils comme le SGS3 l'ont. Il est là pour rester, malheureusement. Et cela entrave sérieusement la convivialité.
Timo Ohr
4

Je ne sais pas si c'est ce que vous recherchez, mais j'ai créé un sous-menu dans le menu de la barre d'action et défini son icône pour correspondre à l'icône du menu de débordement. Bien que les éléments ne lui soient pas automatiquement envoyés (c'est-à-dire que vous devez choisir ce qui est toujours visible et ce qui déborde toujours), il me semble que cette approche peut vous aider.

Chris
la source
2

Dans l'application Gmail fournie avec ICS préinstallé, le bouton de menu est désactivé lorsque plusieurs éléments sont sélectionnés. Le menu de débordement est ici "forcé" d'être déclenché par l'utilisation du bouton de débordement au lieu du bouton de menu physique. Theres une bibliothèque tierce appelée ActionBarSherlock qui vous permet de "forcer" le menu de débordement. Mais cela ne fonctionnera qu'au niveau d'API 14 ou inférieur (pré-ICS)

Borislemke
la source
2

Si vous utilisez la barre d'outils , vous pouvez afficher le débordement sur toutes les versions et tous les appareils, j'ai essayé sur certains appareils 2.x, cela fonctionne.

TeeTracker
la source
1

Désolé si ce problème est mort.

Voici ce que j'ai fait pour résoudre l'erreur. Je suis allé aux mises en page et en ai créé deux contenant des barres d'outils. L'un était une mise en page pour la version 8 de sdk et l'autre pour la version 21 de sdk. Sur la version 8, j'ai utilisé la barre d'outils android.support.v7.widget.Toolbar tandis que j'ai utilisé android.widget.Toolbar sur la mise en page sdk 21.

Ensuite, je gonfle la barre d'outils dans mon activité. Je vérifie le sdk pour voir s'il était de 21 ou plus. Je gonfle ensuite la mise en page correspondante. Cela force le bouton matériel à mapper sur la barre d'outils que vous avez réellement conçue.

Dolandlod
la source
0

Pour tous ceux qui utilisent le nouveau 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
la source