最近两天一直在纠结个问题,就是我们新版的软件通过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类里面 /** * Android 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 [email protected] 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> [email protected] android.app.AlarmManager#set AlarmManager.set} becomes interpreted as * an inexact value, to give the system more flexibility in scheduling alarms.</li> * <li> [email protected] android.content.Context#getSharedPreferences(String, int) * Context.getSharedPreferences} no longer allows a null name.</li> * <li> [email protected] android.widget.RelativeLayout} changes to compute wrapped content * margins correctly.</li> * <li> [email protected] android.app.ActionBar}'s window content overlay is allowed to be * drawn.</li> * <li>The [email protected] 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 * [email protected] android.Manifest.permission#READ_EXTERNAL_STORAGE} or * [email protected] 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 UNSPECIFIEDwas 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>[email protected] android.view.View.MeasureSpec#UNSPECIFIED}</li>14015 * <li>[email protected] android.view.View.MeasureSpec#EXACTLY}</li>14016 * <li>[email protected] android.view.View.MeasureSpec#AT_MOST}</li>14017 * </ul>14018 *14019 * @param size the size of the measure specification14020 * @param mode the mode of the measure specification14021 * @return the measure specification based on size and mode14022 */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>[email protected] android.view.View.MeasureSpec#UNSPECIFIED}</li> * <li>[email protected] android.view.View.MeasureSpec#EXACTLY}</li> * <li>[email protected] 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. * [email protected] 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的。这就是和原因所在。
详细解决方案
关于android:targetSdkVersion所导致的有关问题
热度:61 发布时间:2016-04-28 01:57:04.0
相关解决方案
- android 读取byte[]中的元素解决方案
- android 标题栏兑现方式
- android 中Activity向BroadcastReceiver发送数据,该怎么解决
- Android 4.0 为什么模拟器老是提示小弟我谷歌拼音输入法已停止
- android:getSharedPreferences() 这是哪个类的方法解决思路
- android 怎么判断一个程序是否联网
- android 大量数据按周分组,该如何解决
- android RadioButton如何设置默认选中
- ksoap2-android-这个包,连接webService怎么设置超时
- android 怎么重新设置锚点
- android UI界面设计解决方案
- android 图片对象获取的有关问题
- android 怎么调用淘宝支付宝接口
- Android 沿袭InputMethodService自定义输入法
- android 关于服务连接的疑义
- android 两个activity如何通信
- android 怎么实现对view的放大和缩小
- android 教程解决方法
- android ID,该如何处理
- 准备复习2-3个月,看java+android,请问有经验者,怎么看效果最好》
- android UI线程与AsyncTask的有关问题
- android(java)中的java.net能不能和c#的system.net.sockets进行tcp通信,该如何解决
- android ListView 中的onItemClick Intent 没法跳转
- android(java) 中文乱码的有关问题
- c#c++,android,ios(iphone),php,java视屏课程 散分
- android Post文件到ASP.NET的有关问题,能收到参数收不到文件
- RIM 替 Android 开发者提供免费的 PlayBook!2月13日前
- android 动态设立控件高度
- Android test project 编译方法
- android -相机使用教程(1)解决方法