146

I can easily do it when I am using onCreateOptionsMenu or onOptionsItemSelected methods.

But I have a button somewhere in screen, and on clicking that button, it should enable/disable context menu items.

Janusz
  • 187,060
  • 113
  • 301
  • 369
Vikas
  • 24,082
  • 37
  • 117
  • 159

12 Answers12

295

Anyway, the documentation covers all the things.

Changing menu items at runtime

Once the activity is created, the onCreateOptionsMenu() method is called only once, as described above. The system keeps and re-uses the Menu you define in this method until your activity is destroyed. If you want to change the Options Menu any time after it's first created, you must override the onPrepareOptionsMenu() method. This passes you the Menu object as it currently exists. This is useful if you'd like to remove, add, disable, or enable menu items depending on the current state of your application.

E.g.

@Override
public boolean onPrepareOptionsMenu (Menu menu) {
    if (isFinalized) {
        menu.getItem(1).setEnabled(false);
        // You can also use something like:
        // menu.findItem(R.id.example_foobar).setEnabled(false);
    }
    return true;
}

On Android 3.0 and higher, the options menu is considered to always be open when menu items are presented in the action bar. When an event occurs and you want to perform a menu update, you must call invalidateOptionsMenu() to request that the system call onPrepareOptionsMenu().

Flimm
  • 136,138
  • 45
  • 251
  • 267
Vikas
  • 24,082
  • 37
  • 117
  • 159
  • 3
    `setEnable()` does change what happens when you press this menu but doesn't change how it looks (what's wrong, Android developers?). So it is clearer to either disable *and* change the title, or preferably just make the `MenuItem` invisible. – Vlad Aug 17 '12 at 17:58
  • 20
    Quick tip: return `false` to disable the menu completely. – Bart Friederichs Dec 27 '12 at 11:25
  • 5
    Plus the API comment on onPrepareOptionsMenu clearly states: Deriving classes should always (!) call through to the base class implementation. You forgot your super call there. – AgentKnopf Jan 16 '13 at 20:43
  • Documentation: http://developer.android.com/guide/topics/ui/menus.html#ChangingTheMenu – Jorge Jan 03 '14 at 15:55
  • How do you know the integer ID of MenuItem added at run-time? You can only add them by name. – Antonio Sesto Apr 12 '15 at 14:36
73

On all android versions, easiest way: 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
  • 1
    Why set alpha to 0 when you can set visibility to false? – MLProgrammer-CiM Feb 17 '14 at 11:48
  • 9
    I'm not setting alpha to 0, but to 130. – Frank Feb 17 '14 at 12:18
  • Why set alpha to 130 when you can set visibility to false? Invisible button is better UI than greyed out. – MLProgrammer-CiM Feb 17 '14 at 14:16
  • 9
    Depends on the context of the button. If the feature the button would normally serve is completely unusable in the current state, then yes the visibility should be invisible/gone. However if the feature the button would normally serve COULD be usable given whatever reasons (eg. If the user becomes a premium member, if the user signs in, if the user connects to internet, etc.) then it's nice to have it greyed out because then the it lets the user know there is some existing feature they don't have access to for some reason. – yiati May 01 '14 at 19:58
  • 7
    Setting icon to invisible is not better in nearly all use cases. In the case you have a group its better they stay in their positions and greyed out indicates to the user that there is an option that can be enabled in a certain program state. making thing invisible and invisible and probably causing layout changes is very silly. There is a reason most menus grey out items and not removed them depending on mode. – RichieHH Aug 01 '14 at 15:21
  • What if the menu item inside of the overflow icon, so it will show text only? How can we show to the user that the item is disabled? – HendraWD Aug 16 '17 at 04:00
  • then it greys out automatically when you set it disabled, I believe. Haven't tested – Frank Aug 28 '17 at 06:37
  • @RichieHH Relying solely on color is an accessibility issue though! – maracuja-juice Mar 30 '18 at 22:38
  • @maracuja-juice no one said any different. But you can disable a button without it becoming invisible. The fact its greyed out for someone with proper sight doesn't mean it loses its accessibility for someone without. Think. It's "meta data" marks it as unavailable. Never hide buttons unless there's a bigger picture. That is an accessibility issue. – RichieHH Apr 01 '18 at 08:23
  • 1
    How if menu not contain icon? You can't setAlpha(). – Latief Anwar Aug 24 '19 at 20:28
