一、Android的Menu简介
Android系统的Menu主要有三种,OptionsMenu、ContextMenu、PopupMenu。以及菜单下面二级菜单SubMenu。
二、菜单的创建和使用
1、ContextMenu的创建和使用
ContextMenu针对在Activity的View中的所有控件,在长按View控件达到2秒钟弹出一个菜单项。
效果图:
实现方式:
①在Activity的OnCreate()方法中对Button按钮或者整个活动页面添加菜单响应监听:
//长按弹出上下文菜单的Button
Button contextMenu = (Button) findViewById(R.id.contextMenu);
//长按页面弹出上下文菜单
View view = findViewById(R.id.theLayout);
contextMenu.setOnCreateContextMenuListener(this);//为Button单独实现ContextMenu
this.registerForContextMenu(view);//长按页面空白地方会弹出Menu
因为Activity已经实现了OnCreateContextMenuListener,所以要在Activity中重写该类的一些方法:
②创建菜单
//方法2,因为activity已经实现了OnCreateContextMenuListener,所以只需要重写onCreateContextMenu()方法
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {//这里是ContextMenu参数
setContextMenuIconEnable(menu, true);//设置菜单的Icon图标可见,这个方法后面详解,针对Android高版本的菜单图标显示的方法
menu.add(1, 2, 1, "添加").setIcon(android.R.drawable.ic_menu_add);
menu.add(1, 3, 2, "删除").setIcon(android.R.drawable.ic_menu_delete);
SubMenu m = menu.addSubMenu(0, 4, 3, "子菜单").setIcon(android.R.drawable.ic_media_next);
m.add(0, 41, 0, "子菜单项1");
m.add(0, 42, 0, "子菜单项2");
m.add(0, 43, 0, "子菜单项3");
super.onCreateContextMenu(menu, v, menuInfo);
}
③菜单关闭
@Override
public void onContextMenuClosed(Menu menu) {
Toast.makeText(this,"关闭",Toast.LENGTH_SHORT).show();
super.onContextMenuClosed(menu);
}
④菜单被点击
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 2:
Toast.makeText(this, "添加", Toast.LENGTH_SHORT).show();
break;
case 3:
Toast.makeText(this, "删除", Toast.LENGTH_LONG).show();
break;
default:
break;
}
return super.onContextItemSelected(item);
}
ContextMenu菜单实现很简单
2、OptionsMenu的创建和使用
OptionsMenu在页面的标题栏的右侧创建一个菜单按钮,前提是菜单栏可以显示的时候才有,否则会被隐藏。点击按钮弹出OptionsMenu菜单。
效果图:
实现方式:
因为Activity活动本身支持OptionsMenu菜单,所以,我们只需要在Activity里面实现其方法即可
①创建菜单的方法
* 当标题栏存在时,实现该方法则在标题栏出现Menu按钮,不实现则没有这个按钮
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {//这里是Menu
//下面这个方法针对OptionsMenu和PopupMenu的图标显示设置,稍后讲解
setOptionsAndPopupMenuIconEnable(menu, true);
menu.add(0, 5, 1, "添加1").setIcon(android.R.drawable.ic_menu_add);
//add()方法返回的是MenuItem对象,调用其setIcon()方法,为相应MenuItem设置Icon
menu.add(0, 6, 2, "删除1").setIcon(android.R.drawable.ic_menu_delete);
return super.onCreateOptionsMenu(menu);
}
②点击菜单按钮时候触发方法
//在点击OptionsMenu菜单按钮的时候触发,onPrepareOptionsPanel()这个方法先于onPrepareOptionsMenu()执行
@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
Toast.makeText(this, "onPrepareOptionsPanel...", Toast.LENGTH_SHORT).show();
return super.onPrepareOptionsPanel(view, menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
/**
* 在onCreateOptionsMenu执行后,菜单被显示前调用;如果菜单已经被创建,则在菜单显示前被调用。 同样的,
* 返回true则显示该menu,false 则不显示; (可以通过此方法动态的改变菜单的状态,比如加载不同的菜单等) TODO
* Auto-generated method stub
*/
Toast.makeText(this, "menu没出来时,处理方法自己定义即可", Toast.LENGTH_SHORT).show();
return super.onPrepareOptionsMenu(menu);
}
③当菜单的菜单项被点击的时候的响应函数
//当OptionItem被选择的时候
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 5:
Toast.makeText(this, "添加1", Toast.LENGTH_SHORT).show();
break;
case 6:
Toast.makeText(this, "删除1", Toast.LENGTH_LONG).show();
break;
case R.id.add_item:
Toast.makeText(this, "add", Toast.LENGTH_LONG).show();
break;
case R.id.remove_item:
Toast.makeText(this, "remove", Toast.LENGTH_LONG).show();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
④当菜单被关闭的时候(还存在问题)
//没有测试出结果,以为是菜单关闭时候,以下三种结果不是
//每次菜单被关闭时调用. (菜单被关闭有三种情形,menu按钮被再次点击、back按钮被点击或者用户选择了某一个菜单项)
@Override
public void onOptionsMenuClosed(Menu menu) {
Toast.makeText(this, "关闭222222", Toast.LENGTH_SHORT).show();
super.onOptionsMenuClosed(menu);
}
3、PopupMenu菜单的创建和使用
PopupMenu菜单是通过点击View组件的时候触发Activity相应函数,从而新建PopupMenu菜单
效果图:
实现方式:
①为控件添加属性如下:
android:onClick="popUpMenu"
②在Activity中添加对应函数
public void popUpMenu(View view) {
/**
* 第一个参数上下文对象
* 第二个参数为锚点,何谓锚点?就是你这个弹出式菜单以哪个view为标准弹出,
* 如果在这个view的下面有空间就在这个view下面的空间弹出,如果没有空间就
* 在此view的上面弹出
*/
PopupMenu popupMenu = new PopupMenu(this, view);
setOptionsAndPopupMenuIconEnable(popupMenu.getMenu(), true);//显示Menu的图标方法,稍后详解
/**
* 第一个参数为菜单布局文件
* 第二个参数为Menu对象,通过getMenu()获得。
*/
getMenuInflater().inflate(R.menu.menu, popupMenu.getMenu());
/**
* 要想显示必须调用.show()方法
*/
//弹出菜单的点击事件也没有回调事件,需要自己设置监听事件onMenuItemCilckListener(),
//并在其中实现逻辑,代码如下:
//值得注意的是最后的返回值,这个涉及到view的事件分发。简单的意思就是,返回true代码消耗这次点击事件,
//事件不会继续传播,如果返回false则事件会继续传播的。
//比如这个弹出菜单的又设置了setOnDismissListener这个事件,
//那么如果返回true,则这个事件不起作用,返回false则点击事件处理完后,这个事件还会起作用。
//但是亲测true好像没有用
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
Toast.makeText(MainActivity.this, menuItem.getTitle().toString(), Toast.LENGTH_SHORT).show();
return false;
}
});
popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu popupMenu) {
Toast.makeText(MainActivity.this, "PopupMenu的Dismiss", Toast.LENGTH_SHORT).show();
}
});
popupMenu.setGravity(Gravity.BOTTOM);//没有用
popupMenu.show();
}
上面可以看出关于PopupMenu菜单的所有的实现以及点击按钮事件都在这个方法内完成。
三、关于菜单的创建说明
1、add方法的参数解析
// add()方法的四个参数,依次是:
// 1、组别,如果不分组的话就写Menu.NONE,
// 2、Id,这个很重要,Android根据这个Id来确定不同的菜单
// 3、顺序,哪个菜单项在前面由这个参数的大小决定
// 4、文本,菜单项的显示文本
menu.add(0, 5, 1, "添加1").setIcon(android.R.drawable.ic_menu_add);
2、通过布局文件创建菜单
①新建布局文件,在res资源目录下新建一个menu资源文件夹,选择类型就是menu。然后在menu文件下面新建一个menu.xml文件,这个名字可以自己取。下面包含了二级菜单的创建。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:icon="@android:drawable/ic_menu_add"
android:title="Add" />
<item
android:id="@+id/remove_item"
android:icon="@android:drawable/ic_popup_reminder"
android:title="Remove" />
<item
android:id="@+id/item_china"
android:orderInCategory="100"
android:title="中国">
<menu>
<item
android:id="@+id/item_beijing"
android:orderInCategory="200"
android:title="北京"/>
<item
android:id="@+id/item_shanghai"
android:orderInCategory="200"
android:title="上海"/>
</menu>
</item>
</menu>
②通过资源文件新建菜单
getMenuInflater().inflate(R.menu.menu, Menu);
通过该方法直接将meun.xml的菜单解析到Menu实例中。
四、关于菜单图片的显示问题
Android的4.0之前,设置图标是默认显示的,但是4.0之后,就不在默认了。现在针对三种菜单Menu的图标显示方法如下
1、针对ContextMenu菜单
// 经过试验ContextMenu有效,对OptionMenu无效
private void setContextMenuIconEnable(Menu menu, boolean enable) {
if (menu != null) {
//com.android.internal.view.menu.ContextMenuBuilder,这是菜单menu的实体类
Log.d("AAA", menu.getClass().getName().toString());
try {
//这是ContextMenu加载菜单的Builder,com.android.internal.view.menu.MenuBuilder
Class<?> clazz = Class.forName("com.android.internal.view.menu.MenuBuilder");
Method m = clazz.getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
Log.d("aaa", m.toString());
m.setAccessible(true);
//MenuBuilder实现Menu接口,创建菜单时,传进来的menu其实就是MenuBuilder对象(java的多态特征)
m.invoke(menu, enable);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、针对PopupMenu和OptionsMenu菜单
//这是针对OptionsMenu和PopupMenu菜单的设置Icon图标可见的方法
private void setOptionsAndPopupMenuIconEnable(Menu menu, boolean enable) {
if (menu != null) {
Log.d("BBB", menu.getClass().getName().toString());
if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
try {
//这是OptionMenu加载菜单的Builder,android.support.v7.view.menu.MenuBuilder
//Class clazz = Class.forName("android.support.v7.view.menu.MenuBuilder");
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, enable);
} catch (Exception e) {
}
}
}
}