Android Menu

在使用 Android 版微信时发现,微信移除了侧滑操作,改为弹窗实现。个人对该方式很喜欢,所以对 Menu 的内容进行了整理。

选项菜单 OptionMenu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@[+][package:]id/resource_name"
android:title="string"
android:titleCondensed="string"
android:icon="@[package:]drawable/drawable_resource_name"
android:onClick="method name"
android:showAsAction=["ifRoom" | "never" | "withText" | "always" | "collapseActionView"]
android:actionLayout="@[package:]layout/layout_resource_name"
android:actionViewClass="class name"
android:actionProviderClass="class name"
android:alphabeticShortcut="string"
android:alphabeticModifiers=["META" | "CTRL" | "ALT" | "SHIFT" | "SYM" | "FUNCTION"]
android:numericShortcut="string"
android:numericModifiers=["META" | "CTRL" | "ALT" | "SHIFT" | "SYM" | "FUNCTION"]
android:checkable=["true" | "false"]
android:visible=["true" | "false"]
android:enabled=["true" | "false"]
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="integer" />
<group android:id="@[+][package:]id/resource name"
android:checkableBehavior=["none" | "all" | "single"]
android:visible=["true" | "false"]
android:enabled=["true" | "false"]
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="integer" >
<item />
</group>
<item >
<menu>
<item />
</menu>
</item>
</menu>
  • android:showAsAction

    • ifRoom: Only place this item in the app bar if there is room for it
    • never: overflow menu
    • withText: only text android:title
    • always: on the bar
    • collapseActionView: android:actionLayout or android:actionViewClass
  • android:actionViewClass

1
2
3
4
5
<item android:id="@+id/action_search"
android:title="@string/action_search"
android:icon="@drawable/ic_search"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />

通过 setOnActionExpandListener() 监听展开收缩事件

  • android:actionViewLayout
    引入布局用于操作窗口,效果类似于android:actionViewClass

  • android:actionProviderClass

1
2
3
4
5
<item android:id="@+id/action_share"
android:title="share"
app:showAsAction="always"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider"
/>

显示布局及可以监听相关事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem shareItem = menu.findItem(R.id.action_share);
if (searchItem != null) {
mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);
if (!setShareIntent()){
menu.removeItem(R.id.action_share);
//没有第三方可以分享,可以自定义
//如果一个应用程序需要接受Share Intent发送的共享数据,
// 那么需要在该应用程序的Manifest.xml文件中定义<intent-filter/>元素
//android.intent.action.SEND,
// 指明应用组件想要接受的intent
}
}
return super.onCreateOptionsMenu(menu);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Call to update the share intent
private boolean setShareIntent() {
if (mShareActionProvider != null) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "share content");//EXTRA_STREAM

PackageManager pm = getPackageManager();
//检查手机上是否存在可以处理这个动作的应用
List<ResolveInfo> infoList = pm.queryIntentActivities(shareIntent, 0);
if (!infoList.isEmpty()) {
mShareActionProvider.setShareIntent(shareIntent);
return true;
}
return false;
}
return false;
}

这种会记录偏好,可通过setShareHistoryFileName()设置记录的xml文件名

设置setOnShareTargetSelectedListener监听条目点击事件

可继承support包下ActionProvider自定义实现
android.support.design.R.dimen.abc_action_bar_default_height_material

关于overflow menu在高版本不显示icon

