57

My app's main activity has a navigation drawer, instantiated in the XML in this way:

<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/drawer_header"
    app:menu="@menu/application_drawer"
    android:background="@color/white"/>

Now, the menu entry for the navigation drawer is the following:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
    <item
        android:id="@+id/login"
        android:icon="@drawable/ic_action_person"
        android:title="@string/login"/>
    <item
        android:id="@+id/settings"
        android:icon="@drawable/ic_action_settings"
        android:title="@string/settings"/>
    <item
        android:id="@+id/terms"
        android:icon="@drawable/ic_action_about"
        android:title="@string/terms_and_conditions_menu"/>
    <item
        android:id="@+id/about"
        android:icon="@drawable/ic_action_about"
        android:title="@string/info_hotelsclick"/>
</group>

What I'd like to do is to change the first item (and possibly the others as well) dynamically under some conditions. For instance, I'd like to change the "Login" entry with a "logout" one once the user has logged in ;-)

How can I achieve that? I managed to add an item to the Drawer in this way

    Menu menu = navigationView.getMenu();
    menu.add("Test");

but it doesn't sound that good to me, I'm pretty sure there must be a cleaner way.

...but does it?

Marco Zanetti
  • 4,051
  • 6
  • 32
  • 48

4 Answers4

109

I thing the best approach to this is to include all your items in the menu and the change their visibility.

<item
    android:id="@+id/login"
    android:icon="@drawable/ic_action_person"
    android:title="@string/login"
    android:visible="true" />

<item
    android:id="@+id/logout"
    android:icon="@drawable/ic_action_person"
    android:title="@string/logout"
    android:visible="false" />

then

navigationView.getMenu().findItem(R.id.login).setVisible(false);
navigationView.getMenu().findItem(R.id.logout).setVisible(true);

You can also do this with whole groups of items

<group
    android:id="@+id/group_1"
    android:checkableBehavior="single"
    android:visible="false">
    ...
</group>

and

navigationView.getMenu().setGroupVisible(R.id.group_1, true)
Lamorak
  • 10,957
  • 9
  • 43
  • 57
  • 3
    it works but the space remails as such, any idea as how to remove the space? – Bala Vishnu Dec 16 '15 at 11:57
  • 1
    Great answer! I think there is few practical information about NavigationView. – hata Jan 10 '16 at 13:36
  • 6
    Use navigationView.getMenu().findItem(R.id.login).setVisibility(View.GONE) and setVisibility(View.VISIBLE) instead so the invisible UI element doesnt take the space. – Joakim Sandqvist Feb 24 '16 at 12:19
  • 4
    What @JoakimSandqvist wrote is incorrect. You cannot call `setVisibility` because findItem doesn't return a view – 11m0 Aug 07 '18 at 15:54
  • Why remove group or clear does not works in dynamic updating mode. I can not dynamically remove items and add new items. My fragment should re create to show. – Mahdi Jan 09 '20 at 05:52
62

Simple solution:
Add two xml files in menu directory:

  1. navigation_with_login.xml Navigation Menu for logged in users

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        <group android:checkableBehavior="single">
            <item
                android:id="@+id/nav_camera"
                android:icon="@drawable/ic_menu_camera"
                android:title="Import" />
            <item
                android:id="@+id/nav_gallery"
                android:icon="@drawable/ic_menu_gallery"
                android:title="Gallery" />
            <item
                android:id="@+id/nav_slideshow"
                android:icon="@drawable/ic_menu_slideshow"
                android:title="Slideshow" />
            <item
                android:id="@+id/nav_login"
                android:icon="@drawable/ic_menu_login"
                android:title="Login" />
        </group>
    
    
    </menu>
    
  2. navigation_with_logout.xml Navigation Menu for default user:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        <group android:checkableBehavior="single">
            <item
                android:id="@+id/nav_camera"
                android:icon="@drawable/ic_menu_camera"
                android:title="Import" />
            <item
                android:id="@+id/nav_gallery"
                android:icon="@drawable/ic_menu_gallery"
                android:title="Gallery" />
            <item
                android:id="@+id/nav_slideshow"
                android:icon="@drawable/ic_menu_slideshow"
                android:title="Slideshow" />
            <item
                android:id="@+id/nav_logout"
                android:icon="@drawable/ic_menu_logout"
                android:title="Logout" />
        </group>
    
    </menu>
    

Now you can change NavigationView items,just write few lines of code.

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);

if(islogin)
    {
        navigationView.getMenu().clear();
        navigationView.inflateMenu(R.menu.navigation_with_login);
    } else
    {
        navigationView.getMenu().clear();
        navigationView.inflateMenu(R.menu.navigation_with_logout);
    }
yennsarah
  • 5,467
  • 2
  • 27
  • 48
manish jain
  • 620
  • 5
  • 11
  • 1
    This is the best solution! – Otieno Rowland Jul 19 '16 at 20:42
  • 1
    This looks more elegant to me, specially considering that in the other solution the hidden items still take up space, apparently (didn't test it myself but the comments said so). – João Josézinho Aug 29 '16 at 22:02
  • Thanks João Josézinho – manish jain Aug 30 '16 at 06:14
  • @manish jain: Thank you very much. You solved my problem related, having 2 drawers list for different logins in app. – BK19 Dec 01 '16 at 13:18
  • bad approach, what if i need two items to change and one of them has 3 states. so I would need 6 menus lol – Bob Oct 18 '17 at 07:16
  • This should be the accepted answer since using visibility won't be efficient – Ege Kuzubasioglu Dec 26 '17 at 07:34
  • I have one question, when using this approach and when clicking on the menu, the menu changes in the instance I click on the menu, so as a user I see the change, I want this to happen before the menu appears, so the user does not see the changes. Thanks! – Isma Haro Jan 14 '18 at 02:35
  • @Mikhail It is not a bad approach. For most of the cases this will be a good solution. There are exceptions of course. One solution will never fit all the problem. Your scenario will be one of the exceptional case. That will have to be worked differently. Cases where you need to have different menu when user is logged in and logged out, this is a good approach. – ABN May 27 '20 at 08:50
3

Frst get the navigationmenu

 NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
Menu menu = navigationView.getMenu();

To add the menu dynamically

 if(loggedOut){
        menu.add(R.id.submenu_others, R.id.action_logout, Menu.NONE, "logout");
}

Here is menu.add(groupId, menuItemId, orderOfMenu, menuItem text)

     if(loggedIn){
        menu.removeItem(R.id.action_logout);
}
yubaraj poudel
  • 3,821
  • 1
  • 31
  • 28
2

I got it done, try this when you need to change the title:

navigationView.getMenu().findItem(R.id.yourItemId).setTitle("my title");

Hope it helped!

Harin
  • 2,413
  • 3
  • 15
  • 30
  • 1
    This would work as well, of course, but I'd also need to change the action related and the icon... I'd rather use the show/hide solution that Lamorak provided below. Thank you anyway! :-) – Marco Zanetti Jun 19 '15 at 09:01