32

I've created two groups with unique ids (I need a divider) and they both have checkableBehavior set to single. This allows multiple items from different groups to be checked at once, and that's exactly what I'm trying to avoid. I'd like to have one item checked at maximum, across all groups.

Since I haven't found any way to do this in XML, I tried to implement a simple logic in onNavigationItemSelected to uncheck the previous menu item:

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

but setChecked(false) method has never worked for me - the item stays checked.

Here's my sample code:

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

I need a hint! Thanks.

EyesClear
  • 28,077
  • 7
  • 32
  • 43
  • 1
    If group id is not important for you just remove it. Then It will provide sharable check for different group and you don't have to follow my abswer. but the problem is when you remove group id it will also remove group devider. – Moinkhan Jun 11 '15 at 06:16
  • @Moinkhan Yeah, I need the divider. Edited my question to reflect that. – EyesClear Jun 11 '15 at 13:34
  • I have give you alternative either go by removing the group id. or the answer i provide. So for devider give group id and apply my answer to code – Moinkhan Jun 11 '15 at 13:50
  • I have copy your xml and run it to my side with my answer's code..So just go with it . it will work perfectly.. – Moinkhan Jun 11 '15 at 13:52
  • How about adding an extra parent `group` with `android:checkableBehavior="single"` containing all your groups? something like [this](https://stackoverflow.com/a/45558961/1041046) – AaA Aug 08 '17 at 04:01

6 Answers6

39

Here's the solution.

Step 1: Remove

android:checkableBehavior="single"

from both groups.

Step 2: Add the following logic to the listener:

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

Note: instead of calling menuItem.setCheckable(true) you can set android:checkable="true" for each item in XML.

@Moinkhan's solution should work as well (thanks, upvoted), but I didn't want to loop through menu's items each time a new position is selected.

EyesClear
  • 28,077
  • 7
  • 32
  • 43
12

Actually, you don't need separate parallel groups for separators. You can have everything in one group and place submenus (have titles) or subgroups (no title) in it. There will be separators for those submenus and subgroups. This way the checkable behavior works without a workaround. (Fyi: This is on 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>

In code, I simply do the following and single checking works as expected:

@Override
public boolean onNavigationItemSelected(MenuItem item) {
    mNavigationView.setCheckedItem(item.getItemId());
    ...
}
Till - Appviewer.io
  • 4,529
  • 1
  • 31
  • 35
  • I believe this creates titles for the groups, which not everybody wants. I also stated this in my question. – Longi Nov 23 '15 at 18:19
  • As you can see in the second sub group.. there is no title, just a divider – Till - Appviewer.io Nov 23 '15 at 23:51
  • Works for me even without the Java-Code. Keep in mind that if you add MenuItems programatically, you need to set `setCheckable(true)` for each item. Also, you don't need the nested `group` tag; _group>item>menu>icon_ works fine. – Quoting Eddie Dec 29 '16 at 19:17
7

No need to remove checkableBehavior="single", just use

navigationView.setCheckedItem(item.getItemId());

instead of item.setChecked(true).

Jahid
  • 21,542
  • 10
  • 90
  • 108
  • 2
    I was messing with this bullcrap for far too long and, at last, your solution worked. I can't say thanks enough! Don't know why everything else I tried didn't work, but glad to at last have a solution. Seriously, thank you!! Was going mad... – Jester Jeffrey Mar 22 '16 at 04:17
4

Ok I found the solution .. just add this snippet code to onNavigationItemSelected method

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

that's it .. And yes setChecked(false) not working. Just because from list of menu there should be a one single item must be cheked. So you can only uncheck it when you select another item .. And if you click on selected item, the navigationview will never unchecked it. Just because the scenario i have explaind.

Moinkhan
  • 12,732
  • 5
  • 48
  • 65
3

There appears to be an invalidation bug in the NavigationView implementation, but you don't need to use setCheckable(), you just need to invalidate the NavigationView after calling setChecked(false), like so:

    previousItem.setChecked(false);
    navigationView.invalidate();

However the menu group must not have checkableBehavior="single", because in exclusive mode calling setChecked(boolean) on a MenuItem in the group will set that item as checked whether you pass true or false. So in order for setChecked(false) to work, you must remove checkableBehavior="single" from your menu definitions.

I created an issue about this here: https://code.google.com/p/android/issues/detail?id=178709

Lorne Laliberte
  • 6,261
  • 2
  • 35
  • 36
1

Here's a simple and sweet solution i posted here:)

Making different groups does make a divider in between But creates the problem of two checked items in the nav drawer.

A simple way how I handled this was:

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

}

Community
  • 1
  • 1
Zorawar Sachdev
  • 334
  • 1
  • 4