Android 彻底搞清“dp(dip)”

前言:大家都知道"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

今日头条屏幕适配方案

百度百科


1.屏幕基础概念

像素(px):是指在由一个数字序列表示的图像中的一个最小单位,称为像素。

分辨率(Resolution):这里指物理分辨率,是液晶屏最高可显示的像素数(假设一个发光点能显示一个像素点,可以简单理解为屏幕设备能发光的点数)。

dpi(Dots Per Inch,每英寸点数):每英寸的像素点数。

density:每平方英寸像素点数。

我有下面这样一部手机,蓝色的是发光点。那么这部手机屏幕的dpi是3,density就是3的平方9。这部手机的分辨率是20(5乘以4),最高能显示分辨率为20的图片。

Android 彻底搞清“dp(dip)”_第1张图片

从上面概念看,dpi是每英寸的像素点数,density是每平方英寸像素点数,大多数手机x轴方向的dpi和y轴方向dpi是相同的,从概念上可以得出density和dpi是平方的关系:density= dpi^{2}

再来看dpi和分辨率的关系,如果手机屏幕面积为s,则density = Resolution / s,dpi = \sqrt{density} = \sqrt{Resolution / s}

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。

2.dip的值

通过上面的计算过程,可以让dip使用者达到不同手机显示相同效果。dip转换成实际显示要用的px,需要用到dpi。根据公式dpi = \sqrt{density} = \sqrt{Resolution / s},可以看出,每个设备的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,是可以达到相同的效果的。


3.Android系统自带dp转px的方法

    public static float dp2px(float dp) {
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
    }

 

你可能感兴趣的:(Android知识归纳)