Android Drawable适配

0 前言:

前两年Android手机机型碎片化问题非常严重,资源适配也是面试中经常会遇到的问题。随着Android的发展,市场的选择,手机厂商们也渐渐统一相关机型的配置,比如说手机分辨率,当然不排除一些奇葩手机厂商,特立独行。最近刚好在整理应用中的图片资源,故把官网的适配规则又过一遍,总结下。

1 概述

在将适配之前,首先要明确几个相关的概念,因为这个特别影响对Android适配的理解,本人深有体会。

1.1 相关概念

屏幕尺寸(Screen size)

官方解释:
按屏幕对角测量的实际物理尺寸。
为简便起见,Android 将所有实际屏幕尺寸分组为四种通用尺寸:小(small)、 正常(normal)、大(large)和超大(xlarge)。
这里就有哥们问了,为什么屏幕尺寸时对角线计算还是英寸计算?这个问题还真把我问倒了,好尴尬,经过谷歌,百度,维基百科等等,求助终于找到靠谱的解释:最大可视可视区域。
但是也有知乎上是这么说的-》[为什么屏幕大小以对角线长度为标准?](https://www.zhihu.com/question/19640648) 我决定也蛮有道理的。
- 注:1英寸(inch)=2.54厘米(cm)

屏幕分辨率(Screen resolution)

官方解释:
屏幕上物理像素的总数。添加对多种屏幕的支持时, 应用不会直接使用分辨率;而只应关注通用尺寸和密度组指定的屏幕尺寸及密度。
这个没什么好争议的,就是指屏幕像素点总数。

屏幕密度(Screen density)

官方解释:
屏幕物理区域中的像素量,即每英寸屏幕所拥有的点数,这个点和像素点不一样;通常称为 dpi(Dots Per Inch,每英寸点数),Android 将所有屏幕密度分组为六种通用密度: 低、中、高、超高、超超高和超超超高。
计算公式:像素密度=√{(长度像素数^2+宽度像素数^2)}/ 屏幕尺寸

密度无关像素(Density independent pixel)

官方解释:
在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度或位置。
密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单: px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
说白了就是和密度无关的一种单位。

说了这么多这些概念真的是很奇葩很让人摸不着头脑!!!!

1.2 相关单位

ppi(Pixels Per Inch)

像素密度:是一个表示打印图像或显示器单位面积上像素数量的指数。(来自维基百科)
关于ppi与dpi的区别,一个是每英寸像素点个数,一个是每英寸点个数,这一个点可能不是一个像素值。
有一篇比较全面的解释,不过我还是没有能全看明白,看这里-》PPI vs. DPI: what’s the difference?
还有一篇中文解释:看这里-》DPI 和 PPI 的区别是什么?

dip(device-independent pixel,density-independent pixel, dip, dp)

密度无关像素,上面已经介绍过了,这里就不在多说了。
这个单位只适用于Android

sp(Scaleable pixels)

可缩放像素值,在安卓中支持字体,和dp类似。

px(Pixel)

代表的是屏幕上实际的像素。

in(Inch)

英寸,屏幕的物理尺寸单位,1英寸(inch)=2.54厘米(cm)。

pt(Points)

物理尺寸,即屏幕尺寸中一英寸的1/72.
关于更详细的信息可以参考Google Design DocumentationDimension

1.3 Android屏幕密度范围

说完这些单位,我们来了解下Android 支持多种屏幕尺寸和密度。
四种通用尺寸:小、正常、 大 和超大
六种通用的密度:

屏幕密度类型屏幕密度代表屏幕分辨率px
ldpi~120dpi240 X 3200.75px = 1dp x (120 / 160)
mdpi~160dpi320 X 4801px = 1dp x (160 / 160)
hdpi~240dpi480 X 8001.5px = 1dp x (240 / 160)
xhdpi~360dpi720 X 12802px = 1dp x (360 / 160)
xxhdpi~480dpi1080 X 19203px = 1dp x (480 / 160)
xxxhdpi~640dpi3840 X 21604px = 1dp x (6400 / 160)


超大屏幕至少为 960dp x 720dp
大屏幕至少为 640dp x 480dp
正常屏幕至少为 470dp x 320dp
小屏幕至少为 426dp x 320dp

screens-ranges

1. 4 限定符

配置限定符值说明
MCC 和 MNC“mcc310,mcc310-mnc004,mcc208-mnc00”移动国家代码 (MCC)
语言和区域en,fr,en-rUSfr-rCA等不区分大小写
布局方向ldrtl,ldltr应用的布局方向
smallestWidth“swdp:sw320dp sw600dp sw720dp”最小可能宽度
可用宽度wdp最小可用屏幕宽度
屏幕尺寸small, normal, large, xlarge如果没有匹配则崩溃
屏幕纵横比long,notlong宽屏,非宽屏
圆形屏幕round, notround手表,手机/平板
屏幕方向port, land横竖屏
UI 模式car, desk车载,电视,手表
夜间模式night, notnight
屏幕像素密度ldpi,mdpi,hdpi,nodpi等
平台版本(API 级别)v4,v7,v13,v21等


其中屏幕相关限定符:
屏幕尺寸,屏幕密度,方向,横纵比。通过官方提供的多屏适配,每个限定符都有特定的规则,如果适用不当也会出现资源找不到且出现崩溃的现象,上方给出的表格中是按官方提供的限定符优先级进行排列的。

限定符使用规则:
1. 可以使用多个限定符,并使用短划线分隔,例如:drawable-en-land
2. 限定符必须遵循上表中的顺序,例如:drawable-port-hdpi
3. 不能嵌套备用资源目录
4. 值不区分大小写
5. 每个限定符只支持一个值

1.5 Android 最佳匹配资源

匹配流程:
1. 淘汰与设备配置冲突的资源文件(除屏幕密度)
2. 选择列表(表 2)中(下一个)优先级最高的限定符。
3. 是否有资源目录包括此限定符?
4. 淘汰不含此限定符的资源目录。
5. 返回并重复第 2 步、第 3 步和第 4 步,直到只剩下一个目录为止

这里写图片描述

注:屏幕密度适配规则会略有不同,Android会去选择最接近设备屏幕密度的资源,且倾向于缩小大型原始图片,而不是缩小。

2. 为什么要进行适配

说了这么多,我们都没有谈及Android为什么要进行适配,花这么多的精力去做这么多的工作,原因究竟是什么?
碎片化,上面我们提到,多屏适配,也就是说,Android机型中存在着多种多样的屏幕,接下来我们来看下一些权威的报告。

Android机型屏幕的碎片化

这里写图片描述

注:上图来自于 Android Fragmentation Report August 2015 - OpenSignal

ios与Android版本的对比:
这里写图片描述

注:上图来自于 Android Fragmentation Report August 2015 - OpenSignal

这是目前Android机型设备分辨率的变化趋势
这里写图片描述

注:上图来自于腾讯大数据研究中心
从上面三幅图片我们可以明显看出,Android碎片化的严重性,相比ios,Android开发对屏幕,api版本适配需要付出更多的工作量

3. 适配实验

根据官网关于Android适配的规则,总结如下:
1. 不同设备使用不同资源(尽量为不同设备提供不同资源,当然考虑到程序包大小也不能完全适配)
2. 提供默认资源(个别限定符,如果没有默认资源,会导致崩溃)
3. px = dp * (dpi / 160) px与dp的换算

根据以上规则,对比实验如下:
- 相同图片,在不同屏幕密度上
- 相同图片,不同限定符资源文件夹,相同屏幕密度
- 不同图片,不同限定符资源文件夹,非对应屏幕密度手机

测试图片如下:144*144
这里写图片描述

3.1 实验一

将测试图片放置在创建的drawable目录下,默认的drawable文件夹资源是适配mdpi的,使用不同屏幕密度的模拟器进行显示。

布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <ImageView
        android:id="@+id/iv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/test"/>

</LinearLayout>

模拟器:

avd

预期结果:
根据适配规则,所有模拟器都会使用drawable中的test.png资源,且不会崩溃,根据不同的屏幕密度进行缩放,也就是除了AVD_MDPI之外,其他模拟器都会对图片进行放大,这里并不设定ImageView的大小。
测试结果:

这里写图片描述

这个效果看起来不是很明显,但是高密度手机图片会比较模糊一点。

3.2 实验二

将测试图片(test.png, 144*144)放置在不同密度的drawable目录下(drawable-mdpi,drawable-hdpi,drawable-xhdpi,drawable-xxhdpi),使用相同屏幕密度的模拟器(AVD_XXHDPI)进行显示。
预期结果:
根据适配规则,模拟器不会崩溃,显示相同的效果,也就是除了AVD_XXHDPI之外,其他模拟器都会对图片进行缩小,7不设定ImageView的大小。
测试结果:

这里写图片描述

通过模拟器结果:这个并不是我们预期的效果,在相同模拟器上显示了不同的效果。低密度手机显示的图片资源明显过大,这是为什么呢?尝试把对应的资源大小打印出来看看!

这里写图片描述

通过打印的日志可以看出,同一图片资源文件,放置在不同密度的drawable文件夹内,所加载的大小是不一样的,假设没有给控件设置长宽,其实加载的是图片资源的实际宽高,并根据不同的密度文件夹进行缩放。这也是官方中提到的适配规则,Android源码是如何实现的呢?

3.3源码

4. 参考

  1. What is the difference between “px”, “dp”, “dip” and “sp” on Android?
  2. screens_support
  3. providing-resources
  4. Android Fragmentation Visualized
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值