Menus
HelloMenus Project
HelloMenu
Three types of menus
Options menu - Icon Menu
Icon Menu - The menu items visible at the bottom of the screen at the press of the MENU key. It supports a maximum of six menu items. These are the only menu items that support icons and the only menu items that do not support checkboxes or radio buttons.
Options menu - Expanded menu
Expanded Menu - The vertical list of menu items exposed by the "More" menu item in the Icon Menu. When the Icon Menu is full, the expanded menu is comprised of the sixth menu item and the rest.
Context menu
Context Menu - A floating list of menu items that appears when the user performs a long-press on a View.
Submenu
Submenu - A floating list of menu items that the user opens by pressing a menu item in the Options Menu or a context menu. A submenu item cannot support a nested submenu.
HelloMenu
Project, Application, & Activity all just:
HelloMenu
Image resources
Stolen then edited from the various web locations. Mainly:
http://developer.android.com/guide/practices/ui_guidelines/icon_design.html
Fair warning
Options menu icons should NOT contain color.
http://developer.android.com/guide/practices/ui_guidelines/icon_design_menu.html
But I wanted color for my example. :)
Icon image sizes
From Icon Design Guidelines, Android 2.0
http://developer.android.com/guide/practices/ui_guidelines/icon_design.html
Icon filenames
From Icon Design Guidelines, Android 2.0
http://developer.android.com/guide/practices/ui_guidelines/icon_design.html
Application icon
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="edu.rosehulman.hellomenu"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/ic_hello_menu" android:label="@string/app_name">
<activity android:name=".HelloMenu"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
Options menu
public boolean onCreateOptionsMenu(Menu menu)
When the menu button is first pressed, this function is called to create the items in the initial menu.
public boolean onOptionsItemSelected(MenuItem item)
When an item in the menu is selected this function is the click listener.
public boolean onPrepareOptionsMenu(Menu menu)
Every time the menu button is pressed this function is called just before the menu is displayed to make changes to account for the current activity state
Declarative vs. Programatic creation
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem incMenuItem = menu.add(0, ITEM_ID_INCREMENT, Menu.NONE, R.string.increment);
MenuItem decMenuItem = menu.add(0, ITEM_ID_DECREMENT, Menu.NONE, R.string.decrement);
incMenuItem.setIcon(R.drawable.add);
decMenuItem.setIcon(R.drawable.remove);
return true;
}
Google's Advice - Use the XML
Instead of instantiating Menu objects in your application code, you should define a menu and all its items in an XML menu resource, then inflate the menu resource (load it as a programmable object) in your application code. Defining your menus in XML is a good practice because it separates your interface design from your application code (the same as when you define your Activity layout).
res/menu/options_menu.xml
<?xml version="1.0" encoding="utf-8"?>�<menu xmlns:android="http://schemas.android.com/apk/res/android>� <item android:id="@+id/new_game"� android:icon="@drawable/ic_new_game"� android:title="@string/new_game" />� <item android:id="@+id/quit"� android:icon="@drawable/ic_quit"� android:title="@string/quit" />�</menu>
Google's example
Add this code to HelloMenu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater optionsMenuInflater = getMenuInflater();
optionsMenuInflater.inflate(R.menu.options_menu, menu);
return true;
}
We'll make res/menu/options_menu.xml next
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, HelloMenu!</string>
<string name="app_name">HelloMenu</string>
<string name="red_background">Red Background</string>
<string name="yellow_background">Yellow Background</string>
<string name="blue_background">Blue Background</string>
<string name="green_background">Green Background</string>
<string name="white_background">White Background</string>
<string name="black_background">Black Background</string>
</resources>
options_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
</menu>
New Android XML menu file
Root element created as a menu
GUI editor
options_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:alphabeticShortcut="y"
android:title="@string/yellow_background"
android:id="@+id/yellow_background"
android:icon="@drawable/ic_menu_yellow_background">
</item>
</menu>
Run and click menu
Neat!
Obviously nothing happens if you click the menu item... yet.
onOptionsItemSelected
public static final String HM = "HM";
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch( item.getItemId() ) {
case R.id.yellow_background:
Log.d(HM, "Yellow");
// TODO: Actually change the color
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Run it!
Then we'll prepare the main.xml layout
main.xml
FrameLayout and 200dp wide TextView
main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/frame_layout">
<TextView
android:layout_height="wrap_content"
android:text="@string/hello"
android:layout_width="200dp"
android:layout_gravity="center"
android:gravity="center"
android:id="@+id/movie_quote_text_view" />
</FrameLayout>
Capture the objects
// Member variables
private FrameLayout mFrameLayout;
private TextView mMovieQuoteTextView;
// Within onCreate
mFrameLayout = (FrameLayout) findViewById(R.id.frame_layout);
mMovieQuoteTextView = (TextView) findViewById(R.id.movie_quote_text_view);
Changing color
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch( item.getItemId() ) {
case R.id.yellow_background:
Log.d(HM, "Yellow");
this.mFrameLayout.setBackgroundColor(Color.YELLOW);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Add a few more. I'll end up with Yellow, White, and Blue, but add whatever you like.
options_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:alphabeticShortcut="y"
android:title="@string/yellow_background"
android:id="@+id/yellow_background"
android:icon="@drawable/ic_menu_yellow_background"></item>
<item
android:alphabeticShortcut="w"
android:title="@string/white_background"
android:id="@+id/white_background"
android:icon="@drawable/ic_menu_white_background"></item>
<item
android:alphabeticShortcut="b"
android:title="@string/blue_background"
android:id="@+id/blue_background"
android:icon="@drawable/ic_menu_blue_background"></item>
</menu>
onClick
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch( item.getItemId() ) {
case R.id.yellow_background:
this.mFrameLayout.setBackgroundColor(Color.YELLOW);
return true;
case R.id.white_background:
this.mFrameLayout.setBackgroundColor(Color.WHITE);
return true;
case R.id.blue_background:
this.mFrameLayout.setBackgroundColor(Color.BLUE);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Options menu
Run with 3
Then try it again for more than 6 just to force an expanded menu
Expanded menu
Just repeated the 3 items 3 times YWB YWB YWB
Submenu
Google's sample submenu
Note: It's inside the item
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">� <item android:id="@+id/file" android:icon="@drawable/file"� android:title="@string/file" >� <!-- "file" submenu -->� <menu">� <item android:id="@+id/new" android:title="@string/new" />� <item android:id="@+id/open" android:title="@string/open" />� </menu>� </item>�</menu>
Menu groups
<?xml version="1.0" encoding="utf-8"?>�<menu xmlns:android="http://schemas.android.com/apk/res/android">� <item android:id="@+id/item1" android:icon="@drawable/item1" android:title="@string/item1" />� <!-- menu group -->� <group android:id="@+id/group1">� <item android:id="@+id/groupItem1" android:title="@string/groupItem1" />� <item android:id="@+id/groupItem2" android:title="@string/groupItem2" />� </group>�</menu>
Google's sample group
Note: A group is not within a item, it "groups" items.
Mainly used for RadioButtons, but can also be used to apply properties to everything in the group (like visibility)
Changing Text Color Item
<string name="text_color">Text Color</string>
<string name="red">Red</string>
<string name="yellow">Yellow</string>
<string name="blue">Blue</string>
<string name="green">Green</string>
<string name="white">White</string>
<string name="black">Black</string>
I'm only gonna use like 4 or so, but here are 6 options
New item with submenu in it
Then in that submenu the items are grouped
options_menu.xml properties
Type | id | title | other |
Item | @+id/change_text_color | @string/text_color | @drawable/ic_menu_brush |
Sub-menu | -- | -- | -- |
Group | -- | -- | checkable = single |
Item | @+id/red_text_color | @string/red | |
Item | @+id/white_text_color | @string/white | checked = true |
Item | @+id/black_text_color | @string/black | |
Item | @+id/green_text_color | @string/green | |
Additions to options_menu.xml
<item android:id="@+id/change_text_color"
android:alphabeticShortcut="t"
android:title="@string/text_color"
android:icon="@drawable/ic_menu_brush">
<menu>
<group android:checkableBehavior="single">
<item android:id="@+id/red_text_color"
android:title="@string/red"></item>
<item android:id="@+id/white_text_color"
android:title="@string/white"
android:checked="true"></item>
<item android:id="@+id/black_text_color"
android:title="@string/black"></item>
<item android:id="@+id/green_text_color"
android:title="@string/green"></item>
</group>
</menu>
</item>
Run it. Submenu & Groups!
Performing an action on a click
//.. Within onOptionsItemSelected
case R.id.black_text_color:
item.setChecked(true);
this.mMovieQuoteTextView.setTextColor(Color.BLACK);
return true;
case R.id.white_text_color:
item.setChecked(true);
this.mMovieQuoteTextView.setTextColor(Color.WHITE);
return true;
case R.id.red_text_color:
item.setChecked(true);
this.mMovieQuoteTextView.setTextColor(Color.RED);
return true;
case R.id.green_text_color:
item.setChecked(true);
this.mMovieQuoteTextView.setTextColor(Color.GREEN);
return true;
Taking action
Make any ugly combo you like
Your turn!
Some text to make an item, submenu, group the submenu items and make that group checkable = all
<string name="alignment">Alignment</string>
<string name="top">Top</string>
<string name="left">Left</string>
Images on next slide
Text alignment submenu
group checkable = all
Adding a checkbox submenu
options_menu.xml
<item android:alphabeticShortcut="a"
android:title="@string/alignment"
android:icon="@drawable/ic_menu_compass">
<menu>
<group android:checkableBehavior="all">
<item android:id="@+id/top"
android:title="@string/top"></item>
<item android:id="@+id/left"
android:title="@string/left"></item>
</group>
</menu>
</item>
Changing the alignment properties
FrameLayout.LayoutParams frameLayoutParams = (FrameLayout.LayoutParams) this.mMovieQuoteTextView.getLayoutParams();
frameLayoutParams.gravity ^= Gravity.LEFT;
this.mMovieQuoteTextView.setLayoutParams(frameLayoutParams);
item.setChecked(!item.isChecked());
Implementing layout params changes
// Above switch
FrameLayout.LayoutParams frameLayoutParams = (FrameLayout.LayoutParams) this.mMovieQuoteTextView.getLayoutParams();
// Within switch
case R.id.left:
item.setChecked(!item.isChecked());
frameLayoutParams.gravity ^= Gravity.LEFT;
this.mMovieQuoteTextView.setLayoutParams(frameLayoutParams);
return true;
case R.id.top:
item.setChecked(!item.isChecked());
frameLayoutParams.gravity ^= Gravity.TOP;
this.mMovieQuoteTextView.setLayoutParams(frameLayoutParams);
return true;
More robust approach (if you care)
if (item.isChecked()) {
frameLayoutParams.gravity |= Gravity.LEFT;
} else {
frameLayoutParams.gravity &= ~Gravity.LEFT;
frameLayoutParams.gravity |= Gravity.CENTER_HORIZONTAL;
}
if (item.isChecked()) {
frameLayoutParams.gravity |= Gravity.TOP;
} else {
frameLayoutParams.gravity &= ~Gravity.TOP;
frameLayoutParams.gravity |= Gravity.CENTER_VERTICAL;
}
// Handy if the checkBox starts off checked. No changes required for you.
Three types of menus
A note from Google
Context Menu
A context menu is conceptually similar to the menu displayed when the user performs a "right-click" on a PC. You should use a context menu to provide the user access to actions that pertain to a specific item in the user interface. On Android, a context menu is displayed when the user performs a "long press" (press and hold) on an item.
Context menu
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
When a long press is detected on a view that is registered for the context menu, this function is called to create the items in the initial context menu.
public boolean onContextItemSelected(MenuItem item)
When an item in the context menu is selected this function is the click listener.
Declarative approach again
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater contextMenuInflater = getMenuInflater();
contextMenuInflater.inflate(R.menu.context_menu, menu);
}
We'll make res/menu/context_menu.xml next
Add some strings (only a few needed)
<string name="select_quote">Select Quote</string>
<string name="movie_quote_0">Is this heaven? No, it\'s Iowa.</string>
<string name="movie_quote_1">...Bond. James Bond.</string>
<string name="movie_quote_2">Show me the money.</string>
<string name="movie_quote_3">I\'ll be back.</string>
<string name="movie_quote_4">Mama always said, Life was like a box of chocolates.</string>
<string name="movie_quote_5">Get busy livin or get busy dyin."</string>
<string name="movie_quote_6">Gentlemen, you can\'t fight in here! This is the War Room.</string>
<string name="movie_quote_7">It\'s good to be the king!</string>
<string name="movie_quote_8">Hello. My name is Inigo Montoya. You killed my father. Prepare to die.</string>
<string name="movie_quote_9">There\'s no crying in baseball!</string>
res/menu/context_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/movie_quote_0"/>
<item android:title="@string/movie_quote_1" />
<item android:title="@string/movie_quote_2" />
<item android:title="@string/movie_quote_3" />
<item android:title="@string/movie_quote_4" />
<item android:title="@string/movie_quote_5" />
<item android:title="@string/movie_quote_6" />
<item android:title="@string/movie_quote_7" />
<item android:title="@string/movie_quote_8" />
<item android:title="@string/movie_quote_9" />
</menu>
Register for the context menu
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mFrameLayout= (FrameLayout) findViewById(R.id.frame_layout);
mMovieQuoteTextView = (TextView);
findViewById(R.id.movie_quote_text_view);
registerForContextMenu(this.mMovieQuoteTextView);
}
No declarative solution?
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
menu.setHeaderTitle(getString(R.string.select_quote));
}
Let me know if someone finds a way to set the header in XML.
Displays the context menu
Selections don't do anything... yet!
onContextItemSelected
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
String msg = item.getTitle().toString();
this.mMovieQuoteTextView.setText(msg);
return true;
}
Changing the quote
Finished HelloMenu app
Learning objectives