pt/dp/px,dpi/density相关概念辨析

这几天参加学校Android开发方向的实训,刚开头几天一直学的是界面的绘制方法,布局啊控件啊之类的.学习中知道了Android界面绘制时,控件尺寸一般都是使用dp作为单位,文字使用sp作为单位,但是老师一直没讲为什么这样,参考书上也有点语焉不详,加上一直以来对Android手机dpi和density的概念弄得不是很明白,于是干脆就花了点时间把这些都研究了一下,这里写成文本一者理清思路加深自己的印象,二者也许可以帮到其他有相同困惑的人.

pt

pt 者,其实就是word里面常见到的单位"磅",就是一个物理长度单位,起源于印刷行业.
1pt = 1/72 inch.在数字显示里引入一个长度单位就是为了使屏幕上显示的内容与真实世界的物品(印刷品)相对应. 具体到Android开发上,我们用来衡量界面元素的实际大小,跟什么厘米,毫米是一样的.


px

px 者,即 pixel 也,即像素也,就是屏幕上的一个点,我想没什么好解释的了吧.


dpi

dpi 者,dots per inch 也,也是来自印刷行业,在数字显示领域理论上等同于ppi(pixels per inch).但是在现在大多数的Android手机相关的语境里,两者是有些差别的.ppi常指屏幕的物理像素密度,计算方法即为对角线像素数量/屏幕尺寸.而dpi指的是绘制界面时采用的屏幕像素密度,可以通过软件设置,两者常常不相等.举几个栗子吧,
比如HTC G1,分辨率320*480,ppi 180,而系统实际使用的dpi是160即所谓的mdpi,(这是为什么呢,可以参考知乎的这个问题);
再比如MX3,分辨率1080*1800,ppi 412,系统实际使用的dpi数值是 400,介于xhdpi和xxhdpi之间.

那么后文中,我们就约定用PPI指代设备的物理像素密度,dpi指代设备的界面绘制使用的像素密度值,不过不必过分纠结这两者的区别,没有同时出现的时候就当做一回事好了.

到这,我们就会发现,通过dpi π其实就可以算出一个α px的屏幕元素的实际物理尺寸是多少,就是 α/π 咯.


density

density 者,似乎是Android系统才有的一个概念,字面含义是像素密度/浓度 似乎应该是dpi的含义才对吧喂,而在Android系统中用来表示屏幕dpi与mdpi(160)的比值.比如mdpi(160)的density是1,hdpi(240)的density是1.5,xhdpi(320)的density是2,xxhdpi(480)的density是3.

不过我感到有趣的一点是,Android手机build.prop文件里一般会有一个ro.sf.lcd_density项,这个项目的值其实是当前系统的dpi,而不是dpi与mdpi的比值,从前一些传说中的发烧友们所谓的修改手机dpi改变屏幕元素大小的方法其实就是修改的这个数值. 不过听说Android5.0之后这个方法不是那么管用了?


dp

dp 者,比较通用的说法是 Density-independent Pixels, 密度无关像素.从字面意思上理解,就是使用这种长度单位,可以使界面元素在不同密度(dpi)的屏幕上保持近似的物理尺寸大小,是碎片化的Android系统解决多分辨率适配问题的重要工具.那么问题来了,这是 怎么做到的?!?!?!

dp作为一个智慧的或者说是抽象的屏幕显示尺寸单位,在实际绘制界面的时候当然还是要跟真正的显示单位px进行换算的.这个换算的过程其实很简单,只需要乘以一个系数Ω就可以了,即1 dp = Ω px,而这个系数 Ω 当然是跟屏幕显示dpi是相关的了,怎么相关呢?

首先,我们要求dp这个单位达到的效果就是成为一个与物理尺寸单位(like "pt")有恒定系数换算关系的数字显示尺寸单位,我们可以就把他当成pt那样的物理尺寸单位.
那么要显示一个长度为10dp的屏幕元素时,在不同dpi的屏幕上,屏幕dpi越高,所需要的像素点px就越多,我们设定当在某dpi为160的屏幕上1 dp = 1px作为基准,如果dpi提高1倍变成320,那么1dp就要等于2倍的px才能保证两块屏幕上同为1dp的界面元素物理尺寸也是一样的,那么我们就可以看出来,系数 Ω 其实就是当前屏幕dpi与这个基准dpi的比值.

说了这么一大堆乱七八糟的废话,如果你没被绕晕的话,想必就知道了,这个系数 Ω 就是前面提到的像素密度density,Android定下的那个"基准dpi"就是160dpi,即所谓的mdpi.


习题时间

240_hdpi屏幕上,density = 1.5,1dp = 1.5px;
320_xhdpi屏幕上,density = 2,1dp = 2px;
480_xxhdpi屏幕上,density = 3,1dp = 3px.

Nexus5, 5inch,1080*1920分辨率,ppi 445,显示dpi为xxhdpi_480dpi,density为3,即在Android Studio中3px = 1dp,设置界面时屏幕宽度为1080/3 = 360dp.
小米3, 5inch,1080*1920分辨率,ppi 441,显示dpi是xxhdpi_480dpi,density为3,设计界面屏幕宽度为1080/3 = 360dp.
MX3, 5.1inch,1080*1800分辨率,ppi 412,显示dpi为400,density为2.5,2.5px = 1dp,设计界面屏幕宽度为1080/2.5 = 432dp,这样的话MX3 5.1inch的屏幕一行就可以显示更多的内容惹, 真是机智.
Nexus6, 5.95inch, 1440*2560分辨率, ppi 493, 显示dpi默认560, density3.5, 3.5px = 1dp,设计界面时屏幕宽度为1440/3.5 ≈ 411.4dp..这什么鬼真的是这样吗Android Studio实测了下貌似其实是412dp

曾经流传的通过修改系统density改变界面元素大小的方法,思路就很明朗了,通过减小系统dpi,使density减小,于是显示同一个dp所需要的px减少,视觉上看就是界面元素变小了,屏幕也就可以容纳更多的内容.


后记

写到这,回看了一下全文,发现这行文造句似乎也就满足了文首的第一点要求,梳理了一下自己的思路,如果有人看完了还是云里雾里,勿怪勿怪..

推荐阅读更多精彩内容