Android 屏幕適配經驗總結

developerzjyIP屬地: 山西
字數 3,797

本文記錄一些適配問題的研究,基礎概念不做過多介紹。

Android在做屏幕適配的時候一般考慮兩個因素:分辨率和dpi。分辨率是屏幕在橫向、縱向上的像素點數總和,一般用“寬x高”的形式表示,例如:1080x1920。dpi是dots per ich的縮寫,表示每英寸的像素點數,例如160dpi指手機水平或垂直方向上每英寸距離有160個像素點。

一、dp和px

dp和px都是編寫布局時的單位,它們之間可以通過dpi來換算,換算公式如下:

  • 公式一:px值 = dp值 * (dpi/160)

常見的還有一個density的概念,表示基準比例,density = (dpi/160),所以還有如下公式:

  • 公式二:px值 = dp值 * density

注:公式中的160是android中規定的基準,即:在160dpi的屏幕上,1dp=1px


通過上面的公式理解dp的概念

dp和dip的含義相同,都是density-independent pixel的縮寫,表示密度無關像素,可以保證在不同像素密度的屏幕上顯示相同的效果。舉個例子:

兩種常見的屏幕參數如下:

  • 屏幕1:分辨率=720x1280,dpi=320
  • 屏幕2:分辨率=1080x1920,dpi=480

如果要實現一個view的寬度占屏幕1寬度的一半,由分辨率可知需要view的寬度是360px,根據公式一可以知道使用180dp可以實現相同的效果(360 = dp值 * (320/160) -> dp值=180)。
接下來再根據公式一,看看180dp在屏幕2上的顯示效果,px值 = 180 * (480/160) = 540,正好是屏幕2寬度的一半。所以,使用180dp就可以同時在兩個屏幕上顯示相同的效果了。


二、實際工作中如何使用dp適配

實際工作中,我們一般會使用限定符建立多個資源文件來做適配,一般會適配mdpi,hdpi,xhdpi,xxhdpi等,比如res下的values資源目錄如下:

  • res/values-mdpi
  • res/values-hdpi
  • res/values-xhdpi

然后在不同的目錄下定義不同的dp值。上面180dp的例子說明了dp值已經保證了在不同像素密度的屏幕上顯示相同的效果,那么為什么還要針對不同的屏幕定義不同的dp值?不同的dp值如何確定的?下面結合例子解釋這兩個問題。

1. 為什么要針對不同的屏幕定義不同的dp值?

這是因為android機型屏幕尺寸太碎片化了,一個dp值并不能滿足所有機型,比如常見的還有如下這種:

  • 屏幕3:分辨率=480x800,dpi=240

通過前面的公式計算可以知道,前面例子的180dp在這個屏幕上并不是寬度的一半,160dp才是一半。由此可見,一個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的對應關系后面給出

2. 不同限定符下的dp值如何確定的?

比如現在項目中有了一套屏幕1(xhdpi)的資源res/values-xhdpi/dimens.xml,其中有一個資源值是90dp,如果要求再適配一下屏幕3(hdpi),那么res/values-hdpi/dimens.xml中與90dp同名的資源應該是多少dp呢?通過前面的介紹,這個值是很好計算的:

  1. 根據公式一計算90dp在屏幕1上是多少px
    px = 90 * (320/160) = 180
  2. 根據1中計算出的像素,計算在屏幕3下同樣比例的像素數
    屏幕3下的px = 480 * (180/720) = 120
  3. 根據上一步的結果和公式一計算出在屏幕3的dp值
    dp = 80

如果把計算過程中的90dp改成任意值r,那么最終,屏幕1的每一個資源值 r 乘以 8/9 就是在屏幕3下的dp值,對應的資源文件就是:

  • res/values-xhdpi/dimens.xml 中定義資源 r dp (適配屏幕1)
  • res/values-hdpi/dimens.xml 中定義資源 r*8/9 dp (適配屏幕3)

這里計算出來的8/9是使用橫向分辨率和dpi計算出來的,如果是縱向的話并不是這個比例,所以適配的時候需要區分橫豎向不同的比例轉換。比如寬、橫向邊距等使用橫向的比例,高、豎直邊距等使用豎向比例。

