有过android手机或者盒子(TV)开发经历的工程师都知道,app的适配是很头疼的工作。最近也在做相关工作,为此把自己学习和实战的经验分享在这儿,希望对有需要的同学有所帮助。
首先分享几篇适配相关的android官方文档:
API Guides-->Best Practices-->Supporting Multiple Screens
http://developer.android.com/guide/practices/screens_support.html
API Guides-->App Resources-->Providing Resources
http://developer.android.com/guide/topics/resources/providing-resources.html
Training-->Best Practices for User Interface -->Designing for Multiple Screens
http://developer.android.com/training/multiscreen/index.html
9图 API Guides-->Animation and Graphics-->Canvas and Drawables
http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
上面几篇文章都是讲如何适配的,笔者也是初略的看了一遍,需要多看几遍才能很好的理解和掌握。
有几点笔者需要说明一下,也是自己不理解的地方。
(1)按照android官方定义,dpi也就是屏幕密度是屏幕每英寸上的像素点。但是对于,盒子设备来说,根本就没有屏幕,dpi如何来?可是我们从系统中是能读到density的。这就是笔者不理解的一个地方。
下面以一个实际的例子来说明适配,以drawable为例来说明
项目中如下几个drawable文件夹
drawable
drawable-ldpi
drawable-sw1000dp-mdpi
drawable-sw600dp-hdpi
drawable-xhdpi
有这个三星S4 GT-I9500型号的手机,参数如下:
screenWidth = 1080 screenHeight = 1920 densityDpi = 480 scale = 3.0 fontScale = 3.0 xdpi = 442.451 ydpi = 443.345 screenInches = 4.971247911145661
在看下面分析过程前,请先看一下第二篇文章中的How Android Finds the Best-matching Resource部分。
dpi为480,属于xxhpi。
1080*1920px换算成dp是360*640dp。按照下图中的第一步,drawable-sw1000dp-mdpi和drawable-sw600dp-hdpi被排除。注意dpi是不会用于排除条件的,文章中有强调这一点。
Exception: If the qualifier in question is screen pixel density, Android selects the option that most closely matches the device screen density. In general, Android prefers scaling down a larger original image to scaling up a smaller original image. See Supporting Multiple Screens.
接下来就是根据dpi的选择了,请看上面的描述。设备的density是xxhpi,选择最近的dpi就是xhdpi,所以系统会从drawable-xhdpi文件夹下读取资源。需要注意的,xhdpi文件夹下的图片会scaling up到xxhpi。
为了验证我们的想法,我们可以先把drawable-xhdpi文件夹删了,安装到S4上试试,app直接就崩溃了,因为找不到资源文件。
经过试验有种奇怪的现象,不知道为什么但很重要,需要说明一下。
比如我们有如下的文件夹
drawable
drawable-ldpi
drawable-mdpi
drawable-sw1000dp-mdpi
drawable-sw600dp-hdpi
drawable-xhdpi
我们有个杰科的盒子,参数如下:
screenWidth = 1280 screenHeight = 672 densityDpi = 160 scale = 1.0 fontScale = 1.0 xdpi = 160.15764 ydpi = 160.42105 screenInches = 9.0233918316934
drawable
drawable-ldpi
drawable-mdpi
drawable-sw600dp-hdpi
drawable-xhdpi
执行步骤2、3,我们需要排除drawable、drawable-ldpi、drawable-mdpi、drawable-xhdpi文件夹,因为这些文件夹不包含size的qualifier。
最终只保留drawable-sw600dp-hdpi这个文件夹,刚好672dp满足sw600dp,hdpi到mdpi也没有问题。完美,到这是问题好像一切都解决了。
But,有中情况,假如有个资源文件在drawable-sw600dp-hdpi文件夹下没有,在drawable-mdpi文件夹下有,你认为此时应用会崩溃吗?
按照上面的分析,答案是肯定的,应用肯定会崩溃,因为找不到资源。
但是,实际不是这样的,应用不会崩溃,而是从drawable-mdpi取到资源。这就是我理解的地方。
我是这样的理解的,可能对size这个qualifer来说(也就是sw***),明显不满足的可以排除,比如:drawable-sw1000dp-mdpi。对于剩下的都可以保留,只不过drawable-sw600dp-hdpi是最满意的,如果drawable-sw600dp-hdpi中没有想要的资源,可以从其他文件夹中取,只有根据相应的dpi scale就行。
values下面的dimens选择需要说明一下:
dimens中值的单位都是sp或者dp。从dimens读出来来的值*(设备density/160)=真正的值px。
以S4为例,从values-xhdpi文件夹下读取的值*(480/160)。这个结果肯定是不对的。因此,我们需要建一个values-xxhdpi的文件夹,里面的dimens要重新按比例换算一遍。这样才对。
再说一下图片的scale问题
有如下的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<ImageView
android:layout_width="@dimen/user_record_listgallery_item_width"
android:layout_height="@dimen/user_record_listgallery_item_height"
android:background="@color/yellow"
android:scaleType="fitXY"
android:src="@drawable/focus_shadow" />
<ImageView
android:layout_width="@dimen/user_record_listgallery_item_width"
android:layout_height="@dimen/user_record_listgallery_item_height"
android:layout_marginLeft="20dp"
android:background="@color/yellow"
android:scaleType="fitXY"
android:src="@drawable/focus_shadow_new" />
</LinearLayout>
focus_shadow和focus_shadow_new都是相同的9图,只是名字不一样而已。
我们有个杰科的盒子,参数如下:
screenWidth = 1280 screenHeight = 672 densityDpi = 160 scale = 1.0 fontScale = 1.0 xdpi = 160.15764 ydpi = 160.42105 screenInches = 9.0233918316934
在此设备上运行测试。
目录结构如下:
focus_shadow放到drawabl-nodpi文件夹下。
focus_shadow_new放到drawable-sw600dp-hdpi文件夹下。
运行效果如下:
focus_shadow放到drawabl-nodpi文件夹下。
focus_shadow_new放到drawable-mdpi文件夹下。
注意:这种情况就是上面我们描述的特殊情况。
focus_shadow放到drawabl-nodpi文件夹下。
focus_shadow_new放到drawable-sw1000dp-mdpi文件夹下。
app崩溃
focus_shadow放到drawabl-nodpi文件夹下。
focus_shadow_new放到drawable-xxhdpi文件夹下。
这几种情况为什么会这样,上面都已经描述过了,大家自己分析吧。
有什么问题,欢迎留言交流。