文章来自:
PX(pixel 像素)
px 也叫像素点,是屏幕物理上的最小显示单位,如手机分辨率 1080 x 1920 表示宽有 1080 像素点,高有 1920 像素点。像素的大小是没有固定长度的,不同设备上一个单位像素色块的大小是不一样的。尺寸面积大小相同的两块屏幕,分辨率大小可以是不一样的,分辨率高的屏幕上面像素点(色块)就多,所以屏幕内可以展示的画面就更细致,单个色块面积更小,而分辨率低的屏幕上像素点(色块)更少,单个像素面积更大,可以显示的画面就没那么细致。
dpi(dots per inch)
dpi也被称为屏幕密度,就是每英寸所打印的点数。实际上dpi的计算是通过勾股定理算出屏幕对角的像素数再除以对角的尺寸就是最终的dpi。以一台的机器为例:
- 尺寸:6.0英寸(一般所说的尺寸就是机器的对角线尺寸。)
- 分辨率:1080*1920
那么dpi算法则为1080的平方加上2160的平方再开方约等于2203。再用2203除以6得到约367。则367dpi为该机器的像素密度。
ppi(pixels per inch)
ppi和dpi这两个措辞的差别,表面上看来只在于是在谈 dot,还是 pixel,但实际上 dot 可以打印的墨点,可以指扫描仪的采样点,可以指数字图像的最小单位(即 pixel),可以指屏幕的物理像素,可以指操作系统的抽象像素……在不同的语境下可以指不同的概念,而同样 pixel 也可以指数字图像的数据 pixel,可以指屏幕物理像素,也可以指代操作系统的抽象像素……在不同语境下的意义也不同。这两个单位完全就是时常混用的,在电子屏幕显示中提到的 ppi 和 dpi 可以认为是一样的,所以你可以忽略在措辞上用 dpi 或者 ppi 有什么不同,不过在 Android 平台上常用 dpi 这种表述方式。
dp/dip(device independent pixels)
dp也称设备独立像素,dp和dip是同一个概念,都是device independent pixels的简称。举个例子:
宽2英寸,长3英寸的屏幕上,如果将一个控件的宽度设为160px,则会出现如下的情况:
- 屏幕分辨率宽度为320px,则该控件占据屏幕宽度一半。
- 屏幕分辨率宽度为640px,则该控件占据屏幕宽度的四分之一。
这十分影响体验。所以Android引入了设备独立像素的概念,也就是dp这种单位。首先Android规定在160dpi的设备上1dp=1px,当屏幕密度为320dpi时,1dp=2px,以此类推。。。正式如此,我们得以保证控件在不同屏幕密度上显示一致,从而完成屏幕适配。回到上面的例子,当我们使用新的单位dp时,当将控件的宽度设为160dp: - 在分辨率为 320 x 480(既 dpi 为 160)的屏幕上,则 160dp 等价于 160px,控件占屏幕宽的一半;
在分辨率为 640 x 960(既 dpi 为 320)的屏幕上,则 160dp 等价于 320px,控件依然占屏幕宽的一半,就解决大小比例不一致的问题。
下面是网上常用的 dp 和 px 之间转换的工具类:
public static int dp2px(Context context, int values) {
float scale = context.getResources().getDisplayMetrics().density;
return (int) (values * scale + 0.5f);
}
public static int px2dip(Context context, float pxValue) {
float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
//+0.5 就是为了在精度丢失的情况下进行四舍五入。
drawable 目录加载和 dpi 间的关系
说到 dpi 这里就不得不联想到在 Android 工程里 res 目录下的 drawable-hdpi, drawable-xhdpi, drawable-xxhdpi 等文件夹。我们知道 Android 会根据屏幕的 dpi 去选择对应的 drawable 文件夹,Android项目的资源文件下存在以下目录:
- drawable-ldpi ( 当dpi为120时,使用此目录下的资源)
- drawable-mdpi ( 当dpi为160时,使用此目录下的资源)
- drawable-hdpi ( 当dpi为240时,使用此目录下的资源)
- drawable-xhdpi ( 当dpi为320时,使用此目录下的资源)
- drawable-xxhdpi ( 当dpi为480时,使用此目录下的资源)
- drawable-xxxhdpi ( 当dpi为640时,使用此目录下的资源)
Android 正是根据设备DPI值得不同,选择清晰度不同的资源使用,完成屏幕的适配, 可以看出系数比例关系:0.75:1:1.5:2:3:4,但手机屏幕千奇百怪,例如如果一个手机屏幕是 420 dpi 不属于上述文件分类中的任何一个,因此上述文件夹不是指定具体的分辨率,而是一个范围:
- drawable-ldpi(value <= 120 dpi)
- drawable-mdpi(120 dpi < value <= 160 dpi)
- drawable-hdpi(160 dpi < value <= 240 dpi)
- drawable-xhdpi(240 dpi < value <= 320 dpi)
- drawable-xxhdpi(320 dpi < value <= 480 dpi)
- drawable-xxxhdpi(480 dpi < value <= 640 dpi)
以此类推,所以 420 dpi 会优先加载 xxdpi 中的资源文件,如果对应 dpi 目录下没有找到该资源文件,遵循先高再低原则,然后按比例缩放图片,比如当前为 xhdpi 设备,则 drawable 的寻找顺序为:
xhdpi -> xxhdpi -> xxxhdpi (如果没有更高的了) -> nodpi (如果有的话) -> hdpi -> mdpi,如果在 xxhdpi 中找到目标图片,则压缩 2/3 来使用,如果在 mdpi 中找到图片,则放大 2 倍来使用。
sp(scalable pixels)
最后来说一说 sp,sp 与缩放无关的像素单位,类似 dp ,不同之处在于它还会根据用户字体大小配置而缩放。所以开发中指定字体大小时建议使用 sp ,因为 sp 作为字体大小单位会随着系统的字体大小改变,下面是 sp 和 px 之间转换工具类:
public static int px2sp(Context context, float pxValue) {
float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
从代码中也可以看出 sp 和 px 间转换使用的是 scaledDensity,而 dp 和 px 间转换使用的是 density,也就是 sp 会随着系统字体设置缩放,dp 不会。
一般UI拿过来的图就是px标注的,然后会告诉你做图时所用的尺寸是多大的,如1334x750,4.7寸的屏幕- 计算UI给出的图纸屏幕的dpi:
1334*1344+750*750=2342056
2342056开方约等于1530
1530/4.7=325dpi
- 所以在325dpi下为80px,在标准的160dpi的计算为:
80 * 160 / 325 约等于40px。
在160dpi的像素为40px,所以设置为40dp。