需要注意的是這里是按比例計算出來的,最終的值還得根據實際情況和顯示效果而定。因為不一定所有界面的設計都是按比例適配的;還有就是有些帶虛擬按鍵的1080x1920的手機,真實的豎直像素數應該是1920減去虛擬按鍵的高度;還有一點,在android開發中所說的dpi的值并不是物理定義的,而是系統文件寫進去的,所以這個值是可以被修改的。另一層意思是,dpi并不是由分辨率和屏幕尺寸計算出來的固定值。比如當前常見的一種機型分辨率是1080x1920,尺寸是5.15英寸,dpi是480。按照dpi的定義,使用這個分辨率和尺寸計算dpi的話,結果并不是480。所以對分辨率和尺寸都相同的手機,dpi值不一定相同,完全看手機廠商如何定義。不過,為了使顯示效果最好,一般比較標準的手機dpi和分辨率都和下表一致:

ldpi mdpi hdpi xhdpi xxhdpi
分辨率 240x320 320x480 480x800 720x1280 1080x1920
dpi 120 160 240 320 480

所以,不同限定符下的dp值,使用上面1,2,3步的計算方法能滿足大部分主流機型,但不一定能完美適配所有機型。

限定符與dpi的具體對應關系如下:

限定符 ldpi mdpi hdpi xhdpi xxhdpi
dpi dpi<=120 120<dpi<=160 160<dpi<=240 240<dpi<=320 320<dpi<=480

限定符的知識不僅如此,這里不做過多介紹。


三、DisplayMetrics類和wm命令

代碼中,可以通過DisplayMetrics類來獲取屏幕的一些信息,有三種方式可以獲取DisplayMetrics的實例:

//方法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();

可以通過DisplayMetrics獲取的如下信息:

//等號后面的數值是某個手機的參數,分辨率=1080x1920,dpi=480,尺寸5.15英寸
metrics.heightPixels = 1920 
metrics.widthPixels = 1080
metrics.densityDpi = 480   //dpi值
metrics.density = 3.0   //基準比例,dpi/160
metrics.xdpi = 422.03    //x方向準確的物理像素密度
metrics.ydpi = 424.069  //y方向準確的物理像素密度
metrics.scaledDensity = 3.0

這里的xdpi和ydpi和densityDpi的值不同,再次說明android開發中所說的dpi的值不是由硬件決定的
利用DisplayMetrics類,可以實現一些常用的工具方法,比如dp轉px,px轉dp等。或者在一些不方便使用資源適配的情況下,可以通過這個類判斷不同的dpi來通過java代碼來適配。

wm命令

wm命令是高通平臺下對手機分辨率、像素密度等進行設置的命令。用法很簡單:
首先使用adb shell命令進入手機的shell中,然后就可以使用wm命令了,常用的命令如下:

wm size  //輸出手機的分辨率信息

如果要修改手機分辨率,上面的命令加上分辨率參數即可,比如把手機分辨率修改為800x1280,命令如下

wm size 800x1280 

如果要把上面命令修改的分辨率還原為手機原始的分辨率,使用下面的命令

wm size reset

上面是分辨率相關的命令,dpi的命令和上面的類似,如下:

wm density 
wm density 240 
wm density reset

掌握wm命令后,就可以方便的查看手機分辨率和dpi了,也可以使用同一部手機測試多種分辨率的適配。

注:使用wm命令修改分辨率或dpi后,在某些手機中,再使用reset命令還原后,手機某些內容可能會顯示不正常(比如狀態欄、輸入法等),重啟手機即可解決。


四、小結

通過前面幾節,應該知道以下幾點:

  1. 屏幕適配的時候一般考慮的兩個因素:分辨率和dpi。
  2. 利用dpi換算dp和px的值(兩個公式)
  3. 使用限定符適配不同dpi的屏幕,不同限定符下dp值的計算
  4. android開發中使用的dpi的值是不固定的,可以修改的
  5. 獲取手機屏幕信息的DisplayMetrics類和修改屏幕參數的wm命令


五、drawable下的圖片適配

這里介紹drawable下的圖片資源的讀取和縮放的規則,也可以這樣說,如何使用一套圖片資源適配多種dpi。郭神的一篇博客 Android drawable微技巧,你所不知道的drawable的那些細節講的很清晰,這里取其精華做一個總結。

圖片資源的讀取規則

當我們使用資源id來去引用一張圖片時,Android會使用一些規則來幫我們匹配最適合的圖片。什么叫最適合的圖片?比如我的手機屏幕密度是xxhdpi,那么drawable-xxhdpi文件夾下的圖片就是最適合的圖片。因此,當我引用一張圖片時,如果drawable-xxhdpi文件夾下有這張圖就會優先被使用,在這種情況下,圖片是不會被縮放的。但是,如果drawable-xxhdpi文件夾下沒有這張圖時, 系統就會自動去其它文件夾下找這張圖了,優先會去更高密度的文件夾下找這張圖片,也就是drawable-xxxhdpi文件夾,然后發現這里也沒有android_logo這張圖,接下來會嘗試再找更高密度的文件夾,發現沒有更高密度的了,這個時候會去drawable-nodpi文件夾找這張圖,發現也沒有,那么就會去更低密度的文件夾下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi。

