Android 屏幕適配經(jīng)驗(yàn)總結(jié)

本文記錄一些適配問(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ì)算的:

  1. 根據(jù)公式一計(jì)算90dp在屏幕1上是多少px
    px = 90 * (320/160) = 180
  2. 根據(jù)1中計(jì)算出的像素,計(jì)算在屏幕3下同樣比例的像素?cái)?shù)
    屏幕3下的px = 480 * (180/720) = 120
  3. 根據(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):

  1. 屏幕適配的時(shí)候一般考慮的兩個(gè)因素:分辨率和dpi。
  2. 利用dpi換算dp和px的值(兩個(gè)公式)
  3. 使用限定符適配不同dpi的屏幕,不同限定符下dp值的計(jì)算
  4. android開(kāi)發(fā)中使用的dpi的值是不固定的,可以修改的
  5. 獲取手機(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)題
  1. 內(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)存也一樣。
  2. 需要注意的是,使用一套圖片做適配,在某些情況下可能會(huì)有一些問(wèn)題,看:Android 開(kāi)發(fā)中 drawable 有必要放多套分辨率的圖片資源嗎?
  3. 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é)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容