ContextMenu:上下文菜单
在Activity中长按某个View会弹出一个菜单,类似Windows上右键弹出的菜单。在Activity中只需要实现已有的几个空方法即可自定义上下文菜单:onCreateContextMenu、onContextItemSelected、onContextMenuClosed。不同的手机系统,显示样式和特性稍有差异。

1、ContextMenu
传统Menu的上下文菜单扩展,提供了修改上下文菜单标题的功能。上下文菜单不支持快捷方式和图标。长按时显示上下文菜单,只需要在Activity中调用Activity.registerForContextMenu(),并重写Activity.onCreateContextMenu()方法。
ContextMenu是继承Menu的接口
public interface ContextMenu extends Menu {
/**
* 将上下文菜单标题的标题设置为titleRes资源标识符中给定的标题。
*
*/
public ContextMenu setHeaderTitle(@StringRes int titleRes);
/**
* 将上下文菜单标题的标题设置为title中给定的标题。
*
*/
public ContextMenu setHeaderTitle(CharSequence title);
/**
* 将上下文菜单标题的图标设置为iconRes资源ID中给定的图标。
*
*/
public ContextMenu setHeaderIcon(@DrawableRes int iconRes);
/**
* 将上下文菜单标题的图标设置为Drawable图标中给定的图标。
*
*/
public ContextMenu setHeaderIcon(Drawable icon);
/**
* 将上下文菜单的标题设置为view中给定的View。 这将替换标题和图标(然后替换标题和图标)。
*
*/
public ContextMenu setHeaderView(View view);
/**
* 清除上下文菜单的标题。
*/
public void clearHeader();
/**
* 有关创建上下文菜单的其他信息。 例如,AdapterView使用它在启动上下文菜单的适配器中传递确切的项目位置。
*/
public interface ContextMenuInfo {
}
}2、实现上下文菜单
重写Activity.onCreateContextMenu()方法创建上下文菜单。
2.1、代码方式
直接调用ContextMenu参数对象add(int groupId, int itemId, int order, CharSequence title)方法,添加菜单项。
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, 0, 1, "文件");
menu.add(0, 1, 2, "打开");
menu.add(0, 2, 3, "关闭");
menu.add(1, 3, 4, "剪切");
menu.add(1, 4, 5, "复制");
menu.add(1, 5, 6, "移动");
}2.2、加载xml菜单
通过MenuInflater加载xml布局。
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.file_context_menu, menu);
}定义在res/menu/中的xml菜单:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/file"
android:icon="@drawable/setting_icon_2"
android:title="文件"
app:showAsAction="always" />
<item
android:id="@+id/open"
android:title="打开" />
<item
android:id="@+id/close"
android:title="关闭" />
<item
android:id="@+id/cut"
android:title="剪切" />
<item
android:id="@+id/copy"
android:title="复制" />
<item
android:id="@+id/move"
android:title="移动" />
</menu>3、注册上下文菜单
给View注册上下文菜单,长按会弹出ContextMenu:registerForContextMenu(View view),注册要在给定View中显示的上下文菜单(多个视图可以显示上下文菜单)。 此方法会将视图上的OnCreateContextMenuListener设置为此活动,因此在显示上下文菜单时将调用onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
@Override
protected void onCreate(Bundle savedInstanceState) {
...
registerForContextMenu(mView);
}ContextMenu也需要反注册,提供了反注册方法:unregisterForContextMenu(View view),禁止显示给定视图的上下文菜单。 此方法将删除视图上的OnCreateContextMenuListener。
@Override
protected void onDestroy() {
super.onDestroy();
unregisterForContextMenu(mView);
}也可以直接调用View的setOnCreateContextMenuListener(OnCreateContextMenuListener l)方法,给指定View设置上下文监听器,也就是当前Activity。
mView.setOnCreateContextMenuListener(this::onCreateContextMenu);取消直接设置为null即可:
mView.setOnCreateContextMenuListener(null);4、上下文菜单点击
上下文菜单选项点击的时候会调用onContextItemSelected(MenuItem item)方法。
前面代码方式手动添加的菜单项,传入的参数有菜单组groupId和菜单id,可以通过下面的方式获取到。
int id = item.getItemId();
int groudID = item.getGroupId();使用布局加载ContextMenu的时候不建议,因为这个时候id是布局中随机生成的R.id,直接引用布局中的id判断即可。
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.file:
//
break;
case R.id.open:
//
break;
case R.id.close:
//
break;
case R.id.cut:
//
break;
case R.id.copy:
//
break;
case R.id.move:
//
break;
default:
break;
}
return true;
}5、ContextMenu相关源码
5.1、registerForContextMenu/unregisterForContextMenu
在Activity类中该方法的定义:
public void registerForContextMenu(View view) {
view.setOnCreateContextMenuListener(this);
}调用View的setOnCreateContextMenuListener方法。内部将View设置为可长按,同时设置上下文监听器。
public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
getListenerInfo().mOnCreateContextMenuListener = l;
}5.2、OnCreateContextMenuListener
前面设置ContextMenu的OnCreateContextMenuListener,也就是当前Activity。
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
}在父类Activity中是空方法,也就是实现上下文菜单的时候需要重写该方法创建菜单。
OnCreateContextMenuListener接口定义在View中。
public interface OnCreateContextMenuListener {
/ **
*在构建此视图的上下文菜单时调用。 此方法返回后,保持菜单不安全
*
* @param menu 正在构建的上下文菜单
* @param v 要为其构建上下文菜单的视图
* @param menuInfo 有关应为其显示上下文菜单的项目的额外信息。 该信息将根据v的类别而变化。
* /
void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo);
}Activity父类默认就实现了OnCreateContextMenuListener接口。
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
...
}
