创建菜单
菜单是应用程序的重要组成部分。它们一般用于调出程序功能和设置。Android提供了一套很简单的创建标准菜单的接口。
Android提供了三种基本的菜单类型:
选项菜单(Options Menu)是最主要的菜单类型。它使用MENU键呼出。在选项菜单中有两组菜单项:
图标菜单 这是按下menu后一开始就可见的一组菜单。最大支持6个菜单项。这是唯一一种支持图标的菜单项,也是唯一一种不支持单选框和复选框的菜单。
扩展菜单 这是一个直排菜单,使用图标菜单上的"More"项调出。它只在图标菜单项目大于6个时存在。
快捷菜单(Context Menu)是在某个View对象上长按呼出的菜单。
子菜单(Submenu)是按下选项菜单或快捷菜单的一项时弹出的菜单。子菜单不支持嵌套子菜单。
Options Menu 选项菜单
选项菜单使用menu键调出。选项菜单最多容纳6个元素,如果多余6个则自动放在扩展菜单中。
选项菜单是你应该包含应用程序基本功能的地方,以及其他的项目(例如一个主屏幕或者一个应用程序设置)。你也可以增加子菜单来管理程序设置。
当菜单第一次被打开时,Android系统会调用onCreateOptionsMenu()方法。你可以使用xml或者add()方法来给菜单增加项目。 add()方法增加一个菜单项目并返回它的引用。你可以用返回的引用来设置额外的属性,例如图标、快捷键、intent等。
有好几个add()方法。一般来说你使用接受一个itemId参数的那一个。itemId是一个用来标识一个菜单项的唯一的整数。当一个菜单项被选中时,系统调用onOptionsItemSelected()方法。该回调函数传递一个MenuItem对象表示你选中的对象。你可以使用getItemId()方法来得到id,以进行对应的操作。
这里是一个增加菜单项的例子:
/* Creates the menu items */
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_NEW_GAME, 0, "New Game");
menu.add(0, MENU_QUIT, 1, "Quit");
return true;
}
/* Handles item selections */
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_NEW_GAME:
newGame();
return true;
case MENU_QUIT:
quit();
return true;
}
return false;
}
add()方法接受4个参数:
groupId: 将该菜单项关联到某个特定的组(见后面的介绍)。
itemId: 用于在回调函数标识一个菜单项。
order: 用于定义它的显示顺序 —— 默认按照add()的顺序来显示。
title: 菜单项的屏幕显示名称 —— 可以为String,也可以为string资源。
提示:如果你有一些菜单项可以放在同一个标题下,考虑将他们组成一个子菜单。
Adding icons 增加图标
可以使用setIcon()来为菜单增加图标。例如:
menu.add(0, MENU_QUIT, 0, "Quit")
.setIcon(R.drawable.menu_quit_icon);
Modifying the menu 修改菜单
如果你想在选项菜单第一次启动以后去改变它,则需改写onPrepareOptionsMenu()方法,该方法每次菜单打开时都会调用。该方法会将 Menu对象的引用传给你,就像onCreateOptionsMenu()回调一样。这在你需要动态改变菜单内容时很有用。
注意:根据当前选择的菜单项来决定如何改变菜单项是不好的习惯。记住,在触摸模式,不会有一个被选择(或者处于焦点的)菜单项。当你需要基于某个UI元素来提供功能时,你应该用快捷菜单来实现这样的行为。
Context Menu 弹出菜单
Android 弹出菜单在概念上和PC上的右键菜单差不多。当一个view注册了一个快捷菜单时,在这个view上长按会弹出一个浮动菜单。快捷菜单可以被注册到任意 veiw对象,但它们一般来说会被注册到ListView。ListView在被按下时背景颜色会变化,暗示会有快捷菜单弹出。
注意:快捷菜单项不支持图标和快捷键。
要创建一个快捷菜单,你应该改写Activity的onCreateContextMenu()和onContextItemSelected()方法。在onCreateContextMenu()回调函数中,你可以使用add()方法来添加一个菜单项,或者在xml中定义。然后,使用registerForContextMenu()方法来给View注册一个快捷菜单。
例如,下面是一段增加快捷菜单的代码:
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, EDIT_ID, 0, "Edit");
menu.add(0, DELETE_ID, 0, "Delete");
}
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case EDIT_ID:
editNote(info.id);
return true;
case DELETE_ID:
deleteNote(info.id);
return true;
default:
return super.onContextItemSelected(item);
}
}
在onCreateContextMenu(),我们得到ContextMenu用来增加菜单项,被选择的View对象和一个ContextMenuInfo对象,该对象提供了关于该View对象的额外信息。在该例子中,仅仅加入了几个菜单项,没有什么特殊的动作。在onContextItemSelected()回调函数中,我们从MenuItem()中请求AdapterContextMenuInfo,该对象提供了当前被选择的菜单项的信息。我们只需要取得它的ID。
要为ListView所有的项注册快捷菜单,我们将整个ListView对象传给 registerForContextMenu(View) 方法:
registerForContextMenu(getListView());
你可以将任意View对象传给一个快捷菜单。这里,getListView()返回Notepad程序的ListActivity中的ListView对象。这样,该列表中的所有项被注册到快捷菜单。
Submenus 子菜单
一个子菜单可以在除子菜单的任意菜单中被加入。这一点在你的个程序有一大堆功能,并且这些功能可以按主题分类时很有用(例如PC程序中的文件、编辑、视图)。
一个子菜单使用addSubMenu()来创建。该方法返回一个SubMenu对象。你可以使用add()方法来增加菜单项。例如:
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
SubMenu fileMenu = menu.addSubMenu("File");
SubMenu editMenu = menu.addSubMenu("Edit");
fileMenu.add("new");
fileMenu.add("open");
fileMenu.add("save");
editMenu.add("undo");
editMenu.add("redo");
return result;
}
选择子菜单中的选项将被onOptionsItemSelected()回调函数处理。你也可以使用xml来增加子菜单。
Define Menus in XML 在xml中定义菜单
就像Android UI布局一样,你就可以在xml里定义应用程序菜单,然后在你的菜单的onCreate...()回调函数中展开它。这使得你的应用程序代码更加整洁,并且把界面设计放到xml里面也更加直观。
首先,建一个文件夹res/menu。这是你所有的定义菜单的xml文件的位置。
在一个菜单xml布局中,有3个有效元素:
,和。菜单项和组元素必须为菜单的子元素,但菜单项元素也可以为一个组的子元素,另一个菜单元素也可以为一个菜单项的子元素(在子菜单中)。任何文件的根元素必须为一个菜单元素。
我们以一个选项菜单为例,建立一个名为options_menu.xml的文件:
《menu xmlns:android="http://schemas.android.com/apk/res/android"》
《item android:id="@+id/new_game"
android:title="New Game" /》
《item android:id="@+id/quit"
android:title="Quit" /》
《/menu》
然后,在onCreateOptionsMenu() 方法中展开这个菜单:
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
getMenuInflater()方法返回我们的activity的context的MenuInflater。我们然后可以调用inflate(),把指向Menu对象的一个指针传给它。
在创建项目比较多的菜单时,这样做会保持我们代码的清晰。
你可以将菜单项放在一个group元素中来使他们组成一个组,也可以将一个菜单嵌套在另一个菜单中形成子菜单。每个元素支持所有的控制属性包括快捷键、复选框、图标等。详见Available Resource Types一文的 Menus 节。
Menu Features 菜单特性
这里是一些大多数菜单具有的其他特性。
Menu groups 菜单组
当给一个菜单加入项时,你可以把它们组成一个菜单组。一个菜单组是一组菜单项的集合,它们具有某些相似的特性,例如可见性、有效性和可选择性。
一个菜单组使用一个整数定义(在xml的一个资源id)。一个菜单项可以在它加入菜单时被加入菜单组。使用接受groupID的add()方法来完成这一点。
你可以使用setGroupVisible()来显示和隐藏整个菜单组;使用setGroupEnabled()来使菜单组可用和不可用;使用setGroupCheckable()来使菜单组可以或者不可选择。
Checkable menu items 可选取菜单
任何菜单项可以被用作选项开关。可以使用单选框和复选框来完成,如下图。
注意:图标菜单中的菜单项不能显示复选框或者单选框。如果你希望把这些图标菜单设为可选择的,你必须自己使用颜色或者图标变化等方式来保证它们在选定和不选定状态下的可区分性。
要使一个菜单项变为可选择的,使用setCheckable()方法:
menu.add(0, VIBRATE_SETTING_ID, 0, "Vibrate")
.setCheckable(true);
这将显示一个复选框。当这个菜单项被选择时,onOptionsItemSelected()被调用。你可以使用isChecked()和setChecked()来查看和设置它的选择状态。这里是onOptionsItemSelected()的内容:
switch (item.getItemId()) {
case VIBRATE_SETTING_ID:
if (item.isChecked()) item.setChecked(false);
else item.setChecked(true);
return true;
...
}
要创建一组单选框,将同样的组id分配给每个菜单项,并调用setGroupCheckable()。这种情况下,你不需要为每个菜单项调用setCheckable(),因为菜单组整个被设置为可选择。下面为在子菜单中设置两个互斥选项的方法:
SubMenu subMenu = menu.addSubMenu("Color");
subMenu.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "Red");
subMenu.add(COLOR_MENU_GROUP, COLOR_BLUE_ID, 0, "Blue");
subMenu.setGroupCheckable(COLOR_MENU_GROUP, true, true);
setGroupCheckable()方法中, 第一个参数为我们需要设置的组id. 第二个参数为我们是否需要该组的菜单项为可选择. 第三个参数为各个选项之间是否互斥. 当设为互斥时, 选择一个选项将导致另外的选项被取消.
注意: 可选择菜单一般是被用于某个会话, 而并不被保存在设备上(例如, 在地图程序中, 地图模式设置并不会被保存). 如果你需要为用户保存设置, 那么你应该使用Preferences, 使用PreferenceActivity来管理它们.
Shortcut keys 快捷键
可以使用 setAlphabeticShortcut(char) , setNumericShortcut(int)和or setShortcut(char,int)来设置一个菜单项的字母/数字/字母和数字快捷键. 例如:
menu.add(0, MENU_QUIT, 0, "Quit")
.setAlphabeticShortcut('q');
现在,当菜单打开时(或者按住MENU键时), 按下"q"键将选择这个选项.
这个快捷键将被显示为菜单项的一个提示, 在菜单项名称的下面.(除图标菜单之外).
Note: 快捷菜单项不能添加快捷键.
Menu item intents 菜单项intent
如果你读过应用程序基础一节, 你应该对Android Intent 有所了解. 它们允许应用程序互相绑定, 共享信息并合作. 就像你的应用程序可能发出一个intent来启动一个浏览器,一个邮件客户端, 或者你的应用程序中的另一个Activity一样,你可以从一个菜单项中完成这个操作. 有两种方法: 定义一个intent并将它赋给一个菜单项, 或者定义一个intent并让Android在设备中搜索activity并动态为每个合适的activity增加一个菜单项.
关于创建intent和让你的应用程序为其他程序提供服务的信息, 详见Intents and Intent Filters 文档.
Set an intent for a single menu item 为一个菜单项设置一个intent
如果你希望提供一个菜单项来启动一个Activity, 你可以使用setIntent来为这个菜单定义一个特殊的intent.
例如, 在onCreateOptionsMenu()方法中, 你可以定义一个新的菜单项:
MenuItem menuItem = menu.add(0, PHOTO_PICKER_ID, 0, "Select Photo");
menuItem.setIntent(new Intent(this, PhotoPicker.class));
Android将在该项被选择时启动对应的activity.
注意: 这不会返回一个结果给你的Activity. 如果你希望得到一个返回值, 则不要用setIntent(). 使用onOptionsMenuItemSelected() 或 onContextMenuItemSelected()处理选择,并调用startActivityForResult().
Dynamically add intents 动态增加intent
如果有多个activity和你当前的activity或者当前选项相关, 那么你的应用程序可以动态的增加菜单项来运行其他服务. 在菜单创建过程中, 定义一个intent, 它的category为Intent.ALTERNATIVE_CATEGORY 和/或Intent.SELECTED_ALTERNATIVE, 它的当前选定的MIME类型, 和其它的要求, 就像你定义一个满足特定intent filter来启动一个新的Activity的intent一样. 然后调用addIntentOptions()来让Android搜索满足要求的服务并将它们加入菜单. 如果没有应用程序满足要求, 那么就不会增加额外菜单项.
注意: SELECTED_ALTERNATIVE被用于处理当前被选择的元素. 因此它应该只在使用onCreateContextMenu()或onPrepareOptionsMenu()创建菜单时使用.
这里有一个让Android搜索外部服务的例子:
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
// Create an Intent that describes the requirements to fulfill, to be included
// in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
Intent intent = new Intent(null, getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
// Search for, and populate the menu with, acceptable offering applications.
menu.addIntentOptions(
thisClass.INTENT_OPTIONS, // Menu group
0, // Unique item ID (none)
0, // Order for the items (none)
this.getComponentName(), // The current Activity name
null, // Specific items to place first (none)
intent, // Intent created above that describes our requirements
0, // Additional flags to control items (none)
null); // Array of MenuItems that corrolate to specific items (none)
return true;
}
对每个找到的满足intent的Activity, 增加一个菜单项, 使用android:label值作为菜单项的名称. addIntentOptions()还将增加的返回菜单项个数.
同时注意, 当addIntentOptions()被调用时,它将覆盖第一个参数中的菜单组中的所有菜单项.
如果你想让你的Activity为其它应用程序菜单服务, 你只需要像往常一样定义一个intent filter. 只是增加一个ALTERNATIVE 和/或SELECTED_ALTERNATIVE在元素中的name属性中. 例如:
《intent-filter label="Resize Image"》
...
《category android:name="android.intent.category.ALTERNATIVE" /》
《category android:name="android.intent.category.SELECTED_ALTERNATIVE" /》
...
《/intent-filter》