安卓4.0之前会显示icon,高版本中不会显示,可以通过反射去设置icon的显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void setIconEnable(Menu menu, boolean enable) {
if (menu != null) {
try {
Class clazz = menu.getClass();
if (clazz.getSimpleName().equals("MenuBuilder")) {
Method m = clazz.getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
//MenuBuilder实现Menu接口,创建菜单时,传进来的menu其实就是MenuBuilder对象
m.invoke(menu, enable);
}

} catch (Exception e) {
e.printStackTrace();
}

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* android 4.0以后使用AppCompatActivity必须在该方法中调用setIconEnable(),
* 隐藏的menuitem的icon才会显示
* android 4.0以后其他的activity可以再onPrepreOptionMenu()中调用
* android 4.0以前可以正常显示overflow中的menuitem的icon
* @param view
* @param menu
* @return
*/
@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
setIconEnable(menu, true);//让在overflow中的menuitem的icon显示
return super.onPrepareOptionsPanel(view, menu);
}

手机实体菜单按键导致actionbar上的三个点图标不显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 通过反射,设置实体menu键可用与否
* 该方法在onCreate()中调用
* @param enable false:实体键不可用,会在actionbar上显示小点
* true:可用,点击menu实体键才会显示menuitem
*/
public void setHasPermanentMenuKey(boolean enable){
try {
ViewConfiguration mconfig = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
if(menuKeyField != null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(mconfig, enable);
}
} catch (Exception ex) {
}
}

可以通过自定义toolbar中布局实现理想效果

1
2
3
4
5
6
7
ActionBar actionBar = getSupportActionBar();
//设置自定义actionbar的view
actionBar.setCustomView(R.layout.action_bar_layout);
//设置展示的options为DISPLAY_SHOW_CUSTOM
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
//设置showCustom为true
actionBar.setDisplayShowCustomEnabled(true);

上下文菜单 ContextMenu

浮动上下文菜单

  • registerForContextMenu(View view)注册于上下文菜单关联的View
  • ActivityFragment实现 registerForContextMenu(),当关联的View收到长按事件之后,会响应该方法。
1
2
3
4
5
6
7
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
  • 事件监听
1
2
3
4
5
6
7
8
9
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
//TODO
default:
return super.onContextItemSelected(item);
}
}

为单个视图启用上下文操作模式

  • 实现 ActionMode.Callback 接口。在其回调方法中,您既可以为上下文操作栏指定操作,又可以响应操作项目的点击事件,还可以处理操作模式的其他生命周期事件
  • 当需要显示操作栏时,调用activitystartActionMode()方法

弹出菜单 PopupMenu

1
2
3
4
5
6
public void showPopup(View v) {
PopupMenu popup = new PopupMenu(this, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.actions, popup.getMenu());
popup.show();
}

通过setOnMenuItemclickListener()设置监听

基于 Intent 的菜单项

通过intent_filter定义删选规则 CATEGORY_ALTERNATIVECATEGORY_SELECTED_ALTERNATIVE

调用Menu.addIntentOptions()来添加应用列表

ListPopupMenu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
final ListPopupWindow popup = new ListPopupWindow(mContext);
List<String> list = new ArrayList<>();
list.add("不喜欢");
list.add("举报");
popup.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
popup.setAdapter(new ArrayAdapter<>(mContext, R.layout.biz_elevator_layout_note_popup_item, list));
popup.setWidth( mContext.getResources().getDimensionPixelSize(R.dimen.width40));
popup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
popup.setAnchorView(mUserLayout);
popup.setModal(true);
popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
switch (position) {
case 0:
break;
case 1:
break;
}
popup.dismiss();
}
});
popup.setHorizontalOffset(-40);
popup.setDropDownGravity(Gravity.END);
popup.show();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
final PopupWindow popupWindow = new PopupWindow(mContext);
View inflate = LayoutInflater.from(mContext).inflate(R.layout.biz_elevator_layout_note_item_popup, null);
View tvDislike = inflate.findViewById(R.id.popup_dislike);
View tvReport = inflate.findViewById(R.id.popup_report);

tvDislike.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCallback != null) {
mCallback.onDisLikeClicked(v, mPosition);
}
popupWindow.dismiss();
}
});
tvReport.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCallback != null) {
mCallback.onReportClicked(v, mPosition);
}
popupWindow.dismiss();
}
});

popupWindow.setContentView(inflate);
popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);

// 如果需要设置带阴影的 .9 背景,需要在此处设置,而不是在布局文件中写。
Drawable drawableDot9 = getResources().getDrawable(R.drawable.bg_popup_with_shadow);
popupWindow.setBackgroundDrawable(drawableDot9);
// popupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
// this setting can intercept the touch event when popupWindow opened
popupWindow.setFocusable(true);
popupWindow.setTouchable(true);
popupWindow.setOutsideTouchable(true);
PopupWindowCompat.showAsDropDown(popupWindow, v, -10, 10, Gravity.BOTTOM);