由于工作原因,发现很多 Android 初学者对屏幕密度等相关概念以及对应计算方式容易模糊,因此本文对相关知识进行简单介绍。文中只对概念进行阐述,不涉及屏幕适配。因此Android 老鸟不必栖息。
相关概念
DPI
DPI 全称为 Dots Per Inch. Inch的含义是英寸,意思也就是『一英寸内点的个数』。Android 设备采用 DPI 来表示屏幕的『密度』。DPI 原本是印刷行业的概念,意思是一英寸内有多少个墨点。
PPI
原本还有一个概念叫 PPI(Pixels Per Inch) 意思是『每英寸内的像素数』,这是通用的概念。
这两个概念在 Android 中是一样的含义,只不过在 Android 中采用了 DPI 这个不太严谨的概念,要表达的意思是一致的,还是一英寸中像素点的个数。
寸
我们在买一个手机的时候经常会关心这个手机屏幕有多大,称之为多少寸,比如说 5.0 寸,4.7 寸。这个寸其实指的是屏幕对角线的长度,单位就是英寸(Inch)。也称为『物理尺寸』。1英寸等于 2.54cm。
分辨率
所谓的分辨率,是指的一个显示设备横纵方向各有多少个像素点。例如我们说一个手机的分辨率为 1280×720 ,指的就是这个手机纵向有 1280 个像素点,横向有720个像素点,整个手机屏幕上一共就有 1280×720个像素点。
如何计算手机屏幕的密度
由于手机屏幕硬件的差异性,屏幕中同样物理尺寸的一块空间,能包含的像素点的个数可能不一样。
以两款常见的手机为例:
手机型号 | 屏幕尺寸 | 分辨率 |
---|---|---|
红米3s | 5.0寸 | 1280*720 |
HUAWEI nova | 5.0寸 | 1920*1080 |
通过上表可以明显发现,哪怕两个物理尺寸一模一样的手机,里面能容纳的像素点的个数都可能不一样。这种情况就可以称之为两个手机的屏幕密度不一样。根据 DPI 的概念。我们可以通过如下方式计算手机的屏幕的 DPI:
根据此计算公式可以得出两款手机的屏幕密度分别为:
手机型号 | 屏幕尺寸 | 分辨率 | 屏幕密度 |
---|---|---|---|
红米3s | 5.0寸 | 1280×720 | 294dpi(ppi) |
HUAWEI nova | 5.0寸 | 1920×1080 | 441dpi(ppi) |
通过对比我们发现,我们计算的和厂商提供的完全一致,计算公式妥妥的!
另外两个概念
了解了 DPI 的计算方式,我们再来看两个大家很熟悉的概念。
PX
px 就是像素 pixel 的缩写。1px 代表一个显示设备中的一个像素点。
DP(DIP)
DP 或者 DIP 叫做(Density-Independent pixel),或者(Device-Independent Pixel),中文翻译为『密度独立像素』或者『设备独立像素』,Android 中的特有单位,它的大小是由操作系统根据手机屏幕密度动态渲染出来的,1dp 对应多少 px 在不同的设备上,可能是不一致的。
目前我们确实可以通过公式计算出屏幕密度了,又了解了 px 和 dp 的概念。各种概念,有好事的哥们可能急了,你特么到底要说啥?来,看一个常见的情景:
在 Android 开发中,我们经常需要设置一个控件的宽高。假设你采用的测试机为红米 3s,你希望一个控件的宽度占用屏幕的一半。于是将其宽度设置为 360px,运行结果在你的手机上没有问题。但是同样的代码,运行在 HUAWEI nova 的手机上,由于 nova 手机横向有 1080 个像素点,360 / 1080 = 0.33 明显只占据了屏幕的三分之一。
从上面的事例中我们可以发现,在 Android 中通过 px 作为单位设置一个控件的宽高,在不同屏幕密度的手机上必然会出现问题。这也是为什么会存在 dp(dip) 这个单位。这个问题我们先记着,一会儿再回过头来解决它。
PX 与 DP 之间的换算公式
px = dp * (dpi / 160)
dp = px / (dpi / 160)
上面的 dpi /160 也称之为density
看到这个公式可能有的同学会问了,公式中的 160 是个什么鬼?
在 Android 中,160dpi 的设备下, 1dp = 1px,也就说在160dpi 的设备中写 px 和写 dp 效果一样。至于原因,在Google的官方文档给出过解释,因为第一款 Android 设备( HTC 的T-Mobile G1)是属于(注意,是属于,不是等于)160dpi的。
为什么叫属于呢?如果按照 DPI 的计算公式,T-Mobile G1 应该为 180dpi。那么为什么它又属于 160dpi 呢?
看图:
Android 里把主流设备的 dpi 归成了四个档次,120 dpi、160 dpi、240 dpi、320 dpi,可以看出T-Mobile G1的参数属于mdpi区域的,以上就是取160dpi作为基准的原因。
搞清楚这个计算公式之后,回到之前的问题,既然设置 360 px 有问题,那么换成 dp 作为单位改设置为多少呢?根据公式:
dp = px / (dpi / 160)
196dp = 360 / (294 / 160)
在 DPI 为 294 分辨率为 1280×720 的红米手机上将宽度设置为 196dp 没有问题,在 DPI 为 441 分辨率为 1920×1080 的华为手机上有没有问题呢?根据公式:
px = dp (dpi / 160)
540px = 196 (441 / 160)
540px 正好也占据宽度 1080 的一半。相信现在大家对 Android 屏幕以及控件相关单位的概念已经差不多理解了。
为什么要使用 DP 作为单位呢?
以 DP 作为单位,系统会根据不同 DPI 的手机,将 DP 为单位的值转换为合适的 PX 目的就是为了界面中的控件,在不同 DPI 的手机上,显示效果保持一致,也就是让人看起来,控件的比例是一样大小的。
BUT!还没有完!
代码转换
开发中,布局文件中我们习惯使用 dp 单位,但是Android中绝大多数 java 代码的api中默认是以 px 作为单位(如 setPadding等),如果传入同样的值,在不同的手机上同样可能产生问题,因此我们在很多场景下需要进行 dp 和 px 的互转。
DP 转 PX
1 2 3 4 5 6 7 8 9 | /** * 自动根据手机的 dpi 从 dp 的单位 转成为 px(像素) * @param context 上下文 * @param dpValue 需要转换的 dp 值 */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } |
PX 转 DP
1 2 3 4 5 6 7 8 9 10 11 | /** * 自动根据手机的 dpi 从 px(像素) 的单位 转成为 dp * * @param context 上下文 * @param pxValue 需要转换的 px 值 * @return */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } |
上面代码中的
context.getResources().getDisplayMetrics().density;
其实就是 dpi /160 的值。
至于结果为什么要加上 0.5f,你猜猜看?