54

There's a question for the same functionality on Blackberry, and a few different threads referred to this bug (which has since been closed without resolution as far as I can tell), but I haven't found one specifically for Android.

I'm calling setEnabled(false) on certain MenuItems based on some state, but they visually look the same. I'd like them to be offset in some way, so that the user knows that the option currently isn't available -- is there any way to do that?

Community
  • 1
  • 1
Waynn Lue
  • 11,344
  • 8
  • 51
  • 76

9 Answers9

82

On all android versions, easiest way to use this to SHOW a menu action icon as disabled AND make it FUNCTION as disabled as well:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {

    MenuItem item = menu.findItem(R.id.menu_my_item);

    if (myItemShouldBeEnabled) {
        item.setEnabled(true);
        item.getIcon().setAlpha(255);
    } else {
        // disabled
        item.setEnabled(false);
        item.getIcon().setAlpha(130);
    }
}
Frank
  • 12,010
  • 8
  • 61
  • 78
  • Yup, this did the trick for me, nice to be able to keep the original icon. – Dale Zak Nov 25 '14 at 23:28
  • 19
    Note: if this icon is used elsewere, be sure to mutate() it prior to setting alpha, otherwise all places which use this drawable will be adjusted which is not necessarily what one might want. – dimsuz Apr 09 '15 at 19:28
  • Is there any other way to set alpha value?(inside style) – balaji Sep 16 '15 at 12:14
  • 7
    Note, this only works if your MenuItem is an icon. Doesn't work for text-only MenuItems. – Ivan Jun 18 '18 at 23:50
  • How expensive is this compared to just preparing the icons in 2 different colors and swapping them? – Ryuu Apr 24 '20 at 13:20
64

I had the same issue. There are two ways of getting this to work:

  1. Put your icons in a StateList so that a different icon will be used on disable
  2. What I use now. Change the icon yourself with something like this in onPrepareOptionsMenu():

    public boolean onPrepareOptionsMenu(Menu menu) {
        boolean menusEnabled = reachedEndOfSlidehow(); // enable or disable?
        MenuItem item = menu.findItem(R.id.menu_next_slide);
        Drawable resIcon = getResources().getDrawable(R.drawable.ic_next_slide);
    
        if (!menusEnabled)
            resIcon.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN);
    
        item.setEnabled(menusEnabled); // any text will be automatically disabled
        item.setIcon(resIcon);
    }
    

You can call invalidateOptionsMenu() (or from ABS, supportInvalidateOptionsMenu()) to rebuild the menu.

EDIT: Updated solution 2

Source: https://groups.google.com/forum/?fromgroups#!topic/actionbarsherlock/Z8Ic8djq-3o

Oleg Vaskevich
  • 12,444
  • 6
  • 63
  • 80
  • 3
    Does this actually change the color of the menu item text/title though? That is, does it grey it out? From what I understand this will only affect the icon. What if you don't have an icon and only have text? – Tony Chan Nov 07 '12 at 02:25
  • 2
    Calling item.setEnabled() will already make the text looked grayed out. The problem is that on Action Bars the text is grayed out but the icon is not - this code grays it out. – Oleg Vaskevich Nov 07 '12 at 06:57
  • 19
    Nobu Games' response on the Google Groups thread you linked to worked great for me in Jelly Bean: menuItem.getIcon().setAlpha(enabled ? 255 : 64); – poshaughnessy Jan 02 '13 at 16:55
13

I found a new way to solve this issue using a drawable selector xml file. You just create a selector with the icon you want to use in your menu item, then you can either change the tint, alpha or both of the bitmap:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_enabled="true">
        <bitmap android:src="@drawable/ic_menu_item"
            android:tint="@color/enabled_color"
            android:alpha="@integer/enabled_alpha"/>
    </item>

    <item android:state_enabled="false">
        <bitmap android:src="@drawable/ic_menu_item"
            android:tint="@color/disabled_color"
            android:alpha="@integer/disabled_alpha"/>
    </item>
</selector>

As a side note; I like to set the tint to "?android:attr/textColorPrimary" for enabled state and "?android:attr/textColorHint" for disabled state. This way it will adjust depending on the theme used.


Then you can just set the icon in your menu xml file to the selector resource:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/menu_action"
        android:orderInCategory="0"
        android:title="@string/title_menu_action"
        android:icon="@drawable/ic_menu_item_selector"
        app:showAsAction="ifRoom"/>

