长按ListView或GridView列表项删除某一条记录是一种上下文操作,即它是与某个特定屏幕视图(单个列表项)而非整个屏幕相关联的。
在Honeycomb以前版本的设备上,上下文操作是在浮动上下文菜单中呈现的。而在之后的版本设备上,上下文操作主要是通过上下文操作栏呈现的。如下图:
对于上下文操作,我们必须实现两组不同的回调方法,一组用于上下文操作栏,一组用于浮动上下文菜单。
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/menu_item_delete_crime"
android:icon="@android:drawable/ic_delete"
android:title="@string/delete_crime"/>
menu>
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
/**
* 若定义了多个上下文菜单资源,通过检查传入onCreateContextMenu(...)方法的View视图ID,就可以自由决定
* 使用那个资源来生成上下文菜单。
*/
getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context,
menu);
}
/**
* ListFragment也有一个getListView()方法来获取ListFragment管理着的ListView。为什么不使用呢?
* 这是因为,在onCreateView(...)方法完成调用并返回视图之前,getListView()方法返回的永远是null值。
*/
ListView listView = (ListView) view.findViewById(android.R.id.list);
// 为浮动上下文菜单登记视图,之后,长按视图则会触发上下文菜单的创建
registerForContextMenu(listView);
@Override
public boolean onContextItemSelected(MenuItem item) {
// 因为ListView是AdapterView的子类,所以getMenuInfo()方法反悔了一个AdapterView.AdapterContextMenuInfo实例
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
int position = info.position;
CrimeAdapter adapter = (CrimeAdapter) getListAdapter();
Crime crime = adapter.getItem(position);
switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeLab.get(getActivity()).deleteCrime(crime);
adapter.notifyDataSetChanged();
return true;
}
return super.onContextItemSelected(item);
}
/**
* ListFragment也有一个getListView()方法来获取ListFragment管理着的ListView。为什么不使用呢?
* 这是因为,在onCreateView(...)方法完成调用并返回视图之前,getListView()方法返回的永远是null值。
*/
ListView listView = (ListView) view.findViewById(android.R.id.list);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// 为浮动上下文菜单登记视图,之后,长按视图则会触发上下文菜单的创建
registerForContextMenu(listView);
} else {
// 上下文操作模式下设置ListView的多选模式
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
/**
* 为listView设置一个实现AbsListView.MultiChoiceModeListener接口的监听器。该接口包含以下回调方法,视图在选中或撤销时会触发它:
* public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked)
*
* MultiChoiceModeListener实现了另一个接口,即ActionMode.CallBack。用户屏幕进入上下文操作时,会创建一个ActionMode类实例。
* 随后在其生命周期内,ActionMode.CallBack接口的回调方法会在不同时点被调用。以下为ActionMode.CallBack接口必须实现的四个方法:
*/
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
/**
* 在onCreateActionMode(ActionMode mode, Menu menu)方法之后,以及当前上下文操作栏需要刷新显示新数据时调用。
*/
@Override
public boolean onPrepareActionMode(ActionMode arg0, Menu arg1) {
return false;
}
/**
* 在用户退出上下文操作模式或所选菜单项操作已被响应,从而导致ActionMode对象将要销毁时调用。默认的实现会导致已选视图被反选。
* 这里,也可完成在上下文操作模式下,响应菜单操作而引发的相迎fragment更新。
*/
@Override
public void onDestroyActionMode(ActionMode arg0) {
}
/**
* 在ActionMode对象创建后调用。也是实例化上下文菜单资源,并显示在上下文操作栏上的任务完成的地方。
*/
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
/**
* 我们从操作模式,而非activity中获取MenuInflater的。操作模式负责对上下文操作栏进行配置。例如,
* 可调用ActionMode.setTitle(...)方法为上下文操作栏设置标题,而activity的MenuInflater则做不到这一点。
*/
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.crime_list_item_context, menu);
return true;//这里必须返回true,否则会导致操作模式创建失败。
}
/*
* 在用户选中某个菜单项操作时调用。是响应上下文菜单项操作的地方。
*/
@Override
public boolean onActionItemClicked(ActionMode mode,
MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeAdapter adapter = (CrimeAdapter) getListAdapter();
CrimeLab crimeLab = CrimeLab.get(getActivity());
for (int i = adapter.getCount(); i >= 0; i--) {
if(getListView().isItemChecked(i)){
crimeLab.deleteCrime(adapter.getItem(i));
}
}
mode.finish();
adapter.notifyDataSetChanged();
return true;
default:
return false;
}
}
@Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
}
});
}
视图处于激活状态,是指该视图已被用户标记为关注处理对象。基于视图的状态,可使用state list drawable 资源来改变其显示背景。state list drawable是一种以XML定义的资源。该资源定义中,我们指定一个drawable(位图或彩图),并列出该drawable对应的状态。state list drawable一般定义在res/drawable目录下。
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/darker_gray"
android:state_activated="true"/>
selector>
如果视图即非ListView,也非GridView,要使用上下文操作模栏,该如何处理?
首先,设置一个实现View.onLongclickListener接口的监听器。然后再监听器实现体内,调用Activity.startActionMode(…)方法创建一个ActionMode实例。startActionMode(…)方法需要实现一个ActionMode.Callback接口的对象作为参数。
代码地址