Android開發屏幕適配全攻略

Android的屏幕適配一直以來都在折磨着我們這些開發者,本篇文章以Google的官方文檔爲基礎,全面而深入的講解了Android屏幕適配的原因、重要概念、解決方案及最佳實踐,我相信如果你能認真的學習本文,對於Android的屏幕適配,你將有所收穫!

Android屏幕適配出現的原因

在我們學習如何進行屏幕適配之前,我們需要先了解下爲什麼Android需要進行屏幕適配。

由於Android系統的開放性,任何用戶、開發者、OEM廠商、運營商都可以對Android進行定製,修改成他們想要的樣子。

但是這種“碎片化”到底到達什麼程度呢?

在2012年,OpenSignalMaps(以下簡稱OSM)發佈了第一份Android碎片化報告,統計數據表明,

2012年,支持Android的設備共有3997種。
2013年,支持Android的設備共有11868種。
2014年,支持Android的設備共有18796種。
下面這張圖片所顯示的內容足以充分說明當今Android系統碎片化問題的嚴重性,因爲該圖片中的每一個矩形都代表着一種Android設備。
這裏寫圖片描述

而隨着支持Android系統的設備(手機、平板、電視、手錶)的增多,設備碎片化、品牌碎片化、系統碎片化、傳感器碎片化和屏幕碎片化的程度也在不斷地加深。而我們今天要探討的,則是對我們開發影響比較大的——屏幕的碎片化。

下面這張圖是Android屏幕尺寸的示意圖,在這張圖裏面,藍色矩形的大小代表不同尺寸,顏色深淺則代表所佔百分比的大小。

這裏寫圖片描述

而與之相對應的,則是下面這張圖。這張圖顯示了IOS設備所需要進行適配的屏幕尺寸和佔比。

這裏寫圖片描述

當然,這張圖片只是4,4s,5,5c,5s和平板的尺寸,現在還應該加上新推出的iphone6和plus,但是和Android的屏幕碎片化程度相比而言,還是差的太遠。

詳細的統計數據請到這裏查看

現在你應該很清楚爲什麼要對Android的屏幕進行適配了吧?屏幕尺寸這麼多,爲了讓我們開發的程序能夠比較美觀的顯示在不同尺寸、分辨率、像素密度(這些概念我會在下面詳細講解)的設備上,那就要在開發的過程中進行處理,至於如何去進行處理,這就是我們今天的主題了。

但是在開始進入主題之前,我們再來探討一件事情,那就是Android設備的屏幕尺寸,從幾寸的智能手機,到10寸的平板電腦,再到幾十寸的數字電視,我們應該適配哪些設備呢?

其實這個問題不應該這麼考慮,因爲對於具有相同像素密度的設備來說,像素越高,尺寸就越大,所以我們可以換個思路,將問題從單純的尺寸大小轉換到像素大小和像素密度的角度來。

下圖是2014年初,友盟統計的佔比5%以上的6個主流分辨率,可以看出,佔比最高的是480*800,320*480的設備竟然也佔據了很大比例,但是和半年前的數據相比較,中低分辨率(320*480、480*800)的比例在減少,而中高分辨率的比例則在不斷地增加。雖然每個分辨率所佔的比例在變化,但是總的趨勢沒變,還是這六種,只是分辨率在不斷地提高。

這裏寫圖片描述

所以說,我們只要儘量適配這幾種分辨率,就可以在大部分的手機上正常運行了。

當然了,這只是手機的適配,對於平板設備(電視也可以看做是平板),我們還需要一些其他的處理。

好了,到目前爲止,我們已經弄清楚了Android開發爲什麼要進行適配,以及我們應該適配哪些對象,接下來,終於進入我們的正題了!

首先,我們先要學習幾個重要的概念。

重要概念

什麼是屏幕尺寸、屏幕分辨率、屏幕像素密度?
什麼是dp、dip、dpi、sp、px?他們之間的關係是什麼?
什麼是mdpi、hdpi、xdpi、xxdpi?如何計算和區分?

在下面的內容中我們將介紹這些概念。

屏幕尺寸

屏幕尺寸指屏幕的對角線的長度,單位是英寸,1英寸=2.54釐米

比如常見的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等

屏幕分辨率

屏幕分辨率是指在橫縱向上的像素點數,單位是px,1px=1個像素點。一般以縱向像素*橫向像素,如1960*1080。

屏幕像素密度