</menu>

Then when you call item.setEnabled(enabled) the color and/or alpha of the icon will change along with the state!

Bryan
  • 14,756
  • 10
  • 70
  • 125
4

The way I did it is by using "itemIconTint" in NavigationView, you can also grey out the text by using "itemTextColor"

This is Navigationview:

<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:itemBackground="@color/white"
    android:background="@color/white"
    app:itemTextColor="@color/menu_text_color"
    app:itemIconTint="@color/menu_text_color"
    app:menu="@menu/main_drawer" />

and the "@color/menu_text_color" is a selector:

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

Finally, if you want to disable a menuitem,

MenuItem item = mNavigationView.getMenu().findItem(R.id.your_menu_item);
item.setEnabled(isEnable);

Done!

SoYoung
  • 315
  • 1
  • 3
  • 10
3

I was having difficulty with this on modern android with MaterialComponents theme. My problem was I had set <item name="actionMenuTextColor">@color/blue</item> in styles.xml and this overrides the text color whether the item is enabled or disabled. The solution is to set a Color state list and not a color directly.

My styles attribute now looks like:

<item name="actionMenuTextColor">@color/menu_color_selector</item>

Mariusz Wiazowski
  • 2,118
  • 1
  • 16
  • 17
Carson Holzheimer
  • 2,890
  • 25
  • 36
  • 1
    I had exactly the same issue. Thanks! – Mariusz Wiazowski Mar 26 '20 at 16:17
  • I had the same issue but `actionMenuTextColor` didn't make any difference for me (I set it to pink and couldn't see pink anywhere). If anyone looking at this is still having issues with Material components, check my answer https://stackoverflow.com/a/66248473/5437033. – Chrispher Feb 17 '21 at 19:11
3

I had an issue where neither my the text nor the icon was visibly changing. The other answers either didn't work for me or weren't very elegant. Here's an answer that works for the latest Material recommendations.

You should be able to simply call menu.findItem(R.id.menu_my_item).isEnabled = false in onPrepareOptionsMenu(menu: Menu).

(If you need onPrepareOptionsMenu to run again, you can simply call invalidateOptionsMenu() or activity?.invalidateOptionsMenu() (from a fragment) and the application will queue up the menu to be recreated. Alternatively you can store off the menu item in a member variable to modify it later, but be careful to destroy your reference to it within onDestroyOptionsMenu to avoid a memory leak.)

The fact that the menu item is disabled should be enough to grey out the text or the icon automatically. The difficulty is in setting up your styles to make this work.

Short Answer

First create a color state list my_color_state_list.xml that you want your icons and text to use (e.g. black when enabled, grey when disabled). (See the full answer for an example.)

If you're using com.google.android.material.appbar.MaterialToolbar, you can tell it to use this selector for icons and text by providing a custom theme overlay. In your activity's XML, give the toolbar the attribute android:theme="@style/Foo" and define that style somewhere as:

    <style name="Foo">
        <item name="colorControlNormal">@color/my_color_state_list</item>
        <item name="actionMenuTextColor">@color/my_color_state_list</item>
    </style>

Now when the menu item is enabled or disabled via menu.findItem(R.id.menu_my_item).isEnabled = false the text will automatically change color, and any icons which use the color ?attr/colorControlNormal will also automatically change color.

Full answer

My starting place

My menu items are part of a Material toolbar. This answer may help for other kinds of toolbar/app bar, but your mileage may vary. In my activity I have something like this:

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.MaterialComponents.Toolbar.Surface"/>

and the theme I'm using looks something like this:

    <style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="colorPrimary">@color/blue</item>
        <item name="colorSecondary">@color/green</item>
        <item name="colorSurface">@color/lightGrey</item>
        <item name="colorOnSurface">@color/black</item>
        [...]
        <item name="windowActionModeOverlay">true</item>
    </style>

It is also convention that the icon you use in buttons and menu items (and everywhere really) should have their default color be ?attr/colorControlNormal. So for example I might have a vector image which looks like:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="?attr/colorControlNormal"
    android:tintMode="src_atop">
  <path android:pathData="..." android:fillColor="@android:color/white"/>
</vector>

If you download an icon from Material Icons you will see they all use colorControlNormal.

What I needed to do

