Android提供了三种基础菜单类型:
- 选项菜单Options Menu
这是一个活动的主菜单。通过按下设备菜单键来显示它。选项菜单包含两组菜单项:
图标菜单Icon Menu
这个是当用户按下菜单键时最初出现屏幕下方的item集合。它支持最多6个菜单项。只有这些菜单支持图标而且这些菜单并不支持checkboxes或者radio buttons。
扩展菜单Expanded Menu
这是通过按“更多”菜单显现出来的一个竖向的项目列表。它仅当图标菜单过多时存在而且是由6个以及其它选项菜单组成。 - 上下文菜单Context Menu
这是一个浮动菜单列表,通常在你长时间按在一个视图上时出现(比如一个列表项) - 子菜单Submenu
这是一个浮动菜单列表,通过在选项菜单或上下文菜单选择菜单项显露出来。不支持嵌套子菜单。
在XML里定义菜单
就像Android用户界面布局一样,你可以在XML文件中定义菜单,然后在你菜单的onCreate...()
回调函数中扩充它们。这使得你的应用程序代码简洁而且把更多的界面设计分离到XML文件中,这更容易形象化。
首先,在你的工程res/
的目录下创建一个新的目录叫menu
。你所有定义应用程序菜单的XML文件都应该放在这里。
在一个菜单XML布局中,有三个合法的元素:<menu>
,<group>
和<item>
。item
和group
必须是菜单的子元素,而item
元素还可以是group
的子元素,并且另外一个菜单元素可以是一个item
的子元素(来创建一个子菜单)。当然,任何文件的根元素必须是一个 menu
元素。
首先在目录res/menu/
里创建一个名为options_menu.xml
的文件。
1
2
3
4
5
6
|
<
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()方法扩充这个资源:
1
2
3
4
5
|
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
来创建子菜单。每个元素都支持必需的属性来控制快捷键,复选框,图标,以及更多特性。
选项菜单
这个选项菜单通过按设备菜单键打开。打开后,出现图标菜单,可包含6个菜单项。如果添加多于6个菜单项,多出的部分将通过“更多”菜单项在扩展菜单中显示。扩展菜单项在多于6个菜单项时自动添加。
选项菜单应该包含应用程序的基本功能以及任何必要的浏览项(例如,返回桌面或应用程序设置)。你还可以通过增加子菜单Submenus来组织主题和包含额外的菜单功能。
当菜单第一次被打开时,系统会调用活动onCreateOptionsMenu()回调函数。重写该方法并生成传递给你的这个菜单对象。你可以通过扩充定义在XML文件中的一个菜单资源或者通过为你想要的每一个菜单项调用add()方法生成这个菜单。这个方法增加一个菜单项MenuItem,并返回新创建的对象。你可以用返回的MenuItem来设置附加属性如图标,快捷键,意图以及这个菜单项的其它设置。
有多个add()方法。通常,你会使用接受一个itemId参数的那个。这是一个唯一的整数,允许你在回调函数中识别这个item。
当一个菜单项从选项菜单中被选择时,你会接收到一个onOptionsItemSelected()回调。这个回调传给你选中的MenuItem
。
你可以通过请求itemId:getItemId()来识别它,这将返回add()
方法分配的整数。一旦你识别了这个菜单项,就可以采取合适的动作。
下面是一个Activity里的例子,其中我们创建了一个选项菜单并处理菜单项的选择:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/* 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 允许你关联这个菜单到一个菜单组中。itemId是用来识别菜单项的唯一的整数,在回调函数中使用。order 允许我们定义菜单的显示顺序-缺省情况下,它们以添加时的顺序排列。title当然是菜单的名字(可以是一个字符串资源,为了本地化更加方便,建议你使用资源)。
提示: 如果你有一些可以以一个标题归类的菜单项,考虑以子菜单Submenu的方式组织它们。
增加图标Adding icons
图标也可以通过setIcon()函数被添加到菜单项中。
menu.add(0, MENU_QUIT, 0, "Quit")
.setIcon(R.drawable.menu_quit_icon);
修改菜单Modifying the menu
在android 2.3以下,当用户要每次打开menu时就会调用onPrepareOptionsMenu()方法
在android 3.0以上,你必须调用invalidateOptionsMenu() 当你要update你的menu时,因为
action bar是一直出现的。然后系统将调用onPrepareOptionsMenu()更改menu.
上下文菜单Context Menu
Android的上下文菜单在概念上和PC软件的右键菜单类似。当一个视图注册到一个上下文菜单时,执行一个在该对象上的“长按”(按住不动差不多两秒钟)动作,将出现一个提供相关功能的浮动菜单。
上下文菜单可以被注册到任何视图对象中,不过,最常见的是用于列表视图ListView的item,在按中列表项时,会转换其背景色而提示将呈现上下文菜单。 (电话联系人列表提供了关于这个特性的一个很好的例子)。
为了创建一个上下文菜单,你必须重写这个活动的上下文菜单回调函数:onCreateContextMenu() 和onContextItemSelected()。在回调函数onCreateContextMenu()
里,你可以通过使用一个add()方法来添加菜单项,或者通过扩充一个定义在XML中的菜单资源。然后,通过registerForContextMenu()为这个视图注册一个上下文菜单ContextMenu.
比如,下面的代码可以被用到Notepad应用程序中来为列表中的每一个注释添加一个上下文菜单:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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) 方法:
1
|
registerForContextMenu(getListView());
|
记住,你可以传递任何视图对象来注册一个上下文菜单。这里,getListView()返回这个被用于Notepad应用程序的列表活动ListActivity中的列表视图对象。这样,这个列表中的任何item都被注册到这个上下文菜单。
注意:上下文菜单项不支持图标或快捷键。
子菜单Submenus
一个子菜单可以被添加进任何菜单中,但不能加入另外的子菜单中。当你的应用程序有很多功能可以按主题组织的时候,这将非常有用,就好比PC应用程序的菜单栏(文件,编辑,视图,等)。
子菜单通过addSubMenu()加入到已有的菜单中而创建。该函数会返回一个子菜单SubMenu对象(菜单Menu的一个扩展)。然后你可以通过调用add()方法给这个菜单添加其他项,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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中定义父菜单时增加子菜单。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?
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/create_new"
android:title="@string/create_new" />
<
item
android:id="@+id/open"
android:title="@string/open" />
</
menu
>
</
item
>
</
menu
>
|
其他的menu特性
Menu groups
通过组菜单,你可以同时对一组item进行操作,比如setGroupVisible(),setGroupEnabled(),setGroupCheckable()
下面给出一个包含组菜单的资源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?
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
>
|
3个item任然是一个兄弟菜单,不同的是你可以通过组操作一组item
Checkable menu items
通过布局文件创建Checkable menu,可以通过使用item的android:checkable属性
和group的android:checkableBehavior属性。例如:
1
2
3
4
5
6
7
8
9
|
<?
xml
version="1.0" encoding="utf-8"?>
<
menu
xmlns:android="http://schemas.android.com/apk/res/android">
<
group
android:checkableBehavior="single">
<
item
android:id="@+id/red"
android:title="@string/red" />
<
item
android:id="@+id/blue"
android:title="@string/blue" />
</
group
>
</
menu
>
|
The android:checkableBehavior
attribute accepts either:
single
Only one item from the group can be checked (radio buttons)
all
All items can be checked (checkboxes)
none
No items are checkable
1
2
3
4
5
6
7
8
9
10
11
12
|
@Override
public
boolean
onOptionsItemSelected(MenuItem item) {
switch
(item.getItemId()) {
case
R.id.vibrate:
case
R.id.dont_vibrate:
if
(item.isChecked()) item.setChecked(
false
);
else
item.setChecked(
true
);
return
true
;
default
:
return
super
.onOptionsItemSelected(item);
}
}
|
如果你不手动设置它,当你点击一个选项时,checkbox或者radioButton的状态是不会改变的.
Shortcut keys(热键)
你可以为你item设置快速点击热键
在配置文件中通过item的属性android:alphabeticShortcut 和 android:numericShortcut
类文件中通过setAlphabeticShortcut(char) and setNumericShortcut(char).设置的热键不区分大小写
Note: Shortcut keys for menu items only work on devices with a hardware keyboard. Shortcuts cannot be added to items in a Context Menu.
Dynamically adding menu intents
有时候你希望使用menu item来启动一个activity通过intent。你能在onOptionsItemSelected()中使用startActivity() 。
然而如果你不确定用户的移动设备是否拥有一个应用程序来处理这个intent,那么你可以使用Dynamically adding menu intents来决定是否添加此item
一个简单的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Override
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
, dataUri);
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
// Search and populate the menu with acceptable offering applications.
menu.addIntentOptions(
R.id.intent_group,
// Menu group to which new items will be added
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 correlate to specific items (none)
return
true
;
}
|
Allowing your activity to be added to other menus
如果允许你的activity可以被别的menu添加,必须设置CATEGORY_ALTERNATIVE and/or CATEGORY_SELECTED_ALTERNATIVE values for the intent filter category
1
2
3
4
5
6
|
<intent-filter label=
"Resize Image"
>
...
<category android:name=
"android.intent.category.ALTERNATIVE"
/>
<category android:name=
"android.intent.category.SELECTED_ALTERNATIVE"
/>
...
</intent-filter>
|