前言

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 。

屏幕像素点的计算公式如下:
android 尺寸适配计算 手机尺寸适配_Android
即,先用勾股定理计算出对角上的像素点数量,再用该数量除以屏幕尺寸。

例如:4.7 英寸,1080 * 1920 分辨率的手机,它的像素密度为:
android 尺寸适配计算 手机尺寸适配_屏幕尺寸_02
对于 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 文件夹:

android 尺寸适配计算 手机尺寸适配_像素点_03

在创建这些文件夹前,我们需要提前设置好基准尺寸,一般我们会将 UI 设计图中使用的最大宽度对应的 dp 值来作为基准 dp 值,比如下面我这里将会以 360 dp 作为基准,我们来看看基准资源文件 /src/main/res/value/dimens.xml,如下所示:

android 尺寸适配计算 手机尺寸适配_屏幕尺寸_04

接下来,我们创建一个value-sw480dp 文件夹并在其中创建 dimens.xml 文件:

android 尺寸适配计算 手机尺寸适配_android 尺寸适配计算_05

那么这份数据是怎么计算得到的呢,当然也是在基准尺寸的基础上计算得到的,

即:dp_2 = (480 / 基准) x 2 = (480 / 360) x 2 = 2.6667 dp

这种适配方案容错率低,其缺点就是侵入性高,且需要根据不同机型的最大宽度 dp 创建多个文件夹,维护成本也比较大。

相比于使用 dp 原生方案sw 限定符适配方案 显然更加合适。