43

You could save the item as a variable when creating the option menu and then change its properties at will.

private MenuItem securedConnection;
private MenuItem insecuredConnection;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.connect_menu, menu);
    securedConnection = menu.getItem(0);
    insecuredConnection =  menu.getItem(1);
    return true;
}

public void foo(){
       securedConnection.setEnabled(true);
}    
nir
  • 431
  • 4
  • 2
7

How to update the current menu in order to enable or disable the items when an AsyncTask is done.

In my use case I needed to disable my menu while my AsyncTask was loading data, then after loading all the data, I needed to enable all the menu again in order to let the user use it.

This prevented the app to let users click on menu items while data was loading.

First, I declare a state variable , if the variable is 0 the menu is shown, if that variable is 1 the menu is hidden.

private mMenuState = 1; //I initialize it on 1 since I need all elements to be hidden when my activity starts loading.

Then in my onCreateOptionsMenu() I check for this variable , if it's 1 I disable all my items, if not, I just show them all

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu_galeria_pictos, menu);

        if(mMenuState==1){
            for (int i = 0; i < menu.size(); i++) {
                menu.getItem(i).setVisible(false);
            }
        }else{
             for (int i = 0; i < menu.size(); i++) {
                menu.getItem(i).setVisible(true);
            }
        }

        return super.onCreateOptionsMenu(menu);
    }

Now, when my Activity starts, onCreateOptionsMenu() will be called just once, and all my items will be gone because I set up the state for them at the start.

Then I create an AsyncTask Where I set that state variable to 0 in my onPostExecute()

This step is very important!

When you call invalidateOptionsMenu(); it will relaunch onCreateOptionsMenu();

So, after setting up my state to 0, I just redraw all the menu but this time with my variable on 0 , that said, all the menu will be shown after all the asynchronous process is done, and then my user can use the menu.

 public class LoadMyGroups extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mMenuState = 1; //you can set here the state of the menu too if you dont want to initialize it at global declaration. 

        }

        @Override
        protected Void doInBackground(Void... voids) {
           //Background work

            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            mMenuState=0; //We change the state and relaunch onCreateOptionsMenu
            invalidateOptionsMenu(); //Relaunch onCreateOptionsMenu

        }
    }

Results

enter image description here

halfer
  • 19,824
  • 17
  • 99
  • 186
Gastón Saillén
  • 12,319
  • 5
  • 67
  • 77
6

simplify @Vikas version

@Override
public boolean onPrepareOptionsMenu (Menu menu) {

    menu.findItem(R.id.example_foobar).setEnabled(isFinalized);
    return true;
}
Kosrat D. Ahmad
  • 468
  • 8
  • 14
4

What I did was save a reference to the Menu at onCreateOptionsMenu. This is similar to nir's answer except instead of saving each individual item, I saved the entire menu.

Declare a Menu Menu toolbarMenu;.

Then in onCreateOptionsMenusave the menu to your variable

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.main_menu, menu);
    toolbarMenu = menu;
    return true;
}

Now you can access your menu and all of its items anytime you want. toolbarMenu.getItem(0).setEnabled(false);

3

A more modern answer for an old question:

MainActivity.kt

private var myMenuIconEnabled by Delegates.observable(true) { _, old, new ->
    if (new != old) invalidateOptionsMenu()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    findViewById<Button>(R.id.my_button).setOnClickListener { myMenuIconEnabled = false }
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main_activity, menu)
    return super.onCreateOptionsMenu(menu)
}

