1. 程式人生 > >玩轉Android drawable圖片適配

玩轉Android drawable圖片適配

眾所周知,Android機型眾多,螢幕尺寸、解析度各有不同。對於Android開發人員來說,如何提高APP中圖片對各種機型的適配是基本技能之一。藉著專案中遇到的圖片適配問題,在總結專案時,就想著順帶把這部分好好捋一捋,作為一個記錄,也為不是很清楚這部分的人提供一個參考。

先說mipmap

採用Android Studio開發Android APP,在專案的res目錄下,會多出幾個以mipmap開頭的資料夾。

mipmap資料夾

根據Android官方的描述,mipmap僅僅用於存放APP啟動圖示,可由Image Asset Studio生成。Image Asset Studio會生成mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi五種尺寸的圖示。圖示最好不要隨意定義尺寸,解析度過低會模糊,過高徒增APK包大小。各種密度下的圖示建議尺寸為

密度 建議尺寸
mdpi 48*48
hdpi 72*72
xhdpi 96*96
xxhdpi 144*144
xxxhdpi 192*192

如果要上傳到Google Play,還需要一張512*512的圖片用於Google Play Store。

再說drawable圖片適配

Android系統可以在具有不同螢幕尺寸和密度的裝置上執行,並將每個應用的使用者介面調整為適應其顯示的螢幕,會進行縮放和大小調整。為了最大程式優化更多裝置上的使用者體驗,開發者需要針對不同的螢幕尺寸和密度優化應用。對於Android智慧手機來說,螢幕大小、解析度、密度均不盡相同,那麼圖片適配就成了Android中優化應用必不可少的環節之一。

相關概念

  • dpi
    每英寸點數,全稱dots per inch。用來表示螢幕密度,即螢幕物理區域中的畫素量。高密度螢幕比低密度螢幕在給定物理區域的畫素要多。

  • dp
    即dip,全稱device independent pixel。裝置獨立畫素,是一種虛擬畫素單位,用於以密度無關方式表示佈局維度或位置,以確保在不同密度的螢幕上正常顯示UI。在160dpi的裝置上,1dp=1px。

  • density
    裝置的邏輯密度,是dip的縮放因子。以160dpi的螢幕為基線,density=dpi/160。

    getResources().getDisplayMetrics().density

  • sp
    縮放獨立畫素,全稱scale independent pixel。類似於dp,一般用於設定字型大小,可以根據使用者設定的字型大小偏好來縮放。

六種通用密度

Android系統為了簡化開發者為多種螢幕設計使用者介面的方式,Android將實際螢幕尺寸和範圍作了通用規定,稱作“根據可用螢幕寬度管理螢幕尺寸的新技術”。六種通用密度為

密度 dpi範圍
ldpi(低) ~120dpi
mdpi(中) ~160dpi
hdpi(高) ~240dpi
xhdpi(超高) ~320dpi
xxhdpi(超超高) ~480dpi
xxxhdpi(超超超高) ~640dpi

通用密度是以mdpi(中)為基線配置的,此基線基於第一代Android裝置(T-Mobile G1)的螢幕配置。

Android系統適配原則

