5.1 问题
需要为用户在用户界面中的选择提供多个可供执行的动作。
5.2 解决方案
(API Level 11)
对于与单个项相关的上下文动作,使用PopupMenu显示固定到相关视图的这些动作,在应该影响多个项的示例中,可启用ActionMode来响应用户的动作。
注意:
该例使用AppCompat支持库来实现最佳的版本兼容性。如果应用程序单纯地支持较早的平台版本,可以使用原生的API实现相同的结果。关于在项目中包括支持库的更多信息,请参阅http://developer.android.com/tools/support-library/index.html。
5.3 实现机制
对于本例,我们将构造如图所示的Activity。
在点击项视图右侧的溢出按钮时,列表中的每个项通过弹出列表提供上下文动作。长按任何项时,Activity将激活ActionMode,从而可以将同一个动作应用于多个已选择的项。
1.上下文弹出菜单
PopupMenu用于获得一个选项菜单资源并将其作为小型弹出窗口轻松附加到任何视图。首先,我们需要在res/menu中创建一个XML文件以定义菜单自身;将此文件称为contextmenu.xml(参见以下代码)。
res/menu/contextmenu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_delete"
android:icon="@android:drawable/ic_menu_delete"
android:title="Delete Item" />
<item
android:id="@+id/menu_edit"
android:icon="@android:drawable/ic_menu_edit"
android:title="Edit Item" />
</menu>
我们为驻留在此列表中的每一项将此资源扩展到PopupMenu实例中。为更好地封装此逻辑,以下两端代码为列表行布局自定义了ContextListItem类。
自定义行项列表
public class ContextListItem extends LinearLayout implements
PopupMenu.OnMenuItemClickListener,
View.OnClickListener {
private PopupMenu mPopupMenu;
private TextView mTextView;
public ContextListItem(Context context) {
super(context);
}
public ContextListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ContextListItem(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTextView = (TextView) findViewById(R.id.text);
//附加单击处理程序
View contextButton = findViewById(R.id.context);
contextButton.setOnClickListener(this);
//创建上下文菜单
mPopupMenu = new PopupMenu(getContext(), contextButton);
mPopupMenu.setOnMenuItemClickListener(this);
mPopupMenu.inflate(R.menu.contextmenu);
}
@Override
public void onClick(View v) {
//处理上下文按钮单击以显示菜单
mPopupMenu.show();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
String itemText = mTextView.getText().toString();
switch (item.getItemId()) {
case R.id.menu_edit:
Toast.makeText(getContext(), "Edit "+itemText, Toast.LENGTH_SHORT).show();
break;
case R.id.menu_delete:
Toast.makeText(getContext(), "Delete "+itemText, Toast.LENGTH_SHORT).show();
break;
}
return true;
}
}
res/layout/list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.examples.popupmenus.ContextListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightSmall"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:background="?android:attr/activatedBackgroundIndicator" >
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:layout_gravity="center_vertical" />
<ImageView
android:id="@+id/context"
style="@style/Widget.AppCompat.Light.ActionButton.Overflow"
android:layout_width="?android:attr/listPreferredItemHeightSmall"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="false"/>
</com.examples.popupmenus.ContextListItem>
ContextListItem是包含上下文菜单的文本项和图片按钮的LinearLayout。为实现平台一致性,我们对按钮应用Widget.AppCompat.Light.ActionButton.Overflow样式,使其外观和行为类似于标准的溢出菜单按钮。创建视图时,我们构建一个PopupMenu来显示R.menu.contextmenu,并且关联该菜单,使其在按钮溢出按钮时始终会显示。行视图也设置为OnMenuItemClickListener,以便处理从弹出菜单中选择适当的选项。
注意:
ListView内的可单击项需要将android:focusable设置为false,否则就不能够同时单击顶层列表项。
为了将此视图与列表中的数据绑定,我们需要创建引用列表项布局的基本适配器:
ArrayAdapter<String> adapter = new ArrayAdapter<String> (this,R.layout.list_item,R.id.text,ITEMS);
我们将很快看到将此功能与其他功能联系在一起的完整Activity代码,但首先让我们查看一下如何实现多选逻辑。
2.ActionMode
ActionMode API解决了类似的问题,即允许用户对UI中的某个条目执行某种动作,但它的实现方式稍微有点不同。激活ActionMode后会在窗口的ActionBar上出现一个包含所提供的菜单选项的叠层,并出现一个可以退出ActionMode的额外选项。它还允许同时选择多个条目来执行同一个动作。以下代码演示了这一功能。
使用了上下文ActionMode的Activity
public class ActionActivity extends ActionBarActivity implements AbsListView.MultiChoiceModeListener {
private static final String[] ITEMS = {
"Mom", "Dad", "Brother", "Sister", "Uncle", "Aunt",
"Cousin", "Grandfather", "Grandmother"};
private ListView mList;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//为上下文事件注册一个按钮
mList = new ListView(this);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.list_item, R.id.text, ITEMS);
mList.setAdapter(adapter);
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
mList.setMultiChoiceModeListener(this);
setContentView(mList, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// 如果ActionMode一直是无效的,可以在这里做些工作来更新菜单
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
//退出ActionMode时会调用这个方法
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.contextmenu, menu);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
SparseBooleanArray items = mList.getCheckedItemPositions();
//通过条目的ID得到用户选择的动作
switch(item.getItemId()) {
case R.id.menu_delete:
//执行删除动作
break;
case R.id.menu_edit:
//执行编辑动作
break;
default:
return false;
}
return true;
}
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
int count = mList.getCheckedItemCount();
mode.setTitle(String.format("%d Selected", count));
}
}
要使用我们的ListView来激活一个多选择项的ActionMode,需要将choiceMode属性设置为CHOICE_MODE_MULTIPLE_MODAL。它和传统的CHOICE_MODE_MULTIPLE不同,CHOICE_MODE_MULTIPLE是在每个列表条目上都提供一个可选择的小部件。在ActionMode处于激活状态时,它的模式标识只应用这个选择模式。
对于ActionMode,有很多的回调方法需要实现它,因为它不像ContextMenu一样是直接内置在Activity中的。我们需要实现ActionMode.Callback接口来响应创建菜单和选择选项的事件。ListView有一个名为MultiChoiceModeListener的特殊接口,它是ActionMode.Callback的子接口,本例中就实现了这个特殊的接口。
我们在onCreateActionMode()中的响应和onCreateContextMenu()类似,只是构建了叠层中要显示的菜单选项。这个菜单不需要包含图标;这时,ActionMode会显示条目名。选中每个条目后,我们会在onItemCheckedStateChanged()方法中得到通知。这里,我们会更新ActionMode的标题来显示当前选中条目的个数。
当用户结束选择并单击一个菜单选项时会调用onActionItemClicked()方法。因为会同时选择多个条目,所以我通过getCheckedItemPositions()得到所有选择的条目,这样就可以对所有选择的条目进行操作。
Demo下载地址:
[2.5 创建上下文动作]
(https://download.csdn.net/download/qq_41121204/10764629)