override fun onPrepareOptionsMenu(menu: Menu): Boolean {
    menu.findItem(R.id.action_my_action).isEnabled = myMenuIconEnabled
    return super.onPrepareOptionsMenu(menu)
}

menu_main_activity.xml

<?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">
<item
    android:id="@+id/action_my_action"
    android:icon="@drawable/ic_my_icon_24dp"
    app:iconTint="@drawable/menu_item_icon_selector"
    android:title="My title"
    app:showAsAction="always" />
</menu>

menu_item_icon_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?enabledMenuIconColor" android:state_enabled="true" />
<item android:color="?disabledMenuIconColor" />

attrs.xml

<resources>   
    <attr name="enabledMenuIconColor" format="reference|color"/>
    <attr name="disabledMenuIconColor" format="reference|color"/>
</resources>

styles.xml or themes.xml

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="disabledMenuIconColor">@color/white_30_alpha</item>
    <item name="enabledMenuIconColor">@android:color/white</item>
ExpensiveBelly
  • 444
  • 5
  • 8
2

the best solution when you are perform on navigation drawer

@Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.setGroupVisible(0,false);
        return true;
    }
Prashant Gosai
  • 1,186
  • 1
  • 10
  • 18
2

If visible menu

menu.findItem(R.id.id_name).setVisible(true);

If hide menu

menu.findItem(R.id.id_name).setVisible(false);
murugan mani
  • 375
  • 5
  • 6
1
  @Override
        public boolean onOptionsItemSelected(MenuItem item) {

            switch (item.getItemId()) {

                case R.id.item_id:

                       //Your Code....

                        item.setEnabled(false);
                        break;
              }
            return super.onOptionsItemSelected(item);
     }
Tousif Akram
  • 129
  • 6
  • From Review: Hi, please don't answer just with source code. Try to provide a nice description about how your solution works. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). Thanks – sɐunıɔןɐqɐp Oct 11 '18 at 06:35
0
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    // getMenuInflater().inflate(R.menu.home, menu);
    return false;
}
Draken
  • 3,134
  • 13
  • 34
  • 54
  • 2
    While this code may answer the OP's question, a few words of explanation will help current and future users understand your response even better. – Thom Oct 03 '18 at 12:24
-4

Generally can change the properties of your views in runtime:

(Button) item = (Button) findViewById(R.id.idBut);

and then...

item.setVisibility(false)

but

if you want to modify de visibility of the options from the ContextMenu, on press your button, you can activate a flag, and then in onCreateContextMenu you can do something like this:

 @Override
        public void onCreateContextMenu(ContextMenu menu, 
                View v,ContextMenu.ContextMenuInfo menuInfo) {

            super.onCreateContextMenu(menu, v, menuInfo);

                menu.setHeaderTitle(R.string.context_title);

                if (flagIsOn()) {
                    addMenuItem(menu, "Option available", true);
                } else {
                    Toast.makeText(this, "Option not available", 500).show();
                }

        }

I hope this helps

Ganapathy C
  • 5,989
  • 5
  • 42
  • 75
Eric
  • 1,469
  • 1
  • 13
  • 11
  • I must say you are getting wrong, I want menu item to be disabled, not Button. – Vikas Mar 26 '11 at 08:07
  • my answer is completed. This code it works, I've used in my projects – Eric Mar 26 '11 at 08:21
  • 1
    Well thanks for work, but You should read question properly that I've already stated that I can change it in `onCreateContextMenu` method. But I want to access the context menu out side from this method. – Vikas Mar 26 '11 at 08:34
  • `onCreateContextMenu` will be called only once, but I can click on button lots of time to enable/disable menu item. – Vikas Mar 26 '11 at 08:49
  • Yes, but the context menu normally is done to be hidden. If you press your 'somewhere button' and set the flag as I said, this context menu is not visible and the next time that you reload your context menu, this option will be invisible. Another option is to do another kind of menu with the same appearance to handle the events with another methods. – Eric Mar 26 '11 at 08:58
  • I came out with solution. Please check my answer. – Vikas Mar 26 '11 at 09:09