Android 如何在菜单项中添加子菜单,addSubMenu() 方法在哪里?

47
我想在我的OptionsMenu中根据我的参数动态添加一个子菜单到一个menuItem中。我已经查看了Android SDK中的“MenuItem”,但是没有“addSubMenu()”方法,尽管可以找到“hasSubMenu()”和“getSubMenu()”方法。
我正在考虑在onCreateOptionsMenu中完成此操作:
public boolean onCreateOptionsMenu(Menu menu) {

    MenuItem mi = menu.getItem(MYITEMID);  // << this is defined in my XML optionsMenu
    SubMenu subm = mi.addSubMenu(0,1,0,"Map 1"); // no addSubMenu() method!!!???
....

如何在代码中创建菜单项内的子菜单?


我认为这个问题在stackoverflow上已经有答案了。希望这能帮到你!你应该查看这个链接:https://dev59.com/5FjUa4cB1Zd3GeqPUs7w - TheTime
抱歉,但这不是我想要的。我已经使用 XML 定义了一个选项菜单,我希望通过编程方式向该选项菜单的 MenuItem 添加一个子菜单。 - ruhalde
8个回答

75
有时候,安卓的诡异现象真的很惊人(也很有趣...)。我是用以下方法解决的:
a)在XML中定义一个子菜单占位符,像这样:
```html
a) 在XML中定义一个如下所示的子菜单占位符: ```
<item android:visible="true" android:id="@+id/m_area"
   android:titleCondensed="Areas"
   android:title="Areas"
   android:icon="@drawable/restaur"
   android:enabled="true"> 
   <menu>
    <item android:id="@+id/item1" android:title="Placeholder"></item>
   </menu>
</item>

b) 在 OnCreateOptionsMenu 中获取子菜单项,清除它并添加我的子菜单项,像这样:

    public boolean onCreateOptionsMenu(Menu menu) { 
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.mapoptions, menu);

            int idx=0;
            SubMenu subm = menu.getItem(MYITEM_INDEX).getSubMenu(); // get my MenuItem with placeholder submenu
            subm.clear(); // delete place holder

            while(true)
            {
                anarea = m_areas.GetArea(idx); // get a new area, return null if no more areas
                if(anarea == null)
                    break;
                subm.add(0, SUBAREASID+idx, idx, anarea.GetName()); // id is idx+ my constant
                ++idx;
            }
}

2
我们能以编程方式打开这个下拉菜单吗? - Rajiv yadav
3
抱歉,但是我觉得变量名称对我来说很难理解。"anarea" 是什么?它的类型是什么?m_areas 的类型是什么,这样我才能查找 GetArea() 方法。如果您能展示这些变量的声明,那会很有帮助 :) - Neon Warge

34

我知道这是一个老问题,但我最近也遇到了这个问题。 最直接的方法似乎是将该项本身指定为子菜单,然后添加到此项中。 例如:

menu.add(groupId, MENU_VIEW, Menu.NONE, getText(R.string.menu_view));
menu.add(groupId, MENU_EDIT, Menu.NONE, getText(R.string.menu_edit));
SubMenu sub=menu.addSubMenu(groupId, MENU_SORT, Menu.NONE, getText(R.string.menu_sort));
sub.add(groupId, MENU_SORT_BY_NAME, Menu.NONE, getText(R.string.menu_sort_by_name));
sub.add(groupId, MENU_SORT_BY_ADDRESS, Menu.NONE, getText(R.string.menu_sort_by_address));
:
:

1
+1:没错,好主意。不知道为什么我是第一个点赞的人。 - Jim G.
3
如果您以编程方式添加子菜单,它将始终将父菜单显式地添加到溢出菜单中,忽略任何始终在操作栏上显示它的标志。这是有问题的,因为要进入子菜单,需要额外点击打开溢出菜单。您可以尝试跳过上面代码的前两行来验证我所说的。 - Mark Lapasa
5
@MarkLapasa,你可以使用 sub.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); 来解决这个问题。 - Jared Rummler
groupId是从哪里来的?我尝试在我的xml中创建一个组并设置一个id,但我无法检索它。它是一个随机唯一的整数吗? - NaturalBornCamper
对于简短且唯一的非 XML 回答,干得好。 - Humpity

26

以下是一份完整的答案,它基于使用占位符的想法,但主要使用xml添加子菜单。

如果您有一个名为main_menu.xml的菜单:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="My Menu"
    android:id="@+id/my_menu_item">
    <!-- A empty SubMenu -->
    <menu></menu>
</item>
</menu>

创建另一个菜单sub_menu.xml,该菜单将用于my_menu_item:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:title="SubMenu One"
    android:id="@+id/submenu_one" />
  <item android:title="SubMenu Two"
    android:id="@+id/submenu_two" />
  <item android:title="SubMenu Three"
    android:id="@+id/submenu_three" />
</menu>

在你的onCreateOptionsMenu方法中:

