Android 中的dp,px深度解析

dip: device independent pixels(設備獨立像素)。不同設備有不同的顯示效果,這個和設備硬件有關,一般我們爲了支持WVGA、HVGA和QVGA 推薦使用這個,不依賴像素。 
  與密度無關的像素,這是一個基於屏幕物理密度的抽象單位。密度可以理解爲每英寸包含的像素個數(單位是dpi),1dp實際上相當於密度爲160dpi的屏上的一個點(可否理解爲物理尺寸?)。也就是說,如果屏幕物理密度是160dpi時,dp和px是等效的。現在用實際的手機屏幕說一下。一塊擁有320*480分辨率的手機屏幕,如果寬度是2英寸,高度是3英寸,這塊屏幕的密度就是160dpi。如果屏幕大小未變,而分辨率發生了變化,例如,分辨率由320*480變成了480*800,這時屏幕的物理密度就變大了(大於160dpi)。這就意味着屏幕每英寸可以顯示更多的像素點,屏幕的顯示效果就更細膩了。假設一個按鈕的寬度使用dp作爲單位,在160dpi時設爲160,而在更高的dpi下(如320dpi),按鈕的寬度看上去和160dpi時的屏幕一樣。這是由於系統在發現屏幕的密度不是160dpi時,會計算一個轉換比例,然後用這個比例與實際尺寸相乘就得出新的尺寸。計算比例的方法是目標屏幕的密度除以160.如果目標屏幕的密度是320dpi,那麼這個比例就是2。如果按鈕的寬度是160dp,那麼在320dpi的屏幕上的寬度就是320個像素點(dp是抽象單位,在實際的屏幕上應轉換成像素點)。從這一點可以看出,dp可以自適應屏幕的密度。不管屏幕密度怎樣變化,只要屏幕的物理尺寸不變,實際顯示的尺寸就不會變化。如果將按鈕的寬度設成160px,那麼在320dpi的屏幕上仍然會是160個像素點,看上去按鈕的寬度只是160dpi屏幕的一半。Android官方建議棄置表示寬度,高度,位置等屬性時應儘量使用dp作爲尺寸單位。 
  據px = dip * density / 160,則當屏幕密度爲160時,px = dip。根據 google 的建議,TextView 的字號最好使用 sp 做單位,而且查看TextView的源碼可知Android默認使用sp作爲字號單位。將dip作爲其他元素的單位。 
  備註: 根據google的推薦,像素統一使用dip,字體統一使用sp  
  舉個例子區別px和dip: 
  px就是像素,如果用px,就會用實際像素畫,比個如吧,用畫一條長度爲240px的橫線,在480寬的模擬器上看就是一半的屏寬,而在320寬的模擬器上看就是2/3的屏寬了。 
