如何通过菜单按钮在设备上强制使用溢出菜单


159

我想让所有不适合ActionBar的菜单项进入溢出菜单(从Action Bar到达的菜单项而不是菜单按钮),即使在具有Menu Button的设备上也是如此。对于用户而言,这似乎比将其放入单独的菜单列表中要直观得多,该列表要求用户仅从ActionBar的布局不适合他们在栏上的情况下就从触摸(屏幕)交互跳至基于按钮的交互。

在模拟器上,我可以将“硬件后退/主键”值设置为“ no”,并获得此效果。我已经在代码中寻找了一种方法来针对具有菜单按钮但无法正常使用的实际设备执行此操作。谁能帮我?

Answers:


54

编辑:修改以回答物理菜单按钮的情况。

实际上,这是通过设计防止的。根据《 Android设计指南》的“ 兼容性”部分

“ ...操作溢出可通过菜单硬件键获得。弹出的操作弹出窗口将显示在屏幕底部。”

您会在屏幕截图中注意到,带有物理菜单按钮的手机在ActionBar中没有溢出菜单。这避免了用户的歧义,本质上具有两个按钮可用来打开完全相同的菜单。

要解决跨设备的一致性问题:从根本上说,对于用户体验而言,您的应用程序与同一设备上的所有其他应用程序始终保持一致,比其在所有设备上与其自身始终保持一致的行为更为重要。


1
亚历山大-不,我已经尝试了所有的showAsAction值和几种组合,但它们都不能奏效(至少在模拟器上)。仅当我模拟没有菜单按钮的设备时,才会显示操作栏上的溢出菜单。我想看到带有菜单按钮的设备显示了垂直省略号和溢出的项目。我已经对问题进行了编辑,以使其更加清楚。
PaulP '02

41
让我们进入对话并进行讨论:我知道它是被设计阻止的(我阅读了设计指南)。但我认为那有点%$ /%#+。例如:用户正在从Galaxy Nexus(-> w溢出)切换到Nexus One(w 4.0 /->无溢出)。我敢打赌,用户将不再找到菜单项。因此,我希望所有设备使用相同的用法。因此,我最终遇到了与paulp相同的问题。没有可用的解决方法吗?
Sprigg

10
我也首先阅读了《设计指南》。对我而言,这是支持包中设计的错误选择。将用户从按钮上移开的更好策略(目标,对吗?)将使其冗余,将功能显示在屏幕上以及按钮中。到目前为止,该按钮并未列出所有菜单选项,仅列出了不在操作栏上的菜单选项,因此该设计既不支持向操作栏的平滑过渡,也不执行添加操作栏之前的操作(显示所有菜单)选择)。我怀疑您是对的,并且没有简单的解决方法。
PaulP 2012年

18
Google在其新的Google+应用程序中与此相反。无论设备如何,它都有溢出项。.据我所知,它们仍然阻止开发人员执行此操作并提出建议。
卡尔

10
老实说,我已经看到太多用户没有尝试菜单硬键来期望他们这样做。如果该设计表明任何电话上的任何用户都不应在屏幕上看到菜单选项存在的指示,则该设计的信息不充分。
Lance Nanek

323

您还可以在这里使用此小技巧:

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

放置它的好地方将是 onCreate Application类 -Method。

它将强制应用程序显示溢出菜单。菜单按钮仍然可以使用,但是它将打开右上角的菜单。

[编辑]由于它已经出现了好几次了:此hack仅适用于Android 3.0中引入的本机ActionBar,不适用于ActionBarSherlock。后者使用自己的内部逻辑来决定是否显示溢出菜单。如果使用ABS,所有<4.0平台均由ABS处理,因此要遵守其逻辑。该破解程序仍适用于所有搭载Android 4.0或更高版本的设备(您可以放心地忽略Android 3.x,因为实际上并没有带有菜单按钮的平板电脑)。

存在一个特殊的ForceOverflow-Theme主题,它将强制在ABS中显示菜单,但是由于复杂性,它将在以后的版本中删除


4
我浏览了源代码。未记录功能的宝藏:)只要确保您对类似的事情有一个可行的备用。
Timo Ohr,2012年

