1. 概述
Contextual Action Mode是在Android 3.0开始引入的。floating menu是浮动在view上面,而contextual action mode的菜单项,则显示在Action Bar的一部分区域中。效果如下:
此外,针对ListView、GridView,或其他自定义的AbsListView子类,除了支持单选,还支持多选的行为。
2. 主要步骤
包括如下几个步骤:
- 创建一个ActionMode.CallBack对象;主要包括:
- 创建contextual action mode的菜单;
- 处理action item的选择事件;
- 对于要显示contextual action mode的View,设置OnLongClickListener等listener,其主要目的在于:
- 调用Activity或Fragment的startActionMode()方法启用action mode;
3. 示例
本节以一个EditText控件为例来说明contextual action mode的使用方法。这个例子,和Android官网的说明完全一致。对应位置:sdk/docs/guide/topics/ui/menus.html
3.1 菜单资源
<?xml version="1.0" encoding="utf-8"?>
<!-- context_menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/first_context_menu_item"
android:title="@string/first_context_menu_item"
android:icon="@drawable/ic_launcher"
android:titleCondensed="@string/first"
android:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/second_context_menu_item"
android:title="@string/second_context_menu_item"
android:icon="@drawable/ic_launcher"
android:titleCondensed="@string/second"
android:showAsAction="ifRoom|withText"/>
</menu>
3.2 字符串资源
<string name="first_context_menu_item">first item</string>
<string name="second_context_menu_item">second item</string>
<string name="first">first</string>
<string name="second">second</string>
3.3 Java代码
package com.example.hellocontextualactions;
import android.app.Activity;
import android.os.Bundle;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
//private static final String TAG = "MainActivity";
private EditText editText = null;
private ActionMode actionMode = null;
private ActionMode.Callback actionModeCallBack = new ActionMode.Callback() {
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
actionMode = null;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.first_context_menu_item:
Toast.makeText(getApplicationContext(),
"first context menu item clicked.",
Toast.LENGTH_SHORT)
.show();
mode.finish();
return true;
case R.id.second_context_menu_item:
Toast.makeText(getApplicationContext(),
"second context menu item clicked.",
Toast.LENGTH_SHORT)
.show();
mode.finish();
return true;
default:
return false;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hello_contextual_actions);
editText = (EditText) this.findViewById(R.id.edit_text_id);
editText.setText("Hello, Contextual Actions!");
editText.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (actionMode != null) {
return false;
}
actionMode = startActionMode(actionModeCallBack);
return true;
}
});
}
}
3.4 运行效果
应用启动后的界面:
长按EditText控件后,进入action mode,会在action bar上面显示ActionMode.CallBack对象中创建的菜单所包括的action items:
当按了Action Bar左边的Done(勾号)之后,会退出action mode。
选择了某一个action后的效果:
4. 代码解读
4.1 OnLongClickListener
在contextual action mode使用方式下,是由每个View去设置OnLongClickListener对象,用于侦听long click事件。而long click也是用来生成floating context menu或进入contextual aciton mode的时机。
——在floating context menu中,是直接通过重载Activity或Fragment的onCreateContextMenu()来生成菜单。由此可以看到两者上下文菜单的实现方式上的差异。
需要注意的是,对于ListView、GridView,或者其他继承自AbsListView的子类来讲,这些对象没法侦听到long click事件。因为在这些视图中,我们可以click的是一个个的item元素。为此,AbsListView提供了OnItemLongClickListener接口。——此外还有多选的接口,后面再展开讨论。
在OnLongClickListener或OnItemLongClickListener等实现中,需要调用Activity或Fragment的startActionMode(CallBack)来启动action mode。进而在Action Bar上面显示CallBack生成的菜单资源,即所谓的menu actions。
4.2 CallBack
CallBack中的方法,和onCreateContextMenu()、onContextItemSelected()等方法类似,包括了创建菜单、处理选中事件等。这一点通过方法名称、以及代码示例即可理解。
4.3 多个View的处理
如果在一个Activity(UI视图)上有多个View对象都要支持contextual action mode怎么办呢?
- 此时可以在CallBack对象中生成所有的菜单资源,并在onPrepareActionMode()中设置各个action(或称menu item)的显示状态;
- 也可以为每个View对象创建各自的CallBack实现;
- 其他的方式,比如在Activity范围内记录当前所选择的view对象,从而在CallBack中据此创建不同的菜单
4.4 ActionMode的setTitle()方法
调用ActionMode的setTitle()或setSubTitle()方法,可以在Action Mode下面,在Action Bar上显示一些字符串,给用户呈现一些信息。为此,我们对前面的代码做一点修改,如下:
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.first_context_menu_item:
Toast.makeText(getApplicationContext(),
"first context menu item clicked.",
Toast.LENGTH_SHORT)
.show();
//mode.finish();
mode.setTitle("first context menu item clicked.");
mode.setSubtitle("first subtitle");
return true;
case R.id.second_context_menu_item:
Toast.makeText(getApplicationContext(),
"second context menu item clicked.",
Toast.LENGTH_SHORT)
.show();
//mode.finish();
mode.setTitle("second context menu item clicked.");
mode.setSubtitle("second subtitle");
return true;
default:
return false;
}
}
即我们只通过Back或Done action来关闭action mode,同时选择了某个action item之后,在action bar显示一些信息。效果如下: