android定義了四種screen-size:
small
normal
large
xlarge
同時定義了六種dpi級別:
ldpi (low) ~120dpi
mdpi (medium) ~160dpi
hdpi (high) ~240dpi
xhdpi (extra-high) ~320dpi
xxhdpi (extra-extra-high) ~480dpi
xxxhdpi (extra-extra-extra-high) ~640dpi
定義這些級別的目的在於更好地組織資源,以適應不同的設備。參見:http://developer.android.com/guide/practices/screens_support.html#range
系統在運行我們的app時,會自動根據當前設備的級別,來加載合適的資源,而我們就需要為不同級別准備各自的資源,如為大屏幕上提供一個並列放置兩個view的layout,而小屏幕上則一次只顯示一個view。
要做到這一點,我們必須要知道,給定一個設備,如何判斷它是哪個級別的?然後才能有的放矢,把相應的資源放在正確的級別目錄中。
首先看screen size,這個級別會影響layout模板的選取,即指定一個R.layout.id_XXX時,到底是從res/layout下加載,還是從res/layout-large下加載呢?
先看官方給的圖:
第一排是關於size的分檔,在這裡,各個檔是有重疊的,也就是一個大概的范圍。
但是我們知道,程序運行時的加載邏輯必須是精確的,針對每一個設備必須獲得一個確定的值,以判定它是哪個級別,然後才知道要加載哪些資源。
以上圖論,如果我們有一個3.3"的手機,那它到底是small還是normal呢?就無法判斷了。
所以,必然要有更准確的計算方法,而這個方法就牽扯到dpi了。
dpi:dot per inch,就是指每英寸上有多少個像素點,要計算這個值,就要知道它的物理尺寸和物理分辨率(也就是全屏有多少實際像素點,或者就是說能發光的元件)
比如我們建一個虛擬設備來看:
它的物理分辨率是720x1280,物理尺寸是4.7"
可能有人疑問,物理尺寸不也是有寬和高嗎,怎麼才一個值?這個4.7",是對角線的長度。用對角線來做標稱,好處是容許更自由的寬高組合吧。
那麼要計算這個dpi,按常理就沒法算了,因為1個對角線值無法確定惟一的寬高。
但是換一個思路,將面積密度換成線密度,即用對角線上的像素數量來除以對角線長度,其值應該是一致的,所以dpi計算如下:
dpi = sqrt(pixel_width^2+pixel_height^2)/diagonal = sqrt(720^2+1280^2)/4.7 = 312.5
對照上表,可知其dpi級別為xhdpi,與工具界面上顯示的一致。
知道了dpi,那麼圖片類資源的加載目錄就知道了,此例中就是res/drawable-xhdpi,但是layout類資源又如何呢?
layout的選取標准與圖片資源不同,並不以dpi級別為准,而是要看screen size的級別。
上面在講screen size分類方法時轉而插入對dpi解釋,正是為了這裡能說清其含義。
實際上官方對screen size的分類標准如下:
xlarge screens are at least 960dp x 720dp
large screens are at least 640dp x 480dp
normal screens are at least 470dp x 320dp
small screens are at least 426dp x 320dp
這裡給出的寬、高值其實並無單獨比較意義,最終結果是看它的乘積。
也就是說最小的屏幕要有426*320=136,320個dp,而要想成為一個中等屏幕則至少需要470*320=150,400個dp,列表如下:
smal = 136,320
normal = 150,400
large = 307,200
xlarge = 691,200
現在問題來了,dp又是什麼,和dpi什麼關系?針對上例這個設備,它又有多少個dp呢?
先回想一下dpi,它本身是一個比率值,說的是每英寸上有多少個像素點,這是刻劃設備能力的一種屬性。
然而很多時候能力的絕對值對一般人認知理解來說並不直觀(雖然這個例子並不合適,dpi還是很直觀的),其相對值才更具有意義。
那麼選取第一台android設備為基准,它的dpi是160,其它設備的dpi與160相比,得到一個比率值scale,就是該設備的(dp-)scale,
那麼設備的dp則定義為:
dpx = pixel_width/scale
dpy = pixel_height/scale
此例中則其 scale=312.5/160=1.95,dpx=720/scale=369.2,dpy=1280/scale=646.4
也就是以dp單位來衡量,此虛擬設備有一個369 x 646的屏幕,現在可以來計算它的screen size級別了:dp = dpx * dpy = 369 * 646 = 238,374
查上表可知介於150,400和307,200之間,所以它的screen size為normal,當加載layout時,優先使用res/layout裡的資源(而非small/large/xlarge)。
定義和計算過程清楚後,再回頭審視一下dp的含義,為什麼要通過如此曲折的方式來定義這樣一個單位。
1、其實從物理分辨率到dp(也就是screen size),只是轉換了一下手段,但目的還是一樣:就是要解決“數清楚屏幕上到底有多少個點,然後我們的ui面板要做多少個點”這個問題。
2、為什麼不直接用“物理像素”,會有什麼問題?因為不同設備物理尺寸與分辨率之比差別很大,比較極端的情形是,兩個分辨率相同的設備,尺寸卻差一半,比如這兩:
如果以物理像素定義一個面板為400*600px大小,也就是全屏一半的樣子,在10"的tablet上看起來大小正合適,而在4.7"的手機上,那就是小小的一砣,這一砣的物理占地只有tablet上1/16!
如果tablet上的視覺效果是合理的,那麼很難想象肉眼如何適應這1/16的同質元素。
但是改用dp為單位來描述,結果會是如何呢?(計算結果可以在這個網頁方便獲取:https://www.sven.de/dpi/)
對前者,dpi=149.5,scale~=1.0,screen size(dp)=800*1280
對後者,dpi=317.6,scale~=2.0,screen size(dp)=384*640
如果我們需要一個在tablet占地1/4(也就是寬高各1/2)的區域,那麼其dp大小應為400*640
現在計算其在phone上的占地,先換算為像素大小,w=400*2.0=800,h=640*2.0=1280
即這是一個800*1280像素的區域,幾乎剛好是整個Phone屏幕大小(考慮上面計算scale時的捨入誤差)
而這個大小,在物理尺寸上剛好和tablet上的1/4是一樣的。
可見,使用dp來描述大小的優勢在於:確保了該區域在不同分辨率和物理尺寸的屏幕上,其物理面積是一樣大的!
而物理面積,正是肉眼觀察舒適度的重要衡量標准——而不僅僅是分辨率,相信大家都有體會,很多高清屏手機雖然足夠細膩,但如果用來看電子書其實很廢眼,遠不如較低分辨率但更大尺寸的平板)
現在,dp的含義和好處清楚後,還有最後一個問題:如果真要設計一個面板,到底該給它多少dp呢?dp既是一個虛擬單位,如何直觀地以它來估算大小呢?