强制在ActionBarSherlock中显示溢出菜单

26
我希望在早期的ICS设备(2.3-2.1)上使用4.0+的溢出菜单。我正在使用HoloEverywhere和ActionBarSherlock。
我尝试了以下解决方案:ActionBarSherlock&HoloEverywhere-强制溢出?,但它不起作用,因为absForceOverflow不存在。它是在最新版本中被删除了吗?我已经检查了ABS和HE库项目的R文件,这个字段根本不存在。
我的应用程序主题设置为@style/Holo.Theme.Sherlock.Light,并且这就是我尝试继承并添加absForceOverflow参数设置为true的主题。
4个回答

36

从ActionbarSherlock 4.2开始,我们失去了管理溢出菜单可见性的能力。 为使其正常工作,需要结合以下两种方法:

  1. 为了强制在Android 3.x (honeycomb)及以上版本中显示菜单,需要使用hack + 添加检查Android版本的代码:

    public static final int DEVICE_VERSION   = Build.VERSION.SDK_INT;
    public static final int DEVICE_HONEYCOMB = Build.VERSION_CODES.HONEYCOMB;
    if (DEVICE_VERSION >= DEVICE_HONEYCOMB)
        // Code from answer above
    
  2. 为早期的蜂巢之前设备打开菜单:

    • 打开ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java,进入方法reserveOverflow
    • 用以下内容替换原来的内容:

      public static boolean reserveOverflow(Context context) { return true; }

    这将强制显示菜单...

    • 但当点击菜单按钮时,菜单弹出窗口没有显示。为了实现这一点,我们需要在您的activity类中覆盖此操作:

      @Override
      public boolean onKeyUp(int keyCode, KeyEvent event) {
          if (DEVICE_VERSION < DEVICE_HONEYCOMB) {
              if (event.getAction() == KeyEvent.ACTION_UP &&
                  keyCode == KeyEvent.KEYCODE_MENU) {
                  openOptionsMenu();
                  return true;
              }
          }
          return super.onKeyUp(keyCode, event);
      }
      

完成这些操作后,您应该拥有适用于所有Android版本的完全可用的溢出操作栏菜单。


谢谢你的好建议!但我想问一下如何在Fragment中使用onKeyUp,我想处理硬件菜单键的点击事件。 - Shajeel Afzal
“Fragment”没有键处理程序,因此您需要使用众所周知的方法从“Activity”重新发送键事件到您的“Fragment”。我建议您在“Activity”中创建“CustomListener”,并在“Fragment”中订阅它。然后在“Activity”的“onKeyUp”事件中触发它:“listener.onKeyUp(...)”。 - Exterminator13
迄今为止最好的解决方案,特别是比使用具有“ForceOverflow”主题的旧版本ActionBarSherlock更好。+1 - caw
@MarcoW.:我猜你是在提到我的回答。你可能没有注意到的是,我并不建议 OP 使用旧版本(我的回答末尾有一条注释)。如果他选择使用旧版本,我只是给出了解决方案。无论如何,每个人都有自己的选择。;-) - Siddharth Lele
@IceMAN:没错,我也注意到了。我知道你并不是在推荐使用旧版本,但你的解决方案确实是针对旧版本的。我只是想表达一下这个解决方案,它不需要你使用旧版本的ABS,更具有未来性,这是事实。 - caw

27

正如Siddharth Lele指出的那样,在最新版的ABS中已经将其删除,以便与实际操作栏行为相同。因此,乍一看放弃显示此菜单是最佳选择。

然而,在某些设备上显示溢出菜单而在其他设备上不显示是Android操作栏中的一个设计缺陷,这是我个人的观点。原因如下:

在具有硬件菜单键的设备上,该菜单不会在操作栏中显示。大多数最新设备的趋势是将硬件按钮数量减少到最小限度,因为这被认为更加用户友好(而且因为iPhone只有一个按钮,所以他们复制了这个设计)。其他制造商确实包括菜单按钮,但除非你按下它,否则它是隐藏的(是的,当你不再需要它时它会亮起来。这不是明智的设计,但当手机上所有按钮都关闭时,它看起来更像iPhone)。

为了更好地理解这一点,让我们来看一个例子:用户A有一个带有菜单键的设备。他正在使用他最喜欢的邮件客户端。配置邮件帐户的选项与常规选项(帮助、关于等)一起放置在溢出菜单中。他想添加第二个帐户,但不知道如何访问此菜单。屏幕上没有信息可以帮助他意识到该怎么做。所以用户A问他的朋友B,他也在使用这个邮件客户端。用户B拥有最新的Nexus N+1 Googlephone,并且能够在操作栏中查看溢出图标,因为他的设备没有硬件菜单键。他向A展示如何通过打开此菜单来添加第二个帐户。现在用户A完全困惑了,因为他们在使用相同的应用程序版本。受挫的A可能会认为问题在于他的手机过时了。B也感到困惑。

此时,您可能会认为AB都是傻瓜,不知道如何使用智能手机。但与桌面应用程序不同,绝大多数智能手机用户对其设备只知道基础知识。他们之前的手机可能是一个带有简单固件的按键设备。电池已经用尽,他们去商店更换,但是缺货。他们本可以在互联网上订购一部新手机,但价格比购买更新的手机更贵。因此,他们被售出了一个带触摸屏的手机,因为这就是现在的手机。现在他们不得不面对一台配有完整操作系统的小型计算机。更糟的是,手机只提供“快速入门指南”,要获取完整手册,他们必须从互联网上下载PDF文件。你猜怎么着?他们不会这样做。

