在很多类型的应用程序中,菜单是一个常用的用户界面组件。要提供友好的和前后一致的用户体验,就应该使用Menu APIs把Activity的动作和其他选项展现给用户。
从Android3.0(API 级别 11)开始,Android设备不再需要提供一个专用的Menu按钮,随着这种改变,Android应用程序将会从对传统的6项菜单面板的依赖中解脱出来,取而代之的是提供了一个用户展现常用用户动作的操作栏。
尽管针对一些菜单项的设计和用户体验已经改变,但是定义一组动作和选项的语意依然是基于Menu APIs的。本指南展示了如何创建能够在Android所有版本上演示的三种基本类型的菜单和动作。
选项菜单和操作栏
选项菜单(options menu)是针对Activity的主要菜单集合。它是你放置应用程序中有全局影响的动作的地方,如“搜索”、“编写电子邮件”、和“设置”等功能。
如果你针对Android2.3或更低的版本来开发应用,那么用户要通过按Menu按钮来展现选项菜单面板。
在Android3.0或更高版本上,源于选项菜单的项目是通过操作栏(action bar)来展现的,它由屏幕上的动作项目和剩余的选项组合而成。从Android3.0开始,Menu按钮被弃用了(有些设备根本就没有这个按钮),因此,你应该使用操作栏来提供对动作和其他选项的访问。
上下菜单和上下文操作模式
一个上下文菜单是一个当用户在一个元素上执行long-click事件时才显示的浮动菜单。它提供了影响选择内容或上下文框架的动作。
当给Android3.0和更高的版本开发应用程序时,你应该改用上下文动作模式(contextual action mode)来确保被选内容的动作。这种模式把影响选择内容的动作项目显示在屏幕顶部的一个横条中,并允许用户选择多个项目。
弹出菜单
一个弹出菜单在一个垂直列表中显示项目的列表,它靠在调用这个菜单的View对象旁边。它对给相关指定内容提供动作的展现或给一个命令的第二部分提供选项是有好处的。弹出菜单中的动作不应该直接影响对应的内容,相反,弹出菜单是为了扩展Activity中相关内容区域的动作而设计的。
在XML中定义一个菜单
对于所有的菜单类型,Android提供了标准的XML格式来定义菜单项目。你应该在一个XML菜单资源中定义一个菜单和它的所有的项目,而不是在你Activity代码中创建一个菜单。然后你就能够把菜单资源作为一个Menu对象加载到Activity或Fragment对象中。
由于以下原因,使得使用菜单资源是一个好的选择:
1. 更容易看清XML文件中的菜单结构;
2. 它把针对菜单的内容和应用程序的行为代码给分离开了;
3. 它允许你针对不同的平台版本、屏幕尺寸和其他的被应用资源框架利用的配置来创建可选的菜单配置。
要定义菜单,就要在你的项目内部的res/menu/目录内部创建一个XML文件,并且要使用下列元素来构建菜单:
<menu>
定义一个菜单,它包含菜单项。<menu>元素必须是这个文件的根节点,并且能够拥有多个<item>和<group>元素。
<item>
创建一个MenuItem对象,它代表了一个菜单中的单独项目。为了创建一个子菜单,这个元素可以包含一个嵌套的<menu>元素。
<group>
一个可选的针对<item>元素的非可见容器。它允许把菜单项分类,以便它们共享诸如活动状态和可见性等属性。
以下是一个名叫game_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"
android:showAsAction="ifRoom"/>
<item android:id="@+id/help"
android:icon="@drawable/ic_help"
android:title="@string/help" />
</menu>
<item>元素支持以下几个用于定义菜单项的外观和行为的属性:
android:id
菜单项的唯一资源ID,在用户选择这个菜单项时,应用程序能够用这个ID来识别它。
android:icon
指向一个可描画的资源,用作这个菜单项的图标。
android:title
指向一个用作菜单标题的字符串。
android:showAsAction
指明这个菜单项作为操作栏(action bar)中的一个动作项应该显示的时机和方式。
以上只是你应该使用的最重要的属性,但是还有一些有效的属性。有关菜单支持的所有属性信息,请看“菜单资源”的文档。
你能够给菜单中的任意一个项目(子菜单除外)添加一个子菜单,通过把<menu>元素作为<item>元素的一个子元素方式来添加。当应用程序中有很多能够被组织到一个专题中的功能时,子菜单是有益的,就像PC应用程序的菜单栏中的项目(File、Edit、View等)一样,如:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/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>
要在Activity中使用菜单,你需要使用MenuInflater.inflate()方法来加载菜单资源(把XML资源转换成可编程对象)。在下列章节中,你会看到每种类型菜单的创建方法。
创建一个选项菜单
选项菜单应该是包含动作和与当前Activity上下文环境相关的其他选项,如:“搜索”、“编写电子邮件”、和“设置”等。
选项菜单中项目在屏幕上显示的位置依赖与你的应用程序所依赖的Android平台版本:
1. 如果你的应用程序依赖Android2.3.x(API 级别 10)或更低的版本,那么当用户按下Menu按钮时,你的选项菜单内容会显示在屏幕的底部,如图1所示:当菜单被打开时,首先看到的是图标菜单,它们被六个菜单项持有。如果你的菜单包含的菜单项目大于6,那么Adroid会放入第六个项目并把其余的项目放到溢出菜单中,用户能够同选择更多的菜单项来打开其余的菜单项。
图1.Android2.3上的浏览器的可选菜单
2. 如果你的应用程序依赖Android3.0(API 级别 11)或更高的版本,可先菜单的项目在操作栏(action bar)中是有效的。默认情况下,系统会把所有的项目放到动作溢出中,用户能够在操作栏(action bar)的右边看到动作溢出图标(或者,如果Menu按钮有效,按下设备的Menu按钮也可以)。要让重要的动作快速访问,你能够通过在<item>元素中添加android:showAsAction=”ifRoom”属性设置,把一些菜单项目放到操作栏中显示。
注意:即使你的应用不依赖Android3.0或更高的版本,你也能够构建自己的操作栏(action bar)来模仿类似的效果。
图2.Honeycomb Gallery应用程序的操作栏,显示了导航标签和照相机动作项(被加在动作溢出按钮上)
Honeycomb Gallery应用程序网址:
http://developer.android.com/resources/samples/HoneycombGallery/index.html
你既可以给Activity子类,也可以给Fragment子类声明选项菜单项目。如果Activity和Fragment都声明了选项菜单项目,它们会在UI中组合到一起。Activity的选项菜单会首先显示,紧接着按照每个Fragmeng被添加的顺序来显示Fragment的选项菜单。如果需要,你能够在每个需要移动的<item>元素中用android:orderInCategory属性来重新指定选项菜单的顺序。
要给一个Activity指定选项菜单,需要重写onCreateOptionsMenu()方法(Fragment也提供它们自己的onCreateOptionsMenu()方法)。在这个方法中,能够把菜单资源(在XML文件中定义)加载到这个回调方法提供的Menu对象中。如:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.game_menu, menu);
return true;
}
你也能够使用add()方法来添加菜单项,并且用findItem()方法获取要用MenuItem APIs修改属性的菜单项。
如果你的应用程序依赖Android2.3.x或更低的版本,系统会在用户首次打开这个菜单时,调用onCreateOptionsMenu()方法来创建选项菜单。如果你的应用程序依赖Android3.0或更高的版本,系统会在启动Activity时调用onCreateOptionsMenu()方法,以便把选项菜单显示在操作栏(action bar)中。
处理click事件
当用户选择了选项菜单中的一个菜单项(包括操作栏(action bar)中动作),系统会调用Activity的onOptionsItemSelected()方法。这个方法把选择的菜单项作为参数来传递。你能够通过调用getItemId()方法来识别菜单项,这个方法返回了对象菜单项的唯一ID(这个ID是在菜单资源的android:id属性中定义的,或者是传递给add方法的一个整数)。你能够把这个ID与已知的菜单项匹配,让它执行对应的动作,如:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.new_game:
newGame();
return true;
case R.id.help:
showHelp();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
当你成功的处理了一个菜单项时,就返回true。如果你没有处理菜单项,你应该调用父类的onOptionsItemSelected()方法实现(默认的实现返回false)。
如果你Activity包括Fragment,那么系统会首先调用Activity的onOptionsItemSelected()方法,然后会调用每个Fragment的onOptionsItemSelected()方法(按照每个Fragment被添加的顺序),直到其中的一个返回true,或所有Fragment的onOptionsItemSelected()方法都被调用。
提示:Android3.0使用android:onClick属性添加了在XML文件中给菜单项定义on-click行为的能力。这个属性值必须是Activity定义的使用菜单的一个方法名。这个方法必须是公共的,并且要接收一个单一的MenuItem对象参数---当系统调用这个方法时,会用它来传递被选择的菜单项。更多的信息和示例,请看“菜单资源”文档。
提示:如果你的应用程序包含了多个Activity,并且其中的一些Activity都提供了相同的选项菜单,可以考虑创建一个除onCreateOptionsMenu()和onOptionItemSelected()方法以外没有任何其他实现的Activity类,然后,每个Activity都继承这个Activity,这样就实现了相同选项菜单的共享。这种方法,你能够只管理一组处理菜单动作的代码,并且每个子类都会继承这个菜单行为。如果你想要给这个Activity子类添加菜单项,重写那个Activity的onCreateOptionsMenu()方法就可以了,在用menu.add()方法添加新的菜单项时,要调用super.onCreateOptionsMenu(menu)方法,以便初始菜单项被创建。你也能够给独立的菜单项重写其父类的行为。
在运行时改变菜单项
系统调用onCreateOptionsMenu()方法以后,它会保留你组装的这个菜单的一个实例。除非这个菜单对某些屏幕是无效的,否则系统不会再次调用onCreateOptionsMenu()方法。因此,你应该仅在创建初始菜单状态时使用onCreateOptionsMenu()方法,并且在Activity的生命周期内不使它发生改变。
如果你想要基于Activity生命周期期间发生的事件来修改选项菜单,那么你可以在onPrepareOptionsMenu()方法中来做这件事。这个方法把当前存在的Menu对象传递给你,以便你能够修改它,如添加、删除、或禁用菜单项。(Fragment也提供一个onPrepareOptionsMenu()回调方法。)
在Android2.3.x和更低版本上,每次用户打开选项菜单(按下Menu按钮)时,系统都会调用onPrepareOptionsMenu()方法。
在Android3.0和更高的版本上,当菜单项在操作栏(action bar)中展现的时候,选项菜单被认为是始终打开的。当一个事件发生,并且你要执行一个菜单更新的动作时,你必须调用invalidateOptionMenu()方法来请求系统调用onPrepareOptionsMenu()方法。
注意:你永远都不会基于当前焦点中的View对象来改变选项菜单的项目。当在触屏模式中,View对象不需要焦点,因此你永远不会使用焦点作为修改选项菜单中菜单项的基础。如果想要给一个View对象提供一个上下文敏感的菜单,请使用上下文菜单。