Android系统发布十多年以来,关于Android的UI的适配一直是开发环节中最重要的问题,可是我看到仍是有不少小伙伴对Android适配方案不了解。恰好,近期准备对糗事百科Android客户端设计一套UI尺寸适配方案,能够和小伙伴们详细的聊一聊这个问题。android
Android适配最核心的问题有两个,其一,就是适配的效率,即把设计图转化为App界面的过程是否高效,其二如何保证明现UI界面在不一样尺寸和分辨率的手机中UI的一致性。这两个问题都很重要,一个是保证咱们开发的高效,一个是保证咱们适配的成效;今天咱们就这两个核心的问题来聊一聊Android的适配方案。git
首先,你们都知道,在标识尺寸的时候,Android并不推荐咱们使用px这个真实像素单位,由于不一样的手机之间,分辨率是不一样的,好比一个96*96像素的控件在分辨率愈来愈高的手机上会在总体UI中看起来愈来愈小。github
出现相似于上图这样这样,总体的布局效果可能会变形,因此px这个单位在布局文件中是不推荐的。web
针对这种状况,Android推荐使用dp做为尺寸单位来适配UI.bash
那么什么是dp?dp指的是设备独立像素,以dp为尺寸单位的控件,在不一样分辨率和尺寸的手机上表明了不一样的真实像素,好比在分辨率较低的手机中,可能1dp=1px,而在分辨率较高的手机中,可能1dp=2px,这样的话,一个96*96dp的控件,在不一样的手机中就能表现出差很少的大小了。那么这个dp是如何计算的呢? 咱们都知道一个公式: px = dp(dpi/160) 系统都是经过这个来判断px和dp的数学关系,框架
那么这里又出现了一个问题,dpi是什么呢?布局
dpi是像素密度,指的是在系统软件上指定的单位尺寸的像素数量,它每每是写在系统出厂配置文件的一个固定值。post
我为何要强调它是软件系统上的概念?由于你们买手机的时候,每每会听到另外一个叫ppi的参数,这个在手机屏幕中指的也是像素密度,可是这个是物理上的概念,它是客观存在的不会改变。dpi是软件参考了物理像素密度后,人为指定的一个值,这样保证了某一个区间内的物理像素密度在软件上都使用同一个值。这样会有利于咱们的UI适配。spa
好比,几部相同分辨率不一样尺寸的手机的ppi可能分别是是430,440,450,那么在Android系统中,可能dpi会所有指定为480.这样的话,dpi/160就会是一个相对固定的数值,这样就能保证相同分辨率下不一样尺寸的手机表现一致。设计
而在不一样分辨率下,dpi将会不一样,好比:
... | 1080*720 | 1920*1080 |
---|---|---|
dpi | 320 | 480 |
dpi/160 | 2 | 3 |
根据上面的表格,咱们能够发现,720P,和1080P的手机,dpi是不一样的,这也就意味着,不一样的分辨率中,1dp对应不一样数量的px(720P中,1dp=2px,1080P中1dp=3px),这就实现了,当咱们使用dp来定义一个控件大小的时候,他在不一样的手机里表现出相应大小的像素值。
咱们能够说,经过dp加上自适应布局和weight比例布局能够基本解决不一样手机上适配的问题,这基本是最原始的Android适配方案。
这种方式存在两个小问题,第一,这只能保证咱们写出来的界面适配绝大部分手机,部分手机仍然须要单独适配,为何dp只解决了90%的适配问题,由于并非全部的1080P的手机dpi都是480,好比Google 的Pixel2(1920*1080)的dpi是420,也就是说,在Pixel2中,1dp=2.625px,这样会致使相同分辨率的手机中,这样,一个100dp*100dp的控件,在通常的1080P手机上,可能都是300px,而Pixel 2 中 ,就只有262.5px,这样控件的实际大小会有所不一样。
为了更形象的展现,假设咱们在布局文件中把一个ImageView的宽度设置为360dp,那么在下面两张图中表现是不同的:
图一是1080P,480dpi的手机,图二是1080P,420dpi的手机
从上面的布局中能够看到,一样是1080P的手机,差别是比较明显的。在这种状况下,咱们的UI可能须要作一些微调甚至单独适配。
第二个问题,这种方式没法快速高效的把设计师的设计稿实现到布局代码中,经过dp直接适配,咱们只能让UI基本适配不一样的手机,可是在设计图和UI代码之间的鸿沟,dp是没法解决的,由于dp不是真实像素。并且,设计稿的宽高每每和Android的手机真实宽高差异极大,以咱们的设计稿为例,设计稿的宽高是375px*750px,而真实手机可能广泛是1080*1920,
那么在平常开发中咱们是怎么跨过这个鸿沟的呢?基本都是经过百分比啊,或者经过估算,或者设定一个规范值等等。总之,当咱们拿到设计稿的时候,设计稿的ImageView是128px*128px,当咱们在编写layout文件的时候,却不能直接写成128dp*128dp。在把设计稿向UI代码转换的过程当中,咱们须要耗费至关的精力去转换尺寸,这会极大的下降咱们的生产力,拉低开发效率。
为了高效的实现UI开发,出现了新的适配方案,我把它称做宽高限定符适配。简单说,就是穷举市面上全部的Android手机的宽高像素值:
设定一个基准的分辨率,其余分辨率都根据这个基准分辨率来计算,在不一样的尺寸文件夹内部,根据该尺寸编写对应的dimens文件。
好比以480x320为基准分辨率
那么对于800*480的分辨率的dimens文件来讲,
x1=(480/320)*1=1.5px
x2=(480/320)*2=3px
...
这个时候,若是咱们的UI设计界面使用的就是基准分辨率,那么咱们就能够按照设计稿上的尺寸填写相对应的dimens引用了,而当APP运行在不一样分辨率的手机中时,这些系统会根据这些dimens引用去该分辨率的文件夹下面寻找对应的值。这样基本解决了咱们的适配问题,并且极大的提高了咱们UI开发的效率,
可是这个方案有一个致命的缺陷,那就是须要精准命中才能适配,好比1920x1080的手机就必定要找到1920x1080的限定符,不然就只能用统一的默认的dimens文件了。而使用默认的尺寸的话,UI就极可能变形,简单说,就是容错机制不好。
不过这个方案有一些团队用过,咱们能够认为它是一个比较成熟有效的方案了。
鸿洋大佬的适配方案的项目也来自于宽高限定符方案的启发。
使用方法也很简单:
第一步: 在你的项目的AndroidManifest中注明你的设计稿的尺寸。
<meta-data android:name="design_width" android:value="768"> </meta-data> <meta-data android:name="design_height" android:value="1280"> </meta-data>
第二步: 让你的Activity继承自AutoLayoutActivity.
而后咱们就能够直接在布局文件里面使用具体的像素值了,好比,设计稿上是96*96,那么咱们能够直接写96px,APP运行时,框架会帮助咱们根据不一样手机的具体尺寸按比例伸缩。
这能够说是一个极好的方案,由于它在宽高限定符适配的基础上更进一步,而且解决了容错机制的问题,能够说完美的达成了开发高效和适配精准的两个要求。
可是咱们可以想到,由于框架要在运行时会在onMeasure里面作变换,咱们自定义的控件可能会被影响或限制,可能有些特定的控件,须要单独适配,这里面可能存在的暗坑是不可预见的,还有一个比较重要的问题,那就是整个适配工做是有框架完成的,而不是系统完成的,一旦使用这个框架,将来一旦遇到很难解决的问题,替换起来是很是麻烦的,并且项目一旦中止维护,后续的升级就只能靠你本身了,这种代价团队可否承受?固然,它已经中止维护了。
不过仅仅就技术方案而言,不能否认,这是一个很好的开源项目。
讨论的上述几种适配方案都是能够实际用于开发中的比较成熟的方案,并且确实有不少开发者正在使用。不过因为他们各自都存在一些缺陷,因此咱们使用了上述方案后还须要花费额外的精力着手解决这些可能存在的缺陷。
那么,是否存在一种相对比较完美,没有明显的缺陷的方案呢?
smallestWidth适配,或者叫sw限定符适配。指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度值),而后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。
这种机制和上文提到的宽高限定符适配原理上是同样的,都是系统经过特定的规则来选择对应的文件。
举个例子,小米5的dpi是480,横向像素是1080px,根据px=dp(dpi/160),横向的dp值是1080/(480/160),也就是360dp,系统就会去寻找是否存在value-sw360dp的文件夹以及对应的资源文件。
smallestWidth限定符适配和宽高限定符适配最大的区别在于,前者有很好的容错机制,若是没有value-sw360dp文件夹,系统会向下寻找,好比离360dp最近的只有value-sw350dp,那么Android就会选择value-sw350dp文件夹下面的资源文件。这个特性就完美的解决了上文提到的宽高限定符的容错问题。
这套方案是上述几种方案中最接近完美的方案。 首先,从开发效率上,它不逊色于上述任意一种方案。根据固定的放缩比例,咱们基本能够按照UI设计的尺寸不假思索的填写对应的dimens引用。 咱们还有以375个像素宽度的设计稿为例,在values-sw360dp文件夹下的diemns文件应该怎么编写呢?这个文件夹下,意味着手机的最小宽度的dp值是360,咱们把360dp等分红375等份,每个设计稿中的像素,大概表明smallestWidth值为360dp的手机中的0.96dp,那么接下来的事情就很简单了,假如设计稿上出现了一个10px*10px的ImageView,那么,咱们就能够不假思索的在layout文件中写下对应的尺寸。
而这种diemns引用,在不一样的values-sw<N>dp文件夹下的数值是不一样的,好比values-sw360dp和values-sw400dp,
当系统识别到手机的smallestWidth值时,就会自动去寻找和目标数据最近的资源文件的尺寸。
其次,从稳定性上,它也优于上述方案。原生的dp适配可能会碰到Pixel 2这种有些特别的手机须要单独适配,可是在smallestWidth适配中,经过计算Pixel 2手机的的smallestWidth的值是411,咱们只须要生成一个values-sw411dp(或者取整生成values-sw410dp也没问题)就能解决问题。
smallestWidth的适配机制由系统保证,咱们只须要针对这套规则生成对应的资源文件便可,不会出现什么难以解决的问题,也根本不会影响咱们的业务逻辑代码,并且只要咱们生成的资源文件分布合理,,即便对应的smallestWidth值没有找到彻底对应的资源文件,它也能向下兼容,寻找最接近的资源文件。
固然,smallestWidth适配方案有一个小问题,那就是它是在Android 3.2 之后引入的,Google的本意是用它来适配平板的布局文件(可是实际上显然用于diemns适配的效果更好),不过目前全部的项目应该最低支持版本应该都是4.0了(糗事百科这么老的项目最低都是4.0哦),因此,这问题其实也不重要了。
生成diemns文件的过程以及数据计算方法上面已经讲清楚了,你们彻底能够本身去生成这些文件,我在这里附赠生成values-sw的项目代码,你们直接拿去用,是Java工程。点击这里获取项目地址