如果您正在开发移动应用程序,您应该假定用户可能对计算机、操作系统或类似应用程序一无所知。您应该使GUI看起来在各个设备上都相似,以便人们可以学习和记

    // For ActionBarSherlock
    abs__ic_menu_moreoverflow_normal_holo_dark.png
    abs__ic_menu_moreoverflow_normal_holo_light.png

    // For ActionBar
    ic_menu_moreoverflow_normal_holo_light.png
    ic_menu_moreoverflow_normal_holo_dark.png

此外:请将图片复制粘贴到您项目的res文件夹中。您永远不知道未来的ABS版本或下一个ActionBar实现是否会包含它们。


1
有趣。你不仅回答了技术问题,还提到了用户体验原则。我受到了启发,谢谢。 - Evi Song
你的评论足以让我们再次将.ForceOverflow加入库中!感谢您清晰的观点,我完全同意。 - Toverbal
这是一个完美的想法,但有没有办法知道在操作栏中出现了多少项,以便将...项添加到末尾。 - xmen
即使现在 Twitter 的新应用程序也使用了这个技巧,所以它已经成为了基准。 - DjHacktorReborn
最简单的方法。但是从2个XML资源合并菜单项会有一点麻烦,对吧?或者有合并子项的快捷方式吗? - Display Name
显示剩余3条评论

19
如果您使用的是4.2.0版本,则实际上已经删除了.ForceOverflow主题。
来源:版本4.2.0更改日志 更改日志摘录:
Add SearchView widget for standard search interaction (API 8+ only)
Fix: ShareActionProvider in the split action bar no longer fills the entire screen.
Fix: ShareActionProvider now does file I/O on a background thread.
Fix: Automatically correct ColorDrawable not respecting bounds when used as a stacked background.
Fix: Ensure fragments collection is present before dispatching events.
Fix: XML-defined onClick searches the correct context for the declared method.
Fix: Ensure action mode start/finish callbacks are invoked on the activity for the native action bar.
Fix: Allow tab callbacks to have a fragment transaction instance for any FragmentActivity.
Fix: Ensure CollapsibleActionView callbacks are dispatched in both native and compatbility action bars.
Fix: Remove .ForceOverflow themes. These never should have been included.

如果您绝对需要强制溢出,您需要下载早期版本的ABS。您可以在此处查看按发布历史记录排序的下载列表: http://actionbarsherlock.com/download.html 我个人仍然使用ABS 4.1.0版本,因为我目前不想在我的应用程序中进行辅助更改。我还在我的theme.xml中使用它:
<style name="MyTheme" parent="@style/Theme.Sherlock.ForceOverflow">
    <item name="android:windowBackground">@color/background</item>
    <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item>
    <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item>
</style>

<style name="MyTheme.ForceOverflow">
    <item name="absForceOverflow">true</item>
</style>

当为Activitymanifest.xml中应用主题时,我使用以下属性:"@style/Theme.SociallyYOU"

另外,如果你一定要强制溢出,你可能也想阅读CommonsWare在这里另一个问题上的想法:https://stackoverflow.com/a/12872537/450534

注意:话虽如此,如果权衡不是太重要,始终使用最新版本会更好。通过发布我如何强制溢出菜单,我既不建议您使用旧版本,也不推荐这样做。它只是告诉您可能性。


1
非常好的答案。非常感谢!我想在ICS之前的设备上强制使用溢出菜单,因为我发现我的一些用户不知道可以按MENU按钮并在手机上访问某些选项。那我就得使用旧版本或者想出更好的解决方案了。 - Paul
@Paul:很高兴能够帮到你。但在决定之前,请考虑对ABS所做的更改。 - Siddharth Lele
@Paul 我在下面的回答中详细阐述了这一点。如果你认为有必要,你可以提供自己的替代菜单。 - Mister Smith

0
我发现Exterminator13的答案在某些Android设备上不起作用(我认为总数相当小),但为了适应尽可能多的设备,我使用对话框,并使用一个菜单,android:showAsAction="always" 作为溢出菜单按钮。毕竟,actionbarsherlock的溢出菜单效果是通过其源代码实现的,所以如果不麻烦的话,为什么不自己实现该效果呢?如果你能理解中文,你也可以看看我的博客
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/more"
        android:icon="@drawable/overflow"
        android:showAsAction="always" 
    />
</menu>

 

private Dialog popupDialog;
private Boolean popupState=false;

    public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        this.finish();
        return true;
    case R.id.more:
        if (!popupState) {
            showPop();
        }else {
            popupDialog.dismiss();
        }
    default:
        return super.onOptionsItemSelected(item);
    }
}

private void showPop(){
    LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(ResID, null);
    ListView listView = (ListView) view.findViewById(ResID);
    listView.setAdapter(yourOwnAdapter);
    popupDialog = new Dialog(WifiAuthWireActivity.this);
    popupDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    popupDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
    popupDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    popupDialog.setContentView(view);
    // Calculate ActionBar height
    TypedValue tv = new TypedValue();
    ActionBar maActionBar=getSupportActionBar();
    int actionBarHeight=maActionBar.getHeight();
    if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
    {
        actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
    }
    WindowManager.LayoutParams wmlp = popupDialog.getWindow().getAttributes();
    wmlp.gravity = Gravity.TOP | Gravity.RIGHT;
    wmlp.x+=12;
    wmlp.y+=actionBarHeight;
    popupDialog.getWindow().setAttributes(wmlp);
    popupDialog.show();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接