一、android的开发,HTC手机上不显示menu模拟键解决办法:
在AndroidManifest.xml中讲targetSdkVersion改为9。
<uses-sdk
android:minSdkVersion="4"
android: targetSdkVersion="9" />
原因:
(1)华为没有菜单键的硬按钮;
(2)高版本默认OptionsMenu在ActionBar里,不会有虚拟menu键;
二、Menu模拟键与android:targetSdkVersion
今天一个朋友的软件在我的机子上出现了这个问题,就是没有了menu,成了全屏软件,再翻看以前一些同学的代码和网上所谓的教程,也存在同样的问题,是有必要写篇博客了,好进入正题。
如果开发的时候使用的4.x之前的SDk,请在写置android:targetSdkVersion=""的时候不要写4.x的版本,不管你是否需要menu这都不是一个很好的编程习惯,保持界面的一致性是每个开发者应该共同维护的事情。
这里说明一下android:targetSdkVersion的含义:
这里必须提到另外几个概念minSdkVersion、maxSdkVersion
minSdkVersion与maxSdkVersion比较容易理解,就是在安装程序的时候,如果目标设备的API版本小于minSdkVersion,或者大于maxSdkVersion,程序将无法安装(这一点必须注意,如果你的程序希望给2.1用,而且没有用高级的api,请设定尽量低的版本)。一般来说没有必要设置maxSdkVersion,android自身平台具有向下兼容性。
targetSdkVersion相对复杂一些,如果设置了此属性,那么在程序执行时,如果目标设备的API版本正好等于此数值,他会告诉Android平台:此程序在此版本已经经过充分测,没有问题。不必为此程序开启兼容性检查判断的工作了。也就是说,如果targetSdkVersion与目标设备的API版本相同时,运行效率可能会高一些。这里就出现了刚才的问题,如果你为低版本的sdk软件设置了高版本的target,号称适应4.x,系统自然不会帮你检查兼容性,4.x取消了屏幕下方的menu键,也就出现了这个问题。所以要注意,target不是说你能支持的版本,是你的目标版本。新的adt建议设置target,又是指最高版本,google会去发布这个规定吗,所以还是需要多去理解,不能生硬看api才能更多的从android开发中学到东西,android给了我们个人或小团队开发者一个做真正有用产品的机会。
这也带来了另一个必须说的问题,就是比如说,使用了targetSdkVersion这个SDK版本中的一个特性,但是这个特性在低版本中是不支持的,那么在低版本的API设备上运行程序时,可能会报错:java.lang.VerifyError,我今天也刚刚遇到用户反馈的这个问题,现在通过具体了解这个问题也有了更加清晰的认识。这个错误也就是说,此属性不会帮你解决兼容性的测试问题。因此你至少需要在minSdkVersion这个版本上将程序完整的跑一遍来确定兼容性是没有问题的。
在default.properties中的target是指在编译的时候使用哪个版本的API进行编译。
再吐槽几句吧,如果希望开发Android平台,请不要抱怨分辨率,不要抱怨机型,为你的用户定做最合适的产品是你的义务。
另一方面说,如果我们试着去习惯4.x的设计和编码理念,就会减少很多这方面的困扰,google给了我们很多解决方案,我们应该试着去习惯,而不是做一个2.x的开发者,虽然我们的用户大都是2.x的版本,但是理念总归需要先进一点的,开放,共同学习,这才是android。
最后补充官方文档的一些解释:
(1)targetsdkversion即其以上的版本将使用新的功能(os new api),但对低版本的需要进行code支持,即文章【1】写到的使用反射(reflection)的技术对老版本使用的一些api功能进行支持,否则会出现老版本上崩溃的现象;
(2)使用最新的开发平台开发,并设置其对应的targetsdkversion;发布的时候,需要从min到target上均需要进行测试;
(3)文章【2】提到了最大兼容性的问题,体现到两个方面:device feature support(新的属性), 和OS version support(新的api)。当进入新的开发平台时,为了使应用程序最大兼容性,可以对用不到新平台支持的一些特性进行选择设置,使得老版本(不被Market过滤掉)可以安装使用.
在AndroidManifest.xml中讲targetSdkVersion改为9。
<uses-sdk
android:minSdkVersion="4"
android: targetSdkVersion="9" />
原因:
(1)华为没有菜单键的硬按钮;
(2)高版本默认OptionsMenu在ActionBar里,不会有虚拟menu键;
二、Menu模拟键与android:targetSdkVersion
今天一个朋友的软件在我的机子上出现了这个问题,就是没有了menu,成了全屏软件,再翻看以前一些同学的代码和网上所谓的教程,也存在同样的问题,是有必要写篇博客了,好进入正题。
如果开发的时候使用的4.x之前的SDk,请在写置android:targetSdkVersion=""的时候不要写4.x的版本,不管你是否需要menu这都不是一个很好的编程习惯,保持界面的一致性是每个开发者应该共同维护的事情。
这里说明一下android:targetSdkVersion的含义:
这里必须提到另外几个概念minSdkVersion、maxSdkVersion
minSdkVersion与maxSdkVersion比较容易理解,就是在安装程序的时候,如果目标设备的API版本小于minSdkVersion,或者大于maxSdkVersion,程序将无法安装(这一点必须注意,如果你的程序希望给2.1用,而且没有用高级的api,请设定尽量低的版本)。一般来说没有必要设置maxSdkVersion,android自身平台具有向下兼容性。
targetSdkVersion相对复杂一些,如果设置了此属性,那么在程序执行时,如果目标设备的API版本正好等于此数值,他会告诉Android平台:此程序在此版本已经经过充分测,没有问题。不必为此程序开启兼容性检查判断的工作了。也就是说,如果targetSdkVersion与目标设备的API版本相同时,运行效率可能会高一些。这里就出现了刚才的问题,如果你为低版本的sdk软件设置了高版本的target,号称适应4.x,系统自然不会帮你检查兼容性,4.x取消了屏幕下方的menu键,也就出现了这个问题。所以要注意,target不是说你能支持的版本,是你的目标版本。新的adt建议设置target,又是指最高版本,google会去发布这个规定吗,所以还是需要多去理解,不能生硬看api才能更多的从android开发中学到东西,android给了我们个人或小团队开发者一个做真正有用产品的机会。
这也带来了另一个必须说的问题,就是比如说,使用了targetSdkVersion这个SDK版本中的一个特性,但是这个特性在低版本中是不支持的,那么在低版本的API设备上运行程序时,可能会报错:java.lang.VerifyError,我今天也刚刚遇到用户反馈的这个问题,现在通过具体了解这个问题也有了更加清晰的认识。这个错误也就是说,此属性不会帮你解决兼容性的测试问题。因此你至少需要在minSdkVersion这个版本上将程序完整的跑一遍来确定兼容性是没有问题的。
在default.properties中的target是指在编译的时候使用哪个版本的API进行编译。
再吐槽几句吧,如果希望开发Android平台,请不要抱怨分辨率,不要抱怨机型,为你的用户定做最合适的产品是你的义务。
另一方面说,如果我们试着去习惯4.x的设计和编码理念,就会减少很多这方面的困扰,google给了我们很多解决方案,我们应该试着去习惯,而不是做一个2.x的开发者,虽然我们的用户大都是2.x的版本,但是理念总归需要先进一点的,开放,共同学习,这才是android。
最后补充官方文档的一些解释:
(1)targetsdkversion即其以上的版本将使用新的功能(os new api),但对低版本的需要进行code支持,即文章【1】写到的使用反射(reflection)的技术对老版本使用的一些api功能进行支持,否则会出现老版本上崩溃的现象;
(2)使用最新的开发平台开发,并设置其对应的targetsdkversion;发布的时候,需要从min到target上均需要进行测试;
(3)文章【2】提到了最大兼容性的问题,体现到两个方面:device feature support(新的属性), 和OS version support(新的api)。当进入新的开发平台时,为了使应用程序最大兼容性,可以对用不到新平台支持的一些特性进行选择设置,使得老版本(不被Market过滤掉)可以安装使用.
(4)minSDKVersion是向后(老版本)兼容,targetSDKVersion是向前兼容。
三、android:targetSdkVersion引起的问题
项目在三星S3和三星Note II 上调用系统相机点击存储的时候崩溃了。查了半天没弄明白原因,后来发现就是因为在manifest里设置了android:targetSdkVersion = 14,导致程序出问题了。后来查下资料,在sdkversion 12以上,不包含12,设置了android:targetSdkVersion之后,android:configChanges="orientation|keyboardHidden"没有起作用,导致程序onCreate还会走。解决方法就是在android:configChanges="orientation|keyboardHidden|sreenSize".这样就ok了。
最近两天一直在纠结个问题,就是我们新版的软件通过IDEA编译出来运行在4.4的手机上
整个相机UI是完全错乱的,同事几个手机运行都一样,错乱的样子就是整个UI压缩挤压在一起,
完全不是你在布局里面设置的还具有相对位置的样子。 但是通过IDEA的布局文件的design按钮看到
的布局展示demo又是正常的,所以,一直怀疑是编译的问题,或者某些控件的id是否有重复。
于是重新rebuild,重新删除out,gen目录,重新编译,问题依旧。
晕哦,怎么回事?于是,我想仔细去分析下。
于是随意又用meizu的4.1系统的手机去试了下,结果,我擦,居然正常的,怎么回事?
问了同事最近有没有改过相机相关的代码,都说没有。我看了提交记录,的确是没有啊,怎么可能呢?
实在想不明白,后来追踪以前的记录,发现改了manifest里面一个地方就是android:targetSdkVersion这个属性,
晕,这个属相和相机UI展示有毛线关系,哎,还是看看吧。同事从android:targetSdkVersion = 14改成了
android:targetSdkVersion = 19,于是恢复试试,结果,我去,居然运行ok了,再改回19试试,擦,
又不ok了。好了,原因找到了,是android:targetSdkVersion的问题,可是为什么android:targetSdkVersion会
影响到相机UI的展示?八竿子打不着啊!!
android:targetSdkVersion="14",targetSdkVersion属性会告诉系统应用是在api level为14的系统上进行的测试,
应用不允许有向上兼容的行为。当应用运行在版本更高的api level的系统上时,
应用还是按照targetSdkVersion版本的运行,而不需要根据更高版本的系统来显示。但是为什么会出现这个问题?
接下来我看了下4.4系统api的说明,在Build.java类里面
/**
* <a href="http://www.it165.net/pro/ydad/" target="_blank" class="keylink">Android</a> 4.4: KitKat, another tasty treat.
*
* <p>Applications targeting this or a later release will get these
* new changes in behavior:</p>
* <ul>
* <li> The default result of {android.preference.PreferenceActivity#isValidFragment
* PreferenceActivity.isValueFragment} becomes false instead of true.</li>
* <li> In {@link android.webkit.WebView}, apps targeting earlier versions will have
* JS URLs evaluated directly and any result of the evaluation will not replace
* the current page content. Apps targetting KITKAT or later that load a JS URL will
* have the result of that URL replace the content of the current page</li>
* <li> {@link android.app.AlarmManager#set AlarmManager.set} becomes interpreted as
* an inexact value, to give the system more flexibility in scheduling alarms.</li>
* <li> {@link android.content.Context#getSharedPreferences(String, int)
* Context.getSharedPreferences} no longer allows a null name.</li>
* <li> {@link android.widget.RelativeLayout} changes to compute wrapped content
* margins correctly.</li>
* <li> {@link android.app.ActionBar}'s window content overlay is allowed to be
* drawn.</li>
* <li>The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
* permission is now always enforced.</li>
* <li>Access to package-specific external storage directories belonging
* to the calling app no longer requires the
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} or
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
* permissions.</li>
* </ul>
*/
public static final int KITKAT = 19;
里面有句话说了android.widget.RelativeLayout} changes to compute wrapped content margins correctly,说
重新修改了RelativeLayout计算margin的方式,我想,可能是不是和改动RelativeLayout有关呢?接下来又去
看了RelativeLayout的api说明。擦,这下貌似有问题了,
RelativeLayout里面介绍说
Note: In platform version 17 and lower, RelativeLayout was affected by a measurement bug that could cause child views to be measured with incorrect MeasureSpec values.
(See MeasureSpec.makeMeasureSpec for more details.)
This was triggered when a RelativeLayout container was placed in a scrolling container,
such as a ScrollView or HorizontalScrollView.
If a custom view not equipped to properly measure with the MeasureSpec mode UNSPECIFIED
was placed in a RelativeLayout, this would silently work anyway as RelativeLayout
would pass a very large AT_MOST MeasureSpec instead.
This behavior has been preserved for apps that set android:targetSdkVersion="17"
or older in their manifest's uses-sdk tag for compatibility.
Apps targeting SDK version 18 or newer will receive the correct behavior
他说RelativeLayout里面的MeasureSpec.makeMeasureSpec在17以前实现是有问题的,17以后才改了,哦,
MeasureSpec.makeMeasureSpec这个方法有问题,好了,继续调查。
我们的相机ui是这样写的
<com.pinguo.camera360.camera.controller.CameraLayout
android:id="@+id/layout_camera_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ff000000">
<!-- 预览取景区域,ID不能随意变动 -->
<include
android:id="@+id/layout_camera_preview"
layout="@layout/camera_preview_container"/>
<!-- 底部bar,ID不能随意变动 -->
<include
android:id="@+id/layout_camera_bottom_bar"
layout="@layout/layout_camera_bottom_menu2"/>
</com.pinguo.camera360.camera.controller.CameraLayout>
这个CameraLayout是一个自定义的View,继承的是ViewGroup,
----public class CameraLayout extends ViewGroup-----
然后CameraLayout里面重写了onMeasure方法,也用到了MeasureSpec.makeMeasureSpec方法,恩,可能有问题,继续
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
calcLayoutRect(width, height);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
switch (child.getId()) {
// 就是这个android:id="@+id/layout_camera_preview"
case PREVIEW_ID:
int wm2 = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,preLayRect.right - preLayRect.left);
int hm2 = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,preLayRect.bottom - preLayRect.top);
child.measure(wm2, hm2);
break;
// 就是这个android:id="@+id/layout_camera_bottom_bar"
case BOTTOM_ID:
int wm3 = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,botLayRect.right - botLayRect.left);
int hm3 = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,botLayRect.bottom - botLayRect.top);
child.measure(wm3, hm3);
break;
default:
break;
}
}
}
}
仔细一看,发现 MeasureSpec.makeMeasureSpec(preLayRect.right - preLayRect.left,MeasureSpec.EXACTLY);这个方法
参数传反了,public static int makeMeasureSpec(int size, int mode)方法第一个应该是size,第二个是mode,但是我们自己
不小心写错了。那么为什么写错了,一直没出错呢,用户没反馈呢,不应该啊。
继续看源码
当我们把android:targetSdkVersion="14"配置成14的时候,也就是使用4.0的实现,
4.0源码的makeMeasureSpec是这样实现的,
4009 /**
14010 * Creates a measure specification based on the supplied size and mode.
14011 *
14012 * The mode must always be one of the following:
14013 * <ul>
14014 * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
14015 * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
14016 * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
14017 * </ul>
14018 *
14019 * @param size the size of the measure specification
14020 * @param mode the mode of the measure specification
14021 * @return the measure specification based on size and mode
14022 */
14023 public static int makeMeasureSpec(int size, int mode) {
14024 return size + mode;
14025 }
大爷的,这就是上面系统说的那个问题了,这个实现不管你参数传没传反,都是返回一样的结果。
所以,配置成android:targetSdkVersion="14,即使参数传反了,也没问题。
如果配置android:targetSdkVersion="19",那么采用4.4的实现来运行代码。
而4.4修改后的实现是这样的
/**
* Creates a measure specification based on the supplied size and mode.
*
* The mode must always be one of the following:
* <ul>
* <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
* <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
* <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
* implementation was such that the order of arguments did not matter
* and overflow in either value could impact the resulting MeasureSpec.
* {@link android.widget.RelativeLayout} was affected by this bug.
* Apps targeting API levels greater than 17 will get the fixed, more strict
* behavior.</p>
*
* @param size the size of the measure specification
* @param mode the mode of the measure specification
* @return the measure specification based on size and mode
*/
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
sUseBrokenMakeMeasureSpec是什么呢?从源码可知它是用来适配的。
// Older apps may need this compatibility hack for measurement.
sUseBrokenMakeMeasureSpec = targetSdkVersion <= JELLY_BEAN_MR1;
恩,问题找到了,当你配置android:targetSdkVersion="19"后,此时
sUseBrokenMakeMeasureSpec == false,然后走的逻辑就是
return (size & ~MODE_MASK) | (mode & MODE_MASK);因为此时我们方法
参数传发了,所以,就导致UI布局整个出了问题,错乱了。
当配置android:targetSdkVersion="14"后,及时当前运行在4.4的手机上,
sUseBrokenMakeMeasureSpec也为ture,系统走的还是老的错误实现方式布局(虽然是错误的,
但是google能让它正常运行,只是实现不对而已),所以,就还是ok的。
这就是和原因所在。