Android - Correct use of invalidateOptionsMenu()
Asked Answered
C

9

47

I have been searching a lot on invalidateOptionsMenu() and I know what it does. But I cannot think of any real life example where this method could be useful.

I mean, for instance, let's say we want to add a new MenuItem to our ActionBar, we can simply get the Menu from onCreateOptionsMenu(Menu menu) and use it in any button's action.

Now to my real question, is following the only way of using invalidateOptionsMenu()?

bool _OtherMenu;
protected override void OnCreate (Bundle bundle)
{
    _OtherMenu = false;
    base.OnCreate (bundle);
    SetContentView (Resource.Layout.Main);
    Button button = FindViewById<Button> (Resource.Id.myButton);
    button.Click += delegate
    {
        if(_OtherMenu)
            _OtherMenu = false;
        else
            _OtherMenu = true;

        InvalidateOptionsMenu ();
    };
}

public override bool OnCreateOptionsMenu (IMenu menu)
{
    var inflater = this.SupportMenuInflater;
    if(_OtherMenu)
        inflater.Inflate (Resource.Menu.another_menu, menu);
    else
        inflater.Inflate (Resource.Menu.main_activity_menu, menu);

    return base.OnCreateOptionsMenu (menu);
}

Click the button and a different menu appears. Click the button again and previous menu appears.

P.S. Sorry for the C# syntax.

Chinkapin answered 16/1, 2015 at 12:31 Comment(5)
what's your minSdkVersion?Tibbitts
It works on 11+. Mine is 15.Chinkapin
So it should call OnCreateOptionsMenu(), you could try to run debug to go through the codesTibbitts
@Xingchen Sir, i know what this function does. And it is doing what is should do. I just don't understand the point of this function. How is it useful?Chinkapin
This function is intended to update your action bar menu. In my case, I call invalidateOptionsMenu() each time when my menu item text value changed, then call onPrepareOptionsMenu() to change item valueTibbitts
M
83

invalidateOptionsMenu() is used to say Android, that contents of menu have changed, and menu should be redrawn. For example, you click a button which adds another menu item at runtime, or hides menu items group. In this case you should call invalidateOptionsMenu(), so that the system could redraw it on UI. This method is a signal for OS to call onPrepareOptionsMenu(), where you implement necessary menu manipulations. Furthermore, OnCreateOptionsMenu() is called only once during activity (fragment) creation, thus runtime menu changes cannot be handled by this method.

All can be found in documentation:

After the system calls onCreateOptionsMenu(), it retains an instance of the Menu you populate and will not call onCreateOptionsMenu() again unless the menu is invalidated for some reason. However, you should use onCreateOptionsMenu() only to create the initial menu state and not to make changes during the activity lifecycle.

If you want to modify the options menu based on events that occur during the activity lifecycle, you can do so in the onPrepareOptionsMenu() method. This method passes you the Menu object as it currently exists so you can modify it, such as add, remove, or disable items. (Fragments also provide an onPrepareOptionsMenu() callback.)

On Android 2.3.x and lower, the system calls onPrepareOptionsMenu() each time the user opens the options menu (presses the Menu button).

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().

Monodrama answered 16/1, 2015 at 12:44 Comment(3)
but i dont have to call invalidateOptionsMenu(). I simply do menu.add() and it is shownChinkapin
It's needed for dynamic menus. How could you call menu.add() from outside onCreateOptionsMenu()? How could you make, e.g., menu items change when user scrolls down a listview? By invalidating the menu on scroll, forcing the activity to recreate it, and checking for a certain condition within onCreateOptionsMenu(). This wouldn't work if you check for the condition within onCreateOptionsMenu() without invalidating, because the activity would think the menu is still OK.Blinkers
i don't know how to write code here but lets see: OnCreateOptionsMenu(IMenu menu){ _Menu = menu; } And you have the reference to Menu. Now do whatever you want to doChinkapin
C
25

use this to reload new menu during app lifecycle:

new:

getActivity().invalidateOptionsMenu();

old

ActivityCompat.invalidateOptionsMenu(getActivity());
Concertize answered 13/1, 2017 at 9:41 Comment(4)
in which lifecycle of Fragment should we call this?Crackerbarrel
ActivityCompat .invalidateOptionsMenu() is Deprecated nowVirtue
Deprecated, now the correct call is just invalidateOptionsMenu() (from Activity)Joyjoya
Deprecated, from fragment call getActivity().invalidateOptionsMenu();.Tavis
B
3

You need to override method onPrepareOptionsMenu(), write your update code of action menu in same method and if you are using fragment then add setHasOptionsMenu(true); in onCreateView().

Hope it helps you

