问题?
在将项目工程最小sdk版本和target版本提高的14之后,也就是最低支持4.0之后,menu按键在系统上显示不出来了,对于某些资深android玩家来说这点是比较坑爹的。
那么下面就是解决问题的过程了。
如需转载 注明转自http://blog.csdn.net/luoxiaozhuo/article/details/50592567
查询phonewindow的源码可以看到这样一段代码
3335 final Context context = getContext(); 3336 final int targetSdk = context.getApplicationInfo().targetSdkVersion; 3337 final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; 3338 final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; 3339 final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP; 3340 final boolean targetHcNeedsOptions = context.getResources().getBoolean( 3341 R.bool.target_honeycomb_needs_options_menu); 3342 final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); 3343 3344 if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) { 3345 addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); 3346 } else { 3347 clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); 3348 }
明显看出来,是需要满足某些条件这个menu按钮才会显示出来的。比如targetsdk等,当然也可以通过反射直接调用这个显示menu按键的方法来实现这个功能。这个也是网上一大堆资料告诉我们的解决办法。
try {
getWindow().addFlags(WindowManager.LayoutParams.class.getField("FLAG_NEEDS_MENU_KEY").getInt(null));
}catch (NoSuchFieldException e) {
// Ignore since this field won't exist in most versions of Android
}catch (IllegalAccessException e) {
Log.w("lxz", "Could not access FLAG_NEEDS_MENU_KEY in addLegacyOverflowButton()", e);
}
当然,这个解决了一大部分4.0之后的问题,但是我手机升级到 了5.1.1之后,这个方法失效了。安卓就是个坑。。。。。
然后这个时候又想到去阅读源码,只能每个版本的源码去对比,看这个之间具体发生了什么变化。
发现从5.1.0版本之后。这块的源代码变成了这样
3451 final Context context = getContext(); 3452 final int targetSdk = context.getApplicationInfo().targetSdkVersion; 3453 final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; 3454 final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; 3455 final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP; 3456 final boolean targetHcNeedsOptions = context.getResources().getBoolean( 3457 R.bool.target_honeycomb_needs_options_menu); 3458 final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); 3459 3460 if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) { 3461 setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE); 3462 } else { 3463 setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE); 3464 }
看这个对比一下,我了个去,谷歌这么干是有道理的。本来一个方法能干到的事情,不需要两个方法,只需要一个方法不同的两个参数就ok,也是谷歌在规范代码吧~
但是苦了我们这些码农。。。。
然后后面其实就简单了,只需要仿照那个反射来找方法来实现这个功能就ok了。
Method m = Window.class.getDeclaredMethod("setNeedsMenuKey", int.class);
m.setAccessible(true);
m.invoke(getWindow(), new Object[]{WindowManager.LayoutParams.class.getField(
"NEEDS_MENU_SET_TRUE").getInt(null)});
注意一点这里需要调用的反射是
getDeclaredMethod();// 而不是 <span style="font-family: Arial, Helvetica, sans-serif;">getMethod();</span>
getMethod();
Returns a <code>Method</code> object which represents the public method with the specified name and parameter types. <code>(Class[]) null</code> is equivalent to the empty array. This method first searches the class C represented by this <code>Class</code>, then the superclasses of C and finally the interfaces implemented by C and finally the superclasses of C for a method with matching name.
<pre code_snippet_id="1566952" snippet_file_name="blog_20160127_2_9556582" name="code" class="java">getDeclaredMethod()
Returns a <code>Method</code> object which represents the method matching the specified name and parameter types that is declared by the class represented by this <code>Class</code>.
当然完整代码可能就需要加上一个版本判断来做不同的处理了。
通过这个Build.VERSION.SDK_INT来判断是否满足条件,去反射不同的方法来实现。
Build.VERSION.SDK_INT 21 android 5.0.1
Build.VERSION.SDK_INT 22 android 5.1.1
这觉B是坑爹。。。。
我只能再通过下载下来的源码再跟进去查一次了
发现这个是从sdk 22开始才有的特性
故此具体代码就变成了
if (Build.VERSION.SDK_INT < 22) {
getWindow().addFlags(
WindowManager.LayoutParams.class.getField(
"FLAG_NEEDS_MENU_KEY").getInt(null));
} else {
Method m = Window.class.getDeclaredMethod("setNeedsMenuKey",
int.class);
m.setAccessible(true);
m.invoke(getWindow(),
new Object[] { WindowManager.LayoutParams.class
.getField("NEEDS_MENU_SET_TRUE").getInt(null) });
}
具体的try catch 直接用ide处理下就ok了。