如何在NavigationView中为所有组设置共享的可选行为?

32

我创建了两个带有唯一ID的组(我需要一个分隔符),它们都设置了checkableBehaviorsingle。这允许从不同组中同时选中多个项目,而这正是我想避免的。我希望在所有组中最多只能选中一个项目。

由于我没有找到在XML中实现这一点的方法,因此我尝试在onNavigationItemSelected中实施简单的逻辑来取消选中先前的菜单项:

if (previousItem != null)
   previousItem.setChecked(false);
currentItem.setChecked(true);

但是setChecked(false) 方法从未对我起作用 - 项目仍然保持选中状态。

这是我的示例代码:

menu_navigation.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
        android:id="@+id/nav_group_1"
        android:checkableBehavior="single">
    <item
            android:id="@+id/nav_feed"
            android:title="@string/feed"/>
    <item
            android:id="@+id/nav_people"
            android:title="@string/people"/>
</group>
<group
        android:id="@+id/nav_group_2"
        android:checkableBehavior="single">
    <item
            android:id="@+id/nav_settings"
            android:title="@string/settings"/>
    <item
            android:id="@+id/nav_help_feedback"
            android:title="@string/help_feedback"/>
    <item
            android:id="@+id/nav_logout"
            android:title="@string/logout"/>
</group>

NavigationItemSelectedListener:

 mUiNavigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        if (previousItem != null)
                           previousItem.setChecked(false);
                        currentItem.setChecked(true);
                        //...
                        changeCurrentFragment(...);
                        return true;
                    }
                });

我需要提示!谢谢。


1
如果组ID对您不重要,只需将其删除即可。然后它将为不同的组提供可共享的检查,并且您不必遵循我的答案。但问题是,当您删除组ID时,它也会删除组分隔符。 - Moinkhan
@Moinkhan 是的,我需要那个除数。已经编辑了我的问题以反映这一点。 - EyesClear
我给了你两个选择,要么通过删除组ID来进行操作,要么使用我提供的答案。因此,对于分隔符,请提供组ID并将我的答案应用到代码中。 - Moinkhan
我已经复制了你的 XML 并使用我的答案代码在我的端上运行了一遍。所以直接使用它就可以了,它会完美地工作。 - Moinkhan
你觉得加一个额外的父级group,并且使用android:checkableBehavior="single"属性来包含所有的组怎么样?就像这个 - AaA
6个回答

39

这是解决方案。

步骤 1:删除

android:checkableBehavior="single"

来自两个群体。

步骤2:将以下逻辑添加到监听器中:

mUiNavigationView.setNavigationItemSelectedListener(
            new NavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(MenuItem menuItem) {
                    menuItem.setCheckable(true);
                    menuItem.setChecked(true);
                    if (mPreviousMenuItem != null) {
                        mPreviousMenuItem.setChecked(false);
                    }
                    mPreviousMenuItem = menuItem;
                    //...
                    changeCurrentFragment(...);
                    return true;
                }
            });

注意:你可以在XML中为每个项目设置 android:checkable="true",而不是调用 menuItem.setCheckable(true)

@Moinkhan 的解决方案也应该可行(谢谢,已点赞),但我不想每次选择新位置时都循环遍历菜单的项目。


之前我无法让它工作。我漏掉了setCheckable(true)。现在它可以工作了,谢谢! - hungryghost
1
mPreviousMenuItemsetDrawerMenuItem(...);是什么? - Anggrayudi H
@AnggrayudiH MenuItem mPreviousMenuItem只是我的Activity实例的一个成员。setDrawerMenuItem()方法用于根据所选菜单位置交换当前片段。 - EyesClear
2
逻辑应该是 if (mPreviousMenuItem != null && mPreviousMenuItem != menuItem) { ... },否则连续两次选择同一个菜单项会取消选中它。我认为大多数人不希望出现这种行为。 - Jarrod Smith
@EyesClear。谢谢,这对我很有帮助。 - Vision Coderz
显示剩余2条评论

12
实际上,您不需要为分隔符创建单独的并行组。您可以将所有内容放在一个组中,并在其中放置子菜单(具有标题)或子组(没有标题)。这些子菜单和子组将具有分隔符。这样,可选行为就可以正常工作,无需解决问题。 (提示:这是关于Design Support Lib 23.1.1的)

