116

I am using the new Android Design Support library to implement a navigation drawer in my application.

I can't figure out how to change the color of a selected item!

Here is the xml of the menu :

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
    <item
        android:id="@+id/navigation_item_1"
        android:icon="@drawable/ic_1"
        android:title="@string/navigation_item_1"/>

    <item
        android:id="@+id/navigation_item_2"
        android:icon="@drawable/ic_2"
        android:title="@string/navigation_item_2"/>
</group>

And here is the navigationview xml which is placed inside a android.support.v4.widget.DrawerLayout :

<android.support.design.widget.NavigationView
    android:id="@+id/activity_main_navigationview"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/drawer_header"
    app:itemIconTint="@color/black"
    app:itemTextColor="@color/primary_text"
    app:menu="@menu/menu_drawer">

    <TextView
        android:id="@+id/main_activity_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:textColor="@color/primary_text" />

</android.support.design.widget.NavigationView>

Thank you for your help !

[EDIT] I have already looked at solutions such as this one : Change background color of android menu.

It seems to be quite a hack and I thought that with the new Design Support Library, something cleaner would have been introduced?

Community
  • 1
  • 1
Greg
  • 1,570
  • 2
  • 12
  • 22

7 Answers7

272

Well you can achieve this using Color State Resource. If you notice inside your NavigationView you're using

app:itemIconTint="@color/black"
app:itemTextColor="@color/primary_text"

Here instead of using @color/black or @color/primary_test, use a Color State List Resource. For that, first create a new xml (e.g drawer_item.xml) inside color directory (which should be inside res directory.) If you don't have a directory named color already, create one.

Now inside drawer_item.xml do something like this

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="checked state color" android:state_checked="true" />
    <item android:color="your default color" />
</selector>

Final step would be to change your NavigationView

<android.support.design.widget.NavigationView
    android:id="@+id/activity_main_navigationview"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/drawer_header"
    app:itemIconTint="@color/drawer_item"  // notice here
    app:itemTextColor="@color/drawer_item" // and here
    app:itemBackground="@android:color/transparent"// and here for setting the background color to tranparent
    app:menu="@menu/menu_drawer">

Like this you can use separate Color State List Resources for IconTint, ItemTextColor, ItemBackground.

Now when you set an item as checked (either in xml or programmatically), the particular item will have different color than the unchecked ones.

bond
  • 11,236
  • 7
  • 48
  • 62
Sash_KP
  • 5,551
  • 2
  • 25
  • 34
  • 1
    Remember that for pressed to work you need to change it from android:state_checked to android:state_pressed. – Programista Jul 05 '15 at 13:08
  • 33
    for ItemBackground you need to use a drawable instead of color because you cannot define a background using a color resource. – sirFunkenstine Oct 14 '15 at 16:44
  • @sash if we want to do change icon image on select navigation item then how we will do this ? – mohit May 18 '16 at 11:34
  • @sirFunkenstine If you are using color selector then you can put it in color folder in your res folder. – Kishan Vaghela Sep 05 '16 at 12:10
  • @KishanVaghela doing what you suggests produces this error: Binary XML file line #3: tag requires a 'drawable' attribute or child tag defining a drawable – iOSAndroidWindowsMobileAppsDev Sep 14 '16 at 07:33
  • @aqm, you will need to update itemBackground to be `app:itemBackground="@android:color/transparent"`. If it still does not work for you. Please provide the error you are seeing. – bond Oct 18 '16 at 16:23
  • It is working for me, but I had to explicitly set setNavigationItemSelectedListener and set the checked property of the menu item to true. like this: menuItem.setChecked(true). – Ankit Aggarwal Aug 22 '18 at 13:24
  • 1
    **DON'T FORGET TO APPLY "CHECKABLE=TRUE" IN YOUR MENU XML FOR THE BACKGROUND TO WORK PROPERLY** – Ankit Gupta Sep 05 '20 at 01:08
81

I believe app:itemBackground expects a drawable. So follow the steps below :

Make a drawable file highlight_color.xml with following contents :

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
     <solid android:color="YOUR HIGHLIGHT COLOR"/>
</shape>

