如何在NavigationView中的菜单组中添加项目

34

在一款 Android 单词游戏中,我目前有一个硬编码的菜单,从 left_drawer_menu.xml 中填充,并由 3 个组成(我的回合、对手的回合和其他内容):

应用程序截图

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

现在我正在尝试动态更改那个菜单。

我有一个包含所有游戏数据的SQLite实例,并使用IntentService读/写数据库 - 这部分正常工作。

我的当前困难是:使用以下代码,新项目添加在R.id.my_move组之外:

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

更新:

作为进一步的测试,我尝试使用以下代码将偶数项和奇数项分配给2个不同的组:

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);

但是结果看起来很混乱:

应用截图

此外,我发现了(可能已经修复的?)Issue 176300,不知道是不是应该使用子菜单而不是菜单组?


1
你能否发布一下你定义组的xml呢? - random
谢谢,这是链接:https://gist.github.com/afarber/03408cf4d1e3f267589f - Alexander Farber
1
请参考此链接,它可能会对您有所帮助:https://dev59.com/2ovda4cB1Zd3GeqPa40r - pRaNaY
3个回答

41

查看MenuItemImpl源代码后,发现

     ...
     *    @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) {

因此,在您的xml中应定义排序(给一组中的项目相同的顺序,并在每个后续组中递增)

<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>

在您的代码中添加项时,请给出适当的顺序值。因此,如果您想将该项添加到第一组的末尾,请将其添加为:

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

如果你想添加到第二个组,请这样做:

menu.add(R.id.his_move, Menu.NONE, 1, "Item2");
你的代码问题可能是XML中的所有项都具有默认的“orderInCategory”值0,因此新项将添加到所有这些项之后。 更新: 为了添加图标,请使用MenuItemsetIcon方法,具体方法请参见文档
menu.add(R.id.my_move, Menu.NONE, 0, "Item1").setIcon(R.drawable.ic_stars_black_24dp);

这个很好用,颜色也一样(当使用子菜单时,高亮颜色与原始菜单不同)。但我想知道,如何为动态添加的菜单条目设置图标? - Alexander Farber
1
使用 setIcon 方法来设置图标,答案已更新。 - random
5
这似乎有点愚蠢... 你指定了一个组来添加项目,但安卓系统却无视它,并将其倒入列表末尾,除非你为不在同一组中的项目指定顺序。谷歌到底是怎么回事?此外,对我来说,它仍然在组后面添加项目 :/ - NaturalBornCamper
@NaturalBornCamper 因为文档不够详细,我也不知道 'groupId' 在 "..menu.add(groupId: 1, itemId: 2,....... 中的含义。它是指添加并生成一个新的带有传递id的组,还是像你所说的将其添加到我们得到的菜单中具有此id的组?! 但经过一些测试后,我现在知道groupId指的是您即将创建和添加的组的ID,而不是您想要添加到其中的那个组的ID!为了实现这一点,您可以通过索引的方式指定组,从0开始,例如:...getMenu().getItem(index:0),然后 .add(groupId [新组ID] - Araz

20

enter image description here

我是这样解决的:

  1. 设置菜单:

<?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>
onNavigationItemSelected() 中,通过 order id(或通过 findItem())获取要展开的 MenuItem,然后从中获取 SubMenu 并向其中添加新项目:
@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;
}

我希望这有所帮助。


感谢你的出色回答。你知道怎么为子菜单项添加图标吗?此外,这些项目的高亮颜色似乎与其他不同... - Alexander Farber

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

您还将订单(第三个参数)分配为i。我猜这会覆盖groupId。尝试将其设置为NONE,如此处所述。

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

编辑: 也许可以像这样

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

Order是类别和排序的组合,所以可能不像这样直截了当。


不幸的是,Menu.NONE 无法解决问题,菜单项仍会添加到 NavigationView 菜单底部,而不是最上面的组。 - Alexander Farber
1
你可以尝试根据菜单中已有的项目计算所需订单并设置该订单。 - Roshan

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