前言:大家都知道"dp"这个单位,在Android上画布局时使用,用来取代px,使得画出的控件能够在不同手机上展示出相图的效果。记得有人这样跟我描述过:某个dp长度的一条线,在一个手机上展示是1cm,在其他手机上都会展示为1cm。dp是如何工作让不同屏幕手机展示“相同效果”呢?上面对dp展示的描述是否准确呢?
答案是并不是这样。
以下内容有几个简单的计算公式(小学数学水平)需要注意,还要注意dpi和dip的区别。
参考资料:
https://www.jianshu.com/p/5678f23faed3
强烈建议看这篇文章: https://www.jianshu.com/p/cd66b7e01d4a
https://www.cnblogs.com/H-BolinBlog/p/5647548.html
今日头条屏幕适配方案
百度百科
像素(px):是指在由一个数字序列表示的图像中的一个最小单位,称为像素。
分辨率(Resolution):这里指物理分辨率,是液晶屏最高可显示的像素数(假设一个发光点能显示一个像素点,可以简单理解为屏幕设备能发光的点数)。
dpi(Dots Per Inch,每英寸点数):每英寸的像素点数。
density:每平方英寸像素点数。
我有下面这样一部手机,蓝色的是发光点。那么这部手机屏幕的dpi是3,density就是3的平方9。这部手机的分辨率是20(5乘以4),最高能显示分辨率为20的图片。
从上面概念看,dpi是每英寸的像素点数,density是每平方英寸像素点数,大多数手机x轴方向的dpi和y轴方向dpi是相同的,从概念上可以得出density和dpi是平方的关系:。
再来看dpi和分辨率的关系,如果手机屏幕面积为s,则density = Resolution / s,dpi = = 。
dip(Device Independent Pixels,设备独立像素):基于计算机控制的坐标系统和抽象像素(虚拟像素),由底层系统的程序使用,转换为物理像素的应用。
概念有点复杂,简单理解就是:我代码设置了1dip宽度的一个按钮,用不同的手机显示,我用尺子量都是一样长度(就是一样的物理长度)。
怎么达到这样的效果呢?
需要dpi和px一起来合作计算。我们知道了:dpi是每英寸像素点数,px是像素点数,那我有60px的实际物理长度不就是(60/dpi)英寸嘛,1px的实际长度就是 1/dpi 英寸咯。可以得到公式:长度 L(英寸) = px / dpi。手机的dpi不同,那1px的物理长度其实就不同。
dip其实为了达到展示一样物理长度才弄出来的一个概念,最后还是需要转换成像素来显示的。
为了让每部手机上dip都显示一样物理长度,那只需要dip和px转换满足关系:dip = n * px /dpi (n > 0)。那么
px = dip * dpi / n。设备通过这样的对应关系来决定显示的像素值,这样显示出的像素值不同,而保证物理长度相同了。
我们来算一下看。比如20dip的长度:先计算20dip对应的px为20 * dpi /n,物理长度为 px / dpi = (20 * dpi / n) /dpi = 20/n(英寸)。n是一个固定值,那么对于所有设备来说, 20 dip都会显示成 20/n 英寸长的像素。
Android将这个n值定为160。这个值设为160,是因为第一款Android设备(HTC的T-Mobile G1)是属于160dpi。
那么,当设备dpi为160时,1px = 1dip。
通过上面的计算过程,可以让dip使用者达到不同手机显示相同效果。dip转换成实际显示要用的px,需要用到dpi。根据公式dpi = = ,可以看出,每个设备的dip与设备的分辨率和面积相关。
实际,Android在进行换算时做了个偷懒,并没有根据设备的分辨率和面积来计算。而是内置了几个默认的 dpi ,在特定的分辨率下自动调用,忽略考虑设备的面积(厂商也可以根据自己设备修改系统参数,来自定义dpi)。
分辨率 dpi 基准比例
ldpi 240x320 120 0.75
mdpi 320x480 160 1
hdpi 480x800 240 1.5
xhdpi 720x1280 320 2
xxhdpi 1080x1920 480 3
手机中的 /system/build.prop 文件中有一行定义了系统使用的dpi(有些厂商会自行修改):
ro.sf.lcd_density=480
可以看出系统中提供的dpi和设备屏幕的物理dpi在android默认情况下不相同。
可以通过以上代码获取系统提供的屏幕信息:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
介绍DisplayMetrics中的一些成员变量:
// 设备的基准比例,density = dpi / 160
// (这里的density仅代表基准比例,与我们上面基础概念中的density并不相同,注意不要混淆)
// px = density * dip
density
// 系统提供的dpi
// density = densityDpi / 160
// px = densityDpi * dip /160
densityDpi
如此,可以得出结论,Android中dip可以根据屏幕就分辨率动态适配为合适的px。由于没有考虑屏幕尺寸,使得相同dip在不同手机上并不能达到完全相同的物理效果。如果厂商根据自己设备设置合理的dpi,是可以达到相同的效果的。
public static float dp2px(float dp) {
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}