public boolean onCreateOptionsMenu(Menu menu) {
   // Inflate your main_menu into the menu
   getMenuInflater().inflate(R.menu.main_menu, menu);

   // Find the menuItem to add your SubMenu
   MenuItem myMenuItem = menu.findItem(R.id.my_menu_item);

   // Inflating the sub_menu menu this way, will add its menu items 
   // to the empty SubMenu you created in the xml
   getMenuInflater().inflate(R.menu.sub_menu, myMenuItem.getSubMenu());

}

这个解决方案很好,因为充气器处理了大部分工作。


19

最好的方法是在您的xml菜单文件中完成。您可以通过在item内创建一个新的menu对象来实现此目的:

<menu>
  <item>
    ...
    <menu>
      ...
    </menu>
    ...
  </item>
</menu>

1
那么程序上怎么做呢?这是我的问题,我已经定义了一个XML选项菜单,我想通过代码向MenuItem添加子菜单。 - ruhalde
抱歉,但是似乎无法这样做,会出现java.lang.ClassCastException。 - ruhalde
1
似乎没有编程的方法可以做到那一点。如果你检查“add”方法及其重载对,每个都有一个Title属性,表明将始终创建一个新的ItemMenu。我猜我必须走其他路径,也许不定义我的菜单项在XML中,并在addSubmenu()内以编程方式创建它。 - ruhalde
2
好的,我通过在我的MenuItem中放置一个占位符子菜单来解决了这个问题,然后使用getItem().getSubMenu()程序化地获取该子菜单,清除它并使用add()添加我的子菜单项。等一下我会发布代码,但是这个系统只有在我提问8个小时之后才允许我这样做,顺便说一句,这真的是一个很糟糕的功能,如果我的回复不会影响我的声誉而不是不能发布我的答案,那将更好,这真的很荒谬。 - ruhalde
@Phil:是的,这就是我做的方式,但我无法更改子菜单样式。有什么线索可以做到这一点吗? - Luis A. Florit

0
你应该考虑使用ActionProvider。
public class MyActionProvider extends ActionProvider {

    private Context mContext;

    public MyActionProvider(Context context) {
        super(context);

        mContext = context;
    }

    @Override
    public View onCreateActionView() {
        //LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        return null;
    }

    @Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        super.onPrepareSubMenu(subMenu);

        subMenu.clear();

        subMenu.add("menu 1");
        subMenu.add("menu 2");
        subMenu.add("menu 3");
    }

    @Override
    public boolean hasSubMenu() {
        return true;
    }

    @Override
    public boolean onPerformDefaultAction() {
        return super.onPerformDefaultAction();
    }
}

0
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu1" android:alphabeticShortcut="a"
    android:title="Menu No. 1" android:orderInCategory="1" />
<item android:id="@+id/menu2" android:alphabeticShortcut="b"
    android:title="Menu No. 2" android:orderInCategory="2">
    <menu >
    <group android:id="@+id/group2" android:checkableBehavior="single">
        <item android:id="@+id/submenu1" android:title="SubMenu No. 1" />
        <item android:id="@+id/submenu2" android:title="SubMenu No. 2" />
    </group>   
    </menu>
</item>


0
我会在XML文件中创建子菜单,在运行时从菜单对象中获取子菜单(使用findItem(id)方法),并使用submenu.setVisible(boolean)在运行时添加/删除它。

0
为了全面展示Phil的答案,这里是我完整、可用的XML代码,用于创建一个包含两个选项的菜单,每个选项都是一个包含三个选项的子菜单。我打算在顶层再添加一个第三个菜单...
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:HTMLCode="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/Examine"
        android:title="@string/Examine"
        HTMLCode:showAsAction="always">

        <menu xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:HTMLCode="http://schemas.android.com/apk/res-auto" >
            <item android:id="@+id/load"
                android:title="@string/load"
                HTMLCode:showAsAction="ifRoom|withText" />

            <item android:id="@+id/findfirst"
                android:title="@string/findfirst"
                HTMLCode:showAsAction="ifRoom|withText" />

            <item android:id="@+id/findnext"
                android:title="@string/FindNext"
                HTMLCode:showAsAction="ifRoom|withText" />
        </menu>
    </item>

    <item android:id="@+id/Redirect"
        android:title="@string/Redirect"
        HTMLCode:showAsAction="ifRoom|withText">

        <menu xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:HTMLCode="http://schemas.android.com/apk/res-auto" >
            <item android:id="@+id/getRedirect"
                android:title="@string/getRedirect"
                HTMLCode:showAsAction="ifRoom|withText" />

            <item android:id="@+id/toggleRedirect"
                android:title="@string/toggleRedirect"
                HTMLCode:showAsAction="ifRoom|withText" />

            <item android:id="@+id/copyRedirect"
                android:title="@string/copyRedirect"
                HTMLCode:showAsAction="ifRoom|withText" />
        </menu>
    </item>
</menu>

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