Android 中基于宽度的屏幕适配方案

目录

  • 1 一些基本概念
  • 2 传统标准适配难题
  • 3 基于宽度的屏幕适配原则




1 一些基本概念

  • ppi:pixels per inch,即物理设备每英寸的像素数。计算公式为:
ppi = sqrt(width² + height²) / 屏幕尺寸(inch)

为什么一定要计算出屏幕斜线的像素数呢?因为手机的屏幕尺寸指的就是手机屏幕斜线的长度(单位:英寸)。所以,理论上来说,如果知道屏幕宽度的尺寸,也可以直接通过 ppi = width / 宽度尺寸(inch) 计算出来。

  • dpi:dot per inch,即每英寸的点数。这个单位原本用于印刷行业,描述的是打印机的打印精度,即打印机在印刷纸上每英寸范围内可以均匀印射出多少个颜色点,dpi 越高说明打印机的打印精度越高,效果越细腻。安卓借用了 dpi 的概念来表示屏幕的像素密度,但根据 ppi 的概念我们知道 ppi 可以更直观地表征屏幕像素密度,那为什么安卓要借用印刷行业的 dpi 概念来重新表示屏幕密度呢?找遍了谷歌文档,并没有一个官方正式的解释。一方面,在实践中我们发现,dpi 是约等于实际的 ppi 的;另一方面,对于不同的屏幕密度需要提供不同分辨率的图片资源,而为了让设备能够加载正确的图片资源,理想状态下就需要对每一种具体的屏幕密度都提供一张不同分辨率的图片,这样做显然不现实,所以安卓对屏幕密度做了一个标准分类,如:ldpi(低密度≈120dpi)、mdpi(正常密度≈160dpi)、hdpi(高密度≈240dpi)、xhdpi(超高密度≈320dpi)、xxhdpi(超超高密度≈80dpi)、xxxhdpi(超超超高密度≈640dpi)。每一个设备都需要划分到这些标准分类中的一类中,一种比较简单的做法就是,直接将其 dpi 设置成谷歌推荐的几个值(120/160/240/320/480/640)之一,规则就是选择与其实际 ppi 最接近的那一个,之所以不直接设置成 ppi,我觉得很可能是根据这几个标准值最终计算出来的 density(后面会介绍)值更加易于计算。总之,实际运用中我们可以粗略地将 dpi 表征为物理屏幕的像素密度。

  • dip/dp:density-independent pixels,即密度无关像素。如前所述,dpi 表征了屏幕的像素密度。如果在一个 160dpi 的屏幕上和一个 320dpi 的屏幕上展现一个用户看起来大小一样的矩形,那么实际上这个一样的大小指的并不是其像素点的多少一样,而是长宽的物理尺寸(单位为英寸或者厘米)相同。所以我们在进行开发的时候,需要一种单位来衡量在不同像素密度的设备上相同大小的界面元素。理论上可以直接用英寸或者厘米或者毫米,但是这些单位因为与像素之间没有直接关联,所以在渲染的时候无法转换为最终的像素点数。为此,安卓发明了 dp 这样一个单位。其原理是以 160dpi 的设备作为所有其它设备的参考系,规定 160dpi 的设备中,一个 px 等于一个 dp,并将其它所有设备的 每一个英寸 都等分为 160 份,每一份即为该设备的一个 dp。比如,320dpi 的设备,其一个英寸等分为 160 份后,每一份由 2 个像素点组成,所以其一个 dp 表示 2 个 px。其它设备以此类推。于是我们找到了一种既可以表征屏幕物理尺寸(英寸/厘米/毫米),又可以转换为像素值(px)的单位——dp。

  • density:理解了 dpi 和 dp 的概念后,density 的概念也就很容易理解了。density 我将其理解为 pixels per dp,即单位 dp 里的像素数。其计算公式为:

density = dpi / 160 (一个英寸等分为 160 份后,每一份的像素数即为 density)

这样,dp,px,density 之间的关系为:

dp = px / density = px / (dpi / 160)

各标准屏幕密度下的 density 值如下:

密度 ldpi(120dpi) mdpi(160dpi) hdpi(240dpi) xhdpi(320dpi) xxhdpi(480dpi) xxxhdpi(640dpi)
density 0.75 1 1.5 2 3 4




2 传统标准适配难题