4
非常感谢!真的很棒 我想将评论,错误报告和共享操作放到溢出中,但是它并没有显示在Nexus S上。用户甚至都不会单击“菜单”按钮。有了溢出,用户会看到额外的操作可用。
Yuriy Kulikov

4
@Ewoks我在这里发表评论还很晚,但是我只是使用最新版本的ActionBarCompat尝试了此操作,并且DID可以正常工作。
jacobhyphenated

3
这适用于Samsung Galaxy S3和S4,但不适用于LG G2(怀疑它们正在硬编码检查以显示溢出菜单按钮)
DVD

18
美丽!如果Eclipse为您提供了6种不同的进口商品供您Field选择java.lang.reflect.Field;)
Eugene van der Merwe 2013年

35

我过去通过定义这样的菜单来解决该问题(在示例中也使用了ActionBarSherlock图标):

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

我承认这可能需要在您的xml中进行手动“溢出管理”,但是我发现此解决方案很有用。

您还可以在活动中强制设备使用硬件按钮打开溢出菜单:

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
如何使用硬件按钮打开溢出菜单?
Bladefury 2014年

1
请参阅我更新的答案,以使用HW按钮打开溢出菜单。:)
贝拉克(Berťák)2014年

11

如果要使用支持库(android.support.v7.app.ActionBar)中的操作栏,请使用以下命令:

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

使用支持库时,我也实现了这一点,并且在3.0之前和之后的设备上都可以正常工作
leafcutter 2014年

7

Android Developers Design System阻止了这种方法,但是我找到了一种方法:

将此添加到您的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" />

接下来,创建一个名为“ AppPickActionProvider”的类,并将以下代码复制到该类:

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

使用此方法与仅使用子菜单之间有什么区别,如下所示:stackoverflow.com/a/14634780/878126
android开发人员

5

我认为亚历山大·卢卡斯提供了(不幸的)正确答案,因此我将其标记为“正确”答案。我要在此处添加的替代答案只是将任何新读者指向Android开发者博客中的这篇文章作为对该主题的相当完整的讨论,并提出了一些有关从预备级别11过渡时如何处理代码的具体建议。到新的操作栏。

我仍然认为这是一个设计错误,没有菜单按钮在启用菜单按钮的设备中充当多余的“动作溢出”按钮,这是转移用户体验的一种更好的方法,但在此时此刻它是无处不在的。


我完全同意您的设计错误-感到沮丧!
Paul Hunnisett,2012年

1
如果不是因为Google本身在其最近的G +应用中开始违反该规则,我将对此表示同意。当然是这样。菜单按钮不是旧版,即使像SGS3这样的新设备也具有它。不幸的是,它在这里停留。而且它严重影响了可用性。
Timo Ohr,2012年

4

我不确定这是否是您要寻找的东西,但是我在ActionBar的菜单中建立了一个子菜单,并将其图标设置为与Overflow菜单的图标匹配。尽管它不会自动发送项目(例如,您必须选择始终可见的内容和始终溢出的内容),但在我看来,这种方法可能会有所帮助。


2

在预装有ICS的gmail应用中,如果您选择了多个项目,则菜单按钮将被禁用。溢出菜单在这里被“强制”通过使用溢出按钮而不是物理菜单按钮来触发。有一个第三方库,称为ActionBarSherlock,可让您“强制”溢出菜单。但这仅适用于API级别14或更低版本(ICS之前的版本)


2

如果您使用工具栏,则可以在所有版本和所有设备上显示溢出,我已经在某些2.x设备上尝试过,它可以正常工作。


1

很抱歉,如果这个问题仍然存在。

这是我为解决错误所做的事情。我进入布局并创建了两个包含工具栏的布局。一个是sdk版本8的布局,另一个是sdk版本21的布局。在版本8上,我在sdk 21布局上使用了android.support.v7.widget.Toolbar,而我使用了android.widget.Toolbar。

然后,在活动中为工具栏充气。我检查sdk,看它是否为21或更高。然后,我为相应的布局充气。这将迫使硬件按钮映射到您实际设计的工具栏上。


0

对于使用新功能的任何人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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.