How to add an item to a menu group in NavigationView

31,596

Solution 1

On checking MenuItemImpl source code

     ...
     *    @param group Item ordering grouping control. The item will be added after
     *            all other items whose order is <= this number, and before any
     *            that are larger than it. This can also be used to define
     *            groups of items for batch state changes. Normally use 0.
     ...

    MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
        CharSequence title, int showAsAction) {

So you should define ordering in your xml (give same order to items in one group and increment in each following group)

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:id="@+id/my_move" android:checkableBehavior="single">
        <item
            android:orderInCategory="0"
            android:id="@+id/game1"
            android:icon="@drawable/ic_stars_black_24dp"
            android:title="Game #1" />
        <item
            android:orderInCategory="0"
            android:id="@+id/game2"
            android:icon="@drawable/ic_stars_black_24dp"
            android:title="Game #2" />
    </group>
    <group android:id="@+id/his_move" android:checkableBehavior="single">
        <item
            android:orderInCategory="1"
            android:id="@+id/game5"
            android:icon="@drawable/ic_clock_black_24dp"
            android:title="Game #5" />
        <item
            android:orderInCategory="1"
            android:id="@+id/game6"
            android:icon="@drawable/ic_clock_black_24dp"
            android:title="Game #6" />
        <item
            android:orderInCategory="1"
            android:id="@+id/game7"
            android:icon="@drawable/ic_clock_black_24dp"
            android:title="Game #7" />
    </group>
    .....

</menu>

and give an appropriate order value while adding the item in your code. So if you want to add the item at the end of first group, add it as:

menu.add(R.id.my_move, Menu.NONE, 0, "Item1");

and if you want to add to second group, add it as:

menu.add(R.id.his_move, Menu.NONE, 1, "Item2");

The problem with your code could be that all items in the xml have default orderInCategory 0 and so the new item gets added after all these items.

UPDATE

To add icon use setIcon method for MenuItem

menu.add(R.id.my_move, Menu.NONE, 0, "Item1").setIcon(R.drawable.ic_stars_black_24dp);

Solution 2

enter image description here

I've solved it this way:

  1. Set up the menu:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:title="my moves"
              android:id="@+id/submenu_1">
            <menu>
                <item
                    android:id="@+id/my_dummy_item_1"
                    android:icon="@drawable/ic_menu_camera"
                    android:title="Import" />
                <item
                    android:id="@+id/my_dummy_item_2"
                    android:icon="@drawable/ic_menu_gallery"
                    android:title="Gallery" />
                <item
                    android:id="@+id/add_item"
                    android:icon="@drawable/ic_menu_manage"
                    android:title="Add Item" />
            </menu>
        </item>
        <item android:title="opponent's moves"
              android:id="@+id/submenu_2">
            <menu>
                <item
                    android:id="@+id/opponent_dummy_item_1"
                    android:icon="@drawable/ic_menu_camera"
                    android:title="Import" />
                <item
                    android:id="@+id/opponent_dummy_item_2"
                    android:icon="@drawable/ic_menu_gallery"
                    android:title="Gallery" />
                <item
                    android:id="@+id/opponent_dummy_item_3"
                    android:icon="@drawable/ic_menu_manage"
                    android:title="Tools" />
            </menu>
        </item>
    </menu>
    
  2. In onNavigationItemSelected(), get MenuItem you want to expand by order id (or via findItem()), then get SubMenu from it and add new item into it:

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        int id = item.getItemId();
    
        if (id == R.id.add_item) {
            Random r = new Random();
            int i = r.nextInt(100);
            MenuItem myMoveGroupItem = navigationView.getMenu().getItem(0);
            // MenuItem myMoveGroupItem = navigationView.getMenu().findItem(R.id.submenu_1);  -- it also works!
            SubMenu subMenu = myMoveGroupItem.getSubMenu();
            subMenu.add("Item "+i);
        }
    
        return true;
    }
    

I hope, it helps

Solution 3

menu.add(R.id.my_move, i, i, "Item " + i);

You are also assigning the order (3rd param) as i. I am guessing that this is overriding the groupId. Try setting it as NONE as mentioned here

menu.add(R.id.my_move, i, NONE, "Item " + i);

Edit: Maybe something like this

MenuItem lastItem = menu.findItem(R.id.<lastItemId>);
int lastOrder= lastItem.getOrder();
menu.add(R.id.my_move, i, lastOrder-5, "Item " + i);

Order is a combination of category and order, so it might not be as straight forward as this.

Share:
31,596
Alexander Farber
Author by

Alexander Farber

/me/likes: Java, С#, Perl, PHP, JavaScript, PostgreSQL, Linux, Azure /me/speaks: German, English, Russian /me/learns: https://github.com/afarber/android-questions https://github.com/afarber/unity-questions https://github.com/afarber/ios-questions

Updated on January 01, 2020

Comments

  • Alexander Farber
    Alexander Farber over 4 years

    In a word game for Android I currently have a hardcoded menu inflated from left_drawer_menu.xml and consisting of 3 groups (my turn, opponent turn and finally other stuff):

    app screenshot

    mLeftDrawer = (NavigationView) findViewById(R.id.left_drawer);
    mLeftDrawer.setNavigationItemSelectedListener(
            new NavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(final MenuItem menuItem) {
                    Menu menu = mLeftDrawer.getMenu();
    
                    if (menuItem.getGroupId() == R.id.my_move) {
                        menu.setGroupCheckable(R.id.my_move, true, true);
                        menu.setGroupCheckable(R.id.his_move, false, false);
                        menu.setGroupCheckable(R.id.extras, false, false);
                    } else if (menuItem.getGroupId() == R.id.his_move) {
                        menu.setGroupCheckable(R.id.my_move, false, false);
                        menu.setGroupCheckable(R.id.his_move, true, true);
                        menu.setGroupCheckable(R.id.extras, false, false);
                    } else if (menuItem.getGroupId() == R.id.extras) {
                        menu.setGroupCheckable(R.id.my_move, false, false);
                        menu.setGroupCheckable(R.id.his_move, false, false);
                        menu.setGroupCheckable(R.id.extras, true, true);
                    }
    
                    menuItem.setChecked(true);
                    mLeftItem = menuItem.getItemId();
                    mDrawerLayout.closeDrawer(mLeftDrawer);
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if (mLeftItem == R.id.start) {
                                startNewGame();
                            } 
                        }
                    },DRAWER_CLOSE_DELAY);
    
                    return true;
                }
            });
    

    Now I am trying to change that menu dynamically.

    I have SQLite instance containing all game data and use IntentService to read/write the database - that part works fine.

    My current difficulty is: with the following code, the new items are added outside the R.id.my_move group:

    if (mLeftItem == R.id.start) {
        startNewGame();
    
        Random r = new Random();
        int i = r.nextInt(100);
        menu.add(R.id.my_move, i, i, "Item " + i);   // why is my_move ignored?
    } 
    

    app screenshot

    UPDATE:

    As a further test I have tried assigning even and not even items to 2 separate groups with this code:

    Random r = new Random();
    int i = r.nextInt(100);
    int group = 1 + (i % 2); // can be 1 or 2
    menu.add(group, i, i, "Item " + i);
    

    However the result looks chaotic:

    app screenshot

    Also I have discovered the (probably already fixed?) Issue 176300 and wonder if maybe sub-menus should be better used instead of menu groups?