创建菜单 Creating Menus
菜单是任何应用程序的一个重要部分,提供了透露应用程序功能和设置的通用接口。 Android 为开发者提供了一个简单的编程接口来实现各种条件下的标准化应用程序菜单。
Android 提供了三种基础菜单类型:
选项菜单 Options Menu
这是一个活动的主菜单。通过按下设备菜单键来显示它。选项菜单包含两组菜单项:
图标菜单 Icon Menu
这个是当用户按下菜单键时最初出现屏幕下方的 item 集合。它支持最多 6 个菜单项。只有这些菜单支持图标而且这些菜单并不支持 checkboxes 或者 radio buttons 。
扩展菜单 Expanded Menu
这是通过按“更多”菜单显现出来的一个竖向的项目列表。它仅当图标菜单过多时存在而且是由 6 个以及其它选项菜单组成。
上下文菜单 Context Menu
这是一个浮动菜单列表,通常在你长时间按在一个视图上时出现(比如一个列表项)
子菜单 Submenu
这是一个浮动菜单列表,通过在选项菜单或上下文菜单选择菜单项显露出来。不支持嵌套子菜单。
选项菜单 Options Menu
这个选项菜单通过按设备菜单键打开。打开后,出现图标菜单,可包含 6 个菜单项。如果添加多于 6 个菜单项,多出的部分将通过“更多”菜单项在扩展菜单中显示。扩展菜单项在多于 6 个菜单项时自动添加。
选项菜单应该包含应用程序的基本功能以及任何必要的浏览项(例如,返回桌面或应用程序设置)。你还可以通过增加子菜单 Submenus 来组织主题和包含额外的菜单功能。
当菜单第一次被打开时,系统会调用活动 onCreateOptionsMenu() 回调函数。重写该方法并生成传递给你的这个菜单对象。你可以通过扩充定义在 XML 文件中的一个菜单资源或者通过为你想要的每一个菜单项调用 add() 方法生成这个菜单。这个方法增加一个菜单项 MenuItem ,并返回新创建的对象。你可以用返回的 MenuItem 来设置附加属性如图标,快捷键,意图以及这个菜单项的其它设置。
有多个 add() 方法。通常,你会使用接受一个 itemId 参数的那个。这是一个唯一的整数,允许你在回调函数中识别这个 item 。
当一个菜单项从选项菜单中被选择时,你会接收到一个
onOptionsItemSelected()
回调。这个回调传给你选中的
MenuItem
。
你可以通过请求
itemId
:
getItemId()
来识别它,这将返回
add()
方法分配的整数。一旦你识别了这个菜单项,就可以采取合适的动作。
下面是一个活动里的例子,其中我们创建了一个选项菜单并处理菜单项的选择:
/* Creates the menu items */
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_NEW_GAME, 0, "New Game");
menu.add(0, MENU_QUIT, 0, "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()
方法有四个参数:
groupId
,
itemId
,
order
,
和
title
。
groupId
允许你关联这个菜单到一个菜单组中(更多参见下面的菜单组
Menu groups
)
-
这个例中,我们忽略掉它。
itemId
是用来识别菜单项的唯一的整数,在回调函数中使用。
order
允许我们定义菜单的显示顺序
-
缺省情况下,它们以添加时的顺序排列。
title
当然是菜单的名字(可以是一个字符串资源,为了本地化更加方便,建议你使用资源)。
提示 : 如果你有一些可以以一个标题归类的菜单项,考虑以子菜单 Submenu 的方式组织它们。
增加图标 Adding icons
图标也可以通过 setIcon() 函数被添加到菜单项中。
menu.add(0, MENU_QUIT, 0, "Quit")
.setIcon(R.drawable.menu_quit_icon);
修改菜单 Modifying the menu
如果有些时候你想在选项菜单被打开的时候 re-write 它,可以 override onPrepareOptionsMenu() 方法,该方法在每一次菜单被打开的时候调用。它将传递给你菜单对象,就像回调一样。这对于根据应用程序状态调整菜单选项很有用。
注意 : 当改变菜单项时,根据当前选择的 item 来这样做是一个不好的行为。记住,在触摸模式中,将不会有一个选择或聚焦的 item 。相反,当你想基于 UI 中的某个特定 item 来提供功能时,你应该使用一个 Context Menu 来完成这种行为。
上下文菜单 Context Menu
Android 的上下文菜单在概念上和 PC 软件的右键菜单类似。当一个视图注册到一个上下文菜单时,执行一个在该对象上的“长按”(按住不动差不多两秒钟)动作,将出现一个提供相关功能的浮动菜单。上下文菜单可以被注册到任何视图对象中,不过,最常见的是用于列表视图 ListView 的 item ,在按中列表项时,会转换其背景色而提示将呈现上下文菜单。 (电话联系人列表提供了关于这个特性的一个很好的例子)。
注意:上下文菜单项不支持图标或快捷键。
为了创建一个上下文菜单,你必须重写这个活动的上下文菜单回调函数:
onCreateContextMenu()
和
onContextItemSelected()
。在回调函数
onCreateContextMenu()
里,你可以通过使用一个
add()
方法来添加菜单项,或者通过扩充一个定义在
XML
中的菜单资源。然后,通过
registerForContextMenu()
为这个视图注册一个上下文菜单
ContextMenu
.
比如,下面的代码可以被用到 Notepad 应用程序中来为列表中的每一个注释添加一个上下文菜单:
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()
中,除了给出将添加
MenuItems
的
ContextMenu
外,还需要给定选中的视图和一个上下文菜单信息
ContextMenuInfo
对象,该对象提供了选中对象的附加信息。在本例中,
onCreateContextMenu()
没做什么特别的事
-
只是添加了一些菜单项。在
onContextItemSelected()
回调函数中,我们从
MenuItem
中请求
AdapterContextMenuInfo
,该对象提供当前选中项的信息。我们从中所要的只是这个选中项的列表
ID
,所以无论编辑还是删除一个注释,我们通过这个对象的
AdapterContextMenuInfo.info
字段来找到该
ID
。这个
ID
被传递给
editNote()
和
deleteNote()
方法来执行相应的动作。
现在,要为一个列表视图中的所有项注册上下文菜单,我们可以传递整个的列表视图对象给 registerForContextMenu(View) 方法:
registerForContextMenu(getListView());
记住,你可以传递任何视图对象来注册一个上下文菜单。这里, getListView() 返回这个被用于 Notepad 应用程序的列表活动 ListActivity 中的列表视图对象。这样,这个列表中的任何 item 都被注册到这个上下文菜单。
子菜单 Submenus
一个子菜单可以被添加进任何菜单中,但不能加入另外的子菜单中。当你的应用程序有很多功能可以按主题组织的时候,这将非常有用,就好比 PC 应用程序的菜单栏(文件,编辑,视图,等)。
子菜单通过 addSubMenu() 加入到已有的菜单中而创建。该函数会返回一个子菜单 SubMenu 对象(菜单 Menu 的一个扩展)。然后你可以通过调用 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 中定义父菜单时增加子菜单。
在 XML 里定义菜单 Define Menus in XML
就像
Android
用户界面布局一样,你可以在
XML
文件中定义菜单,然后在你菜单的
onCreate...()
回调函数中扩充它们。这使得你的应用程序代码简洁而且把更多的界面设计分离到
XML
文件中,这更容易形象化。
首先,在你的工程
res/
的目录下创建一个新的目录叫
menu
。你所有定义应用程序菜单的
XML
文件都应该放在这里。
在一个菜单
XML
布局中,有三个合法的元素:
<menu>
,
<group>
和
<item>
。
item
和
group
必须是菜单的子元素,而
item
元素还可以是
group
的子元素,并且另外一个菜单元素可以是一个
item
的子元素(来创建一个子菜单)。当然,任何文件的根元素必须是一个
menu
元素。
作为一个例子,我们将定义和在上面的选项菜单
Options Menu
章节中所创建的相同的菜单,我们首先在目录
res/menu/
里创建一个名为
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()
方法里,我们通过
MenuInflater.inflate()
方法扩充这个资源:
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
getMenuInflater() 方法返回我们活动上下文的 MenuInflater 。 然后我们调用 inflate() ,传递给它一个指向我们菜单资源的指针以及回调给出的菜单对象。
尽管和在
onCreateOptionsMenu()
创建菜单比较起来,上面的例子看起来做了更多的事情,但是如果处理更多的菜单项,这将省掉很多麻烦并让你的代码简洁。
你可以通过把
item
元素打包进一个
group
中来定义菜单组
menu groups
,然后通过在一个
item
中嵌入另外一个
menu
来创建子菜单。每个元素都支持必需的属性来控制快捷键,复选框,图标,以及更多特性。
要了解这些属性和更多的 XML 语法,请参见可用资源类型 Available Resource Types 中的相关主题。