7.1 问题
应用程序需要为用户提供一个动作集,但又不想占用视图结果的屏幕空间。
7.2 解决方案
(API Level 7)
使用框图中的选项菜单功能在Action Bar内提供常用动作,以及在溢出的弹出式菜单中提供额外的选项。此外,通过使用PopMenu,可以将菜单附加到现有的视图并显示为浮动下拉菜单。此功能用于在应用程序中除了ActionBar之外的任意位置放置菜单,但在用户需要这些菜单之前使它们保持脱离视图。
根据设备平台的版本的不同,Android的菜单功能也有所不同。在早期的版本中,所有的Android都有一个物理的MENU键可以触发这个功能。从Android3.0开始,出现了没有物理按钮的设备,菜单功能也变成了ActionBar的一部分。
驻留在ActionBar中的动作项还可以展开以显示称为动作视图的自定义小部件。这可以用于提供搜索字段等功能,该功能需要额外的用户输入,但要将其隐藏在某个动作项的后面,直到用户点击时才会显示。
注意:
该例使用来自Android支持库的一些兼容类来向后兼容运行Android 2.1 (API Level 7)的设备。关于在项目中包括支持库的更多信息,请参阅http://developer.android.com/tools/support-library/index.html。
7.3 实现机制
以下代码清单定义了将在XML中使用的选项菜单。
res/menu/options.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:appcompat="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_add"
android:title="Add Item"
android:icon="@android:drawable/ic_menu_add"
appcompat:showAsAction="always|collapseActionView"
appcompat:actionLayout="@layout/view_action" />
<item android:id="@+id/menu_remove"
android:title="Remove Item"
android:icon="@android:drawable/ic_menu_delete"
appcompat:showAsAction="ifRoom" />
<item android:id="@+id/menu_edit"
android:title="Edit Item"
android:icon="@android:drawable/ic_menu_edit"
appcompat:showAsAction="ifRoom" />
<item android:id="@+id/menu_settings"
android:title="Settings"
android:icon="@android:drawable/ic_menu_preferences"
appcompat:showAsAction="never" />
</menu>
title和icon属性定义了每个条目该如何显示,旧版本平台会显示这两个值,而新版本中会显示一个或根据位置显示另一个。只有Android 3.0及以后的设备可以识别showAsAction属性,该属性定义了条目是否应该变成Action Bar中的一个动作或者放到溢出菜单中。这个属性最常用的值如下:
- always : 总是作为一个动作显示其图标。
- never : 总是显示在溢出菜单中并显示其名称。
- ifRoom : 如果Action Bar上有空间,就作为动作显示,否则显示在溢出菜单中。
菜单中的第一个条目还定义了android:actionLayout资源,该资源指向在点击此条目时要展开到其中的小部件;此外还定义了额外的显示标志collapseActionView,该标志告诉框架,此条目有可折叠的动作视图以供显示。此代码清单显示了动作视图布局,这是带有两个CheckBox实例的简单布局。
res/layout/view_action.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/option_first"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="First"/>
<CheckBox
android:id="@+id/option_second"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Second"/>
</LinearLayout>
以下的代码清单显示了完整的Activity,其中将选项菜单填充到Action Bar中,同时在某个动作项中放置可展开的动作视图。
覆写菜单动作的Activity
public class OptionsActivity extends ActionBarActivity implements
PopupMenu.OnMenuItemClickListener,
CompoundButton.OnCheckedChangeListener {
private MenuItem mOptionsItem;
private CheckBox mFirstOption, mSecondOption;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//此例中无附加工作
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//使用此回调创建菜单并执行任何必要的初始化设置
getMenuInflater().inflate(R.menu.options, menu);
//查找并初始化动作项
mOptionsItem = menu.findItem(R.id.menu_add);
MenuItemCompat.setOnActionExpandListener(mOptionsItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
//必须返回true以使项展开
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
mFirstOption.setChecked(false);
mSecondOption.setChecked(false);
//必须返回true以使项折叠
return true;
}
});
mFirstOption = (CheckBox) MenuItemCompat.getActionView(mOptionsItem).findViewById(R.id.option_first);
mFirstOption.setOnCheckedChangeListener(this);
mSecondOption = (CheckBox) MenuItemCompat.getActionView(mOptionsItem).findViewById(R.id.option_second);
mSecondOption.setOnCheckedChangeListener(this);
return true;
}
/* 复选框回调方法 */
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mFirstOption.isChecked() && mSecondOption.isChecked()) {
//隐藏动作视图
MenuItemCompat.collapseActionView(mOptionsItem);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
//使用此回调执行每次菜单打开时需要调用的设置
return super.onPrepareOptionsMenu(menu);
}
//通过PopupMenu点击的回调
public boolean onMenuItemClick(MenuItem item) {
menuItemSelected(item);
return true;
}
//通过标准选项菜单点击的回调
@Override
public boolean onOptionsItemSelected(MenuItem item) {
menuItemSelected(item);
return true;
}
//私有辅助方法,让每个回调可以触发相同的动作
private void menuItemSelected(MenuItem item) {
//按id获得选择的选项
switch (item.getItemId()) {
case R.id.menu_add:
//执行添加动作
break;
case R.id.menu_remove:
//执行删除动作
break;
case R.id.menu_edit:
//执行编辑动作
break;
case R.id.menu_settings:
//执行设置动作
break;
default:
break;
}
}
}
在用户按下设备上的MENU键后(或者Activity在加载时显示了Action Bar),就会调用onCreateOptionsMenu()方法来构建菜单。有一个名为MenuInflater的特殊LayoutInflater对象,可以用来根据XML创建菜单。这里我们使用Activity中已有的getMenuInflater()方法获得MenuInflater实例来创建XML菜单。
如果需要在用户每次打开菜单时传递一些动作,可以在onPrepareOptionsMenu()中完成。这里有一个建议,就是所有变成位于ActionBar中的动作在用户选择它们时将不会触发这个回调方法,但在溢出菜单中的动作还会触发该方法。
用户做了选择后,onOptionsItemSelected()回调方法将会触发同时传入选择的菜单条目。因为我们在XML中为每个条目都定义了唯一的ID,所以可以通过switch语句判断用户的选择项并进行相应的操作。
最后,在onCreateOptionsMenu()中有一些用于可展开动作视图的额外设置。在此获得指向包含动作视图布局的菜单项的引用,并且附加一个onActionExpandListener回调。此外使用回调只是为了在条目折叠时清除动作视图中的已选元素。
要点:
如果提供onActionExpandListener,就需要在onMenuItemActionExpand()内返回true,否则永远不会展开!
可以使用MenuItem中的getActionView()方法获得一个引用,该引用指向在菜单XML中设置的已填充动作布局。在我们的实例中,使用此方法对布局内的每个CheckBox设置选择的侦听器。在动作视图内同时选择这两个条目时,调用collapseActionView()将视图转变回单个动作项的图标。
下图显示了不同版本和配置的设备上这个菜单的样子。依然带有物理按键的设备会在Action Bar上显示提示的动作,但溢出菜单还是通过MENU键触发的。带有软键的设备会挨着Action Bar动作显示一个溢出菜单按钮。
带有物理按键的Android设备 带有软键的Android设备
下图显示了展开的动作视图,单击Action Bar中的Add动作时就会显示该视图。
带有Add动作的Android设备 自定义动作视图
网友评论