Make another drawable file nav_item_drawable.xml with following contents:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="@drawable/highlight_color" android:state_checked="true"/>
</selector>

Finally add app:itemBackground tag in the NavView :

<android.support.design.widget.NavigationView
android:id="@+id/activity_main_navigationview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/drawer_header"
app:itemIconTint="@color/black"
app:itemTextColor="@color/primary_text"
app:itemBackground="@drawable/nav_item_drawable"
app:menu="@menu/menu_drawer">

here the highlight_color.xml file defines a solid color drawable for the background. Later this color drawable is assigned to nav_item_drawable.xml selector.

This worked for me. Hopefully this will help.

********************************************** UPDATED **********************************************

Though the above mentioned answer gives you fine control over some properties, but the way I am about to describe feels more SOLID and is a bit COOLER.

So what you can do is, you can define a ThemeOverlay in the styles.xml for the NavigationView like this :

    <style name="ThemeOverlay.AppCompat.navTheme">

        <!-- Color of text and icon when SELECTED -->
        <item name="colorPrimary">@color/color_of_your_choice</item> 

        <!-- Background color when SELECTED -->
        <item name="colorControlHighlight">@color/color_of_your_choice</item> 

    </style>

now apply this ThemeOverlay to app:theme attribute of NavigationView, like this:

<android.support.design.widget.NavigationView
android:id="@+id/activity_main_navigationview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:theme="@style/ThemeOverlay.AppCompat.navTheme"
app:headerLayout="@layout/drawer_header"
app:menu="@menu/menu_drawer">

I hope this will help.

Ankush
  • 1,888
  • 20
  • 22
  • 1
    Also, this method can be used as `android:background="@drawable/nav_item_drawable"` for other widgets like RadioButton, Checkbox, Button etc. Reduces bloatware programming for handling `setOnCheckedChangeListener` just for color highlighting. – rupinderjeet Sep 07 '16 at 09:24
  • @Ankush this works but text and icon disappears, i have set the item tint color too but its not working. can you please suggest me how it can be done – User Mar 28 '17 at 09:43
  • 1
    @User I don't know what went wrong with your code, but I have added onto the answer. An entirely different way to achieve the same thing with less effort. You can check that out. – Ankush Mar 30 '17 at 07:05
  • 1
    This should be accepted as an answer! Thank you very much, very helpful!!! – Svetlana Rozhkova Jun 11 '20 at 09:25
  • 2
    **DON'T FORGET TO APPLY "CHECKABLE=TRUE" IN YOUR MENU XML FOR THE BACKGROUND TO WORK PROPERLY** – Ankit Gupta Sep 05 '20 at 01:09
  • if i want diferent color wnite<-> yelow on first item and white<-> red on second item? – Fortran Dec 17 '20 at 18:25
5

One need to set NavigateItem checked true whenever item in NavigateView is clicked

//listen for navigation events
NavigationView navigationView = (NavigationView)findViewById(R.id.navigation);
navigationView.setNavigationItemSelectedListener(this);
// select the correct nav menu item
navigationView.getMenu().findItem(mNavItemId).setChecked(true);

Add NavigationItemSelectedListener on NavigationView

  @Override
  public boolean onNavigationItemSelected(final MenuItem menuItem) {
    // update highlighted item in the navigation menu
    menuItem.setChecked(true);
    mNavItemId = menuItem.getItemId();

    // allow some time after closing the drawer before performing real navigation
    // so the user can see what is happening
    mDrawerLayout.closeDrawer(GravityCompat.START);
    mDrawerActionHandler.postDelayed(new Runnable() {
      @Override
      public void run() {
        navigate(menuItem.getItemId());
      }
    }, DRAWER_CLOSE_DELAY_MS);
    return true;
  }
Vikalp Patel
  • 10,669
  • 6
  • 61
  • 96
  • Thank you for your help Vikalp, but I am already doing that (`setChecked(true)`). I can see the color of the checked item changing but I can't modify it myself, the color is always a lighter version of the navigatioview background. – Greg Jun 17 '15 at 09:31
  • @Greg By default `setChecked(true)` on `View` changes background color upto theme that device you uses. If you to change background of item to some other color then you need to inflate your layout in `DrawerLayout` or `NavigationView` and handle on your own. – Vikalp Patel Jun 17 '15 at 09:39
  • thanks again, I have tried to do what you told me but without success, would you have a link explaining how please ? – Greg Jun 18 '15 at 08:21
