Android屏幕适配方案

前言

市面上关于屏幕适配的文章多不胜数,让人看的眼花缭乱。热门文章如下:

骚年你的屏幕适配方式该升级了!-今日头条适配方案
屏幕适配-最全面的解决方案
郭霖: Android官方提供的支持不同屏幕大小的全部方法
鸿洋: Android 屏幕适配方案
凯哥: Android屏幕适配全攻略(最权威的官方适配指导)

当然,Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因、重要概念、解决方案及最佳实践以及结合自己的思考,如果能认真的学习本文,对于Android的屏幕适配,你将会有所收获!

Android屏幕适配出现的原因

下面这张图片所显示的内容足以充分说明当今Android系统碎片化问题的严重性,因为该图片中的每一个矩形都代表着一种Android设备。


重要概念

什么是屏幕尺寸、屏幕分辨率(px)、屏幕像素密度(dpi)?
什么是dp、dip、sp?dp与px的转换?

在下面的内容中我们将介绍这些概念。

一、屏幕尺寸、屏幕分辨率(px)、屏幕像素密度(dpi)以及三者关系?

1. 屏幕尺寸

含义:手机对角线的物理尺寸
单位:英寸(inch),1英寸=2.54cm
Android手机常见的尺寸有5寸、5.5寸、6寸等等 (1寸=3.33厘米)

2. 屏幕分辨率(px)

含义:手机在横向、纵向上的像素点数总和,一般描述成屏幕的"宽x高”=AxB
单位:px(pixel),1px=1像素点
UI设计师的设计图会以px作为统一的计量单位
Android手机常见的分辨率:320x480、480x800、720x1280、1080x1920、2560x1440

3. 屏幕像素密度(dpi)

含义:屏幕像素密度是指每英寸上的像素点数
单位:dpi,即“dot per inch”的缩写。
假设设备内每英寸有160个像素,那么该设备的屏幕像素密度=160dpi

4. 三者关系

一部手机的分辨率是宽x高,屏幕大小是以英寸为单位,那么三者的关系是:

三者关系示意图.png

例如:假设一部手机的分辨率是1080x1920(px),屏幕大小是5寸,问该手机的像素密度是多少?

解:请直接套公式

image.png

二、dp、sp、dp与px的转换

1. 密度无关像素(dp、dip)

含义:density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关。
Android开发时用dp而不是px设置控件大小,是Android特有的单位。
单位:dp ,可以保证在不同屏幕像素密度的设备上显示相同的效果。

场景:假如同样都是画一条长度是屏幕一半的线,如果使用px作为计量单位,那么在480x800分辨率手机上设置应为240px;在320x480的手机上应设置为160px,二者设置就不同了;如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度。

2. 独立比例像素(sp)

含义:scale-independent pixel,叫sp或sip
单位:sp

Android开发时用此单位设置文字大小,可根据字体大小首选项进行缩放。推荐使用12sp、14sp、18sp、22sp作为字体设置的大小,不推荐使用奇数和小数,容易造成精度的丢失问题;小于12sp的字体会太小导致用户看不清。

3. dp与px的转换

因为ui设计师给你的设计图是以px为单位的,Android开发则是使用dp作为单位的,那么我们需要进行转换:

密度类型 分辨率(px) 屏幕密度(dpi) 换算(px/dp) 密度(density)
低密度(ldpi) 240x320 120 1dp=0.75px 0.75
中密度(mdpi) 320x480 160 1dp=1px 1
高密度(hdpi) 480x800 240 1dp=1.5px 1.5
超高密度(xhdpi) 720x1280 320 1dp=2px 2
超超高密度(xxhdpi) 1080x1920 480 1dp=3px 3
超超超高密度(xxxhdpi) 3840x2160 640 1dp=4px 4

解决方案

我们从两个大方面来阐述下Android的屏幕适配:

一、Android屏幕适配的发展
  1、dp直接适配
  2、宽高限定符适配 (屏幕分辨率限定符)
  3、UI适配框架AndroidAutoLayout
二、目前最好的适配方案
  1、SmallestWidth适配(sw限定符适配)
  2、今日头条适配方案
  3、AndroidAutoSize

一、Android屏幕适配的发展

1、dp直接适配

