前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >三种菜单控件的兼容性问题处理集锦

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

作者头像
aqi00
发布2019-01-18 15:12:59
7590
发布2019-01-18 15:12:59
举报
文章被收录于专栏:老欧说安卓老欧说安卓

选项菜单OptionsMenu的兼容问题

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

代码语言:javascript
复制
	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来编译(但将无法使用新版本的功能),具体配置修改如下:

代码语言:javascript
复制
	compile 'com.android.support:appcompat-v7:21.0.3'

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

上下文菜单ContextMenu的兼容问题

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

代码语言:javascript
复制
	@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相互影响,使得程序陷入了死循环。最后的处理办法,还是要把两种长按事件阻隔开,即等待列表项长按事件处理完毕之后,再去触发上下文菜单事件;同时在打开上下文菜单之前,务必清空列表项的长按事件,确保这两种事件不会互相影响。修改后的代码如下所示:

代码语言:javascript
复制
	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),则显示菜单文字左边的图标。具体的判断代码如下所示:

代码语言:javascript
复制
	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,布局代码如下所示:

代码语言:javascript
复制
<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的菜单布局文件:

代码语言:javascript
复制
<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开发笔记的完整目录

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年04月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 选项菜单OptionsMenu的兼容问题
  • 上下文菜单ContextMenu的兼容问题
  • 溢出菜单OverflowMenu的兼容问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档