屏幕像素密度是指每英寸上的像素點數,單位是dpi,即“dot per inch”的縮寫。屏幕像素密度與屏幕尺寸和屏幕分辨率有關,在單一變化條件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。

dp、dip、dpi、sp、px

px我們應該是比較熟悉的,前面的分辨率就是用的像素爲單位,大多數情況下,比如UI設計、Android原生API都會以px作爲統一的計量單位,像是獲取屏幕寬高等。

dip和dp是一個意思,都是Density Independent Pixels的縮寫,即密度無關像素,上面我們說過,dpi是屏幕像素密度,假如一英寸裏面有160個像素,這個屏幕的像素密度就是160dpi,那麼在這種情況下,dp和px如何換算呢?在Android中,規定以160dpi爲基準,1dip=1px,如果密度是320dpi,則1dip=2px,以此類推。

假如同樣都是畫一條320px的線,在480*800分辨率手機上顯示爲2/3屏幕寬度,在320*480的手機上則佔滿了全屏,如果使用dp爲單位,在這兩種分辨率下,160dp都顯示爲屏幕一般的長度。這也是爲什麼在Android開發中,寫佈局的時候要儘量使用dp而不是px的原因。

而sp,即scale-independent pixels,與dp類似,但是可以根據文字大小首選項進行放縮,是設置字體大小的御用單位。

mdpi、hdpi、xdpi、xxdpi

其實之前還有個ldpi,但是隨着移動設備配置的不斷升級,這個像素密度的設備已經很罕見了,所在現在適配時不需考慮。

mdpi、hdpi、xdpi、xxdpi用來修飾Android中的drawable文件夾及values文件夾,用來區分不同像素密度下的圖片和dimen值。

那麼如何區分呢?Google官方指定按照下列標準進行區分:

名稱 像素密度範圍
mdpi 120dpi~160dpi
hdpi 160dpi~240dpi
xhdpi 240dpi~320dpi
xxhdpi 320dpi~480dpi
xxxhdpi 480dpi~640dpi
在進行開發的時候,我們需要把合適大小的圖片放在合適的文件夾裏面。下面以圖標設計爲例進行介紹。
這裏寫圖片描述

在設計圖標時,對於五種主流的像素密度(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)應按照 2:3:4:6:8 的比例進行縮放。例如,一個啓動圖標的尺寸爲48×48 dp,這表示在 MDPI 的屏幕上其實際尺寸應爲 48×48 px,在 HDPI 的屏幕上其實際大小是 MDPI 的 1.5 倍 (72×72 px),在 XDPI 的屏幕上其實際大小是 MDPI 的 2 倍 (96×96 px),依此類推。

雖然 Android 也支持低像素密度 (LDPI) 的屏幕,但無需爲此費神,系統會自動將 HDPI 尺寸的圖標縮小到 1/2 進行匹配。

下圖爲圖標的各個屏幕密度的對應尺寸

屏幕密度 圖標尺寸
mdpi 48x48px
hdpi 72x72px
xhdpi 96x96px
xxhdpi 144x144px
xxxhdpi 192x192px

解決方案

支持各種屏幕尺寸

使用wrap_content、match_parent、weight

要確保佈局的靈活性並適應各種尺寸的屏幕,應使用 “wrap_content” 和 “match_parent” 控制某些視圖組件的寬度和高度。

使用 “wrap_content”,系統就會將視圖的寬度或高度設置成所需的最小尺寸以適應視圖中的內容,而 “match_parent”(在低於 API 級別 8 的級別中稱爲 “fill_parent”)則會展開組件以匹配其父視圖的尺寸。

如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬編碼的尺寸,視圖就會相應地僅使用自身所需的空間或展開以填滿可用空間。此方法可讓佈局正確適應各種屏幕尺寸和屏幕方向。

weight是線性佈局的一個獨特的屬性,我們可以使用這個屬性來按照比例對界面進行分配,完成一些特殊的需求。

但是,我們對於這個屬性的計算應該如何理解呢?

首先看下面的例子,我們在佈局中這樣設置我們的界面

這裏寫圖片描述

我們在佈局裏面設置爲線性佈局,橫向排列,然後放置兩個寬度爲0dp的按鈕,分別設置weight爲1和2,在效果圖中,我們可以看到兩個按鈕按照1:2的寬度比例正常排列了,這也是我們經常使用到的場景,這是時候很好理解,Button1的寬度就是1/(1+2) = 1/3,Button2的寬度則是2/(1+2) = 2/3,我們可以很清楚的明白這種情景下的佔比如何計算。

