总结常用的Android屏幕适配方案

1,829 阅读8分钟

​​这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

 前段时间开启新项目的时候就考虑过Android屏幕适配,说实话以前从来没有想过针对这一块做一些优化,原本的屏幕适配方案简单来说就是利用好dp和weight来进行屏幕适配,当然这种方法在目前来说仍可以以最低的成本去适配至少80%的手机,但是为了精益求精剩下的20%也是我们需要考虑的。

另一个原因也是前段时间看了许多优秀的博文介绍这一块的内容,所以这里结合我自己体验也会简单总结一下:

1.为啥需要适配?

最重要的原因就是Android屏幕尺寸各异,而我们不可能根据各种尺寸都设计一套原型图去匹配,我们需要利用适配这一个过程把同一张原型图设计的样式尽可能以同样地视觉效果呈现在不同地屏幕上

Android适配最核心的问题有两个,其一,就是适配的效率,即把设计图转化为App界面的过程是否高效,其二如何保证实现UI界面在不同尺寸和分辨率的手机中UI的一致性。

下面我会介绍可能目前大家比较常用的几种方法,并且会贴上详细教程的链接,我希望本文能够在大家需要做屏幕适配的时候做一个导引作用,根据具体需求和时间成本去选择适合自己项目的方案!

2.dp适配方案

Android一开始就推荐我们使用dp而非px作为控件的尺寸去进行适配。

2.1 dp是什么?

dp指的是设备独立像素,以dp为尺寸单位的控件,在不同分辨率和尺寸的手机上代表了不同的真实像素,比如在分辨率较低的手机中,可能1dp=1px,而在分辨率较高的手机中,可能1dp=2px,这样的话,一个96*96dp的控件,在不同的手机中就能表现出差不多的大小了。简单来说dp是给开发者用的,但是系统识别最后还是会转化为px。

2.2dp如何计算