If you look back at the definition of my toolbar, you will see it uses a ThemeOverlay ThemeOverlay.MaterialComponents.Toolbar.Surface which is defined as:

<style name="ThemeOverlay.MaterialComponents.Toolbar.Surface" parent="">
    <item name="colorControlNormal">@color/material_on_surface_emphasis_medium</item>
    <item name="actionMenuTextColor">@color/material_on_surface_emphasis_medium</item>
</style>

This sets the menu item text color and icon color to @color/material_on_surface_emphasis_medium which does not respond to being enabled or not. @color/material_on_surface_emphasis_medium looks like:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:alpha="@dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
</selector>

(You may be using ThemeOverlay.MaterialComponents.Toolbar.Primary instead, which has a similar issue - it simply uses colorOnPrimary.)

We need to replace this with our own color state list which responds to enabled state. So, make a new file res/color/menu_item_selector.xml that looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true" android:alpha="@dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
    <item android:alpha="@dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
</selector>

You see I've used the same conventions that the material library does by using their constants to define the alpha values, and I used colorOnSurface as my color. If you were using ThemeOverlay.MaterialComponents.Toolbar.Primary you would want colorOnPrimary instead. Of course you can use any color or alpha here, it's up to you.

And now make a new ThemeOverlay in res/values/styles.xml to point to this selector, inheriting from whatever ThemeOverlay you were using:

<!-- Toolbar - overrides the menu text color to use a selector that responds to whether it's enabled or not -->
    <style name="ThemeOverlay.MyTheme.Toolbar" parent="ThemeOverlay.MaterialComponents.Toolbar.Surface">
        <!-- Color used in the icons of menu actions (i.e. non-overflow menu items). This is just convention, this will affect anything that uses ?attr/colorControlNormal) -->
        <item name="colorControlNormal">@color/menu_item_color_selector</item>
        <!-- Color used in the text of menu actions (i.e. non-overflow menu items) -->
        <item name="actionMenuTextColor">@color/menu_item_color_selector</item>
    </style>

And now finally we can apply this ThemeOverlay to the toolbar:

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.MyTheme.Toolbar"/>
Chrispher
  • 373
  • 2
  • 7
2

setEnabled(false) works fine on API Level < 14 but on 14 the item still clickable.

Dhanuka
  • 2,826
  • 5
  • 27
  • 38
Hoochwo
  • 382
  • 4
  • 14
1

Here's a simple way to do it (using Kotlin):

fun changeMenuItemColour(enabled: Boolean) {
    var menuItem = SpannableString(mCustomToolbar?.menu?.findItem(R.id.some_menu_item)?.title)
    var style = activity?.resources?.getColor(R.color.darkGraphite)!!
    if (enabled) style = activity?.resources?.getColor(R.color.black)!!
    menuItem.setSpan(ForegroundColorSpan(style), 0, menuItem.length, 0)
}
AO_
  • 2,573
  • 3
  • 30
  • 31
1

Have a look at this link

setEnabled can also be used for MenuItems.

Dhanuka
  • 2,826
  • 5
  • 27
  • 38
Pavandroid
  • 1,586
  • 2
  • 15
  • 30
  • 1
    Yeah, I'm using setEnabled, but it doesn't change it visually. – Waynn Lue Mar 10 '12 at 04:08
  • try the below code public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, 1, 1, "Test").setEnabled(false); return super.onCreateOptionsMenu(menu); } – Pavandroid Mar 10 '12 at 04:29
  • When you say it's working fine, do you mean it's changing the text to grey? Or just that it's disabled? I'm trying to do the first. – Waynn Lue Mar 10 '12 at 07:05
  • It will be the android's default disable Menu Item which wont be selectable which can be easily identified and known by the normal Android users. – Pavandroid Mar 10 '12 at 07:26
  • I know it won't be selectable, but visually it looks the exact same. Or are you saying it's visually identifiable as disabled? – Waynn Lue Mar 10 '12 at 12:03
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/8740/discussion-between-pavandroid-and-waynn-lue) – Pavandroid Mar 10 '12 at 15:42
  • Yes, visually one can easily identify. That also depends up on the os version. The normal user of that mobile can easily identify that . – Pavandroid Mar 10 '12 at 15:44
  • This is possible and not hard - check my answer. – Oleg Vaskevich Jun 04 '12 at 16:20