Android推荐使用dp作为尺寸单位来适配UI,通过dp加上自适应布局和weight比例布局可以基本解决不同手机上适配的问题,这基本是最原始的Android适配方案。
但这种方式存在两个小问题:

(1)这种方案只能保证我们写出来的界面解决了90%的适配问题,部分手机仍然需要单独适配,但dpi的不同,界面还是会存在差异。

dp只能解决了大部分的适配问题,因为并不是所有的1080P的手机dpi都是480,比如Google 的Pixel2(1920*1080)的dpi是420,也就是说,在Pixel2中,1dp=2.625px,这样会导致相同分辨率的手机中,这样,一个100dp x 100dp的控件,在一般的1080P手机上,可能都是300px,而Pixel 2 中 ,就只有262.5px,这样控件的实际大小会有所不同。

(2)一般的设计稿都是以px为单位的,所以我们在写layout文件的时候需要将px转为dp,影响开发效率。

该方案无法快速高效的把设计师的设计稿实现到布局代码中,通过dp直接适配,我们只能让UI基本适配不同的手机,但是在设计图和UI代码之间的鸿沟,dp是无法解决的,因为dp不是真实像素。而且,设计稿的宽高往往和Android的手机真实宽高差别极大,以我们的设计稿为例,设计稿的宽高是375px * 750px,而真实手机可能普遍是1080 x 1920

2、宽高限定符适配 (屏幕分辨率限定符)

为了高效的实现UI开发,出现了新的适配方案,我们称之为宽高限定符适配。简单的意思来说,就是穷举市面上所有的Android手机的宽高像素值,如下所示:



然后我们根据一个基准,为基准的意思就是:

比如480*320的分辨率为基准

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

例如对于800*480的宽度480:



可以看到x1 = 480 / 基准 = 480 / 320 = 1.5;当然,其他分辨率也是类似。那么,我们为什么需要这么写呢?

假设我现在需要在屏幕中心有个按钮,宽度和高度为我们屏幕宽度的1/2,我可以怎么编写布局文件呢?


    

可以看到我们的宽度和高度定义为x160,其实就是宽度的50%;效果图如下:


可以看到不论在什么分辨率的机型,我们的按钮的宽和高始终是屏幕宽度的一半。对于设计图来说,假设现在的UI的设计图是按照480*320设计的,且上面的宽和高的标识都是px的值,你可以直接将px转化为x[1-320],y[1-480],这样写出的布局基本就可以全分辨率适配了。
最后,有个最主要的问题,我们没有说,就是分辨率这么多,尼玛难道我们要自己计算,然后手写?
自动生成工具如下:
自动生成工具

但这种方式存在两个问题:

(1)有一个致命的缺陷,那就是需要精准命中才能适配,比如1920x1080的手机就一定要找到1920x1080的限定符,否则就只能用统一的默认的dimens文件了。而使用默认的尺寸的话,UI就很可能变形,简单说,就是容错机制很差。

(2)网上现在基本都是使用 屏幕分辨率限定符 进行适配,即每种屏幕分辨率的设备需要定义一套 dimens.xml 文件。由于不同分辨率的设备太多了,再加上平板那些你会发现 dimens.xml 文件所占的体积已经超过 2M 了!这绝对不是我们想要的。

3、UI适配框架AndroidAutolayout

鸿洋大神的作品,使用也超级简单,核心功能就是在绘制的时候在onMeasure里面做变换,重新计算px。
缺点:我们自定义的控件可能会被影响或限制,可能有些特定的控件(框架没有做适配的控件),需要单独适配,目前处于停止维护状态,不推荐使用。

二、目前最好的适配方案

1、SmallestWidth适配(sw限定符适配)

SmallestWidth适配,或者称之为sw限定符适配。指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度dp值),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。
这种机制和宽高限定符适配原理上是一样的,都是系统通过特定的规则来选择对应的文件。举个栗子,小米5的dpi是480,横向像素是1080px,根据px=dp(dpi/160),可求出横向宽度的dp值为360dp,系统就会去寻找是否存在value-sw360dp的文件夹以及对应的资源文件。