px = dp(dpi/160) 系统都是通过这个来判断px和dp的数学关系(160是规定的基准dpi,为啥160dpi是基准

那么这里又出现了一个问题,dpi是什么呢?

dpi是像素密度,指的是在系统软件上指定的单位尺寸的像素数量,它往往是写在系统出厂配置文件的一个固定值。

举个例子:不同分辨率下首先你的dpi是不同的

...1080*7201920*1080
dpi320480
dpi/16023

所以dp和px的换算比例也是不同的,这也完成了基本的适配功能,同样的dp最后根据屏幕实际大小统一进行放大为对应的px。

我们可以说,通过dp加上自适应布局和weight比例布局可以基本解决不同手机上适配的问题,这基本是最原始的Android适配方案。

2.3 方案缺陷

前面也说了这是一种最简单同时也是适配范围最小的一种解决方案,部分手机需要单独适配,因为不是所有的手机在1920*1080分辨率下的dpi都是480,比如Google 的Pixel2(19201080)的dpi是420,也就是说,在Pixel2中,1dp=2.625px,这样会导致相同分辨率的手机中,这样,一个100dp100dp的控件,在一般的1080P手机上,可能都是300px,而Pixel 2 中 ,就只有262.5px,这样控件的实际大小会有所不同。

还有另外一个问题就是原型图的尺寸不一定按照这个比例,那么我们想要把设计图的尺寸按照比例换算成dp就很麻烦了,原型图的宽高往往和Android的手机真实宽高差别极大,以我们的设计稿为例,设计稿的宽高是375px750px,而真实手机可能普遍是10801920。

平时为了解决这个问题基本都是通过给定百分比,或者通过估算,或者设定一个规范值(实习地方用的这种方法)等等。总之,当我们拿到原型图的时候,设计稿的ImageView是128px128px,当我们在编写layout文件的时候,却不能直接写成128dp128dp。在把设计稿向UI代码转换的过程中,我们需要耗费相当的精力去转换尺寸,这会极大的降低我们的生产力,拉低开发效率。

3.宽高限定符适配方案

3.1概念

简单来说就是利用官方提供的宽高限定符,去尽可能列举市面上所有常用的分辨率单位,如下图所示:

同时我们设定一个基准的分辨率,在该基准分辨率情况下,px和dp是1:1的关系,并且会对每一个像素进行等分:

比如以480x320为基准分辨率

  • 宽度为320,将任何分辨率的宽度整分为320份,取值为x1-x320
  • 高度为480,将任何分辨率的高度整分为480份,取值为y1-y480

那么对于800*480的分辨率的dimens文件来说,

x1=(480/320)*1=1.5px

x2=(480/320)*2=3px

紧接着原型图使用我们规定的基准分辨率去设计,那么我们就可以直接使用x与y去填充UI控件的尺寸,大小与原型图一致,后面系统会自动去对应分辨率的文件夹下面替换为适合的px

3.2 方案缺陷

其实前面也说到了就是必须准确的列出所有可能的分辨率,这样才能够精准的换算成对应的px。

否则就只能用统一的默认的dimens文件了。而使用默认的尺寸的话,UI就很可能变形,简单说,就是容错机制很差

不过这个方案之前用过还是一个比较成熟有效的方案了。

4.今日头条的density适配方案

4.1概念

之前也是看了今日头条的技术公众号,里面提到了一种他们的适配方案,这个方案简单来说就是通过控制修改一个density的值完成适配

今日头条屏幕适配方案的核心原理在于,根据以下公式算出 density

当前设备屏幕总宽度(单位为像素)/ 原型图总宽度(单位为 dp) = density

density 的意思就是 1 dp 占当前设备多少像素。

我们的目的就是通过控制这个density,保证最后得到的原型图总宽度都是一致的

比如,原型图宽度是360px,那么开发这边就会把目标dp值设为360dp,在不同的设备中,动态修改density值,从而保证(手机像素宽度)px/density这个值始终是360dp,这样的话,就能保证UI在不同的设备上表现一致了。

4.2为什么该方案只以宽高的一个作为基准?

有一些方案包括今日头条的方案他们的dp变换比例只会考虑一个宽或者高,那么为什么不两个都考虑呢?这主要是因为大部分市面上的 Android 设备的屏幕高宽比都不一致,比如现在的全面屏的问世,这个问题更加严重,不同厂商推出的全面屏手机的屏幕高宽比都可能不一致这时我们只以高或宽其中的一个作为基准进行适配,就会有效的避免布局在高宽比不一致的屏幕上出现变形的问题。简单来说防止宽高比出现不一致这种情况

4.3详细了解与实现

后续如果考虑这种方案以及想要继续深入的可以看一下官方的公众号文章

4.4 方案优点

上手比较简单,时间成本也很低。

侵入性非常低,该方案和项目完全解耦,在项目布局时不会依赖哪怕一行该方案的代码,而且使用的还是 Android 官方的 API,意味着当你遇到什么问题无法解决,想切换为其他屏幕适配方案时,基本不需要更改之前的代码,整个切换过程几乎在瞬间完成,会少很多麻烦,节约很多时间,试错成本接近于 0

可适配三方库的控件和系统的控件(不止是 ActivityFragmentDialogToast 等所有系统控件都可以适配),由于修改的 density 在整个项目中是全局的,所以只要一次修改,项目中的所有地方都会受益。

4.5 方案缺陷

这套方案对老项目是不太友好的,因为修改了系统的density值之后,整个布局的实际尺寸都会发生改变,如果想要在老项目文件中使用,恐怕整个布局文件中的尺寸都可能要重新按照设计稿修改一遍才行。因此,如果你是在维护或者改造老项目,使用这套方案就要三思了

当这个适配方案不分类型,将所有控件都强行使用我们项目自身的设计图尺寸进行适配时,这时就会出现问题,当某个系统控件或三方库控件的设计图尺寸和和我们项目自身的设计图尺寸差距非常大时,这个问题就越严重

5.smallestWidth适配

5.1 概念

最小宽度限定符适配,使用sw限定符进行适配,指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度值),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。