Menu with dividers

<group
    android:id="@+id/drawer_group"
    android:checkableBehavior="single">
    <item
        android:id="@+id/nav_1"
        android:title="Menu1" />
    <item
        android:id="@+id/nav_2"
        android:title="Menu2" />
    <item
        android:id="@+id/nav_3"
        android:title="Menu3" />
    <item
        android:id="@+id/nav_4"
        android:title="Menu4" />

    <item
        android:id="@+id/drawer_submenu"
        android:title="Subheader">
        <menu>
            <group android:checkableBehavior="single">
                <item
                    android:id="@+id/nav_sub1_1"
                    android:title="Menu_Sub1_1" />
                <item
                    android:id="@+id/nav_sub1_2"
                    android:title="Menu_Sub1_2" />
            </group>
        </menu>
    </item>

    <group android:id="@+id/drawer_subgroup">
        <item
            android:id="@+id/nav_subgroup_item"
            android:title="Menu_Sub2_2" />
    </group>
</group>

在代码中,我只需执行以下操作,单个检查即可正常工作:
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    mNavigationView.setCheckedItem(item.getItemId());
    ...
}

我认为这会为群组创建标题,不是每个人都想要的。我在我的问题中也已经表明了这一点。 - Longi
正如您在第二个子组中所看到的那样... 没有标题,只有一个分隔符。 - Till - Appviewer.io
即使没有Java代码,这对我也起作用。请记住,如果您以编程方式添加菜单项,则需要为每个项目设置“setCheckable(true)”。此外,您不需要嵌套的group标记;_group>item>menu>icon_可以正常工作。 - Quoting Eddie

7
无需删除checkableBehavior="single",只需使用:
navigationView.setCheckedItem(item.getItemId());

而不是使用item.setChecked(true)


2
我一直在折腾这个烂摊子,花了太长时间,但最终你的解决方案起作用了。我无法表达我的感激之情!不知道为什么我尝试的其他方法都没有奏效,但很高兴终于有了解决方案。真的,谢谢你!!我快要疯了... - Jester Jeffrey

4
好的,我找到了解决方法... 只需将此片段代码添加到onNavigationItemSelected方法中。
@Override
    public boolean onNavigationItemSelected(MenuItem menuItem) {
    Menu m = navView.getMenu();
    for (int i=0;i<m.size();i++) {
        MenuItem mi = m.getItem(i);
        if (!(mi.getItemId() == menuItem.getItemId())) {
            mi.setCheckable(false);
        }
    }
    menuItem.setCheckable(true);
    menuItem.setChecked(true);
    return false;
}

就是这样。。而且 setChecked(false) 无法正常工作。因为在菜单列表中,必须有一个单一的项目必须被选中。所以只有当您选择另一个项目时,才能取消选择它..如果您点击选定的项目,则导航视图将永远不会将其取消选择。正如我所解释的情况。


如果您需要在菜单项上设置一个检查器,那么这个方法是有效的,但是在更改时您必须自行将其移除。 - Jan Bergström

3
NavigationView实现中存在一种无效的bug,但是你不需要使用setCheckable(),只需在调用setChecked(false)后使NavigationView失效即可,如下所示:
    previousItem.setChecked(false);
    navigationView.invalidate();

然而,菜单组不能有checkableBehavior="single",因为在独占模式下,在组中调用setChecked(boolean)会将该项设置为已选中,无论您传递true还是false。因此,为使setChecked(false)起作用,必须从菜单定义中删除checkableBehavior="single"
我在这里创建了一个问题:https://code.google.com/p/android/issues/detail?id=178709

1
这是我在这里发布的一个简单而甜美的解决方案 :)
分组确实会在之间创建一个分隔符,但会在导航抽屉中创建两个已选项目的问题。
我处理这个问题的简单方法是:
 public boolean onNavigationItemSelected(final MenuItem menuItem) {

//if an item from extras group is clicked,refresh NAV_ITEMS_MAIN to remove previously checked item
if (menuItem.getGroupId() == NAV_ITEMS_EXTRA) {


    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, false, true);
    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, true, true);
   }else{

    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, true, true);
    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, false, true);


}
//Update highlighted item in the navigation menu
menuItem.setChecked(true);

}


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