但是假如我們的寬度不是0dp(wrap_content和0dp的效果相同),則是match_parent呢?

下面是設置爲match_parent的效果

這裏寫圖片描述

我們可以看到,在這種情況下,佔比和上面正好相反,這是怎麼回事呢?說到這裏,我們就不得不提一下weight的計算方法了。

android:layout_weight的真實含義是:如果View設置了該屬性並且有效,那麼該 View的寬度等於原有寬度(android:layout_width)加上剩餘空間的佔比。

從這個角度我們來解釋一下上面的現象。在上面的代碼中,我們設置每個Button的寬度都是match_parent,假設屏幕寬度爲L,那麼每個Button的寬度也應該都爲L,剩餘寬度就等於L-(L+L)= -L。

Button1的weight=1,剩餘寬度佔比爲1/(1+2)= 1/3,所以最終寬度爲L+1/3*(-L)=2/3L,Button2的計算類似,最終寬度爲L+2/3(-L)=1/3L。

這是在水平方向上的,那麼在垂直方向上也是這樣嗎?

下面是測試代碼和效果

如果是垂直方向,那麼我們應該改變的是layout_height的屬性,下面是0dp的顯示效果

這裏寫圖片描述

下面是match_parent的顯示效果,結論和水平是完全一樣的

這裏寫圖片描述

雖然說我們演示了match_parent的顯示效果,並說明了原因,但是在真正用的時候,我們都是設置某一個屬性爲0dp,然後按照權重計算所佔百分比。

使用相對佈局,禁用絕對佈局

在開發中,我們大部分時候使用的都是線性佈局、相對佈局和幀佈局,絕對佈局由於適配性極差,所以極少使用。

由於各種佈局的特點不一樣,所以不能說哪個佈局好用,到底應該使用什麼佈局只能根據實際需求來確定。我們可以使用 LinearLayout 的嵌套實例並結合 “wrap_content” 和 “match_parent”,以便構建相當複雜的佈局。不過,我們無法通過 LinearLayout 精確控制子視圖的特殊關係;系統會將 LinearLayout 中的視圖直接並排列出。

如果我們需要將子視圖排列出各種效果而不是一條直線,通常更合適的解決方法是使用 RelativeLayout,這樣就可以根據各組件之間的特殊關係指定佈局了。例如,我們可以將某個子視圖對齊到屏幕左側,同時將另一個視圖對齊到屏幕右側。

使用佈局別名

最小寬度限定符僅適用於 Android 3.2 及更高版本。因此,如果我們仍需使用與較低版本兼容的概括尺寸範圍(小、正常、大和特大)。例如,如果要將用戶界面設計成在手機上顯示單面板,但在 7 英寸平板電腦、電視和其他較大的設備上顯示多面板,那麼我們就需要提供以下文件:

  • res/layout/main.xml: 單面板佈局
  • res/layout-large: 多面板佈局
  • res/layout-sw600dp: 多面板佈局

使用自動拉伸位圖

支持各種屏幕尺寸通常意味着您的圖片資源還必須能適應各種尺寸。例如,無論要應用到什麼形狀的按鈕上,按鈕背景都必須能適應。

如果在可以更改尺寸的組件上使用了簡單的圖片,您很快就會發現顯示效果多少有些不太理想,因爲系統會在運行時平均地拉伸或收縮您的圖片。解決方法爲使用自動拉伸位圖,這是一種格式特殊的 PNG 文件,其中會指明可以拉伸以及不可以拉伸的區域。

.9的製作,實際上就是在原圖片上添加1px的邊界,然後按照我們的需求,把對應的位置設置成黑色線,系統就會根據我們的實際需求進行拉伸。

下圖是對.9圖的四邊的含義的解釋,左上邊代表拉伸區域,右下邊代表padding box,就是間隔區域,在下面,我們給出一個例子,方便大家理解。

這裏寫圖片描述

先看下面兩張圖,我們理解一下這四條線的含義。

這裏寫圖片描述

上圖和下圖的區別,就在於右下邊的黑線不一樣,具體的效果的區別,看右邊的效果圖。上圖效果圖中深藍色的區域,代表內容區域,我們可以看到是在正中央的,這是因爲我們在右下邊的是兩個點,這兩個點距離上下左右四個方向的距離就是padding的距離,所以深藍色內容區域在圖片正中央,我們再看下圖,由於右下邊的黑線是圖片長度,所以就沒有padding,從效果圖上的表現就是深藍色區域和圖片一樣大,因此,我們可以利用右下邊來控制內容與背景圖邊緣的padding。