Android為了更好地優化應用在不同螢幕密度下的使用者體驗,在專案的res目錄下可以建立drawab-[density](density為6種通用密度名)目錄,開發者在進行APP開發時,針對不同的螢幕密度,將圖片放置於對應的drawable-[density]目錄,Android系統會依據特定的原則來查詢各drawable目錄下的圖片。查詢流程為:
1. 先查詢和螢幕密度最匹配的資料夾。如當前裝置螢幕密度dpi為160,則會優先查詢drawable-mdpi目錄;如果裝置螢幕密度dpi為420,則會優先查詢drawable-xxhdpi目錄。
2. 如果在最匹配的目錄沒有找到對應圖片,就會向更高密度的目錄查詢,直到沒有更高密度的目錄。例如,在最匹配的目錄drawable-mdpi中沒有查詢到,就會查詢drawable-hdpi目錄,如果還沒有查詢到,就會查詢drawable-xhdpi目錄,直到沒有更高密度的drawable-[density]目錄。
3. 如果一直往高密度目錄均沒有查詢,Android就會查詢drawable-nodpi目錄。drawable-nodpi目錄中的資源適用於所有密度的裝置,不管當前螢幕的密度如何,系統都不會縮放此目錄中的資源。因此,對於永遠不希望系統縮放的資源,最簡單的方法就是放在此目錄中;同時,放在該目錄中的資源最好不要再放到其他drawable目錄下了,避免得到非預期的效果。
4. 如果在drawable-nodpi目錄也沒有查詢到,系統就會向比最匹配目錄密度低的目錄依次查詢,直到沒有更低密度的目錄。例如,最匹配目錄是xxhdpi,更高密度的目錄和nodpi目錄查詢不到後,就會依次查詢drawable-xhdp、drawable-hdpi、drawable-mdpi、drawable-ldpi。

舉個例子,假如當前裝置的dpi是320,系統會優先去drawable-xhdpi目錄查詢,如果找不到,會依次查詢xxhdpi → xxxhdpi → hdpi → mdpi → ldpi。對於不存在的drawable-[density]目錄直接跳過,中間任一目錄查詢到資源,則停止本次查詢。

總結一下圖片查詢過程:優先匹配最適合的圖片→查詢密度高的目錄(升序)→查詢密度低的目錄(降序)。

資源適配流程簡單歸納如下

Android圖片查詢流程

關於Android適配更詳細的介紹可以參見Android 如何查詢最佳匹配資源,當然你可能需要搭個梯子(不過,Google已經發布了針對中國的開發者網站,對應的Android中國開發者網站為:developer.android.google.cn,可以免梯子)。

圖片的放大和縮小

前述說到Android為了能夠更好地適配各種螢幕,會依據當前裝置的dpi對drawable-[density]目錄中的圖片進行縮放,那麼什麼情況下圖片被放大,什麼情況下圖片被縮小呢?

為了更好的描述,把“符合當前裝置dpi的drawable目錄”表示為”匹配目錄“。比如,裝置的dpi為320,這匹配目錄為drawable-xhdpi;裝置的dpi為150,則匹配目錄為drawable-mdpi。圖片的放大和縮小遵循以下規律:

  • 如果圖片所在目錄為匹配目錄,則圖片會根據裝置dpi做適當的縮放調整。
  • 如果圖片所在目錄dpi低於匹配目錄,那麼該圖片被認為是為低密度裝置需要的,現在要顯示在高密度裝置上,圖片會被放大。
  • 如果圖片所在目錄dpi高於匹配目錄,那麼該圖片被認為是為高密度裝置需要的,現在要顯示在低密度裝置上,圖片會被縮小。
  • 如果圖片所在目錄為drawable-nodpi,則無論裝置dpi為多少,保留原圖片大小,不進行縮放。

那麼六種通用密度下的縮放倍數是多少呢?以mdpi為基線,各密度目錄下的放大倍數(即縮放因子density)如下

密度 放大倍數
ldpi 0.75
mdpi 1.0
hdpi 1.5
xhdpi 2.0
xxhdpi 3.0
xxxhdpi 4.0

例如,當前裝置的dpi是480(即xxhdpi),那麼對於存放於mdpi目錄中的圖片會被放大三倍。對於很多裝置,其dpi並不剛好是六種通用密度最大dpi,這種情況下,圖片的縮放倍數如何計算呢?

稍微思考一下,我們就可以得到通用的縮放倍數(縮放因子)計算方法:對於任意裝置,各drawable-[density]目錄下的圖片放大倍數的計算公式

縮放因子計算公式

那麼,圖片的實現顯示尺寸通過圖片尺寸乘以縮放倍數就可以得到了。

