popupMenu
多数时候,我们需要设置popupMenu,这样可以在点击的时候显示菜单,可操作性比系统菜单,toolbar菜单强,所以用处更加广泛。
popupMenu的菜单实际上和toolbar上的是一样的,设置内容的方法类似,都可以从xml文件创建,或者动态添加项目。但是有一点,就是没有图标。
toolbar上的菜单可以设置图标,但是只有选项直接显示在toolbar上的时候才有图标(显示图标的时候就不显示文字),选项溢出在菜单里面的时候,会显示文字,尔没有图标。
(补充一,在后面)
popupMenu干脆就没有图标,只有文字。虽然可以用popupMenu.getMenu()
获得menu的对象,然后设置图标menu.add("name").setIcon(...)
,但是根本不显示。
据说早期android可以设置图标并且显示的,有一个可以设置的public方法设置是否显示图标。
后来4.0版本以后,干脆屏蔽了这个方法,变成了private的,就是说普通方式无法显示图标了。
不知道他们怎么想的。
设置显示图标
网上查到一个方法,可以用“反射”机制来改变这个class,然后设置这个隐藏起来的方法,用来显示图标。
这个反射,就是说可以获得一个class类,然后修改这个类的内容,然后再运行。
看起来很危险容易被恶意利用,但是有些时候会很有用。
private void setIconEnable(Menu menu, boolean enable)
{
try
{
Class<?> clazz = Class.forName("com.android.internal.view.menu.MenuBuilder");
Method m = clazz.getDeclaredMethod("setOptionalIconsVisible", boolean.class);
m.setAccessible(true);
//传入参数
m.invoke(menu, enable);
} catch (Exception e)
{
e.printStackTrace();
}
}
(以上方法是我从网上抄来的)
这个方法传入两个参数,第一个就是menu对象,第二个是boolean,是否启用
整个方法就是改变了setOptionalIconsVisible()
方法,这本来是一个隐藏起来的方法,这里利用反射机制调用之。
这个方法是MenuBuilder类里面的,不是Menu里面的,这个MenuBuilder也是非公开的
这样就可以显示popupMenu的图标了
其他代替的办法
除了上面这种比较特别扽办法意外,可以自建一个类继承PopupMenu,但是操作起来跟上面类似。
或者干脆,用其他的widget代替popupMenu。
比如PopupWindow,自定义一个View,然后添加一个ImageView一个textView,可以做到类似的Menu效果
有一个listPopupWindow,定义了一个特殊的PopupWindow,看起来就跟Menu差不多了,使用起来更方便。
补充一
发现一个可以显示toolbar溢出菜单的图标的方法
设置toolbar菜单项的时候,如果设置showAsAction
为 never,或者ifroom的时候,会放在溢出菜单里面,就是最后面的竖着3个点构成的按钮,里面的菜单是没有图标只有文字的(可以用前面反射的方法让它出现图标)。但是如果设置成多层级菜单,就可以显示图标,在模拟器5.0系统上证实可以。
<item android:id="@+id/action_more"
android:icon="@drawable/ic_action_more"
android:showAsAction="always">
<menu>
<item android:id="@+id/action_search"
android:title="搜索"
android:icon="@drawable/ic_action_search"
android:showAsAction="ifRoom">
</item>
<item android:id="@+id/action_more_one"
android:title="动作一"
android:icon="@drawable/ic_action_search"
android:showAsAction="never">
</item>
<item android:id="@+id/action_more_two"
android:title="动作二"
android:icon="@drawable/ic_action_search"
android:showAsAction="never">
</item>
</menu>
</item>
<item android:id="@+id/action_share"
android:title="分享"
android:icon="@drawable/ic_action_share"
android:showAsAction="always">
</item>
这样设置,最外层有两个图标,点击第一个,就会显示新的层级菜单,这个菜单是有图标的。
这个第二层的菜单称之为subMenu
,官方文档说subMenu不支持图标,但是这里显示了图标,不知道为什么。
于是我找到一种方法,就是用subMenu显示图标。
方法onCreateOptionsMenu
在创建视图的时候调用一次,用来初始化菜单 onPrepareOptionsMenu
在每次菜单显示的时候调用,初始化的时候也调用一次。在点击溢出菜单(就是最右边的竖直三个点图标)的时候也调用一次。
所以,在合适的方法里可以添加subMenu,然后定义这个subMenu的menuItem,
//创建submenu,并且添加到menu里面。
SubMenu subMenu = menu.addSubMenu(groupId, id,order,label);
//把submenu的show_as_action设置为_always,这样会总是显示在界面上
//另外还可以像普通item一样设置图标 setIcon()
subMenu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
//添加menuItem
subMenu.add("Yes").setIcon(R.drawable.ic_action_more);
如此一来就添加了一个像popupMenu一样的二级菜单,并且有图标
另外
有一个ActionProvider
类,可以用来创建子菜单
继承之,然后覆盖里面的一个方法
@Override
public void onPrepareSubMenu(SubMenu subMenu) {
super.onPrepareSubMenu(subMenu);
subMenu.clear();
subMenu.add("menu 1");
subMenu.add("menu 2");
subMenu.add("menu 3");
}
在menuItem里面设置这个actionProvider menu.add("provider").setActionProvider(new MyActionProvider(thisActivity)).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
我觉得没有上一种方法好,但是有效果。