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.
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().
use this to reload new menu during app lifecycle:
ActivityCompat.invalidateOptionsMenu(getActivity());
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
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
.
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.
/**
* 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);
}
The best way that worked for me is demonstrated below
Put the initial state of the menu in onCreateOptionsMenu(...)
Use the invalidateOptionsMenu() to force onCreateOptionsMenu(...) and onPrepareOptionsMenu(...)
In onPrepareOptionsMenu(...), call menu.clear() to remove all items from the menu.
Still in onPrepareOptionsMenu(...) place your dynamic menu changes after the clear
Hope this helps...