Android適配——drawable和values的加載規則

Google搞了一大套 dip、sp、mdpi、hdpi、xhdpi之類的這些東西,簡單說來,就是爲了讓我們輕鬆實現“與設備密度無關的視覺大小一致性”,這裏需要明確的“視覺大小一致性”,就是說無論是在手機、低分辨率平板、高分辨率平板上,這個控件或者圖片在物理尺寸上都是一樣的。

這裏我們借 @雨打萍 的一張圖片來看看:
(出處: http://blog.csdn.net/xiebudong/article/details/37040263

其中,黑色和藍色矩形的視覺大小就是一致的。

另外一個需要明確的是屏幕密度、分辨率、物理尺寸之間的關係:
這裏寫圖片描述

以一個分辨率爲1920x1200,物理尺寸爲7寸的手持平板而言,根據勾股定理,我們可以得出其對角線上的像素數大約爲2264,用2264除以7就是此屏幕的屏幕密度,結果爲323.
也就是說,屏幕密度、分辨率、物理尺寸可以由二推一,hdpi-1920x1200屏幕的物理尺寸絕對要比hdpi-1280x800大,這也是我們之後討論適配的前提。

· 實際密度與系統密度
我們經常見到的Android設備屏幕密度大多爲120、160、240、320、480等,而上述例子中的323dpi則是設備的實際密度,說明這塊屏幕每寸有323個像素。得到實際密度以後,Android系統推薦選擇一個最近的密度作爲系統密度,通過DisplayMetrics類獲取上述設備的系統密度就是320dpi。
但是,現在很多Android廠商不一定會選擇這些值作爲系統密度,而是選擇實際的dpi作爲系統密度,這就導致了很多手機的dpi也不是在這些值內。例如小米Note這樣的xxhdpi的設備他的系統密度並不是480,而是它的實際密度440。

那麼針對這些非規範的系統密度,Google官方指定按照系列標準進行區分:
這裏寫圖片描述

有了這兩個前提,再來看android的適配規則就很清楚了:


drawable目錄

我們經常會給應用程序切幾套圖片,放在drawable-mdpi、drawable-hdpi、drawable-xhdpi等目錄下面。當應用在設備對應dpi目錄下沒有找到某個資源時,遵循“先高再低”原則,然後按比例縮放圖片:

  • 比如,當前爲xhdpi設備,並且只有以下幾個目錄,則drawable的尋找順序爲:
    xhdpi->xxhdpi->xxxhdpi(如果沒有更高的了)->nodpi(如果有的話)->hdpi->mdpi,如果在xxhdpi中找到目標圖片,則壓縮2/3來使用,如果在mdpi中找到圖片,則放大2倍來使用。

這很好理解,如果我們按規則放置兩張圖片,mdpi中爲48x48,xxhdpi中爲144x144,那麼不管我們最後從哪個目錄拿到圖片,在xhdpi設備上顯示的像素大小都是96x96,只是一個被拉伸而來,一個被壓縮而來。由於xhdpi定義了96個像素點的物理尺寸,那麼這張圖的物理尺寸實際就被定下來了。
同樣的,mdpi中48個像素點的物理尺寸與xhdpi中96個像素點的物理尺寸是相同的,這就保證了該圖片在任何設備上顯示出的視覺大小一致。
那麼,一個結論就是,對於期望保持視覺大小一致的那部分圖片而言,如果你也能接受android爲你拉伸/壓縮圖片導致一定程度的模糊或者銳化,那麼這些圖片是不需要在每個drawable目錄下都製作一份的。以現在主流設備來說一般可能在drawable-xxhdpi放置一份即可,這樣可以儘量避免Android爲我們放大圖片所導致的OOM。

當然,在某些情況下,我們會主觀希望打破android提供的“視覺大小一致”這種機制,此時我們就可以建立另外的drawable目錄來放置需要變化的圖片了。

然後附一份比例表吧:
這裏寫圖片描述


values目錄

values目錄用來放置colors.xml,dimens.xml,strings.xml等,也可以根據屏幕密度設置特定的values目錄讓滿足設定的設備進行加載,比如values-mdpi、values-hdpi、values-xhdpi、values-xxhdpi等等,然後每個目錄放置一個demins.xml,使不同分辨率的設備應用不同的尺寸設置。當應用設備在當前dpi對應目錄的demins.xml中沒有找到目標條目時,採用“就近匹配”原則:

  • 比如,當前爲hdpi設備,並且只有以下幾個目錄,則values的尋找順序爲:
    hdpi->xhdpi->mdpi->values,即先向上級dpi目錄查找,再向下級dpi目錄查找,最後一路向下查找到values目錄,如果values下都找不到,就只有找values-ldpi,當然,現在有這個目錄的應用不多了。

那麼,我們需要將mdpi目錄下的值都乘以相應的倍數來放置在其他目錄下面嗎?答案當然是否定的,由於我們對期望屏幕密度無關的值都定義爲了dp或者dip的單位,無論android從哪個目錄最終找到該值,都會直接應用這個值與當前設備的密度來計算最終的尺寸。

也就是說,如果我們同樣在values-xhdpi和values下寫 length=10dp
那麼在mdpi設備上得到的都是10px,在xhdpi設備上得到的都是20px。
但它們看起來“都是一樣寬”,這樣就已經是“保證視覺大小一致性了”。
48dp法則告訴我們,48dp的物理尺寸約等於9mm,是人的手指比較容易點擊到的大小,並且是獨立於設備的。

顯而易見,如果我們在values目錄下寫length=10dp,values-xhdpi目錄下寫length=20dp,mdpi設備上得到的將是10px,而xhdpi設備上得到的將是40px,得到視覺結果就是,該控件在xhdpi設備上看起來比mdpi設備上大了一倍。

  • 考慮這樣一個情況:有一個BottomBar,左右兩端各有一個按鈕,按鈕長寬用dp定義,這樣在一個大屏手機中,兩個按鈕可能就相隔更遠了,因爲按鈕的視覺尺寸是沒有變化的。如果你想保持按鈕在BottomBar中所佔的比例,最好辦法不是添加一個values-xxx,然後重寫這兩個dp值,而是精心設計你的佈局。

那麼,既然最後都要找到values,並且能夠保證視覺大小一致性,那何必再添加其他values分辨率目錄呢?答案是在某些情況下,我們主觀希望某些尺寸不去保持視覺一致性。例如一個Button,在手機上那麼大剛好,但如果在平板設備上,是的,它看起來和在手機上一樣大,但是,它顯得有點小了。

也就是說,我們應該把希望在任何設備上視覺大小都一樣的尺寸都放置在values目錄下並且只放置這一份,其他需要有變化的尺寸則放置在對應目錄下即可

一般而言,使用在物理尺寸相差不大的幾套設備上,一個values可能就夠了,因爲它本身就保證了“視覺大小一致性”,但是如果你的應用需要兼容平板,甚至電視,那麼這種一致性可能是一種災難。這時可以考慮添加一個對應dpi的values目錄,把需要變化的值拷貝進去重寫,但我更推薦採用values-xhdpi-2560x1600,我們很容易通過這裏的屏幕分辨率+dpi計算得到該設備的物理尺寸,顯而易見這是一個平板設備,如此我們的改動便不至於影響同DPI的低物理尺寸設備(手機),而物理尺寸差不多的設備是可以共用一套dimens.xml的。

  • 那麼,如果當前設備爲xhdpi-1184x800,當前目錄有values-xhdpi-1184x800,values-xhdpi-1184x960,values-xhdpi-1184x720,android的尋找順序則是:
    values-xhdpi-1184x800->values-xhdpi-1184x720->values-xhdpi

只向低於自己分辨率的目錄下尋找,直到values-xhdpi,如果依然沒有找到,按照之前的順序繼續進行。(hdpi-1280x800 -> hdpi-1280x720 -> hdpi -> …)
也就是說,對於同dpi的多臺不同分辨率平板設備,如果佈局足夠通用,我們可以只針對最低分辨率設計dimens即可,上面的例子中,則是values-xhdpi-1184x720。
我們還可以將這個分辨率寫得更低,低到我們有把握:如果再出現比這個分辨率更低的設備,那麼它的物理尺寸一定滿足即使採用values-xhdpi中針對手機物理尺寸設計的大小也沒有問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章