专栏首页老欧说安卓三种菜单控件的兼容性问题处理集锦

三种菜单控件的兼容性问题处理集锦

选项菜单OptionsMenu的兼容问题

如果开发者用的是2.*及以上版本的Android Studio,那么极有可能发现openOptionsMenu方法无法调出菜单列表,不是SDK版本不够新,恰恰相反,正是因为SDK版本太新了。我们在Android Studio里面创建一个新的Activity代码,默认都是继承AppCompatActivity,而且build.gradle中也指定了appcompat-v7的编译版本,举例如下:

	compile 'com.android.support:appcompat-v7:24.2.0'

现在就是跟appcompat-v7的版本有关,经过多方实验,如果编译用的appcompat-v7版本大等于22.1.0,那么openOptionsMenu方法将失效;如果appcompat-v7版本小于22.1.0,比如使用21.0.3版本来编译,那么openOptionsMenu方法是能够弹出菜单的。另外,如果页面代码继承Activity,而非AppCompatActivity,则openOptionsMenu方法可正常使用。所以解决这个问题有两种办法: 1、页面代码继承AppCompatActivity,同时build.gradle中指定较低版本的appcompat-v7来编译(但将无法使用新版本的功能),具体配置修改如下:

	compile 'com.android.support:appcompat-v7:21.0.3'

2、页面代码改为继承Activity,可是如此一来,App中的各页面风格可能无法保持一致。 如果嫌麻烦的话,干脆就不要用选项菜单的openOptionsMenu方法了。自己写个PopupMenu或者ListPopupWindow实现弹出菜单的功能,PopupMenu和ListPopupWindow使用说明参见《Android开发笔记(一百二十一)列表弹窗PopupMenu和ListPopupWindow》;也可以使用更灵活的弹窗控件PopupWindow,该控件的使用说明参见《Android开发笔记(六十五)多样的菜单》。

上下文菜单ContextMenu的兼容问题

一般情况下使用上下文菜单没什么问题,但是给ListView的列表项注册上下文菜单就得注意了。比如下面的代码,本来想在长按列表项时弹出上下文菜单:

	@Override
	public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
		registerForContextMenu(view);
		openContextMenu(view);
		unregisterForContextMenu(view);
		return true;
	}

可是运行时程序却异常退出,查看日志发现,打开上下文菜单时不停地调用AbsListView.showContextMenuForChild,最后出现栈溢出异常“java.lang.StackOverflowError”,这是因为上下文菜单的长按事件与列表项的长按监听器OnItemLongClickListener相互影响,使得程序陷入了死循环。最后的处理办法,还是要把两种长按事件阻隔开,即等待列表项长按事件处理完毕之后,再去触发上下文菜单事件;同时在打开上下文菜单之前,务必清空列表项的长按事件,确保这两种事件不会互相影响。修改后的代码如下所示:

	private View mCurrentView;
	@Override
	public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
		mCurrentView = view;
		mHandler.postDelayed(mPopupMenu, 100);
		return true;
	}
	
	private Handler mHandler = new Handler();
	private Runnable mPopupMenu = new Runnable() {
		@Override
		public void run() {
			lv_cart.setOnItemLongClickListener(null);
			registerForContextMenu(mCurrentView);
			openContextMenu(mCurrentView);
			unregisterForContextMenu(mCurrentView);
			lv_cart.setOnItemLongClickListener(ShoppingCartActivity.this);
		}
	};

溢出菜单OverflowMenu的兼容问题