3

Step 1: Build a checked/unchecked selector:

selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/yellow" android:state_checked="true" />
    <item android:color="@color/white" android:state_checked="false" />
</selector>

Step 2: use the XML attribute app:itemTextColor within NavigationView widget for selecting the text color.

<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/navigation_header_layout"
    app:itemTextColor="@drawable/selector"
    app:menu="@menu/navigation_menu" />

Step 3 & 4:

Step 3: To make menu icons check-able: wrap all items in a <group> and setandroid:checkableBehavior="single"

Step 4: To change icon color: set the selector to all the items with app:iconTint

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    
    <group android:checkableBehavior="single">
    
        <item
            android:id="@+id/nav_account"
            android:checked="true"
            android:icon="@drawable/ic_person_black_24dp"
            android:title="My Account"
            app:iconTint="@drawable/selector" />
        <item
            android:id="@+id/nav_settings"
            android:icon="@drawable/ic_settings_black_24dp"
            android:title="Settings"
            app:iconTint="@drawable/selector" />

        <item
            android:id="@+id/nav_logout"
            android:icon="@drawable/logout"
            android:title="Log Out"
            app:iconTint="@drawable/selector" />
    </group>

</menu>

Step 5:: make sure that onNavigationItemSelected() callback returns true to consume selection event

navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        drawerLayout.closeDrawer(GravityCompat.START);
        return true;
    }
});

Result:

Side Note:

If setting android:checkableBehavior="single" not working, then you handle this programmatically by manually making the selected item checked and clear the previously selected item:

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {

    int id = item.getItemId();
    
    // remove all colors of the items to the `unchecked` state of the selector
    removeColor(mNavigationView);
    
    // check the selected item to change its color set by the `checked` state of the selector
    item.setChecked(true);

    switch (item.getItemId()) {
      case R.id.dashboard:
          ...
    }

    drawerLayout.closeDrawer(GravityCompat.START);
    return true;
}

private void removeColor(NavigationView view) {
    for (int i = 0; i < view.getMenu().size(); i++) {
        MenuItem item = view.getMenu().getItem(i);
        item.setChecked(false);
    }
}
Zain
  • 37,492
  • 7
  • 60
  • 84
  • if i can diferent color wnite<-> yelow on first item and white<-> red on second item? – Fortran Dec 17 '20 at 18:24
  • @Fortran If I understand you well, I think you need different colors for different Nav Drawer items; then you can create multiple selector drawable, each with different color; and use `app:iconTint="@drawable/selector" ` with the particular selector you want – Zain Dec 17 '20 at 18:30
2

Here is the another way to achive this:

public boolean onOptionsItemSelected(MenuItem item) {

    int id = item.getItemId();

    item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            item.setEnabled(true);
            item.setTitle(Html.fromHtml("<font color='#ff3824'>Settings</font>"));
            return false;
            }
        });


    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

}

Abhishek
  • 153
  • 1
  • 2
  • 11
  • My dobut was regarding what if I want to change color of selected item of popup menu, but thanks to you that i got one great idea from your this solution to change color of selected menu item... so giving you vote !!! And the work around is like :: item.setTitle(Html.fromHtml("Settings")); – Tarit Ray Jun 05 '18 at 16:21
1

Here's how you can do it in your Activity's onCreate method:

NavigationView navigationView = findViewById(R.id.nav_view);
ColorStateList csl = new ColorStateList(
    new int[][] {
        new int[] {-android.R.attr.state_checked}, // unchecked
        new int[] { android.R.attr.state_checked}  // checked
    },
    new int[] {
        Color.BLACK,
        Color.RED
    }
);
navigationView.setItemTextColor(csl);
navigationView.setItemIconTintList(csl);
tguen
  • 189
  • 1
  • 9
0

if you want to keep the fillet of selected item in material 3, use app:itemShapeFillColor="@color/main_navigation_view" instead of app:itemBackground="@color/main_navigation_view"

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 05 '22 at 16:32