45

I want to add a submenu inside my OptionsMenu to a menuItem, programatically according to my parameters. I've checked "MenuItem" in android sdk and there is no addSubMenu() method!, although you can find "hasSubMenu()" and "getSubMenu".

Was thinking on doing this in 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!!!???
....

How do I create a submenu inside a menuitem in code?

ruhalde
  • 3,321
  • 3
  • 20
  • 28
  • I think this question has already an answer, here at stackoverflow. Hope this help ! You should check this : [http://stackoverflow.com/questions/6543745/create-sub-menu-in-android-through-code](http://stackoverflow.com/questions/6543745/create-sub-menu-in-android-through-code) – TheTime Aug 12 '11 at 17:52
  • Sorry but is not this what I want. I have already defined an options menu with XML, I want to add a submenu to a MenuItem of that optionMenu, programatically by code. – ruhalde Aug 12 '11 at 18:01

8 Answers8

72

Sometimes Android weirdness is really amazing (and amusing..). I solved it this way:

a) Define in XML a submenu placeholder like this:

<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) Get sub menu item in OnCreateOptionsMenu, clear it and add my submenu items, like this:

    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;
            }
}
Abhinav Chauhan
  • 985
  • 1
  • 4
  • 20
ruhalde
  • 3,321
  • 3
  • 20
  • 28
  • 2
    can we open this menu dropdown pragmatically ? – Rajiv yadav Feb 28 '14 at 09:40
  • 2
    I am sorry, but I really find the variables name obfuscating for me. What is anarea? What type is that? What is is the type of m_areas so I can lookup GetArea() method. It would help if you can show us the declarations of those variables :) – Neon Warge Sep 26 '15 at 08:40
30

I know this is an old question, but I just came across this problem myself. The most straightforward way of doing this, seems to be to simply specify the item itself as a submenu, then add to this item. E.g.:

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));
:
:
Einar H.
  • 535
  • 5
  • 5
  • +1: Yep. Good call. Not sure why I'm the first one to upvote this. – Jim G. Apr 04 '14 at 03:11
  • 3
    I find as soon you add a sub menu programatically, it will always add the parent menu into the overflow menu explicitly ignoring any flags to always show it on the action bar. This is problematic because to get to a sub menu, it adds the additional tap of opening the overflow menu. You can try what I am talking about by skipping the first to lines of your code above. – Mark Lapasa May 28 '14 at 16:02
  • 5
    @MarkLapasa, you can do `sub.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);` to fix that. – Jared Rummler Feb 28 '15 at 23:47
  • Where does groupId come from? I tried to create a group in my xml and setting an id but I can't retrieve it. Is is a random unique int? – NaturalBornCamper Dec 25 '17 at 20:13
22

Here's a complete answer which builds on the idea of using a placeholder but uses mostly xml to add the submenu.

If you have a menu like so called 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>

Create another menu sub_menu.xml which will be used in 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>

In your 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());

}

This solution is nice since the inflater handles most of the work.

Robert
  • 1,012
  • 9
  • 13
20

The best way to do this is in your xml menu file. You can do this by creating a new menu object inside of an item:

<menu>
  <item>
    ...
    <menu>
      ...
    </menu>
    ...
  </item>
</menu>
Phil
  • 34,061
  • 21
  • 117
  • 154
  • And programatically? that was my question, I have a XML options menu already defined, I want to add a submenu to a MenuItem by code. – ruhalde Aug 12 '11 at 18:03
  • Sorry but yes --> java.lang.ClassCastException, can't do that aparently – ruhalde Aug 12 '11 at 18:23
  • 1
    It seems there is no way of doing that programatically. If you check "add" methods and its overloaded pairs, everyone has a Title property indicating that a new ItemMenu would always be created. I've guess I have to go other paths, maybe not defining my menuitem in XML and creating it programatically inside addSubmenu(). – ruhalde Aug 12 '11 at 18:33
  • 2
    Ok, I solved by putting a placeholder submenu in my MenuItem, getting that submenu programatically with getItem().getSubMenu(), clearing it and adding my submenus items with add(). I'll post code later when this system let me, I can't do it only after 8 hours of my question, btw thats really a lame feature, would be better if my own reply doesn't add to my reputation instead of not able to post my answer, its really ludicrous.. – ruhalde Aug 12 '11 at 19:13
  • @Phil: yes, this is the way I do it, but I cannot change the submenu style. Any clue on how to do that? – Luis A. Florit Aug 14 '13 at 17:29
0

To provide a comprehensive example of Phil's answer, here is my complete, working XML for a menu with two choices, each of which is a menu with three choices. I intend to add a third menu to the top level ...

<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>
Mick
  • 567
  • 6
  • 16
0

You should consider use a ActionProvider instead.

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();
    }
}
Robin
  • 3,123
  • 1
  • 18
  • 25
Sibelius Seraphini
  • 4,023
  • 7
  • 30
  • 52
0

I would just create the submenu in xml file, and in run time get the submenu from menu object, (using findItem(id) method) and use submenu.setVisible(boolean) to add/remove it on run time.

sorry_I_wont
  • 489
  • 6
  • 16
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>

Paul Roub
  • 35,100
  • 27
  • 72
  • 83
Android Enthusiast
  • 4,447
  • 2
  • 11
  • 27