How can I refresh the ActionBar when onPrepareOptionsMenu switched menu entries?

80,250

Solution 1

My method of choice is to create a helper class. For example:

class VersionHelper
{
    static void refreshActionBarMenu(Activity activity)
    {
        activity.invalidateOptionsMenu();
    }
}

Now in your code above, replace invalidateOptionsMenu(); with:

if (Build.VERSION.SDK_INT >= 11)
{
    VersionHelper.refreshActionBarMenu(this);
}

Credit for this method goes to CommonsWare (search for HoneycombHelper, and check out his books - highly recommended)

Solution 2

Thanks to the accepted answer. I am using ActionBarActivity. in this class you can use

supportInvalidateOptionsMenu();

Solution 3

Use

ActivityCompat.invalidateOptionsMenu(Activity activity)

from the compatibility library.

See: https://stackoverflow.com/a/14748687/435855

Solution 4

save a reference to the menu and call:

this.menu.clear();
this.onCreateOptionsMenu(this.menu);

Solution 5

Based on "Klaasvaak" answer above. I am using its subMenus. This works for me :

// Declare and save the menu as global, so it can be called anywhere.
Menu absTopSubMenus;

public boolean onCreateOptionsMenu(Menu menu) {

absTopSubMenus = menu;  // Used for re-drawing this menu anywhere in the codes.

// The remainder of your code below
}

Then, to re-draw this, just call :

// Redraw the top sub-menu
absTopSubMenus.clear();
onCreateOptionsMenu(absTopSubMenus);
Share:
80,250
Harald Wilhelm
Author by

Harald Wilhelm

Updated on December 14, 2020

Comments

  • Harald Wilhelm
    Harald Wilhelm over 3 years

    Within my apps I often enable/disable menu entries and do make them visible from onPrepareOptionsMenu.

    Today I started to add the android:showAsAction menu attribute to some of my Android 2.x apps to show menu entries used most on the ActionBar.

    The ActionBar does not reflect the enable/disable and visibility immediately. I need to click on the menu dropdown on the right to see this change happen.

    Ok, I do understand that the menu fires onPrepareOptionsMenu. But what do I need to do to refresh the ActionBar? I think this change needs to be applied from within onOptionsItemSelected but I don't know what I should call.

    Here's the menu:

    <item
        android:icon="@drawable/ic_menu_mapmode"
        android:id="@+id/men_mapview"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_mapview" />
    
    <item
        android:icon="@drawable/ic_menu_mapmode"
        android:id="@+id/men_satelliteview"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_satelliteview" />
    

    Here's the onPrepareOptionsMenu:

    @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {
        MenuItem menuItemMapView = menu.findItem(R.id.men_mapview);
        MenuItem menuItemSatelliteView = menu.findItem(R.id.men_satelliteview);
    
        if (mapView.isSatellite()) {
            menuItemMapView.setEnabled(true).setVisible(true);
            menuItemmenuItemSatelliteView.setEnabled(false).setVisible(false);
        } else {
            menuItemMapView.setEnabled(false).setVisible(false);
            menuItemmenuItemSatelliteView.setEnabled(true).setVisible(true);
        }
    
        return super.onPrepareOptionsMenu(menu);
    }
    

    Here's the onOptionsItemSelected

    @Override
    public boolean onOptionsItemSelected(final MenuItem menuItem) {
        switch (menuItem.getItemId()) {
            case R.id.men_mapview:
                mapView.setSatellite(false);
                mapView.setStreetView(true);
                mapView.invalidate();
    
                invalidateOptionsMenu(); // This works on Android 3.x devices only
                return true;
            case R.id.men_satelliteview:
                mapView.setSatellite(true);
                mapView.setStreetView(false);
                mapView.invalidate();
    
                invalidateOptionsMenu(); // This works on Android 3.x devices only
                return true;
        }
    
        return super.onOptionsItemSelected(menuItem);
    }
    

    EDIT: If I add invalidateOptionsMenu this works on Android 3.x apps but crashes on Android 2.x devices because of a missing method. What's the recommended way to do it right?

  • topwik
    topwik over 11 years
    what's the differnece between your answer and simply putting the version check around his initial call of if (Build.VERSION.SDK_INT >= 11) { invalidateOptionsMenu(); }? you could possibly stick the version check in the versionHelper method call?
  • Tony Chan
    Tony Chan over 11 years
    @towpse The reason he has the helper class structured as it is is to avoid a crash due to non-backward compatibility. The method invalidateOptionsMenu() only exists in API 11 (honeycomb) and up. So if your app is to run on anything lower than API 11 it will crash. To avoid this you wrap the method in question in another static method (e.g. refreshActionBarMenu()) and only call this static method if you're running on API>11 (thus the version check before you call the static method). This works because the VersionHelper class isn't loaded until you actually use it.
  • topwik
    topwik over 11 years
    @Turbo ok cool, thanks for the explanation , so what would one do in the else case? not update the menu at all or what's the older way of doing the menu item update if user is running an older OS?
  • Tony Chan
    Tony Chan over 11 years
    @towpse I'm actually figuring out that situation right now. I believe that in the else case for this type of situation you should just do nothing. This is because the older way (pre API11) of doing the menu update is in the method onPrepareOptionsMenu which automatically gets called every time the user opens the menu. So to handle the older OS and the newer API>11 ones, you put the code for the menu change in onPrepareOptionsMenu (this is to handle old OS), then to handle newer OS you call invalidateOptionsMenu when an event occurs that would trigger a menu change.
  • Mike Repass
    Mike Repass over 10 years
    This is the correct answer for how to programmatically toggle visibility of Options Menu Items in the Action Bar on pre-API 11 devices.
  • Brill Pappin
    Brill Pappin over 10 years
    I the helper class protects from old SDKs, then why not do the version check inside them method?
  • Ricardo
    Ricardo over 10 years
    @BrillPappin I think that was the point of CommonsWare's book. You don't need the helper if you are having the API check with the rest of your code. Unlike what @Turbo said, the app won't crash in older devices if it doesn't go inside the if. By using the VersionHelper and putting those ifs inside of it, you make your code cleaner.
  • lemuel
    lemuel about 10 years
    On Gingerbread, onCreateOptionsMenu() does not get called after calling this method.
  • Urizev
    Urizev about 10 years
    Yes. That's because preHoneycomb does not support on screen buttons and a hardware menu button is expected. So onCreateOptionMenu() method is called when user press menu button. invalidateOptionsMenu is focused on refreshing the actionbar 3.0+ supporting the old menu hardware button.
  • Cjames
    Cjames about 10 years
    Give this man a cookie.... The reason why I can't set the visibility of the menu to false is that I've only use invalidateOptionsMenu(); until I found your answer. Thanks dude.
  • Guy
    Guy almost 10 years
    This should be the selected answer.
  • milosmns
    milosmns about 9 years
    Since actions (icons) in Toolbar are now internally represented by a TextView instead of ImageView (and I understand why), I had to manually recolor compound drawables inside the TextView, and additionally assign a TouchListener which would recolor them when pressed/activated. So basically I am coloring icons only. Anyone with a similar situation? Invalidating the menu didn't work for me..
  • Amir Dora.
    Amir Dora. about 6 years
    this method is deprecated.
  • hetsgandhi
    hetsgandhi almost 5 years
    Thank you it saved much of my time!