作者:JessYan

地址:https://www.jianshu.com/p/55e0fca23b4f1

前言

这个月在 Android 技术圈中 屏幕适配 这个词曝光率挺高的,为什么这么说呢?因为这个月陆续有多个大佬发布了屏幕适配相关的文章,公布了自己认可的屏幕适配方案。

上上个星期 Blankj 老师发表了一篇力挺今日头条屏幕适配方案的 文章(https://juejin.im/post/5b6250bee51d451918537021),提出了很多优化的方案,并开源了相关源码。

上个星期 拉丁吴 老师在 鸿神 的公众号上发布了一篇 文章(https://mp.weixin.qq.com/s/X-aL2vb4uEhqnLzU5wjc4Q),详细描述了市面上主流的几种屏幕适配方案,并发布了他的 smallestWidth 限定符适配方案和相关源码 (其实早就发布了),文章写的很好,建议大家去看看。

其实大家最关注的不是市面上有多少种屏幕适配方案,而是自己的项目该选择哪种屏幕适配方案,可以看出两位老师最终选择的屏幕适配方案都是不同的

我下面就来分析分析,我作为一个才接触这两个屏幕适配方案的吃瓜群众,我是怎么来验证这两种屏幕适配方案是否可行,以及怎样根据它们的优缺点来选择一个最适合自己项目的屏幕适配方案

这是我推荐给大家的屏幕适配框架:

Github : 您的 Star 是我坚持的动力 (https://github.com/JessYanCoding/AndroidAutoSize)✊

浅谈适配方案

在 拉丁吴 老师的文章中谈到了两个比较经典的屏幕适配方案,在我印象中十分深刻,我想大多数兄弟都用过,在我的开发生涯里也是有很长一段时间都在用这两种屏幕适配方案

第一种就是宽高限定符适配,什么是宽高限定符适配呢?

├── src/main│   ├── res│   ├── ├──values│   ├── ├──values-800x480│   ├── ├──values-860x540│   ├── ├──values-1024x600│   ├── ├──values-1024x768│   ├── ├──...│   ├── ├──values-2560x1440

就是这种,在资源文件下生成不同分辨率的资源文件,然后在布局文件中引用对应的 dimens,大家一定还有印象

第二种就是 鸿神 的 AndroidAutoLayout

这两种方案都已经逐渐退出了历史的舞台,为什么想必大家都知道,不知道的建议看看 拉丁吴 老师的文章,所以这两种方案我在文章中就不在阐述了,主要讲讲现在最主流的两种屏幕适配方案,今日头条适配方案(https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA) 和 smallestWidth 限定符适配方案(https://mp.weixin.qq.com/s/X-aL2vb4uEhqnLzU5wjc4Q)。

建议大家不清楚这两个方案的先看看这两篇文章,才清楚我在讲什么,后面我要讲解它们的原理,以及验证这两种方案是否真的可行,最后对他们进行深入对比,对于他们的一些缺点给予对应的解决方案,绝对干货。

今日头条屏幕适配方案

原理

上面已经告知,不了解这两个方案的先看看上面的两篇文章,所以这里我就假设大家已经看了上面的文章或者之前就了解过这两个方案,所以在本文中我就不再阐述 DPI、Density 以及一些比较基础的知识点,上面的文章已经阐述的够清楚了。今日头条屏幕适配方案的核心原理在于,根据以下公式算出 density:

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

density 的意思就是 1 dp 占当前设备多少像素。为什么要算出 density,这和屏幕适配有什么关系呢?

public static float applyDimension(int unit, float value, DisplayMetrics metrics){    switch (unit) {    case COMPLEX_UNIT_PX:        return value;    case COMPLEX_UNIT_DIP:        return value * metrics.density;    case COMPLEX_UNIT_SP:        return value * metrics.scaledDensity;    case COMPLEX_UNIT_PT:        return value * metrics.xdpi * (1.0f/72);    case COMPLEX_UNIT_IN:        return value * metrics.xdpi;    case COMPLEX_UNIT_MM:        return value * metrics.xdpi * (1.0f/25.4f);    }    return 0;}

大家都知道,不管你在布局文件中填写的是什么单位,最后都会被转化为 px,系统就是通过上面的方法,将你在项目中任何地方填写的单位都转换为 px 的。所以我们常用的 px 转 dp 的公式 dp = px / density,就是根据上面的方法得来的,density 在公式的运算中扮演着至关重要的一步。

要看懂下面的内容,还得明白,今日头条的适配方式,今日头条适配方案默认项目中只能以高或宽中的一个作为基准,进行适配,为什么不像 AndroidAutoLayout 一样,高以高为基准,宽以宽为基准,同时进行适配呢

这就引出了一个现在比较棘手的问题,大部分市面上的 Android 设备的屏幕高宽比都不一致,特别是现在大量全面屏的问世,这个问题更加严重,不同厂商推出的全面屏手机的屏幕高宽比都可能不一致

这时我们只以高或宽其中的一个作为基准进行适配,就会有效的避免布局在高宽比不一致的屏幕上出现变形的问题。明白这个后,我再来说说 density,density 在每个设备上都是固定的,DPI / 160 = density,屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度

  • 设备 1,屏幕宽度为 1080px,480DPI,屏幕总 dp 宽度为 1080 / (480 / 160) = 360dp

  • 设备 2,屏幕宽度为 1440,560DPI,屏幕总 dp 宽度为 1440 / (560 / 160) = 411dp

可以看到屏幕的总 dp 宽度在不同的设备上是会变化的,但是我们在布局中填写的 dp 值却是固定不变的

这会导致什么呢?假设我们布局中有一个 View 的宽度为 100dp,在设备 1 中 该 View 的宽度占整个屏幕宽度的 27.8% (100 / 360 = 0.278);但在设备 2 中该 View 的宽度就只能占整个屏幕宽度的 24.3% (100 / 411 = 0.243),可以看到这个 View 在像素越高的屏幕上,dp 值虽然没变,但是与屏幕的实际比例却发生了较大的变化,所以肉眼的观看效果,会越来越小,这就导致了传统的填写 dp 的屏幕适配方式产生了较大的误差

这时我们要想完美适配,那就必须保证这个 View 在任何分辨率的屏幕上,与屏幕的比例都是相同的

这时我们该怎么做呢?改变每个 View 的 dp 值?不现实,在每个设备上都要通过代码动态计算 View 的 dp 值,工作量太大

如果每个 View 的 dp 值是固定不变的,那我们只要保证每个设备的屏幕总 dp 宽度不变,就能保证每个 View 在所有分辨率的屏幕上与屏幕的比例都保持不变,从而完成等比例适配,并且这个屏幕总 dp 宽度如果还能保证和设计图的宽度一致的话,那我们在布局时就可以直接按照设计图上的尺寸填写 dp 值:

屏幕的总 dp 宽度 = 屏幕的总 px 宽度 / density

在这个公式中我们要保证 屏幕的总 dp 宽度 和 设计图总宽度 一致,并且在所有分辨率的屏幕上都保持不变,我们需要怎么做呢?屏幕的总 px 宽度 每个设备都不一致,这个值是肯定会变化的,这时今日头条的公式就派上用场了:

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

这个公式就是把上面公式中的 屏幕的总 dp 宽度 换成 设计图总宽度,原理都是一样的,只要 density 根据不同的设备进行实时计算并作出改变,就能保证 设计图总宽度 不变,也就完成了适配。

验证方案可行性

上面已经把原理分析的很清楚了,很多文章只是一笔带过这个公式,公式虽然很简单但我们还是想晓得这是怎么来的,所以我就反向推理了一遍,如果还是看不懂,那我只能说我尽力了,原理讲完了,那我们再来现场验证一下这个方案是否可行?

假设设计图总宽度为 375 dp,一个 View 在这个设计图上的尺寸是 50dp * 50dp,这个 View 的宽度占整个设计图宽度的 13.3% (50 / 375 = 0.133),那我们就来验证下在使用今日头条屏幕适配方案的情况下,这个 View 与屏幕宽度的比例在分辨率不同的设备上是否还能保持和设计图中的比例一致

验证设备 1

屏幕总宽度为 1080 px,根据今日头条的的公式求出 density,1080 / 375 = 2.88 (density)。

这个 50dp * 50dp 的 View,系统最后会将高宽都换算成 px,50dp * 2.88 = 144 px (根据公式 dp * density = px)144 / 1080 = 0.133,View 实际宽度与 屏幕总宽度 的比例和 View 在设计图中的比例一致 (50 / 375 = 0.133),所以完成了等比例缩放。

某些设备总宽度为 1080 px,但是 DPI 可能不同,是否会对今日头条适配方案产生影响?其实这个方案根本没有根据 DPI 求出 density,是根据自己的公式求出的 density,所以这对今日头条的方案没有影响

上面只能确定在所有屏幕总宽度为 1080 px 的设备上能完成等比例适配,那我们再来试试其他分辨率的设备

验证设备 2

屏幕总宽度为 1440 px,根据今日头条的的公式求出 density,1440 / 375 = 3.84 (density)。

这个 50dp * 50dp 的 View,系统最后会将高宽都换算成 px,50dp * 3.84 = 192 px (根据公式 dp * density = px)192 / 1440 = 0.133,View 实际宽度与 屏幕总宽度 的比例和 View 在设计图中的比例一致 (50 / 375 = 0.133),所以也完成了等比例缩放。

两个不同分辨率的设备都完成了等比例缩放,证明今日头条屏幕适配方案在不同分辨率的设备上都是有效的,如果大家还心存疑虑,可以再试试其他分辨率的设备,其实到最后得出的比例不会有任何偏差, 都是 0.133

优点

  1. 使用成本非常低,操作非常简单,使用该方案后在页面布局时不需要额外的代码和操作,这点可以说完虐其他屏幕适配方案。

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

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

  4. 不会有任何性能的损耗。

缺点

暂时没发现其他什么很明显的缺点,已知的缺点有一个,那就是第三个优点,它既是这个方案的优点也同样是缺点,但是就这一个缺点也是非常致命的。

只需要修改一次 density,项目中的所有地方都会自动适配,这个看似解放了双手,减少了很多操作,但是实际上反应了一个缺点,那就是只能一刀切的将整个项目进行适配,但适配范围是不可控的。

这样不是很好吗?这样本来是很好的,但是应用到这个方案是就不好了,因为我上面的原理也分析了,这个方案依赖于设计图尺寸,但是项目中的系统控件、三方库控件、等非我们项目自身设计的控件,它们的设计图尺寸并不会和我们项目自身的设计图尺寸一样。

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

举个栗子

假设一个三方库的 View,作者在设计时,把它设计为 100dp * 100dp,设计图的最大宽度为 1000dp,这个 View 在设计图中的比例是 100 / 1000 = 0.1,意思是这个 View 的宽度在设计图中占整个宽度的 10%,如果我们要完成等比例适配,那这个三方库 View 在所有的设备上与屏幕的总宽度的比例,都必须保持在 10%。

这时在一个使用今日头条屏幕适配方案的项目上,设置的设计图最大宽度如果是 1000dp,那这个三方库 View,与项目自身都可以完美的适配,但当我们项目自身的设计图最大宽度不是 1000dp,是 500dp 时,100 / 500 = 0.2,可以看到,比例发生了较大的变化,从 10% 上升为 20%,明显这个三方库 View 高于作者的预期,比之前更大了

这就是两个设计图尺寸不一致导致的非常严重的问题,当两个设计图尺寸差距越大,那适配的效果也就天差万别了。

解决方案

方案 1

调整设计图尺寸,因为三方库可能是远程依赖的,无法修改源码,也就无法让三方库来适应我们项目的设计图尺寸,所以只有我们自身作出修改,去适应三方库的设计图尺寸,我们将项目自身的设计图尺寸修改为这个三方库的设计图尺寸,就能完成项目自身和三方库的适配

这时项目的设计图尺寸修改了,所以项目布局文件中的 dp 值,也应该按照修改的设计图尺寸,按比例增减,保持与之前设计图中的比例不变。

但是如果为了适配一个三方库修改整个项目的设计图尺寸,是非常不值得的,所以这个方案支持以 Activity 为单位修改设计图尺寸,相当于每个 Activity 都可以自定义设计图尺寸,因为有些 Activity 不会使用三方库 View,也就不需要自定义尺寸,所以每个 Activity 都有控制权的话,这也是最灵活的。

但这也有个问题,当一个 Activity 使用了多个设计图尺寸不一样的三方库 View,就会同样出现上面的问题,这也就只有把设计图改为与几个三方库比较折中的尺寸,才能勉强缓解这个问题。

方案 2

第二个方案是最简单的,也是按 Activity 为单位,取消当前 Activity 的适配效果,改用其他的适配方案。

使用中的问题

有些文章中提到了今日头条屏幕适配方案可以将设计图尺寸填写成以 px 为单位的宽度和高度,这样我们在布局文件中,也就能直接填写设计图上标注的 px 值,省掉了将 px 换算为 dp 的时间 (大部分公司的设计图都只标注 px 值),而且照样能完美适配。但是我建议大家千万不要这样做,还是老老实实的以 dp 为单位填写 dp 值,为什么呢?

直接填写 px 虽然刚开始布局的时候很爽,但是这个坑就已经埋上了,会让你后面很爽,有哪些坑?

第一个坑

这样无疑于使项目强耦合于这个方案,当你遇到无法解决的问题想切换为其他屏幕适配方案的时候,layout 文件里曾经填写的 px 值都会作为 dp

比如你的设计图实际宽度为 1080px,你不换算为 360dp (1080 / 3 = 360),却直接将 1080px 作为这个方案的设计图尺寸,那你在 layout 文件中,填写的也都是设计图上标注的 px 值,但是单位却是 dp

一个在设计图上 300px * 300px 的 View,你可以直接在 layout 文件中填写为 300dp,而由于这个方案可以动态改变 density 的原因还是可以做到等比例适配,非常爽!

但你不要忘了,这样你就强耦合于这个方案了,因为当你不使用这个方案时,density 是不可变的!

举个栗子

使用这个方案时,在屏幕宽度为 1080px 的设备上,将设计图宽度直接填写为 1080,根据今日头条公式:

density = 当前设备屏幕总宽度 /  设计图总宽度

这时得出 density 为 1 (1080 / 1080 = 1),所以你在 layout 文件中你填写的 300dp 最后转换为 px 也是 300px (300dp * 1 = 300px 根据公式 dp * density = px)

在这个方案的帮助下非常完美,和设计图一模一样完成了适配。但当你不使用这个方案时,density 的换算公式就变为官方的 DPI / 160 = density,在这个屏幕宽度为 1080px,480dpi 的设备上,density 就固定为 3 (480 / 160 = 3)

这时再来看看你之前在 layout 文件中填写的 dp,换算成 px 为 900 px (300dp * 3 = 900px 根据公式 dp * density = px)

原本在在设计图上为 300px 的 View,这时却达到了惊人的 900px,3倍的差距,恭喜你,你已经强耦合于这个方案了,你要不所有 layout 文件都改一遍,要不继续使用这个方案。

第二个坑

第二个坑其实就是刚刚在上面说的今日头条适配方案的缺点,当某个系统控件或三方库控件的设计图尺寸和和我们项目自身的设计图尺寸差距非常大时,这个问题就越严重。

你如果直接填写以 px 为设计图的尺寸,这不用想,肯定和所有的三方库以及系统控件的设计图尺寸都不一样,而且差距都非常之大,至少两三倍的差距,这时你在当前页面弹个 Toast 就可以明显看到,比之前小很多,可以说是天差万别,用其他三方库 View,也是一样的,会小很多。

因为你以 px 为单位填写设计图尺寸,人家却用的 dp,差距能不大吗,你如果老老实实用 dp,哪怕三方库的设计图尺寸和你项目自身的设计图尺寸不一样,那也差距不大,小到一定程度,基本都不用调整,可以忽略不计,而且很多三方库的设计图尺寸其实也都是那几个大众尺寸,很大可能和你项目自身的设计图尺寸一样。

总结

可以看到我讲的非常详细,可以说比今日头条官方以及任何博客写的都清楚,从原理到优缺点再到解决方案一应俱全,因为篇幅有限,如果我还想把 smallestWidth 限定符适配方案写的这么详细,那估计这篇文章得有一万字了。

所以我把这次的屏幕适配文章归位一个系列,一共分为三篇,第一篇详细的讲 今日头条屏幕适配方案,第二篇详细的讲 smallestWidth 限定符适配方案,第三篇详细讲两个方案的深入对比以及如何选择,并发布我根据 今日头条屏幕适配方案 优化的屏幕适配框架 AndroidAutoSize。

今日头条屏幕适配方案 官方公布的核心源码只有 30 行不到,但我这个框架的源码有 1500 行以上,在保留原有特性的情况下增加了不少功能和特性,功能增加了不少,但是使用上却变简单了

<manifest>    <application>                    <meta-dataandroid:name="design_width_in_dp"android:value="360"/>        <meta-dataandroid:name="design_height_in_dp"android:value="640"/>                application>           manifest>

只要这一步填写了设计图的高宽以 dp 为单位,你什么都不做,框架就开始适配了。

大家可以提前看看我是怎么封装和优化的,我后面的第三篇文章会给出这个框架的原理分析,敬请期待!

近期文章:

  • 你有多懂程序猿?

  • 程序员如何预估自己的项目开发时间?!

  • 加密混淆,应用就安全了嘛?

今日问题:

你的APP主要适配了哪些屏幕尺寸?

打卡格式:

打卡 X 天,答:xxx 。

今日头条适配方案_煮酒论英雄之屏幕适配相关推荐

  1. ckpt下载 deeplabv3_煮酒论英雄:深度学习CV领域最瞩目的成果top46

    原标题:煮酒论英雄:深度学习CV领域最瞩目的成果top46 来源:Smarter 作者:皮特潘 [新智元导读]本文盘点深度学习CV领域杰出的工作,从基础研究.分类骨架.语义分割.实例分割.目标检测.生 ...

  2. 2019互联网年会图鉴:去年煮酒论英雄,今年却似新亭会

    2019年的冬天静悄悄. 往年此时,马化腾的freestyle,马云的视觉系摇滚.王健林的老歌独唱,早已组成了吃瓜群众过年回家之前的最后一波谈资. 还是得怪2019年太冷,人实在是懒了,互联网公司从上 ...

  3. [转载]煮酒论英雄nbsp;-nbsp;漫谈Java数据库存取技术

    煮酒论英雄nbsp;-nbsp;漫谈Java数据库存取技术 IT技术日新月异,新技术的出现令人目不暇接,似乎每一天都在产生着新名词.不过归根结底IT所要实现的价值不外乎数据收集,然后再以客户希望的形式 ...

  4. 曹操煮酒论英雄谈龙(转)

    曹操煮酒论英雄章有如下精彩描述                                        随至小亭,已设樽俎:盘置青梅,一樽煮酒.二人对坐,开怀畅饮.酒至半酣,忽阴云漠漠,聚雨将至 ...

  5. 今日头条适配方案_今日头条信息流广告创意优化方案!

    今日头条信息流广告创意优化方案! 时段设置常见错误: 投放时段选取过窄导致展现量低, 优质时段出价无竞争力导致展现量低 时段设置方法: 依据广告主产品或服务目标受众在推广平台上的活跃时间来设定; 根据 ...

  6. 今日头条适配方案_ 今日头条大改版,小程序强势登场

    今日头条大改版,此次改版最新奇的就是在首页底部的中心位置增加了"常用"功能,点击进入后会出现小程序和一些常用功能,页面上拉则会形成全屏页面,此外"常用"功能界面 ...

  7. 今日头条适配方案_探索头条小程序开发!JavaScript为什么成了众多小程序的首选?...

    从13年百度率先提出轻应用的概念以来,支付宝,微信等好多大流量App都借助自己的平台,推出了小程序.小程序的优势很明显:轻量.无需安装.随用随走.性能又可以媲美原生的应用,更重要的是可以充分发挥各自平 ...

  8. python爬取今日头条瀑布流_连续动作:滚屏采集瀑布流网页—以头条新闻为例

    常见的网页大多数在页面下方会有翻页的按钮,比如"下一页"."加载更多",这类网页设置翻页就可以搞定,但是瀑布流网页没有这些按钮,而是随着鼠标滚动会不停的加载更多 ...

  9. python今日头条新闻爬虫_头条爬虫最新资讯

    头条搜索的爬虫UA为"Bytespider"首写字母为大写.头条搜索的ip字段总共涉及 6 个 日前河南一家一家名为今日油条的网红店装修.logo与今日头条极其相似引起众金网友关注 ...

最新文章

  1. 利用TCMalloc替换Nginx和Redis默认glibc库的malloc内存分配
  2. Java自带的多线程监控分析工具(VisualVM)
  3. WebLogic中文博客
  4. [Swift]LeetCode1146. 快照数组 | Snapshot Array
  5. zookeeper 应用开发
  6. git rebase原理(转)
  7. 【Elasticsearch】Elasticsearch中的相似度评分介绍
  8. python list tuple区别_Python list、tuple、dict区别
  9. HTTP缓存-http强制缓存与协商缓存
  10. 【数电基础知识】各逻辑运算符号盘点
  11. C++学习系列(二)—— 核心编程(面向对象)
  12. 【古曲】流水-古琴曲
  13. JD京东爬虫-商品评论爬虫-----附源码
  14. Python中的Nonetype类型怎么判断?
  15. utran体系结构包括_接入网体系结构
  16. 数据结构—冒泡排序 C语言代码实现(从前向后/从后向前两种)
  17. Java包与Import导入
  18. (收藏)漂亮的css button样式汇总
  19. 手机怎么给电脑传送文件,电脑怎么给手机传送文件?
  20. 马上2023了,一起来了解Python未来的发展趋势!

热门文章

  1. HP MSA存储 raid组坏了2块硬盘的数据恢复方法
  2. 【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法
  3. 3-3 兔子跳楼梯 3-4 斐波那契数列(II)
  4. 基于统计概率和机器学习的文本分类技术 —— 社区产品机器审核机制预研报告...
  5. Java SE8 流 最全总结
  6. 【线代】线性方程组:非齐次/齐次方解的个数、系数矩阵的秩、未知数个数的关系?为什么 Ax=0 比 Ax=b 少1个线性无关的解?
  7. 如何让你pycharm用起来更舒服,看起来更美观
  8. linux-v4l2 应用流程
  9. 新浪云sae怎么上传php代码,如何在新浪SAE中上传文件和在线修改模板
  10. 机器学习(1): 线性回归——最小二乘法 小结