How to force use of overflow menu on devices with

2018-12-31 15:37发布

问题:

I\'d like to have all of the menu items that don\'t fit into the ActionBar go into the overflow menu (the one that is reached from the Action Bar not the menu button) even on devices that do have a Menu button. This seems much more intuitive for users than throwing them into a separate menu list that requires the user to jump from a touch(screen) interaction to a button based interaction simply because the layout of the ActionBar can\'t fit them on the bar.

On the emulator I can set the \"Hardware Back/Home Keys\" value to \"no\" and get this effect. I\'ve searched for a way to do this in code for an actual device that has a menu button but can\'t fine one. Can anyone help me?

回答1:

EDIT: Modified to answer for the situation of physical menu button.

This is actually prevented by design. According to the Compatibility Section of the Android Design Guide,

\"...the action overflow is available from the menu hardware key. The resulting actions popup... is displayed at the bottom of the screen.\"

You\'ll note in the screenshots, phones with a physical menu button don\'t have an overflow menu in the ActionBar. This avoids ambiguity for the user, essentially having two buttons available to open the exact same menu.

To address the issue of consistency across devices: Ultimately it\'s more important to the user experience that your app behave consistently with every other app on the same device, than that it behave consistently with itself across all devices.



回答2:

You can also use this little hack here:

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

Good place to put it would be the onCreate-Method of your Application class.

It will force the App to show the overflow menu. The menu button will still work, but it will open the menu in the top right corner.

[Edit] Since it has come up several times now: This hack only works for the native ActionBar introduced in Android 3.0, not ActionBarSherlock. The latter uses its own internal logic to decide whether to show the overflow menu. If you use ABS, all platforms < 4.0 are handled by ABS and are thus subjected to its logic. The hack will still work for all devices with Android 4.0 or greater (you can safely ignore Android 3.x, since there aren\'t really any tablets out there with a menu button).

There exists a special ForceOverflow-Theme that will force the menu in ABS, but apperently it is going to be removed in future versions due to complications.



回答3:

I use to workaround it by defining my menu like this (also with ActionBarSherlock icon used in my example):

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

I admit that this may require manual \"overflow-management\" in your xml, but I found this solution useful.

You can also force device to use HW button to open the overflow menu, in your activity:

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

:-)



回答4:

If you are using the action bar from the support library (android.support.v7.app.ActionBar), use the following:

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


回答5:

This kind of method is prevented by the Android Developers Design System, but I found a way to pass it:

Add this to your XML menu file:

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

Next, create a class named \'AppPickActionProvider\', and copy the following code to it:

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


回答6:

Well I think that Alexander Lucas has provided the (unfortunately) correct answer so I\'m marking it as the \"correct\" one. The alternative answer I\'m adding here is simply to point any new readers to this post in the Android Developers blog as a rather complete discussion of the topic with some specific suggestions as to how to deal with your code when transitioning from pre-level 11 to the new Action Bar.

I still believe it was a design mistake not have the menu button behave as a redundant \"Action Overflow\" button in menu button enabled devices as a better way to transition the user experience but its water under the bridge at this point.



回答7:

I\'m not sure if this is what you\'re looking for, but I built a Submenu within the ActionBar\'s Menu and set its icon to match the Overflow Menu\'s Icon. Although it wont have items automatically sent to it, (IE you have to choose what\'s always visible and what\'s always overflowed) it seems to me that this approach may help you.



回答8:

In the gmail app that comes with ICS pre-installed, the menu button is disabled when you have multiple items selected. The overflow menu is here \"forced\" to be triggered by the use of the overflow button instead of the physical menu button. Theres a 3rd-party lib called ActionBarSherlock which lets you \"force\" the overflow menu. But this will only work on API level 14 or lower(pre-ICS)



回答9:

If you use Toolbar, you can show the overflow on all versions and all devices, I\'ve tried on some 2.x devices, it works.



回答10:

Sorry if this problem is dead.

Here is what I did to resolve the error. I went to layouts and created two ones containing toolbars. One was a layout for sdk version 8 and the other was for sdk version 21. On version 8, I used the android.support.v7.widget.Toolbar while I used android.widget.Toolbar on the sdk 21 layout.

Then I inflate the toolbar in my activity. I check the sdk to see if it was 21 or higher. I then inflate the corresponding layout. This forces the hardware button to map onto the toolbar you actually designed.



回答11:

For anyone using the new 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);
}