Blythebm answered 16/1, 2015 at 12:44 Comment(4)
okay, but onPrepareOptionsMenu() is only a fixed method. By fixed i mean, its not the case that one time onPrepareOptionsMenu() does something and other time it does something else.Chinkapin
Why not? There's such thing as conditions (if-else), you know. You can make it do whtever you want in whatever situation you imagineMonodrama
@AlexanderZhak so, what is the answer ? is this the only way or there can be other scenarios?Chinkapin
I have not received any concrete answer yet :(Chinkapin
U
3

One use I've found is forcing an order of operations between onResume and onCreateOptionsMenu/onPrepareOptionsMenu. The natural order (as of platform 22 at least) seems to flip flop around, especially when re-orientating your device.

Call invalidateOptionsMenu() in onResume() and you'll guarantee that onPrepareOptionsMenu will be called after onResume (it may additionally be called before). For example, this will allow enabling a menu item based on data retrieved in onResume.

Untrue answered 30/8, 2017 at 4:35 Comment(0)
O
1
/**
 * Set a hint for whether this fragment's menu should be visible.  This
 * is useful if you know that a fragment has been placed in your view
 * hierarchy so that the user can not currently seen it, so any menu items
 * it has should also not be shown.
 *
 * @param menuVisible The default is true, meaning the fragment's menu will
 * be shown as usual.  If false, the user will not see the menu.
 */
public void setMenuVisibility(boolean menuVisible) {
    if (mMenuVisible != menuVisible) {
        mMenuVisible = menuVisible;
        if (mHasMenu && isAdded() && !isHidden()) {
            mHost.onSupportInvalidateOptionsMenu();
        }
    }
}

XML menu sample:

<?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_edit"
    android:icon="@drawable/ic_edit"
    android:title="Edit Task"
    app:showAsAction="always" />

<item
    android:id="@+id/action_delete"
    android:icon="@drawable/ic_delete"
    android:title="Delete Task"
    app:showAsAction="always" />

<item
    android:id="@+id/action_check"
    android:icon="@drawable/ic_check"
    android:title="Check Task"
    app:showAsAction="always" />

<item
    android:id="@+id/action_uncheck"
    android:icon="@drawable/ic_undo"
    android:title="Uncheck Task"
    app:showAsAction="always" />
</menu>

Code inside a sample fragment:

private boolean isMenuItemChecked;

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    setMenuVisibility(false);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.my_menu, menu);
    super.onCreateOptionsMenu(menu, inflater);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    try {
        menu.findItem(R.id.action_check).setVisible(!isMenuItemChecked);
        menu.findItem(R.id.action_uncheck).setVisible(isMenuItemChecked);
    }
    catch(Exception e) {
        Log.e(TAG, "onPrepareOptionsMenu error");
    }
}

public void loadUi(boolean isMenuItemChecked) {
    this.isMenuItemChecked = isMenuItemChecked;
    setMenuVisibility(true);
}
Oneself answered 24/11, 2016 at 17:15 Comment(1)
You code showed my a great fix to my own problem . Thanx for the snippetDurwood
M
1
  1. Put the initial state of the menu in onCreateOptionsMenu(...).

  2. Use the invalidateOptionsMenu() to force onCreateOptionsMenu(...) and onPrepareOptionsMenu(...).

  3. In onPrepareOptionsMenu(...), call menu.clear() to remove all items from the menu.

  4. Still in onPrepareOptionsMenu(...) place your dynamic menu changes after the clear.

Microfarad answered 19/5, 2017 at 10:27 Comment(0)
M
1

It's old, but hope this helps some one out in the future.

One use I found on real life scenario:

Assume you've a list of items that are stored into database, and you've 2 activities:

  1. DisplayActivity: which displayed these objects after getting them from database.
  2. EditActivity: used to edit an existing item & save that into database.

You decided to have a couple of options to go from DisplayActivity to EditActivity:

  • First: To add a brand-new item into database.
  • Second: To edit an existing item.

In order not to repeat yourself by duplicating code, you decided to use EditActivity for both purposes.

And so, you want to customize Options Menu according to each purpose. For this case you'd build a default options menu using onCreateOptionsMenu(), and leave it as-is when it's time to edit an existing item; and invalidateOptionsMenu() it when it's time to create new items; and in this case onPrepareOptionsMenu() is auto triggered for customizing your menu.

For instance the Options menu can have a delete option for editing an existing item, and this should be hidden when adding a new item.

Martguerita answered 8/3, 2019 at 0:14 Comment(0)
C
0

Edit: Here is a better answer to the question.

A good use for invalidateOptionsMenu() is when we have a ListView and Delete All MenuItem so when the ListView is empty we should use invalidateOptionsMenu() to remove the Delete All MenuItem.

Here is a question related to this answer: Question.

Clearance answered 31/10, 2016 at 11:48 Comment(2)
If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From ReviewPatriliny
Actually this link provide context to the answer @PatrilinyClearance
T
0

From fragment call getActivity().invalidateOptionsMenu();.

Tavis answered 2/9, 2019 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.