android項目資源下的drawable(不帶任何限定符)目錄默認就是drawable-mdpi的意思

作者:郭霖
鏈接:http://blog.csdn.net/guolin_blog/article/details/50727753

圖片資源的縮放規則

根據上面的讀取規則,如果最終沒有讀取到最適合的圖片,而是讀取了低密度或高密度的圖片,那么系統會自動做一個縮放操作(低密度的圖片放大,高密度的圖片縮小)。具體的縮放比例就是dpi的比例:

ldpi mdpi hdpi xhdpi xxhdpi
dpi 120 160 240 320 480
比例 3 4 6 8 12

比如當前手機dpi是160(對應mdpi)
如果讀取了drawable-mdpi下的圖片大小是48x48,那么顯示到屏幕上的圖片大小是48x48(最適合的圖片不縮放)
如果讀取了drawable-ldpi下的圖片大小是48x48,那么顯示到屏幕上的圖片大小是36x36(48/36 = 4/3)
如果讀取的是drawable-xhdpi下的圖大小是48x48,那么顯示到屏幕上的圖片大小是96x96。

一套圖片資源適配多種dpi

根據Android的開發建議,我們在準備圖片資源時盡量應該給每種密度的設備都準備一套,這樣程序的適配性就可以達到最好,比如AndroidStudio中新建項目的時候,AS會默認給我們生成不同規格的ic_launcher.png作為默認的app圖標,如下:

  • mipmap-mdpi/ic_launcher.png (48x48)
  • mipmap-hdpi/ic_launcher.png (72x72)
  • mipmap-xhdpi/ic_launcher.png (96x96)
  • mipmap-xxhdpi/ic_launcher.png (144x144)

括號中是圖片大小,mipmap看成drawable即可。

上面的4個資源,通過計算可知,完全符合前面的圖片資源的縮放規則的比例關系,所以,上面的資源只保留一個的話同樣可以適配另外3種dpi。
類似的,項目中,UI設計師只需要給我們提供一種dpi下的一套圖片即可適配所有dpi,原理就是圖片資源的讀取縮放規則。那么,我們希望UI給我們哪種dpi的圖片呢?當然是高dpi下的圖片了,因為高dpi目錄下的圖片,顯示到低dpi的設備下,由縮放規則可以知道圖片會被縮小。相反的,低dpi圖片顯示到高dpi的設備上,圖片會被放大。圖片縮小幾乎沒有什么副作用,而放大可能會影響圖片質量,所以,使用高dpi目錄下的圖片適配效果更好,比如ic_launcher.png只保留一個的話,選擇保留 mipmap-xxhdpi/ic_launcher.png (144x144)。當然,也不是越高越好,比如當前手機市場有更高密度的手機xxxhdpi,但那是極少數,為了這極少數增加軟件包的大小是不劃算的。當然如果以后xxxhdpi的手機普及了,針對這種級別的屏幕密度來設計圖片就是首選了。

其他問題
  1. 內存方面,使用xxhdpi規格的圖片適配和使用mdpi規格的圖片適配,理論上它們對內存的占用最終是相同的。比如上面ic_launcher.png的例子,如果選擇使用mdpi適配,那么原圖大小是48x48,顯示到xxhdpi設備上之后,圖片被放大到144x144。如果使用xxhdpi適配,那么原圖大小就是144x144,顯示到xxhdpi設備上不會縮放,所以兩種適配方法,最終顯示到xxhdpi設備上的大小是一樣的,占用內存也一樣。
  2. 需要注意的是,使用一套圖片做適配,在某些情況下可能會有一些問題,看:Android 開發中 drawable 有必要放多套分辨率的圖片資源嗎?
  3. wm命令對于測試圖片并不好用,比如480dpi(對應xxhdpi)的手機,如果使用wm命令將手機dpi改為160(對應mdpi),那么這時候依然會優先讀取xxhdpi下的圖片資源,我試了兩個不同廠商的手機都是這樣。



參考文章:
Android中px dpi dip density densityDpi 的相關說明
DPI、PPI、DP、PX 的詳細計算方法及算法來源是什么?
ANDROID 屏幕適配
Android 適配時資源限定符的說明
Android drawable微技巧,你所不知道的drawable的那些細節

最后編輯于
©著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
24人點贊
developerzjy原昵稱陰月有晴_<br>
總資產124共写了3.7W字获得184个赞共126个粉丝
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容