SmallestWidth限定符适配和宽高限定符适配最大的区别在于,前者有很好的容错机制,如果没有value-sw360dp文件夹,系统会向下寻找,比如离360dp最近的只有value-sw350dp,那么Android就会选择value-sw350dp文件夹下面的资源文件。这个特性就完美的解决了上文提到的宽高限定符的容错问题。

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


而这种dimens引用,在不同的values-swdp文件夹下的数值是不同的,比如values-sw360dpvalues-sw400dp:

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

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

SmallestWidth的适配机制由系统保证,我们只需要针对这套规则生成对应的资源文件即可,不会出现什么难以解决的问题,也根本不会影响我们的业务逻辑代码,而且只要我们生成的资源文件分布合理,即使对应的smallestWidth值没有找到完全对应的资源文件,它也能向下兼容,寻找最接近的资源文件。

不过这种方式仍然存在缺陷,那就是多个dimens文件可能导致apk变大,这是事实,根据生成的dimens文件的覆盖范围和尺寸范围,apk可能会增大300kb-800kb左右,我认为这是可以接受的。

最后,生成diemns文件的过程以及数据计算方法上面已经讲清楚了,大家完全可以自己去生成这些文件,我在这里附赠生成values-sw的项目代码。点击这里获取项目地址

2、今日头条适配方案

今日头条适配方案 ,之前确实没有接触过,我简单看了一遍,可以说,这也是相对比较完美的方案,我先简单说一下这个方案的思路,它是通过修改density值,强行把所有不同尺寸分辨率的手机的宽度dp值改成一个统一的值,这样就解决了所有的适配问题。

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

优点:这个方案侵入性很低,而且也没有涉及私有A,修改一次项目所有地方都会适配,无性能损耗。
缺点:
 1.只需要修改一次 density,项目中的所有地方都会自动适配,这个看似解放了双手,减少了很多操作,但是实际上反应了一个缺点,那就是只能一刀切的将整个项目进行适配,但适配范围是不可控的。
 2.这个方案依赖于设计图尺寸,但是项目中的系统控件、三方库控件、它们的设计图尺寸并不会和我们项目自身的保持一致。

3、AndroidAutoSize

AndroidAutoSize是基于今日头条适配方案。

以下这段话是作者对AndroidAutoSize由来的解释:

AndroidAutoSize今日头条屏幕适配方案的关系,相当于汽车和发动机的关系,今日头条屏幕适配方案 官方公布的代码,只实现了修改系统 density 的相关逻辑,这的确在屏幕适配中起到了最关键的作用,但这还远远还不够。
要想让使用者能够更傻瓜式的使用该方案,并且能够应对日常开发中的所有复杂需求,那在架构框架时,还需要考虑 API 的易用性以及合理性、框架的扩展性以及灵活性、功能的全面性、注释和文档的易读性等多个方面的问题。
于是我带着我的这些标准在网上搜寻了很久,发现并没有任何一个开源框架或解决方案能够达到我的所有标准,它们大多数还只是停留在将 今日头条屏幕适配方案 封装成工具类来引入项目的阶段,这样在功能的扩展上有限制,并且对用户的使用体验也不好,而我想做的是一个全面性的产品级屏幕适配框架,这离我最初的构想,差距还非常大,于是我只好自己动手,将我的所有思想实现,这才有了 AndroidAutoSize

使用也非常简单只需两步:

  1. 引入:
 implementation  'me.jessyan:autosize:1.1.2'
  1. 在 AndroidManifest 中填写全局设计图尺寸 (单位 dp),如果使用副单位,则可以直接填写像素尺寸,不需要再将像素转化为 dp。

                
        
                   
                

具体使用可参考:https://github.com/JessYanCoding/AndroidAutoSize

总结

以上就是Android屏幕适配的所有内容。最后提醒一句,如果UI设计上明显更适合使用wrap_contentmatch_parentlayout_weight等,我们就要毫不犹豫的使用,而且在高这个维度上,我们要依照情况设计为可滑动的方式,或者match_parent,尽量不要写死。总之,所有的适配方案都不是用来取代match_parentwrap_content的,而是用来完善他们的。

参考与感谢:

Android 目前最稳定和高效的UI适配方案
今日头条屏幕适配方案终极版正式发布!
屏幕适配-最全面的解决方案

你可能感兴趣的:(Android屏幕适配方案)