前言
Android 系统能发展到今天,离不开其开源性,但是随着越来越多的设备接入 Android 系统,并对 Android 系统进行各种各样的定制,导致长期以来出现了各种碎片化严重的问题。例如,Android 屏幕尺寸多种多样,如 5 寸、5.9 寸、6 寸、6.1 寸 等等,当然,屏幕分辨率也是多种多样,这很容易导致同一元素在不同手机上显示的效果不同的问题,因此,Android 应用开发中的屏幕适配工作也越来越重要。
本篇文章就介绍一下 Android 中屏幕适配相关的知识。
屏幕适配基本概念
屏幕尺寸
屏幕尺寸是指手机对角线的物理长度,单位是 英寸
(inch,1 inch = 2.54 cm),
例如我们常见的 6 英寸手机,表示手机的对角线长度是 6 * 2.54 = 15.24 cm 。
屏幕分辨率
指的是手机在横向、纵向上的像素点数总和,单位是 px
(pixel,1 pixel = 1 像素点) ,
分辨率一般描述成 A * B ,例如:1080 * 1920,表示屏幕每一行有 1080 个像素点,每一列有 1920 个像素点。
对于 Android 设备,我们可以通过 ADB 命令快速查看屏幕分辨率信息:
adb shell wm size
屏幕像素密度
指的是每英寸的像素点数量,单位是 dpi
(dots per ich),假设设备内每英寸有160个像素,那么该设备的屏幕像素密度为 160 dpi 。
屏幕像素点的计算公式如下:
即,先用勾股定理计算出对角上的像素点数量,再用该数量除以屏幕尺寸。
例如:4.7 英寸,1080 * 1920 分辨率的手机,它的像素密度为:
对于 Android 设备,我们可以通过 ADB 命令快速查看屏幕像素密度信息:
adb shell wm density
密度无关像素
密度无关像素,英文名 density-independent pixel,单位 dp,它是 Android 特有的单位,它与设备上的实际物理像素点无关,只与屏幕像素密度有关,它可以保证在不同屏幕像素密度的设备上显示相同的效果。
注意:在 Android 中,1 英寸 = 160 dp 。
为什么要用 dp 而不直接用 px 呢?我们举个简单的例子:
假设需要在屏幕上显示一条长度为屏幕宽度一半的线,用 px 来表示的话,那么:
- 在分辨率 480 * 800 ,屏幕密度 240 dpi 的设备上(约 3.89 英寸),这条线的长度应该为 240 px
- 在分辨率 320 * 480 ,屏幕密度 160 dpi 的设备上(约 3.61 英寸),这条线的长度应该为 160 px
对于以上情况,如果使用 px 设置这条线的长度,需要使用两个不同的值才能适配两个不同分辨率的设备。
但如果我们使用 dp
为单位,那么在以上这两种情况,都只需要使用 160 dp 就可以将这条线显示为屏幕一半的长度。
在平时的开发中,UI 设计师给我们的设计图都是以 px
为单位的,而我们则是要使用 dp
作为单位进行开发,因此我们需要将 px 转化为 dp,他们之间的转换关系如下所示:
密度类型 | 代表的分辨率(px) | 屏幕密度(dpi) | 换算(px/dp) |
低密度(ldpi) | 240 * 320 | 120 | 1 dp = 0.75 px |
中密度(mdpi) | 320 * 480 | 160 | 1 dp = 1 px |
高密度(hdpi) | 480 * 800 | 240 | 1 dp = 1.5 px |
超高密度(xhdpi) | 720 * 1280 | 320 | 1 dp = 2 px |
超超高密度(xxhdpi) | 1080 * 1920 | 480 | 1 dp = 3 px |
由此可见,在设备屏幕密度为 160 dpi 的情况下,1 dp = 1 px 。
独立比例像素
独立比例像素,英文名 scale-independent pixel,单位 sp,它是 Android 特有的单位,在 Android 中一般用于设置字体大小。
一般来说,1 sp = 1dp 。推荐使用偶数 sp 值设置字体大小,不推荐使用奇数和小数,因为容易造成精度的丢失问题。
屏幕适配方案
dp 原生方案
我们需要思考,Android 使用 dp
究竟解决了什么问题?
在上面 dp 概念的介绍中,我们知道,在 Android 中,1 英寸 = 160 dp,
对于相同尺寸,不同分辨率的设备,他们拥有着不同的屏幕像素密度,因此他们每 1 dp 所代表的像素数量是不一样的,
如果使用相同的 px 值显示一条线,那么这条线的长度在两个设备上将会显示不同的比例,
如果使用 dp 作为单位,由于每一 dp 它所对应的物理长度是一样的,因此,使用相同的 dp 值,他们的显示的比例是一样的。
实际上,dp 的优势也主要体现在相同尺寸,不同分辨率的设备的适配效果。对于不同尺寸不同分辨率的设备,dp 的适配效果就差强人意了!
举个例子。
假设我们的 UI 设计图是按屏幕分辨率 1080 * 2670,屏幕尺寸为 6 寸的设备(dpi 约为 480)来设计的,则通过计算,UI 图中设备屏幕的最大宽度为 360 dp 。
但是对于屏幕分辨率 1080 * 1920,屏幕尺寸为 5 寸的设备(dpi 约为 440),通过计算,
它的屏幕宽度其实为 1080 / (440/160) = 392.7 dp
,
也就是屏幕是比设计图要宽的。
这种情况下, 即使使用 dp 也无法在不同设备上显示为同样效果, 同时还存在部分设备屏幕宽度不足 360 dp,这时就会导致按 360 dp 宽度来开发,实际却显示不全的情况。
总结:仅使用 dp 原生方案进行屏幕适配,适配性非常差,不建议使用该方案。
sw 限定符适配方案
sw 限定符适配即 smallestWidth 适配,也叫最小宽度限定符适配。
它的原理是,Android 会识别屏幕可用高度和宽度的最小尺寸的 dp 值,然后根据识别到的结果去 资源文件
中寻找对应限定符的文件夹下的资源文件。其实就是系统通过特定的规则来选择对应的文件。
举个例子。
某手机的屏幕分辨率是 1080 * 1920,屏幕像素密度是 480 dpi,通过计算,
它的最大宽度对应的 dp 值是:1080 / (480 / 160) = 360 dp
根据这个 360 dp
值,系统会去寻找 value-sw360dp
的文件夹以及对应的资源文件并使用它。
如果没有 value-sw360dp
文件夹,系统会向下寻找,比如离 360 dp 最近的只有 value-sw350dp
,那么Android就会选择 value-sw350dp
文件夹下面的资源文件。
一般来说,value-swXXXdp 这些文件夹都会创建在 /src/main/res/
目录下,例如我工程中创建的 value-sw480dp
文件夹:
在创建这些文件夹前,我们需要提前设置好基准尺寸,一般我们会将 UI 设计图中使用的最大宽度对应的 dp 值来作为基准 dp 值,比如下面我这里将会以 360 dp 作为基准,我们来看看基准资源文件 /src/main/res/value/dimens.xml
,如下所示:
接下来,我们创建一个value-sw480dp
文件夹并在其中创建 dimens.xml
文件:
那么这份数据是怎么计算得到的呢,当然也是在基准尺寸的基础上计算得到的,
即:dp_2 = (480 / 基准) x 2 = (480 / 360) x 2 = 2.6667 dp
这种适配方案容错率低,其缺点就是侵入性高,且需要根据不同机型的最大宽度 dp 创建多个文件夹,维护成本也比较大。
相比于使用 dp 原生方案
,sw 限定符适配方案
显然更加合适。