个人原创,转载请注明出处:https://www.jianshu.com/p/1dcb9eff6052
来简书的第一篇文章,想了想就以这个作为开头了^^
概述
写自己的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代码如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
...
<item
android:id="@+id/nav_xxx"
android:icon="@drawable/nav_xxx"
android:title="@string/donate"
app:actionLayout="@layout/anchor"/> <!--插入的具体布局-->
...
</menu>
注意命名空间,anchor.xml代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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
参考
https://stackoverflow.com/questions/41238545/anchoring-a-popupmenu-to-navigationview-menu-items
https://stackoverflow.com/questions/15454995/popupmenu-with-icons#
https://stackoverflow.com/questions/41150995/appcompatactivity-oncreate-can-only-be-called-from-within-the-same-library-group
网友评论