【问题标题】:Force overflow menu in ActionBarSherlockActionBarSherlock 中的强制溢出菜单
【发布时间】:2012-11-01 14:51:22
【问题描述】:

我希望在 ICS 之前的设备 (2.3 - 2.1) 上使用 4.0+ 溢出菜单。我将 HoloEverywhere 与 ActionBarSherlock 一起使用。

我尝试了以下解决方案:

ActionBarSherlock & HoloEverywhere - Forcing Overflow?

但它不起作用,因为absForceOverflow 不存在。它是在最新版本中删除还是什么?我检查了 ABS 和 HE 库项目的 R 文件,但该字段根本不存在。

我的应用程序的主题设置为@style/Holo.Theme.Sherlock.Light,这是我试图继承的主题并将absForceOverflow 参数集添加到true

【问题讨论】:

    标签: android android-actionbar actionbarsherlock android-holo-everywhere


    【解决方案1】:

    从 ActionbarSherlock 4.2 开始,我们失去了管理溢出菜单可见性的能力。 要使其发挥作用,您需要结合 2 种方法:

    1. 要强制 Android 3.x (honeycomb) 及更高版本的菜单可见性,您需要使用 this 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) { 返回真; }

      这将强制显示菜单...

      • 但是当点击菜单按钮菜单弹出不显示。为了实现这一点,我们需要在您的 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,我想处理hardware menu key 的点击。
    • Fragment 没有密钥处理程序,因此您需要使用众所周知的方法将密钥事件从Activity 重新发送到您的Fragment。我建议您在Activity 中创建"CustomListener" 并在Fragment 中订阅它。然后在ActivityonKeyUp事件中触发:listener.onKeyUp(...)
    • +1 迄今为止最好的解决方案,尤其是比使用具有 ForceOverflow 主题的旧版本 ActionBarSherlock 更好。
    • @MarcoW.:我想你指的是我的回答。您没有也许注意到的是,我并不是建议 OP 使用旧版本(我的答案末尾有一个注释)。如果他选择使用旧版本,我只是给出了一个解决方案。反正各有各的。 ;-)
    • @IceMAN:没错,我确实注意到了这一点。我知道您不建议使用旧版本,但您的解决方案 is 适用于旧版本。我只是想表达,这里的这个解决方案,不需要你使用旧版本的 ABS,更具前瞻性,这是事实。
    【解决方案2】:

    作为Siddharth Lele has pointed out,它已在最新的 ABS 版本中被删除,以便与实际操作栏的行为相同。所以乍一看放弃显示这个菜单是最好的选择。

    但是,在我看来,溢出菜单在某些设备的屏幕上显示而对其他设备不显示是 Android 操作栏的一个重大设计缺陷。原因如下:

    在具有硬件菜单键的设备中,菜单不会显示在操作栏中。最近设备的趋势是将硬件按钮的数量减少到最低限度,因为它被认为对用户更友好(并且因为 iphone 只有一个按钮,所以他们复制了这种设计)。其他制造商确实包含菜单按钮,但它是隐藏的,除非你按下它(是的,当你不再需要灯时它会亮起。这不是一个明智的设计,但同样,当所有按钮都关闭时,手机看起来更 iphonish)。

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

    此时您可能会认为AB 都是不懂如何使用智能手机的傻瓜。但与桌面应用程序不同的是,绝大多数智能手机用户只知道他们设备的基础知识。他们以前的手机很可能是一个带有简单固件的键盘设备。电池没电了,他们去商店换了,但没货了。他们本可以在互联网上订购一个,但它比购买一部新手机要贵。因此,他们出售了一款支持触摸屏的手机,因为这就是如今的手机。现在他们必须面对一台配备成熟操作系统的小型计算机。更糟糕的是,这款手机只附带“快速入门指南”,要获得完整的手册,他们必须从互联网上下载 pdf。你猜怎么了?他们不会。

    如果您正在开发移动应用程序,您应该假设用户可能对计算机、操作系统或类似应用程序一无所知。您应该使 GUI 在不同设备上看起来相似,以便人们可以学习并记住如何使用它。不要责怪操作栏设计者:如果像AB 这样的用户首先不知道如何进入选项菜单这是你的错。这就是为什么您应该包含一种始终可见的选项屏幕的方法。

    后退键会发生类似的情况。有些设备可能有硬件返回键,较新的设备通常没有。但是每当我们可以返回我们的应用程序时,我们总是会在操作栏中显示那个类似箭头的按钮,对吧?是的,我知道一个按钮在“导航树”中导航,而另一个按钮“回到过去”,但这也是另一个设计缺陷:对于普通用户来说,返回只是返回。他在屏幕上有这个按钮,他也可以使用硬件按钮,但这是可选的。溢出菜单也应该这样做。

    因此,如果您(像我一样)认为这是一个重要的按钮,请不要放弃。 提供一个常规的主选项菜单并使用相关的子菜单填充它。 将其设为操作按钮并为其分配一个描述性图标,甚至是像“菜单”这样的文字描述。您还可以模仿溢出菜单图标,只需使用以下可绘制对象之一:

        // 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 的下一个实现是否会包含它们。

    【讨论】:

    • 有趣。您不仅在技术上回答,而且还提到了用户体验原则。我得到了启发,谢谢。
    • @Evi Song 我很惊讶有人花时间阅读这么大的答案。感谢您的阅读!
    • 您的评论应该足以将 .ForceOverflow 再次放入库中!感谢您的清晰观点,我完全同意。
    • @Toverbal 不客气。但我认为 Jake Wharton 删除该选项是正确的。 ABS 应该尽可能地模仿实际的操作栏。
    • 我同意,但我觉得当有硬件按钮时不添加溢出项很混乱,我有时真的很怀念弹出能力,经过搜索和思考后我突然意识到我有一个硬件弹出按钮:)
    【解决方案3】:

    如果您使用的是 4.2.0 版,那么 .ForceOverflow 主题实际上已被删除。

    来源: Version 4.2.0 Changelog

    变更日志摘录:

    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>
    

    在为manifest.xml 中的Activity 应用主题时,我将其用作属性:"@style/Theme.SociallyYOU"

    同样,如果您必须绝对强制溢出,您可能还想在此处的另一个问题中阅读 CommonsWare 对此的想法:https://stackoverflow.com/a/12872537/450534

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

    【讨论】:

    • 很好的答案。太感谢了!我想在 ICS 之前的设备上强制使用溢出菜单,因为我发现我的一些用户不知道您可以按 MENU 按钮和您的手机来访问某些选项。我将不得不使用旧版本或提出更好的解决方案。
    • @Paul:很高兴能帮上忙。但在决定之前请考虑对 ABS 所做的更改。
    • @Paul 我在下面的回答中对此进行了详细说明。如果您认为这很重要,您可以提供自己的替代菜单。
    【解决方案4】:

    我发现 Exterminator13 的答案不适用于某些类型的 Android 设备(我认为总数很小),但为了适应尽可能多的设备,我使用对话框并使用带有 android:showAsAction= 的菜单“always”作为溢出菜单按钮。 毕竟actionbarsherlock的溢出菜单效果是通过源码实现的,如果不是那么麻烦,何不自己实现呢。懂中文的也可以看my blog

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-04
      • 2013-02-14
      相关资源
      最近更新 更多