Android系统如何实现UI的自适应
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Android系统如何实现UI的自适应
做Android应用的人都知道,要一个apk适用多个不同的手机屏幕是很容易的,就是在项目的res文件夹下面有多套相关的资源文件。程序运行的时候,Android系统会根据当前设备的信息去加载不同文件夹下的资源文件。但是Android系统是怎么做到这一点的呢?上网上搜了一下,很少有这方便的介绍,只好自己研究下代码了。下面是我研究代码得到的结果(正确性有待确认),在这里分享一下。
这里以ICS上在Activity的onCreate()方法里面调用setContentView(int resourceID)为例,介绍一下系统如何根据我们的id(yout.xxxx)找到合适的layout文件进行解析加载:如果你的res下面有三种不同的layout:layout, layout-sw480dp和layout-sw600dp,这里的sw
Activity.setContentView(int resourceID) -> PhoneWindow.setContentView(int resourceID) -> LayoutInflater.inflate(int resource, ViewGroup root) -> LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot) -> Resources.getLayout(int id) -> Resources.loadXmlResourceParser(int id, String type) -> Resources.getValue(int id, TypedValue outValue, boolean resolveRefs) -> AssetManager.getResourceValue(int ident, int density, TypedValue outValue, boolean resolveRefs) -> AssetManager.loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve)
在上面的掉用链中:
1. 最后加载的是哪个xml是由Resources.getValue(int id, TypedValue outValue, boolean resolveRefs)调用完成之后的outValue.string决定的,因为outValue.string的值就是你的资源文件的具体路径,如:
1) xxx/values/xxx.xml
2) xxx/layout-sw600dp/xxx.xml
2. AssetManager.loadResourceValue()调的是frameworks/base/core/jni/android_util_AssetManager.cpp里面的native方法,如何获得正确的outValue值,在native方法俩面主要有以下几步:
1) 调用frameworks/base/libs/utils/ResourceTypes.cpp 的ResTable::getResource(),遍历所有资源文件
2) 在ResTable::getResource()里面调用ResTable::getEntry()来确定资源文件来自哪个entry,即layout,或者layout-sw
3) 在ResTable::getEntry()里面:
a) 首先获取本设备的configurion信息,屏幕分辨率,屏幕大小,locale,横竖屏等。
b) 根据得到的本设备的configurion信息,过滤掉不适应本设备的entry,比如设备是800x480的,那么超过此分辨率的资源(例:layout-sw600dp)就要被过滤掉,实现在frameworks/base/include/utils/ResourceTypes.h中ResTable_config的match函数中
c) 对过滤后的resource进行最佳适配,找到最符合的entry文件。因为之前已经将不符合的,即大分辨率的entry已经被过滤掉了,所以这里就找剩下的最大的就是最佳适配的。实现在frameworks/base/include/utils/ResourceTypes.h中ResTable_config的isBetterThan()函数中。
3. 我做了一个尝试,就是想让800x480分辨率的设备上的应用都加载layout-sw600dp
里面的资源文件。所以将上面b)步骤的frameworks/base/include/utils/ResourceTypes.h里面ResTable_config的match函数改动如下:
我将settings.smallestScreenWidthDp强制换成了600,这样的话,所有比600dp小的(包含600)在内的资源文件在做过滤时就被保留了下来,而c)步骤不做检查,只找最大的,所以layout-sw600dp就成了系统认为的“最合适”的资源问价了。
将重新编译frameworks/base/libs/utils/生成的lib库push到/system/libs下面,再重启手机,然后启动上述应用,就可以了看见程序加载的layout-sw600dp的ui了。