在Android中PX、DP(DIP)、SP、DPI、PPI的区别
先说比较简单的,PX、DP(DIP)和SP。
PX的全称是Pixel,翻译过来是”像素“的意思,Pixel是由Picture和Element两个单词合并而来的,也就是图像的元素之意。
比较准确的定义是:像素是指由一个数字序列表示的图像中的一个最小单位。
一个数字图像就是一系列像素的集合,每个像素可以由各自的颜色值和具体的位置,通过对图像这样数字化表示,之后就可以被打印、传输或存储。
上面说的像素叫做逻辑像素,例如我们通过查看图片的属性可以看到该图片的尺寸是1080540,这里的1080540就是逻辑像素,也叫图像分辨率。而手机屏幕或显示器由一个个显示点组成,例如锤子 Pro2s的手机屏幕分辨率是2160*1080,这些显示点叫做物理像素,也叫显示分辨率。
问题:
1、将一张1080540的图片全屏显示到21601080的屏幕上会怎么样?
4个物理像素点显示一个逻辑像素,每个逻辑像素点的间距会比较大,在肉眼看来,图片就比较粗糙。
2、反过来,将一张21601080的图片显示到1080540的屏幕上又会怎么样?
4个逻辑像素点经过采样后,显示到一个物理像素点上。
**DP(DIP)**的全称是Density Independent Pixels,即密度无关像素。其中密度是屏幕像素密度,也就是每英寸显示的像素点数。我们在开发中使用dp,而不是px作为控件长度的单位,是为了在不同设备上保持显示一致的UI元素。在160dpi的设备上,1dp=1px。
例如:在5英寸,160dpi的手机上显示160dp长的线,就是160px的长度,换算成实际长度就是160/160 = 1 英寸。
在5英寸,320dpi的手机上显示160dp长的线,就是320px的长度,换算成实际长度也是320/320 = 1 英寸。
所以,这条线在不同屏幕密度的手机上显示效果就保持了一致。
那么问题来了,在3英寸,160dpi的手机上显示1dp的线,他的长度还是160/160 = 1英寸,但是却占用了屏幕1/3的空间,所以就会看起来比较大。
SP Scaled Pixels,可缩放像素,常用于文字大小的单位。和dp类似,但是可以跟随系统的设置的文字大小而缩放。
DPI全称是Dot Per Inch,每英寸包含的点,最早用于印刷行业,即一英寸打印的墨点数,用于打印的清晰度。
PPI全称是Pixel Per Inch,每英寸包含的像素,用于描述显示屏的屏幕密度,PPI越高,显示的图像越清晰。
因为在屏幕中,每个像素就是一个个的物理显示点来显示的,所以在Android系统中定义的DPI和手机厂商宣传的屏幕PPI是一个意思。我们可以通过DisplayMetrics类看到Android系统定义几种屏幕密度。
屏幕密度 | 数值 |
---|---|
低密度 ldpi | 120dpi |
中密度 mdpi | 160dpi |
高密度 hdpi | 240dpi |
超高密度 xhdpi | 320dpi |
超超高密度 xxhdpi | 480dpi |
超超超高密度 xxxhdpi | 640dpi |
在Android系统获取屏幕分辨率、屏幕密度、屏幕尺寸
DisplayMetrics displayMetrics = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
} else {
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
}
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
//屏幕分辨率,在4.2系统之前无法获取实际屏幕分辨率,因为还有状态栏和导航栏
String screenResolution = width + "*" + height;
//屏幕密度比例 当前屏幕密度与基准屏幕密度160dpi的比
String density = String.valueOf(displayMetrics.density);
//屏幕密度,是系统定义的屏幕密度种类之一,并不是实际屏幕密度
String densityDpi = String.valueOf(displayMetrics.densityDpi) + "dpi";
//横竖方向上的屏幕密度,这才是实际屏幕密度
String densityDpiOrientation = displayMetrics.xdpi + ":" + displayMetrics.ydpi;
//屏幕尺寸,屏幕对角线的长度
String screenSize = String.va lueOf(Math.sqrt(Math.pow((width / displayMetrics.xdpi), 2) + Math.pow((height / displayMetrics.ydpi), 2)));
//屏幕缩放密度,用于缩放文字
String scaledDensity = String.valueOf(displayMetrics.scaledDensity);
问题:为什么在一些低屏幕密度的屏幕上显示的控件大小或间距,比高屏幕密度的屏幕上显示出来的还要更大一些。
在回答这个问题之前,我们要知道在xml布局文件中为控件设置的宽高或间距值(例10dp),是怎么把dp值转换为px值的?
// android.util.TypedValue
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;
}
获取xml像素值的getDimensionPixelSize方法最终会调用到TypedValue类的applyDimension方法中。可以看出,如果在布局中使用的单位是DIP(DP)的话,会将其值乘以当前的屏幕密度比率(当前屏幕密度/160),就得到了对应的像素值。而通过DisplayMetrics获取的density或densityDpi是Android系统框架事先定义好的几种值(详细见上表),但是实际屏幕的屏幕密度不一定与其保持一致。
前面已经说到屏幕实际DPI值,可以通过displayMetrics.xdpi和displayMetrics.ydpi获取。
所以结论就是在将dp转换成px时,参与计算的dpi值比实际屏幕dpi大,导致实际显示过大。