弹出菜单是停靠在一个View上的一个模式菜单。如果View对象下方有空间,那么弹出菜单将显示在停靠对象的下方,否则会显示在上方。这是非常有用的:
1. 给指定内容的操作提供一个溢出式菜单(如图4所示的Gmail的邮件头)。
图4. Gmail应用中的一个弹出菜单,停靠于右上角的溢出按钮。
注意:这是跟上下文菜单不一样,上下文菜单是对选择内容有影响的操作。针对应用选择内容的操作,请使用上下文操作模式或浮动内容菜单。
2. 提供命令语句的第二部分(如一个标记为“Add”按钮,用弹出菜单来产生不同的“Add”选项)。
3. 提供一个不保留持久选择的类似Spinner组件的下拉菜单。
注意:弹出菜单是在API 级别 11和更高版本上才有效的。
如果你在XML中定义了你的菜单,以下是如何显示弹出菜单的方法:
1. 用它的构造器实例化一个PopupMenu对象,它需要当前应用程序的Context和菜单要停靠的那个View两个对象作为参数。
2. 使用MenuInflater对象把你的菜单资源装载到由PopupMenu.getMenu()方法返回的Menu对象中。在API 级别 14以上的版本,你能够使用PopupMenu.inflate()方法来替代。
3. 调用PopupMenu.show()方法来显示弹出菜单。
例如,下列是一个带有android:onClick属性的按钮显示弹出菜单的方法:
清单文件中的定义:
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_overflow_holo_dark"
android:contentDescription="@string/descr_overflow_button"
android:onClick="showPopup" />
Activity中的代码:
public void showPopup(View v) {
PopupMenu popup = new PopupMenu(this, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.actions, popup.getMenu());
popup.show();
}
在API 级别 14和更高的版本上,你能够用PopupMenu.inflate()方法把填充菜单的两个代码合并到一起。
当用户选择了一个菜单项或在触摸了菜单区域的外部,这个菜单就会消失。你能使用PopupMenu.OnDismissListener回调方法来监听菜单消失事件。
处理点击事件
当用户选择一个菜单项来执行一个操作时,你必须要实现PopupMenu.OnMenuItemClickListener接口并且通过调用setOnMenuItemClickListener()方法把它注册给你的PopupMenu对象。当用户选择一个项目是,系统会调用你的接口中的onMenuItemClick()回调方法。如:
public void showMenu(View v) {
PopupMenu popup = new PopupMenu(this, v);
// This activity implements OnMenuItemClickListener
popup.setOnMenuItemClickListener(this);
popup.inflate(R.menu.actions);
popup.show();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.archive:
archive(item);
return true;
case R.id.delete:
delete(item);
return true;
default:
return false;
}
}
创建菜单组
菜单组是共享某些特性的菜单项目的集合,使用菜单组,可以做以下事情:
1. 用setGroupVisible()方法显示或隐藏组内的所有菜单项;
2. 用setGroupEnabled()方法启用或禁用组内的所有菜单项;
3. 用setGroupCheckable()方法指定所有组内项目是否可复选。
你能够通过把菜单资源中<item>元素嵌套进<group>元素中来创建分组菜单,或者使用带有分组ID的add()方法。
以下是包含了一个分组的菜单资源
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_save"
android:icon="@drawable/menu_save"
android:title="@string/menu_save" />
<!-- menu group -->
<group android:id="@+id/group_delete">
<item android:id="@+id/menu_archive"
android:title="@string/menu_archive" />
<item android:id="@+id/menu_delete"
android:title="@string/menu_delete" />
</group>
</menu>
这个分组菜单中的项目与第一个项目显示在同一个层次级别上---菜单中的三个项目是同级的,你能够使用上面列出的方法,通过引用组ID来修改分组菜单中两个项目的属性。系统也不会把分组的菜单项给分开。如,如果你给每个菜单项声明了android:showAsAction=”ifRoom”属性,那么它们将既可以同时显示在操作栏中,也可以同时显示在操作溢出中。
使用可复选的菜单项
菜单能够用于切换选项的界面,针对一个独立的选项使用一个可复选的Checkbox菜单,对于一组互斥的选项可使用一组带有单选按钮的菜单(如图5所示)。
图5.带有复选菜单项的子菜单截图。
注意:图标菜单(选项类型的菜单)中的菜单项不能显示一个复选框或单选按钮。如果你选择了让图标菜单中的菜单项可复选,那么就必须在每次状态改变时通过更换手动的更换图标或文本来的指明复选的状态。
你能够使用<item>元素中的android:checkable属性给单独的菜单项定义可复选的行为,或者用<group>元素中的android:checkableBehavior属性给一组菜单项定义可复选行为。如,下例中菜单组中的每个菜单项都带有一个可复选的复选按钮:
<?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>
android:checkableBehavior属性可以有以下三种设置:
1. single:菜单组中仅有一个菜单能够被复选(复选按钮)
2. all:所有菜单项都能够被复选(复选框)
3. none:没有项目是可复选的
你能够在<item>元素中使用android:checked属性给一个菜单项设置默认的复选状态,并且可以用setChecked()方法在代码中改变它。
当一个可复选的菜单项被选择的时候,系统会调用对应被选择的菜单项的回调方法(如onOptionsItemSelected())。你必须在这时设置复选框的状态,因为复选框或复选按钮不会自动的改变它们的状态。你能够用isChecked()方法来查询复选菜单的当前状态(被用户选择之前的状态),然后用setChecked()方法设置复选状态。如:
@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);
}
}
如果不用这种方式设置复选状态,那么当用户选择菜单项(复选框或复选按钮)的时候,它的可视状态将不会发生改变。在你设置这个状态时,Activity会保留菜单的状态,以便用户随后打开这个菜单时,让你设置的复选状态可见。
注意:可复选菜单项打算仅在每个会话的基础上使用,并且在应用销毁之后不保存复选状态。如果你的应用打算保存用户的设置,你应该使用共享的方式来保存数据。
基于Intent对象来添加菜单项
有些时候,你想要菜单项使用一个Intent对象来启动一个Activity(不管这个Activity是你的应用程序中的还是其他应用程序中的)。
当你知道你要使用的Intent对象,并且也指定了启动这个Intent对象的菜单项时,你就能够在对应的on-item-selected回调方法(如onOptionsItemSelected()回调方法)调用期间用startActivity()方法执行这个Intent对象。
但是,如果你不确定用户设备上是否包含了处理这个Intent对象的应用程序,那么添加调用这个Intent对象的菜单项就有可能导致一个非功能性菜单的产生,因为可能没有接受这个Intent对象的Activity。要解决这个问题,Android能够让你在设备上查找处理你的Intent对象的Activity时,动态的把菜单项添加到菜单中。
以下是在能够接受Intent对象的有效的Activity基础上来添加菜单项的方法:
1. 用CATEGORY_ALTERNATIVE或CATEGORY_SELECTED_ALTERNATIVE分类,再加上一些其他的要求,定义一个Intent对象。
2. 调用Menu.addIntentOptions()方法,Android会搜索系统中能够接受这个Intent对象的任何应用程序,并把它们添加到你的菜单中。
如果没有安装能够满足Intent要求的应用程序,那么就不会添加菜单项。
注意:CATEGORY_SELECTED_ALTERNATIVE被用于处理屏幕上当前被选择的元素。因此,应该只在用onCreateContextMenu()方法创建菜单时使用这个分类。
如:
@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;
}
对于找到的每个提供了跟定义的Intent对象匹配的Intent过滤器的Activity,都会添加一个菜单项,这个菜单项使用Intent过滤器的android:label的属性值做为菜单项的标题、应用程序的图标做为菜单项的图标。addIntentOptions()方法返回被添加的菜单的个数。
注意:当你调用addIntentOptions()方法时,它会覆盖在第一个参数中指定的菜单组的所有菜单项。
允许你的Activity被添加给其他菜单
你也能够包Activity的服务提供给其他的应用程序,以便你的应用能够包含在其他应用的菜单中。
要想在其他的应用程序菜单中包含你的应用程序,你需要向通常那样定义一个Intent过滤器,但要确认包括CATEGORY_ALTERNATIVE或CATEGORY_SELECTED_ALTERNATIVE分类,如:
<intent-filter label="@string/resize_image">
...
<category android:name="android.intent.category.ALTERNATIVE" />
<category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
...
</intent-filter>