个人原创,转载请注明出处: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代码如下:
注意命名空间,anchor.xml代码如下:
可见只是一个空的LinearLayout,仅用来定位。这里将height设为1主要是为了防止layout挤占menu的空间导致UI变形。完成后再编译运行,效果如下:
功能正常实现了^^
进阶:给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();
好了大功告成,看看最终效果吧:
参考
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