前言:大家都知道"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的圖片。
從上面概念看,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。
2.dip的值
通過上面的計算過程,可以讓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,是可以達到相同的效果的。
3.Android系統自帶dp轉px的方法
public static float dp2px(float dp) {
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}