而dip,比如你做一條160dip的橫線,無論你在320還480的模擬器上,都是一半屏的長度(當屏幕尺寸不變時)。
Java代碼  收藏代碼
  1. public static int dip2px(Context context, float dipValue) {  
  2.     final float scale = context.getResources().getDisplayMetrics().density;  
  3.     return (int) (dipValue * scale + 0.5f);  
  4. }  
  5.   
  6. public static int px2dip(Context context, float pxValue) {  
  7.     final float scale = context.getResources().getDisplayMetrics().density;  
  8.     return (int) (pxValue / scale + 0.5f);  
  9. }  


  下面這篇文章可以做深入理解之參考,[url=http://blog.csdn.net/eggcalm/article/details/7006378]查看原文[/url]: 
   今天偶然間問了同事一個關於dp單位的問題,然後由這個問題引發的一連串的問題徹底顛覆了我關於dp的理論體系。 

   我那個問題是這樣的:既然dp的本質是物理尺寸,爲什麼不用cm或者mm等傳統長度單位替代? 

   然後他回答我dp是和像素密度無關的。。。我對這個回答不屑一顧,不過他接下來的一句話把我徹底震驚了,那句話是這樣的:在你的手機上320dp就剛好滿屏了,310dp就差一點點滿屏。 

   我的手機是HTC Desire,這個理論我聞所未聞,然後馬上做了個小實驗,事實確實是這樣,把一個TextView背景設成紅色,寬度設成320dp,能看到滿屏,310dp就差那麼一點點。 

   看到這個測試結果的時候,我再一次崩潰了,我希望同事第二句話是一個美麗的錯誤,我無法接受這麼久以來我理解的東西是錯誤的,可是事實是殘酷的。 

   Android Developers關於dp的文檔我看過N次,那個px和dp的轉換公式我記得很清楚: px = dp * (dpi / 160),可是今天翻了源碼了才發現,原來這裏的dpi是歸一化後的dpi,可能值只有120(low)、160(medium)、240(high)、 320(xhigh)四種,而我之前理解的竟然是實際設備真實的dpi! 

   G7的真實dpi是252,根據我以前的理解,310dp換算成px應該是:310 * (252 / 160) = 488像素,而G7水平方向是480px,310dp在這上面絕對滿屏都不止了,事實上Android系統並沒有拿252作爲dpi來計算,而是將G7視 作hdpi設備,然後使用240dpi來計算最終像素,所以在G7上320dp剛好是:320 * (240 / 160) = 480像素,剛好滿屏了,310dp確實要差一點點。 

  搞清楚這個問題後,我心裏稍微好受點了,可是另一個問題接踵而來: 
dp的本質還是物理尺寸,難道不是嗎?儘管Android系統對待dp這事上,將所有設備視爲四種:120(low)、160(medium)、 240(high)、320(xhigh),在160dpi上,160dp就是1英寸,在240dpi上,160dp還是1英寸,120dpi和 320dpi也還是1英寸,雖然他們佔用的像素數不一樣,但是最終顯示出來的效果都是佔用了屏幕上1英寸的範圍。這套體系其實是非常合理的,一個寬爲 160dp的按鈕,它在所有設備上佔用的物理尺寸應該是一樣大才合理,這樣他們對人眼所形成的張角才一樣大,觀看或者閱讀的感覺才一致(姑且不考慮按鈕的 背景是否一樣細緻)。這應該是Android系統引入dp概念的原因,因爲手機屏幕的像素密度相差實在太大了,web那套東西已經完全不適用,你想電腦屏 幕的像素密度能相差多大? 

   終極問題來了,現實生活中真的只有以上四種不同像素密度的設備嗎?不可能。雖然所有這些設備都可以粗略地劃分爲low、medium、high、 xhigh四種密度,可是對於劃在同一範圍內但具有不同像素密度的兩個設備來說,同樣的dp最終所佔用的物理尺寸是不一樣的。舉個例子,G7(HTC Desire)的屏幕尺寸是3.7英寸,分辨率是480*800,像素密度是252dpi,G10(HTC Desire HD)的屏幕尺寸是4.3英寸,分辨率同爲480*800,像素密度是217dpi。假設現在有一個按鈕,它的寬度設爲100dp,由於G7和G10同被 劃分爲hdpi,那麼在這兩個設備上,這個按鈕的實際寬度是:100 * (240 / 160) = 150像素,可由於這兩個設備的實際像素密度是不一樣的,所以真實的顯示效果是:這個按鈕在兩個設備上的實際物理尺寸是不一樣大的。而這,和 Android進入dp的概念是相悖的。 

   你可以說對於同屬於hdpi的設備而言,這個差別很小,但是在ldpi和hdpi之間這個差別就很明顯了。我非常認同,可是如果在將dp轉化爲px的時 候,不是使用歸一化dpi(也就是120(low)、160(medium)、240(high)、320(xhigh)這四種),而是使用設備真實的像 素密度,那麼得出的像素數目雖然各不一樣,但是最終顯示出來的物理尺寸確實一樣大的,而這種計算方法,我認爲是忠於像素密度無關的理論的。 

   最後我還是想說,如果Android希望一個寬度爲160dp的按鈕在任何設備上都是1英寸大,那爲什麼不直接使用英寸作爲度量單位呢?如果你有好的想法,歡迎留言。 

UPDATE: 

   今天下午在回答factar網友的問題的時候,我上面那個“終極問題”終於找到了一個合理的答案。在factar貼的網址裏,我發現一句重要的話: 

“However, bitmap scaling can result in blurry or pixelated bitmaps, which you might notice in the above screenshots.” 

   這句話的意思是說,圖片資源在縮放的時候會造成圖像模糊。按照我以上的分析,如果爲了保證相同的圖片資源在不同像素密度的設備上保持完全一樣的尺寸 大小(這完全可以做到,在dp轉化成px的時候使用設備的物理像素密度參數),那圖片在不同設備上的縮放因子必然不一樣,而這會導致圖像模糊!所以我猜想 Google爲了保證了圖像不會模糊退了一步,讓相同dp在不同設備上“差不多一樣大”。 

   還有,這個答案也糾正了我的一個誤區,現在有很多應用程序開發商爲了降低安裝包的大小,只使用一套hdpi資源或者一套xhdpi資源,而不提供 mdpi資源或ldpi資源,希望在mdpi和ldpi設備上有系統完成縮放適應,雖然可行,但是我們不應忽視因爲縮放帶來的圖像模糊、顯示效果不佳的現象。 


   網友問答參考: 
   不知eggclam現在是否對dp有了更深入的理解,我現在對dp這裏也陷入到了這步,我現在有三個pad,一個10寸,2個7寸, 
   參數如下: 
A:7寸pad 1, 
denstiy:1.0 分辨率 1024X600 
B: 7寸pad 2, 
denstiy: 1.33 分辨率 1280X800 
C: 7寸pad 3, 
denstiy:1.0 分辨率 1280X800 

   按照google 官方文檔稱(http://developer.android.com/guide/practices/screens_support.html) 
Density independence 段落,用dp在不同的denstiy 下,大小看起來應該是一樣的。 
我在三個pad 都設置了同樣dp長度的按鈕,但是在3個pad上的長度看着都不一樣,不知道是因爲什麼呢?能不能加你好友討論下呢? 

   引用“factar”的評論:不知eggclam現在是否對dp有了更深入的理解,我現在對dp這裏也陷入到了這步,我現在有三個pad... 

C 設備應該是10寸吧? 

如果C 設備是10寸,那麼這三款設備的物理dpi大致如下: 
A:169.5 
B:215.6 
C:150.9 

根據這裏的劃分(http://developer.android.com/guide/practices/screens_support.html#range),A和C被評價爲mdpi(density=1.0),B本來應該被評價爲hdpi(density=1.5),但是自從API LEVEL 13開始Android引入了DENSITY_TV(dpi=213),而且你貼的數據也的確證明這一點,所以B設備的density沿用你的數據1.33 

接下來我們算算一根50dp的線條在這三個設備上顯示成多少像素: 
A:50 * 1.0 = 50(px) 
B:50 * 1.33 = 67(px) 
C:50 * 1.0 = 50(px) 

接下來的問題就簡單了,問題直接轉化成這三個像素值在ABC上佔用的物理尺寸一致嗎?我們可以算算: 
A:50 / 169.5 = 0.2950(英寸) = 0.7493(釐米) 
B:67 / 215.6 = 0.3108(英寸) = 0.7894(釐米) 
C:50 / 150.9 = 0.3313(英寸) = 0.8418(釐米) 

根據以上結果,你看到的不完全一樣大是合乎情理的,因爲在Android中,相同dp在所有mdpi設備上雖然像素數量是一樣的,但是因爲各個設備物理dpi不一樣,所以在最終的顯示尺寸上是有微弱差別的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章