来简书的第一篇文章,想了想就以这个作为开头了^^

概述

写自己的app时,发现NavigationView的默认菜单功能不支持类似OptionMenu那样点击后弹出二级子菜单的功能,思考了下想用PopupMenu来实现,现将遇到的坑和具体实现记录如下。

PopupMenu基本用法

这里推荐使用support-v7包下的PopupMenu,兼容更多版本且功能更丰富:

PopupMenu popupMenu = new PopupMenu(context, anchorView);

popupMenu.inflate(R.menu.xxx);

popupMenu.show();

popupMenu.setOnMenuItemClickListener(...);

NavigationView下弹出PopupMenu

上面构造函数的第二个参数anchorView表示popupMenu弹出时确定其位置的锚点。于是自然想到用NavigationView的menuitem来实现:

Menu navMenu = navigationView.getMenu();

MenuItem menuItem = navMenu.findItem(R.id.xxx);

anchorView = menuItem.getActionView();

完事了?还早着呢!编译后发现报错:

IllegalStateException: MenuPopupHelper cannot be used without an anchor

提示没有锚点。咦?我们不是传入了menuItem.getActionView()作为锚点么?

查询MenuItem的getActionView()方法源码后发现该方法返回的不是一个具体的控件或布局的view,所以导致popupMenu无法确定具体的锚点。

在网上搜索了一下解决方法,发现可以通过给NavigationView的menuItem传入一个具体的布局来实现锚点的定位,menu.xml代码如下:

xmlns:app="http://schemas.android.com/apk/res-auto">
...
android:id="@+id/nav_xxx"
android:icon="@drawable/nav_xxx"
android:title="@string/donate"
app:actionLayout="@layout/anchor"/> 
...
注意命名空间,anchor.xml代码如下:
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="1dp" />

可见只是一个空的LinearLayout,仅用来定位。这里将height设为1主要是为了防止layout挤占menu的空间导致UI变形。完成后再编译运行,效果如下:

popupmenu1.gif

功能正常实现了^^

进阶:给PopupMenu加上icon

最早的PopupMenu也是可以像NavigationView里的menu一样给设置icon的,但后来google为了明确区分PopupMenu和PopupWindow的使用,将相应的方法给精简掉了。但别忘了我们使用的是support-v7包的PopupMenu,这里依然可以利用MenuPopupHelper来实现。

MenuPopupHelper popupHelper = new MenuPopupHelper(context,(MenuBuilder) menu,view);
popupHelper.setForceShowIcon(true);

其中第二个参数需要传入当前menu并转化为MenuBuilder类型,而PopupMenu类型本身不能转化,可用popupMenu.getMenu()来实现。第三个参数就是上文已经说过的锚点。然后调用popupHelper的setForceShowIcon(true)方法就可以强制item显示icon了。

这里还有两个小坑:

Android Studio 3.0以上版本调用MenuPopupHelper的以上两行代码时编译器会报“xxx can only be called from within the same library group...”。这应该是个bug,解决方法很简单,在调用以上两个方法的方法体前面加上@SuppressLint("RestrictedApi")的注释,如:

@SuppressLint("RestrictedApi")
private void showPopupMenu(){
...
}

填完上面的坑后,运行,发现还是不能显示icon?这是因为我们对popupMenu的自定义是在popupHelper里进行的,所以最后不能用popupMenu.show(),而是要调用

popupHelper.show();

好了大功告成,看看最终效果吧:

popupmenu2.gif