
Modifying menus and menu items during runtime
Though it's been stated many times, it's considered the "best" programming practice to create UI in XML rather than in Java. There are still times when you may need to do it in code. This is especially true if you wanted a menu item to be visible (or enabled) based on some external criteria. Menus can also be included in resource folders, but there are times when you need code to perform the logic. One example might be if you wanted to offer an upload menu item only if the user is logged in to your app.
In this recipe, we will create and modify the menu only through code.
Getting ready
Create a new project in Android Studio and call it RuntimeMenu
using the default Phone & Tablet option. Select the Empty Activity option when prompted to add an Activity. Since we will create and modify the menu completely in code, we will not need to create a res/menu
directory.
How to do it...
To start, we will add string resources for our menu items and a button to toggle the menu visibility. Open the res/strings.xml
file and follow these steps:
- Add the following two strings to the existing
<resources>
element:<string name="menu_download">Download</string> <string name="menu_settings">Settings</string>
- Add a button to
activity_main.xml
withonClick()
set totoggleMenu
as shown here:<Button android:id="@+id/buttonToggleMenu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Toggle Menu" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:onClick="toggleMenu"/>
- Open
ActivityMain.java
and add the following three lines of code just below the class declaration:private final int MENU_DOWNLOAD = 1; private final int MENU_SETTINGS = 2; private boolean showDownloadMenu = false;
- Add the following method for the button to call:
public void toggleMenu(View view) { showDownloadMenu=!showDownloadMenu; }
- When the activity is first created, Android calls
onCreateOptionsMenu()
to create the menu. Here is the code to dynamically build the menu:@Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_DOWNLOAD, 0, R.string.menu_download); menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings); return true; }
- For best programming practice, don't use
onCreateOptionsMenu()
to update or change your menu; instead, useonPrepareOptionsMenu()
. Here is the code to change the visibility of the Download menu item based on our flag:@Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem menuItem = menu.findItem(MENU_DOWNLOAD); menuItem.setVisible(showDownloadMenu); return true; }
- Though not technically required for this recipe, this
onOptionsItemSelected()
code shows how to respond to each menu item:@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_DOWNLOAD: Toast.makeText(this, R.string.menu_download, Toast.LENGTH_LONG).show(); break; case MENU_SETTINGS: Toast.makeText(this, R.string.menu_settings, Toast.LENGTH_LONG).show(); break; default: return super.onContextItemSelected(item); } return true; }
- Run the program on a device or emulator to see the menu changes.
How it works...
We created an override for onCreateOptionsMenu()
, just like we did in the previous recipe, Creating an Options Menu. But instead of inflating an existing menu resource, we created the menu using the Menu.add()
method. Since we want to modify the menu items later as well as respond to the menu item events, we have defined our own menu IDs and passed them to the add()
method.
onOptionsItemSelected()
is called for all the menu items, so we get the menu ID and use a switch
statement based on the IDs we created. We return true
if we are handling the menu event, otherwise we pass the event to the super class.
Changing the menu occurs in the onPrepareOptionsMenu()
method. To simulate an external event, we created a button to toggle a Boolean flag. The visibility of the Download menu is determined by the flag. This is where you would want to create your custom code based on whatever criteria you set. Your flag could be set using the current player level or maybe when a new level is ready for release; you send a Push message, which enables the menu item.
There's more...
What if we wanted this Download option to be easily noticed to indicate whether it's available? We could tell Android we want the menu in the Action Bar by adding the following code to onPrepareOptionsMenu()
(before the return statement):
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
Now if you run the code, you will see the Download menu item in the Action Bar, but the behavior isn't correct.
Earlier, when we didn't have a menu item in the Action Bar, Android called onPrepareOptionsMenu()
each time we opened the overflow menu so the visibility was always updated. To correct this behavior, add the following line of code to the toggleMenu()
method called by the button:
invalidateOptionsMenu();
The invalidateOptionsMenu()
call tells Android that our option menu is no longer valid, which then forces a call to onPrepareOptionsMenu()
giving us the behavior we expect.
Note
Android considers the menu as always open if a menu item is displayed in the Action Bar.