例項驗證

  • 驗證圖片的放大和縮小

    在配置為1080×1920 - 420dpi的模擬器上,從網上找一張Android logo圖片分別放在drawable-mdpi、drawable-xxhdpi、drawable-xxxhdpi資料夾下,檢視圖片的顯示效果(如下)。從圖中可以明顯看到圖片的放大和縮小,且比裝置螢幕密度低的drawable-mdpi目錄圖片被放大,比裝置螢幕密度高的drawable-xxxhdpi目錄圖片被縮小。

    三種不同密度下的圖片顯示效果

  • 驗證縮放倍數

    在Sketch裡簡單繪製一張圖,分別匯出一倍圖(1x)和三倍圖(3x),並假設實際標註為一倍圖的尺寸。表示如下

    標註尺寸及一倍圖和三倍影象素

    在配置為1080×1920 - 420dpi的模擬器上,1倍圖和3倍圖在wrap_content的情況下,寬高應該符合上表。按照上面對放大倍數的分析,分別把1倍圖和3倍圖置於drawable-mdpi和drawable-xxhdpi目錄下,檢視圖片寬高。

    1倍圖和3倍圖在分別在drawable-mdpi和drawable-xxhdpi目錄下的顯示效果及寬高

    該模擬器螢幕dpi橫縱方向均為420,drawable-mdpi目錄最大dpi值為160,drawable-xxhdpi目錄最大dpi值為480。通過上述分析的計算公式,可計算圖片預期寬高:

    • drawable-mdpi
      scale = 420/160
      1x:98 × scale = 98 × 420/160 = 257
      3x:294 × scale = 294 × 420/160 = 772

    • drawable-xxhdpi
      scale = 420/480
      1x:98 × scale = 98 × 420/480 = 86
      3x:294 × scale = 294 × 420/480 = 257

    由於畫素沒有小數,上述計算結果進行了四捨五入。從結果可以看出,公式計算結果和實際顯示效果一致。另一方面,在該dpi下,圖片放置於drawable-xxhdpi目錄下,圖片的顯示寬高更接近於圖片實際大小。

    這部分的驗證大家可以自己測試一下,基本規律是沒有問題的。

最後定切圖

到此,我們已經知道了Android會按照特定規則對圖片進行縮放,以更好地適配各種配置的螢幕。那麼在我們關注切圖之前,首先考慮一下圖片縮放帶來的影響。

嗯,比如容易想到的一點就是會引起記憶體的變化。當一張圖片被放大時,畫素增加,必然會引起記憶體佔用量增加;圖片被縮小時,畫素減少,記憶體佔用量就會降低。記憶體的使用量可通過Android Monitor來檢視。對於上述Android logo那張圖片,同樣在420dpi的裝置上,分別將圖片放置於drawable-mdpi和drawable-xxhdpi目錄下,記憶體佔用情況如下

特定圖片在drawable-mdpi和drawable-xxhdpi目錄下的記憶體佔用

可以看到,僅僅一張圖片的記憶體佔用差別就已經在MB級別了。圖片放大的記憶體成本將是不得不考慮的一個重要因素了。

關於切圖的選取,Android官方給的建議,各種密度都給出一套圖,分別放置在對應的drawable目錄下,這種適配是最好的。但也存在問題,一是這種方式會增大安裝包的大小;二是很多公司UI在出圖時只會出一套。

在這種情況下,怎麼使用好這一套切圖呢?由於目前的Android智慧手機的螢幕基本都在1080p了,螢幕的dpi多數都處於320~480,為了更好地適配,同時為了節省記憶體成本,建議將切圖放置在drawable-xxhdpi目錄,同時建議UI針對該密度的裝置設計切圖。如果UI的切圖基於不同尺寸設計,Sketch匯出切圖時須調整相應的倍數。

例如,假設切圖基於376×667的一倍螢幕設計,而要適配1080×1920的螢幕,匯出三倍圖存放於drawable-xxhdpi目錄是適配最好的。

好了,關於drawable圖片適配方面就暫時介紹到這,相信看了後會有一定的認識,在處理平時專案相關問題時,也會有一定的想法了。

參考