选项菜单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开发笔记的完整目录