本文記錄一些適配問(wèn)題的研究,基礎(chǔ)概念不做過(guò)多介紹。
Android在做屏幕適配的時(shí)候一般考慮兩個(gè)因素:分辨率和dpi。分辨率是屏幕在橫向、縱向上的像素點(diǎn)數(shù)總和,一般用“寬x高”的形式表示,例如:1080x1920。dpi是dots per ich的縮寫(xiě),表示每英寸的像素點(diǎn)數(shù),例如160dpi指手機(jī)水平或垂直方向上每英寸距離有160個(gè)像素點(diǎn)。
一、dp和px
dp和px都是編寫(xiě)布局時(shí)的單位,它們之間可以通過(guò)dpi來(lái)?yè)Q算,換算公式如下:
- 公式一:px值 = dp值 * (dpi/160)
常見(jiàn)的還有一個(gè)density的概念,表示基準(zhǔn)比例,density = (dpi/160),所以還有如下公式:
- 公式二:px值 = dp值 * density
注:公式中的160是android中規(guī)定的基準(zhǔn),即:在160dpi的屏幕上,1dp=1px
通過(guò)上面的公式理解dp的概念
dp和dip的含義相同,都是density-independent pixel的縮寫(xiě),表示密度無(wú)關(guān)像素,可以保證在不同像素密度的屏幕上顯示相同的效果。舉個(gè)例子:
兩種常見(jiàn)的屏幕參數(shù)如下:
- 屏幕1:分辨率=720x1280,dpi=320
- 屏幕2:分辨率=1080x1920,dpi=480
如果要實(shí)現(xiàn)一個(gè)view的寬度占屏幕1寬度的一半,由分辨率可知需要view的寬度是360px,根據(jù)公式一可以知道使用180dp可以實(shí)現(xiàn)相同的效果(360 = dp值 * (320/160) -> dp值=180)。
接下來(lái)再根據(jù)公式一,看看180dp在屏幕2上的顯示效果,px值 = 180 * (480/160) = 540,正好是屏幕2寬度的一半。所以,使用180dp就可以同時(shí)在兩個(gè)屏幕上顯示相同的效果了。
二、實(shí)際工作中如何使用dp適配
實(shí)際工作中,我們一般會(huì)使用限定符建立多個(gè)資源文件來(lái)做適配,一般會(huì)適配mdpi,hdpi,xhdpi,xxhdpi等,比如res下的values資源目錄如下:
- res/values-mdpi
- res/values-hdpi
- res/values-xhdpi
然后在不同的目錄下定義不同的dp值。上面180dp的例子說(shuō)明了dp值已經(jīng)保證了在不同像素密度的屏幕上顯示相同的效果,那么為什么還要針對(duì)不同的屏幕定義不同的dp值?不同的dp值如何確定的?下面結(jié)合例子解釋這兩個(gè)問(wèn)題。
1. 為什么要針對(duì)不同的屏幕定義不同的dp值?
這是因?yàn)閍ndroid機(jī)型屏幕尺寸太碎片化了,一個(gè)dp值并不能滿足所有機(jī)型,比如常見(jiàn)的還有如下這種:
- 屏幕3:分辨率=480x800,dpi=240
通過(guò)前面的公式計(jì)算可以知道,前面例子的180dp在這個(gè)屏幕上并不是寬度的一半,160dp才是一半。由此可見(jiàn),一個(gè)dp值并不能滿足所有屏幕,所以需要使用限定符適配不同的dpi。最終適配這三種屏幕如下:
- res/values-hdpi/dimens.xml 中定義資源160dp 適配屏幕3
- res/values-xhdpi/dimens.xml 中定義資源180dp 適配屏幕1
- res/values-xxhdpi/dimens.xml 中定義資源180dp 適配屏幕2
限定符mdpi,hdpi,xhdpi等與dpi的對(duì)應(yīng)關(guān)系后面給出
2. 不同限定符下的dp值如何確定的?
比如現(xiàn)在項(xiàng)目中有了一套屏幕1(xhdpi)的資源res/values-xhdpi/dimens.xml,其中有一個(gè)資源值是90dp,如果要求再適配一下屏幕3(hdpi),那么res/values-hdpi/dimens.xml中與90dp同名的資源應(yīng)該是多少dp呢?通過(guò)前面的介紹,這個(gè)值是很好計(jì)算的:
- 根據(jù)公式一計(jì)算90dp在屏幕1上是多少px
px = 90 * (320/160) = 180 - 根據(jù)1中計(jì)算出的像素,計(jì)算在屏幕3下同樣比例的像素?cái)?shù)
屏幕3下的px = 480 * (180/720) = 120 - 根據(jù)上一步的結(jié)果和公式一計(jì)算出在屏幕3的dp值
dp = 80
如果把計(jì)算過(guò)程中的90dp改成任意值r,那么最終,屏幕1的每一個(gè)資源值 r 乘以 8/9 就是在屏幕3下的dp值,對(duì)應(yīng)的資源文件就是:
- res/values-xhdpi/dimens.xml 中定義資源 r dp (適配屏幕1)
- res/values-hdpi/dimens.xml 中定義資源 r*8/9 dp (適配屏幕3)
這里計(jì)算出來(lái)的8/9是使用橫向分辨率和dpi計(jì)算出來(lái)的,如果是縱向的話并不是這個(gè)比例,所以適配的時(shí)候需要區(qū)分橫豎向不同的比例轉(zhuǎn)換。比如寬、橫向邊距等使用橫向的比例,高、豎直邊距等使用豎向比例。
需要注意的是這里是按比例計(jì)算出來(lái)的,最終的值還得根據(jù)實(shí)際情況和顯示效果而定。因?yàn)椴灰欢ㄋ薪缑娴脑O(shè)計(jì)都是按比例適配的;還有就是有些帶虛擬按鍵的1080x1920的手機(jī),真實(shí)的豎直像素?cái)?shù)應(yīng)該是1920減去虛擬按鍵的高度;還有一點(diǎn),在android開(kāi)發(fā)中所說(shuō)的dpi的值并不是物理定義的,而是系統(tǒng)文件寫(xiě)進(jìn)去的,所以這個(gè)值是可以被修改的。另一層意思是,dpi并不是由分辨率和屏幕尺寸計(jì)算出來(lái)的固定值。比如當(dāng)前常見(jiàn)的一種機(jī)型分辨率是1080x1920,尺寸是5.15英寸,dpi是480。按照dpi的定義,使用這個(gè)分辨率和尺寸計(jì)算dpi的話,結(jié)果并不是480。所以對(duì)分辨率和尺寸都相同的手機(jī),dpi值不一定相同,完全看手機(jī)廠商如何定義。不過(guò),為了使顯示效果最好,一般比較標(biāo)準(zhǔn)的手機(jī)dpi和分辨率都和下表一致:
| ldpi | mdpi | hdpi | xhdpi | xxhdpi | |
|---|---|---|---|---|---|
| 分辨率 | 240x320 | 320x480 | 480x800 | 720x1280 | 1080x1920 |
| dpi | 120 | 160 | 240 | 320 | 480 |
所以,不同限定符下的dp值,使用上面1,2,3步的計(jì)算方法能滿足大部分主流機(jī)型,但不一定能完美適配所有機(jī)型。
限定符與dpi的具體對(duì)應(yīng)關(guān)系如下:
| 限定符 | ldpi | mdpi | hdpi | xhdpi | xxhdpi |
|---|---|---|---|---|---|
| dpi | dpi<=120 | 120<dpi<=160 | 160<dpi<=240 | 240<dpi<=320 | 320<dpi<=480 |
限定符的知識(shí)不僅如此,這里不做過(guò)多介紹。
三、DisplayMetrics類和wm命令
代碼中,可以通過(guò)DisplayMetrics類來(lái)獲取屏幕的一些信息,有三種方式可以獲取DisplayMetrics的實(shí)例:
//方法1
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
//方法2
WindowManager wm = (WindowManager) getSystemService(
Context.WINDOW_SERVICE);
DisplayMetrics metrics= new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
//方法3
DisplayMetrics metrics = getResources().getDisplayMetrics();
可以通過(guò)DisplayMetrics獲取的如下信息:
//等號(hào)后面的數(shù)值是某個(gè)手機(jī)的參數(shù),分辨率=1080x1920,dpi=480,尺寸5.15英寸
metrics.heightPixels = 1920
metrics.widthPixels = 1080
metrics.densityDpi = 480 //dpi值
metrics.density = 3.0 //基準(zhǔn)比例,dpi/160
metrics.xdpi = 422.03 //x方向準(zhǔn)確的物理像素密度
metrics.ydpi = 424.069 //y方向準(zhǔn)確的物理像素密度
metrics.scaledDensity = 3.0
這里的xdpi和ydpi和densityDpi的值不同,再次說(shuō)明android開(kāi)發(fā)中所說(shuō)的dpi的值不是由硬件決定的。
利用DisplayMetrics類,可以實(shí)現(xiàn)一些常用的工具方法,比如dp轉(zhuǎn)px,px轉(zhuǎn)dp等?;蛘咴谝恍┎环奖闶褂觅Y源適配的情況下,可以通過(guò)這個(gè)類判斷不同的dpi來(lái)通過(guò)java代碼來(lái)適配。
wm命令
wm命令是高通平臺(tái)下對(duì)手機(jī)分辨率、像素密度等進(jìn)行設(shè)置的命令。用法很簡(jiǎn)單:
首先使用adb shell命令進(jìn)入手機(jī)的shell中,然后就可以使用wm命令了,常用的命令如下:
wm size //輸出手機(jī)的分辨率信息
如果要修改手機(jī)分辨率,上面的命令加上分辨率參數(shù)即可,比如把手機(jī)分辨率修改為800x1280,命令如下
wm size 800x1280
如果要把上面命令修改的分辨率還原為手機(jī)原始的分辨率,使用下面的命令
wm size reset
上面是分辨率相關(guān)的命令,dpi的命令和上面的類似,如下:
wm density
wm density 240
wm density reset
掌握wm命令后,就可以方便的查看手機(jī)分辨率和dpi了,也可以使用同一部手機(jī)測(cè)試多種分辨率的適配。
注:使用wm命令修改分辨率或dpi后,在某些手機(jī)中,再使用reset命令還原后,手機(jī)某些內(nèi)容可能會(huì)顯示不正常(比如狀態(tài)欄、輸入法等),重啟手機(jī)即可解決。
四、小結(jié)
通過(guò)前面幾節(jié),應(yīng)該知道以下幾點(diǎn):
- 屏幕適配的時(shí)候一般考慮的兩個(gè)因素:分辨率和dpi。
- 利用dpi換算dp和px的值(兩個(gè)公式)
- 使用限定符適配不同dpi的屏幕,不同限定符下dp值的計(jì)算
- android開(kāi)發(fā)中使用的dpi的值是不固定的,可以修改的
- 獲取手機(jī)屏幕信息的DisplayMetrics類和修改屏幕參數(shù)的wm命令
五、drawable下的圖片適配
這里介紹drawable下的圖片資源的讀取和縮放的規(guī)則,也可以這樣說(shuō),如何使用一套圖片資源適配多種dpi。郭神的一篇博客 Android drawable微技巧,你所不知道的drawable的那些細(xì)節(jié)講的很清晰,這里取其精華做一個(gè)總結(jié)。
圖片資源的讀取規(guī)則
當(dāng)我們使用資源id來(lái)去引用一張圖片時(shí),Android會(huì)使用一些規(guī)則來(lái)幫我們匹配最適合的圖片。什么叫最適合的圖片?比如我的手機(jī)屏幕密度是xxhdpi,那么drawable-xxhdpi文件夾下的圖片就是最適合的圖片。因此,當(dāng)我引用一張圖片時(shí),如果drawable-xxhdpi文件夾下有這張圖就會(huì)優(yōu)先被使用,在這種情況下,圖片是不會(huì)被縮放的。但是,如果drawable-xxhdpi文件夾下沒(méi)有這張圖時(shí), 系統(tǒng)就會(huì)自動(dòng)去其它文件夾下找這張圖了,優(yōu)先會(huì)去更高密度的文件夾下找這張圖片,也就是drawable-xxxhdpi文件夾,然后發(fā)現(xiàn)這里也沒(méi)有android_logo這張圖,接下來(lái)會(huì)嘗試再找更高密度的文件夾,發(fā)現(xiàn)沒(méi)有更高密度的了,這個(gè)時(shí)候會(huì)去drawable-nodpi文件夾找這張圖,發(fā)現(xiàn)也沒(méi)有,那么就會(huì)去更低密度的文件夾下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi。
android項(xiàng)目資源下的drawable(不帶任何限定符)目錄默認(rèn)就是drawable-mdpi的意思
作者:郭霖
鏈接:http://blog.csdn.net/guolin_blog/article/details/50727753
圖片資源的縮放規(guī)則
根據(jù)上面的讀取規(guī)則,如果最終沒(méi)有讀取到最適合的圖片,而是讀取了低密度或高密度的圖片,那么系統(tǒng)會(huì)自動(dòng)做一個(gè)縮放操作(低密度的圖片放大,高密度的圖片縮小)。具體的縮放比例就是dpi的比例:
| ldpi | mdpi | hdpi | xhdpi | xxhdpi | |
|---|---|---|---|---|---|
| dpi | 120 | 160 | 240 | 320 | 480 |
| 比例 | 3 | 4 | 6 | 8 | 12 |
比如當(dāng)前手機(jī)dpi是160(對(duì)應(yīng)mdpi)
如果讀取了drawable-mdpi下的圖片大小是48x48,那么顯示到屏幕上的圖片大小是48x48(最適合的圖片不縮放)
如果讀取了drawable-ldpi下的圖片大小是48x48,那么顯示到屏幕上的圖片大小是36x36(48/36 = 4/3)
如果讀取的是drawable-xhdpi下的圖大小是48x48,那么顯示到屏幕上的圖片大小是96x96。
一套圖片資源適配多種dpi
根據(jù)Android的開(kāi)發(fā)建議,我們?cè)跍?zhǔn)備圖片資源時(shí)盡量應(yīng)該給每種密度的設(shè)備都準(zhǔn)備一套,這樣程序的適配性就可以達(dá)到最好,比如AndroidStudio中新建項(xiàng)目的時(shí)候,AS會(huì)默認(rèn)給我們生成不同規(guī)格的ic_launcher.png作為默認(rèn)的app圖標(biāo),如下:
- mipmap-mdpi/ic_launcher.png (48x48)
- mipmap-hdpi/ic_launcher.png (72x72)
- mipmap-xhdpi/ic_launcher.png (96x96)
- mipmap-xxhdpi/ic_launcher.png (144x144)
括號(hào)中是圖片大小,mipmap看成drawable即可。
上面的4個(gè)資源,通過(guò)計(jì)算可知,完全符合前面的圖片資源的縮放規(guī)則的比例關(guān)系,所以,上面的資源只保留一個(gè)的話同樣可以適配另外3種dpi。
類似的,項(xiàng)目中,UI設(shè)計(jì)師只需要給我們提供一種dpi下的一套圖片即可適配所有dpi,原理就是圖片資源的讀取縮放規(guī)則。那么,我們希望UI給我們哪種dpi的圖片呢?當(dāng)然是高dpi下的圖片了,因?yàn)楦遜pi目錄下的圖片,顯示到低dpi的設(shè)備下,由縮放規(guī)則可以知道圖片會(huì)被縮小。相反的,低dpi圖片顯示到高dpi的設(shè)備上,圖片會(huì)被放大。圖片縮小幾乎沒(méi)有什么副作用,而放大可能會(huì)影響圖片質(zhì)量,所以,使用高dpi目錄下的圖片適配效果更好,比如ic_launcher.png只保留一個(gè)的話,選擇保留 mipmap-xxhdpi/ic_launcher.png (144x144)。當(dāng)然,也不是越高越好,比如當(dāng)前手機(jī)市場(chǎng)有更高密度的手機(jī)xxxhdpi,但那是極少數(shù),為了這極少數(shù)增加軟件包的大小是不劃算的。當(dāng)然如果以后xxxhdpi的手機(jī)普及了,針對(duì)這種級(jí)別的屏幕密度來(lái)設(shè)計(jì)圖片就是首選了。
其他問(wèn)題
- 內(nèi)存方面,使用xxhdpi規(guī)格的圖片適配和使用mdpi規(guī)格的圖片適配,理論上它們對(duì)內(nèi)存的占用最終是相同的。比如上面ic_launcher.png的例子,如果選擇使用mdpi適配,那么原圖大小是48x48,顯示到xxhdpi設(shè)備上之后,圖片被放大到144x144。如果使用xxhdpi適配,那么原圖大小就是144x144,顯示到xxhdpi設(shè)備上不會(huì)縮放,所以兩種適配方法,最終顯示到xxhdpi設(shè)備上的大小是一樣的,占用內(nèi)存也一樣。
- 需要注意的是,使用一套圖片做適配,在某些情況下可能會(huì)有一些問(wèn)題,看:Android 開(kāi)發(fā)中 drawable 有必要放多套分辨率的圖片資源嗎?
- wm命令對(duì)于測(cè)試圖片并不好用,比如480dpi(對(duì)應(yīng)xxhdpi)的手機(jī),如果使用wm命令將手機(jī)dpi改為160(對(duì)應(yīng)mdpi),那么這時(shí)候依然會(huì)優(yōu)先讀取xxhdpi下的圖片資源,我試了兩個(gè)不同廠商的手機(jī)都是這樣。
參考文章:
Android中px dpi dip density densityDpi 的相關(guān)說(shuō)明
DPI、PPI、DP、PX 的詳細(xì)計(jì)算方法及算法來(lái)源是什么?
ANDROID 屏幕適配
Android 適配時(shí)資源限定符的說(shuō)明
Android drawable微技巧,你所不知道的drawable的那些細(xì)節(jié)