這裏寫圖片描述
如果你還不明白,那麼我們看下面的效果圖,我們分別以圖一和圖二作爲背景圖,下面是效果圖。

我們可以看到,使用wrap_content屬性設置長寬,圖一比圖二的效果大一圈,這是爲什麼呢?還記得我上面說的padding嗎?

這裏寫圖片描述

這就是padding的效果提現,怎麼證明呢?我們再看下面一張圖,給圖一添加padding=0,這樣背景圖設置的padding效果就沒了,是不是兩個一樣大了?

這裏寫圖片描述

ok,我想你應該明白右下邊的黑線的含義了,下面我們再看一下左上邊的效果。

下面我們只設置了左上邊線,效果圖如下

這裏寫圖片描述

上面的線沒有包住圖標,下面的線正好包住了圖標,從右邊的效果圖應該可以看出差別,黑線所在的區域就是拉伸區域,上圖黑線所在的全是純色,所以圖標不變形,下面的拉伸區域包裹了圖標,所以在拉伸的時候就會對圖標進行拉伸,但是這樣就會導致圖標變形。注意到下面紅線區域了嘛?這是系統提示我們的,因爲這樣拉伸,不符合要求,所以會提示一下。

這裏寫圖片描述

支持各種屏幕密度

使用非密度制約像素

由於各種屏幕的像素密度都有所不同,因此相同數量的像素在不同設備上的實際大小也有所差異,這樣使用像素定義佈局尺寸就會產生問題。因此,請務必使用 dp 或 sp 單位指定尺寸。dp 是一種非密度制約像素,其尺寸與 160 dpi 像素的實際尺寸相同。sp 也是一種基本單位,但它可根據用戶的偏好文字大小進行調整(即尺度獨立性像素),因此我們應將該測量單位用於定義文字大小。

例如,請使用 dp(而非 px)指定兩個視圖間的間距,使用 sp 指定文字大小.

提供備用位圖

由於 Android 可在具有各種屏幕密度的設備上運行,因此我們提供的位圖資源應始終可以滿足各類普遍密度範圍的要求:低密度、中等密度、高密度以及超高密度。這將有助於我們的圖片在所有屏幕密度上都能得到出色的質量和效果。

要生成這些圖片,我們應先提取矢量格式的原始資源,然後根據以下尺寸範圍針對各密度生成相應的圖片。

xhdpi:2.0
hdpi:1.5
mdpi:1.0(最低要求)
ldpi:0.75
也就是說,如果我們爲 xhdpi 設備生成了 200×200 px尺寸的圖片,就應該使用同一資源爲 hdpi、mdpi 和 ldpi 設備分別生成 150×150、100×100 和 75×75 尺寸的圖片。

然後,將生成的圖片文件放在 res/ 下的相應子目錄中(mdpi、hdpi、xhdpi、xxhdpi),系統就會根據運行您應用的設備的屏幕密度自動選擇合適的圖片。

這樣一來,只要我們引用 @drawable/id,系統都能根據相應屏幕的 dpi 選取合適的位圖。

還記得我們上面提到的圖標設計尺寸嗎?和這個其實是一個意思。

但是還有個問題需要注意下,如果是.9圖或者是不需要多個分辨率的圖片,就放在drawable文件夾即可,對應分辨率的圖片要正確的放在合適的文件夾,否則會造成圖片拉伸等問題。

最佳實踐

關於高清設計圖尺寸

Google官方給出的高清設計圖尺寸有兩種方案,一種是以mdpi設計,然後對應放大得到更高分辨率的圖片,另外一種則是以高分辨率作爲設計大小,然後按照倍數對應縮小到小分辨率的圖片。

根據經驗,我更推薦第二種方法,因爲小分辨率在生成高分辨率圖片的時候,會出現像素丟失,我不知道是不是有方法可以阻止這種情況發生。

而分辨率可以以1280*720或者是1960*1080作爲主要分辨率進行設計。

ImageView的ScaleType屬性

設置不同的ScaleType會得到不同的顯示效果,一般情況下,設置爲centerCrop能獲得較好的適配效果。

動態設置

有一些情況下,我們需要動態的設置控件大小或者是位置,比如說popwindow的顯示位置和偏移量等,這個時候我們可以動態的獲取當前的屏幕屬性,然後設置合適的數值

發佈了38 篇原創文章 · 獲贊 152 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章