如果直接按照官方的 density 标准,所有设备的宽高尺寸就都可以从 px 单位转换为 dp 单位。比如分辨率为 1080×1920px,480dpi 的设备一(density=3),其 dp 尺寸变为 360×640dp;而分辨率为 1440×2880px,560dpi 的设备二(density=3.5),其 dp 尺寸变为 411×823dp。如果我们在两个设备中放一个 60×60dp 的图片,那么此时该图像占设备一宽度的 16.66%,而只占设备二宽度的 14.59%。

显然,设备二是一种相对宽屏的设备,从设计初衷上来讲是为了在一行容纳更多的界面元素。如果设备的尺寸差异并不太大,那么适配难度是可以接受的,但是随着安卓碎片化的加剧,其适配的复杂性也越来越大。这是因为,当按照一种设备尺寸给出设计图后,用官方的 density 标准设置界面元素的 dp 尺寸后,该元素在不同设备上占据的比例都是不一样的,所以其剩余可容纳的空间也不一。比如我们以中等宽度的屏幕作为设计图把一行填满了,那么在布局到更宽屏的设备上时可能没有问题,但是布局到更窄屏的设备上时,就会发生行溢出,这时就得考虑溢出和不溢出的时候如何布局,或者手动调整为比例尺寸。

为了适应安卓碎片化的事实,现在业界推崇一种基于宽度的屏幕适配方案,以牺牲理想适配原则为代价,换取开发成本的大大降低。




3 基于宽度的屏幕适配原则

基于宽度的屏幕适配原则如下:

将所有的手机设备的屏幕宽度都按照相同的份数进行等分。

没看错,原则就是这么简单!具体来说,假设等分的份数为 N(即设计稿给出的屏幕宽度),任意设备的宽度为 W(px),于是重新定义该设备的 density 为:

density = W / N

这实际上也重新定义了 dp,即所有设备的宽度都为 Ndp。于是我们给定一张正方形的图片,我们要以 60×60dp 来显示,那么在任何设备上,该图片占据设备宽度的的比例都为 60/N。

默认情况下,所有设备的单位尺寸都等分为 160 份,这样每种设备的宽度因为英寸数不一样,最终等分的份数也不一样。在该种方案中,我们将设备的宽度都等分为一样的份数,这样每个界面元素的尺寸都按照这个 份(W/N) 为单位来进行衡量,那么其在不同设备中所占的屏宽比例都是一样的。

比如,假定 N=375,

设备一为 1080×1920px,其 density=1080/375=2.88,利用公式 px = dp × density,60×60dp 的图片实际占据的像素尺寸为 172.8×172.8px。

设备二为 1440×2880px,其 density=1440/375=3.84,60×60dp 的图片实际占据的像素尺寸为 230.4×230.4px。

但是图片在两个设备中占据的屏幕宽度比例都为 172.8/1080=230.4/1440=60/375=16%。

目前比较流行的方案有 今日头条方案SmallestWidth 限定符方案。它们都是遵循的以上原则,只不过在具体实现上有差异。

今日头条方案的实现原理是直接修改系统的 density 和 dpi 来达到全局适配的目的,简单而高效。

而 SmallestWidth 限定符方案是通过生成大量的 values-sw(XX)dp 限定文件夹来尽可能覆盖大部分机型尺寸,然后在各个限定文件夹的 dimens.xml 文件中批量写入对应的虚拟 dp 值来实现适配。比如设计稿以 375 宽度为基准(即 N=375),在 values-sw360dp/dimens.xml 中,其一个虚拟 dp 值为:vdp_1 = 360/375 = 0.96dp,这样我们在布局文件中原本应该设置 60dp 的地方,改为设置 @dimen/vdp_60(即57.6dp) 即可,于是其实际像素值为 57.6×3=172.8px(vdp_60×系统默认density)。即,虚拟 dp 的计算公式为:

vdp = dp × W / N = dp × density

实际上在今日头条方案中,修正后的 density(vdensity)即为:

vdensity = W / N

目前来看,基于宽度的适配方案在前端开发中也成为业界主流方案,因为前端开发也面临和客户端开发一样的屏幕适配难题。这种方案可以极大的减轻程序员业务开发的负担,减少开发成本,易于维护,而且理想型的适配相比这种折中方案来讲在用户体验上并不具有明显的市场优势,所以也可以说这是市场选择的结果。

关于前端开发中的宽度适配方案,可以参考 用rem实现移动设备页面元素适配




加入技术交流群

准备建立一个技术交流群,大家可以讨论技术、内推工作、互相帮助。因为二维码容易失效,个人微信号加太多容易被封,所以请大家先关注公众号——小舍,然后在公众号给我发送消息,我拉大家入群。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容