24

I am creating a menu where one of the items is used the lock an object. When this item is clicked, the menu should be recreated with a button to unlock the item. I created two menus for this. This is working fine. I read that in Android version >= 11 the onPrepareOptionsMenu is no longer called when displaying the menu and I have to call invalidateOptionsMenu(). So I changed the build target (both in the Manifest and in properties) to 11 and ran the app on an AVD of 4.0.3. The program is still working fine, but I thought it shouldn't anymore, and I should check

if (Build.VERSION.SDK_INT >= 11)
{
  invalidateOptionsMenu();
}

This is my code:

public class MainActivity3 extends Activity{

    boolean locked;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        locked = false;
    }

      @Override
      public boolean onCreateOptionsMenu(Menu menu){
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.changing_menu1, menu);

            return true;
        }

        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {

            menu.clear();
            MenuInflater inflater = getMenuInflater();

            if (locked) {
                inflater.inflate(R.menu.changing_menu2, menu);
            }
            else {
                inflater.inflate(R.menu.changing_menu1, menu);
            }

        return super.onPrepareOptionsMenu(menu);
        }

        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {

          case R.id.Menu1:
          break;

          case R.id.Menu2 :
          break;

          case R.id.Menu3 :
          locked = !locked;
          break;

           }
        return true;
        }
}

So the Menu is still refreshed/recreated in 4.0. Did I misunderstand something about the usage of invalidateOptionsMenu();?

erdomester
  • 11,789
  • 32
  • 132
  • 234
  • I don't know how to report it, but even android developers isn 't clear enough about this. Check here: https://developer.android.com/guide/topics/ui/menus?hl=en#ChangingTheMenu If you read just this, you will understand that invalidateOptionsMenu() will not call onCreateOptionsMenu() and that in Android >= 11 OnPrepareOptionsMenu() does not work every time you open the menu... but it does. WTHeck? Great question erdomester and great answer @justinmorris – Juan Ignacio Avendaño Huergo May 30 '18 at 11:21

1 Answers1

36

invalidateOptionsMenu() was added to give us the ability to force onCreateOptionsMenu() to be called again. onPrepareOptionsMenu() is still called every time you call the menu.

What you are trying to achieve above is a good example of when to use invalidateOptionsMenu() but because of backwards compatibility you will need to do both:

if (Build.VERSION.SDK_INT >= 11) {
  invalidateOptionsMenu();
}


@Override
public boolean onCreateOptionsMenu(Menu menu){
    if (Build.VERSION.SDK_INT >= 11) {
        selectMenu(menu);
    }
    return true;
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    if (Build.VERSION.SDK_INT < 11) {
        selectMenu(menu);
    }
    return true;
}

private void selectMenu(Menu menu) {
    menu.clear();
    MenuInflater inflater = getMenuInflater();

    if (locked) {
        inflater.inflate(R.menu.changing_menu2, menu);
    }
    else {
        inflater.inflate(R.menu.changing_menu1, menu);
    }
}
JustinMorris
  • 7,259
  • 3
  • 30
  • 36
  • Hi! This looks good! But I don't see what the invalidateOptionsMenu() does here. At first, onCreateOptionsMenu is called. If API >= 11, selectMenu() is called, but if API < 11 what happens? I haven't run the code (can't at the moment), but I don't why would the options menu open if API < 11. Another thing: If on an item click, I call invalidateOptionsMenu() (when API >= 11), onPrepareOptionsMenu is called by system automatically, but it only inflates the menu if API < 11, so it does nothing, does it? – erdomester Nov 01 '12 at 20:09
  • Using invalidateOptionsMenu() in your use case is nothing more than a micro performance improvement. Ultimately its more efficient on newer devices to call invalidateOptionsMenu() only when "locked" changes. Then 11+ devices will only inflate a new options menu when there is a reason to but older devices will re-inflate an options menu every time the user hits the menu button. You could get the same performance enhancement, but backwards compatible, by using a boolean switch and checking it in onPrepareOptionsMenu() to see if you need to re-inflate the menu. – JustinMorris Nov 02 '12 at 22:42
  • So basically I don't even have to call invalidateOptionsMenu(). It's just a safety method for devices with API > 11, right? And still, in your code the selectMenu is called only when API < 11, so if you call invalidateOptionsMenu() the menu will not be refreshed aka. lock will not changed to unlock. – erdomester Nov 03 '12 at 07:35
  • Correct, you don't need to call invalidateOptionsMenu().. I was just showing you how you could. In my code I only call selectMenu from onPrepareOptionsMenu() if the API < 11 because in my code if the API is higher I would have called invalidateOptionsMenu() and that would cause selectMenu to be called from onCreateOptionsMenu(). – JustinMorris Nov 06 '12 at 17:38
  • 1
    another option is to use one menu xml and have groups. The visiblity of groups can be changed in the onPrepareOptionsMenu() – Patrick Jackson Dec 12 '13 at 20:27