溢出菜单用于在顶部导航栏右侧展示,这个顶部导航栏可以是ActionBar,也可以是Android5.0之后的Toolbar。由于ActionBar与Toolbar使用方式上的差异,因此造成溢出菜单要分别对这种导航栏进行兼容适配。如果读者对ActionBar和Toolbar还不太了解的话,建议先看看这两篇博文《Android开发笔记(二十)顶部导航栏》、《Android开发笔记(一百一十九)工具栏Toolbar》。 举个例子,默认情况下,溢出菜单列表的菜单项不会在文字左边显示图标,即使设置了icon属性也不管用。要想让菜单项显示左侧图标,得调用MenuBuilder的setOptionalIconsVisible方法,通过菜单的featureId判断此菜单是否来源于ActionBar和Toolbar,如果是这二者来源(ActionBar的featureId是8,Toolbar的featureId是108),则显示菜单文字左边的图标。具体的判断代码如下所示:

	public static void setOverflowIconVisible(int featureId, Menu menu) {
		// ActionBar的featureId是8,Toolbar的featureId是108
		if (featureId % 100 == Window.FEATURE_ACTION_BAR && menu != null) {
			if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
				try {
					Method m = menu.getClass().getDeclaredMethod(
							"setOptionalIconsVisible", Boolean.TYPE);
					m.setAccessible(true);
					m.invoke(menu, true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

再举个例子,如果想让溢出菜单的某个菜单图标显示在导航栏上,可以在菜单布局中将showAsAction属性设置为ifRoom或者always,布局代码如下所示:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_refresh"
        android:orderInCategory="1"
        android:icon="@drawable/ic_refresh"
        android:showAsAction="ifRoom"
        android:title="刷新"/>

    <item
        android:id="@+id/menu_about"
        android:orderInCategory="8"
        android:icon="@drawable/ic_about"
        android:showAsAction="never"
        android:title="关于"/>
    
    <item
        android:id="@+id/menu_quit"
        android:orderInCategory="9"
        android:icon="@drawable/ic_quit"
        android:showAsAction="never"
        android:title="退出"/>
</menu>

上面这个菜单布局,在ActionBar时代没有问题;然而到了Toolbar时代,反而出了问题。即使导航栏上还有空间,也设置了ifRoom或者always的菜单项,可是其图标并不会显示在导航栏上。为什么会这样呢?这是因为Toolbar控件不是位于内核的addroid.jar,也不是位于v4的兼容包android-support-v4.jar,而是位于appcompat-v7的兼容包中,开发者要在工程中把appcompat-v7做为一个库导入到本工程中。这就意味着,Toolbar其实是做为一个自定义控件引进来的,倘若在布局文件中使用Toolbar,得声明它的全路径“android.support.v7.widget.Toolbar”;那么在菜单布局中,同样也要补充对自定义控件的相关处理,首先要给根节点menu增加命名空间声明xmlns:app="http://schemas.android.com/apk/res-auto",然后还要把android:showAsAction="ifRoom"改为app:showAsAction="ifRoom"。 下面是修改后适用于Toolbar的菜单布局文件:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/menu_refresh"
        android:orderInCategory="1"
        android:icon="@drawable/ic_refresh"
        app:showAsAction="ifRoom"
        android:title="刷新"/>

    <item
        android:id="@+id/menu_about"
        android:orderInCategory="8"
        android:icon="@drawable/ic_about"
        app:showAsAction="never"
        android:title="关于"/>
    
    <item
        android:id="@+id/menu_quit"
        android:orderInCategory="9"
        android:icon="@drawable/ic_quit"
        app:showAsAction="never"
        android:title="退出"/>
</menu>

点此查看Android开发笔记的完整目录

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android开发笔记(一百三十三)导航视图NavigationView

    很多App都有个人中心的侧滑菜单,通常在页面左侧边缘右拉时,即可弹出个人中心的菜单页面。对于Android来说,侧滑功能用到了抽屉布局DrawerLayout...

    用户4464237
  • Android开发笔记(一百二十七)活用提示窗Toast和Snackbar

    大家平时都经常用Toast,可是你是否发现,系统默认的Toast样式太过单调乏味呢?其实Toast的界面也允许开发者自行定制,只要定义好提示窗的布局文件,即...

    用户4464237
  • 给App的应用页面注册快捷方式

    元数据不单单能传递简单的字符串参数,还能传送更复杂的资源数据,从Android7.1开始新增的快捷方式便用到了这点,譬如在手机上桌面长按支付宝图标,会弹出如下图...

    用户4464237
  • 用Android Studio做一个简单的弹出式菜单

    如下效果图,当点击选择头像的按钮时,会弹出一个菜单,菜单里面有目录,每个目录都有点击事件去执行不同功能。由于按钮设置在底部,当底部空间不足时,会在上方弹出菜单。

    lollipop72
  • 黑客是如何监视你的手机的?

    基础环境:win10,Android studio 3,jd-gui,apktool,dex2jar

    FB客服
  • 精品连载丨安卓 App 逆向课程之三 frida 注入 Okhttp 抓包上篇

    抓包常常是Android协议分析的第一步,抓不到包困扰着众多爬虫工程师,因此很有必要抽丝剥茧,了解和学习Android的网络通信相关知识,并且打算写一些爬虫er...

    崔庆才
  • 安卓开发小效果--走马灯

    听着music睡
  • Android的HttpUrlConnection

    Dream城堡
  • 安卓动态添加碎片

      2.   新建一个类Fragment1.java,继承自Fragment,注意Fragment有两个不同的包,推荐使用support-v4中的,兼容性更好,...

    用户2038589
  • [android] 手机卫士自定义滚动控件

    TextView控件设置单行显示 android:singleLine=”true”

    陶士涵

扫码关注云+社区

领取腾讯云代金券