这种其实跟我们的第二种方案的思路是一样的,系统通过特定的规则来选择对应的文件。 但是他更加的灵活,不要求100%准确命中对应的分辨率。

下面我们举一个例子来看:小米5的dpi是480,横向像素是1080px,根据px=dp(dpi/160),横向的dp值就是360dp,系统就会去寻找是否存在value-sw360dp的文件夹以及对应的资源文件。

如果没有value-sw360dp文件夹,系统会向下寻找,比如离360dp最近的只有value-sw350dp,那么Android就会选择value-sw350dp文件夹下面的资源文件。 这个特性就完美的解决了上文提到的宽高限定符的容错问题。

我们还有以375个像素宽度的设计稿为例,在values-sw360dp文件夹下的dimens文件应该怎么编写呢?

这个文件夹下,意味着手机的最小宽度的dp值是360,我们把360dp等分成375等份,每一个设计稿中的像素,大概代表smallestWidth值为360dp的手机中的0.96dp,那么接下来的事情就很简单了,假如设计稿上出现了一个10px*10px的ImageView,那么,我们就可以不假思索的在layout文件中写下对应的尺寸。

这种diemns引用,在不同的values-swdp文件夹下的数值是不同的,比如values-sw360dp和values-sw400dp,

当系统识别到手机的smallestWidth值时,就会自动去寻找和目标数据最近的资源文件的尺寸。

5.2 方案优点

开发效率上,它不逊色于上述任意一种方案。根据固定的放缩比例,我们基本可以按照UI设计的尺寸不假思索的填写对应的dimens引用。

稳定性上,它也优于上述方案。原生的dp适配可能会碰到Pixel 2这种有些特别的手机需要单独适配,但是在smallestWidth适配中,通过计算Pixel 2手机的的smallestWidth的值是411,我们只需要生成一个values-sw411dp(或者取整生成values-sw410dp也没问题)就能解决问题。

5.3 方案缺陷

需要引入大量的dimens文件,所以可能导入apk包增大,根据生成的dimens文件的覆盖范围和尺寸范围,apk可能会增大300kb-800kb左右。

侵入性高,如果项目想切换为其他屏幕适配方案,因为每个 Layout 文件中都存在有大量 dimens 的引用,这时修改起来工作量非常巨大,切换成本非常高昂。

不能自动支持横竖屏切换时的适配,如上文所说,如果想自动支持横竖屏切换时的适配,需要使用 values-wdp 或 屏幕方向限定符 再生成一套资源文件,这样又会再次增加 App 的体积。

如果想使用 sp,也需要生成一系列的 dimens,导致再次增加 App 的体积

6.自己的总结

以上总共提出了四种屏幕适配可行的方案,不存在完全无用的方案也不存在最完美的方案,下面我们简单说一下我考虑的一些选择因素。

dp适配:该方案使用最简单同时也最直白,如果你的项目规模很小甚至没有专门的设计人员,结合dp和weight以及相对布局、约束布局等自适用布局方案,其实已经可以解决绝大多数的屏幕适配问题了。但是对于公司的主App来说这种方案肯定是不够的。

宽高限定符适配:该方案使用其实思路非常简单,但是需要一定时间成本,使用该方案的情况下还是推荐使用第四种sw限定符方案。

今日头条density适配:该方案适合一些新的项目,另外这些项目可能使用到的第三方UI控件的情况比较少,在这种情况下该方案是比较完美的,使用也简单,切换也方便,简单来说最好不要直接用在老项目上面。

sw限定符适配:该方案适合用在对apk包大小不是特别敏感,且最好没有sp大小和横屏功能的app上面,时间成本上面下面的参考链接有开源项目可以直接生成,也少了很多时间的。

7.参考链接

Android 目前稳定高效的UI适配方案-拉丁吴

dimens自动生成工具-拉丁吴

今日头条适配方案详解-JessYan

SmallestWidth 限定符适配方案-JessYan

AndroidAutoSize开源适配方案-JessYan