屏幕適配最佳實踐

基本概念

屏幕尺寸

手機屏幕對角線的物理尺寸。單位英寸(inch),一英寸大約 2.54cm。常見的手機屏幕尺寸有 4.7 英寸、5.0英寸、5.5 英寸、6.0 英寸等。android

屏幕尺寸示例圖

像素(px)

像素(英語:Picture Element),Pixel 的縮寫。液晶屏顯示圖像,放大來看是一個個小點組成的,這些小點就是像素點。git

像素點

分辨率

分辨率(英語:Image resolution),又稱解析度、解像度,能夠從顯示分辨率與圖像分辨率兩個方向來分類。github

在 Android 設備中指的是顯示分辨率,即屏幕分辨率。也就是屏幕所能顯示的像素有多少,好比:手機分辨率 1920 x 1080。算法

在圖片中指是圖像分辨率,則是單位英寸中所包含的像素點數。好比:圖片分辨率 600 x 400。json

不一樣分辨率的圖像的差異

每英寸點數(DPI)

每英寸點數(英語:Dots Per Inch,縮寫:DPI)是一個量度單位,用於點陣數碼影像,指每一英寸長度中,取樣、可顯示或輸出點的數目。服務器

通常用於打印機、鼠標等設備分辨率的度量單位。網絡

好比:打印機輸出可達 600 DPI 的分辨率,表示打印機能夠在每一平方英寸的面積中能夠輸出 600 x 600 = 360000 個輸出點。app

鼠標的 DPI 參數,指的是鼠標在桌面上移動一英寸的距離的同時,鼠標光標可以在屏幕上移動多少「點」。ide

每英寸像素(PPI)

每英寸像素(英語:Pixels Per Inch,縮寫:PPI),又被稱爲像素密度。佈局

通常用來計量計算機顯示器,電視機和手持電子設備屏幕的精細程度。一般狀況下,每英寸像素值越高,屏幕能顯示的圖像也越精細。

分辨率、DPI、PPI 之間的關係

當咱們把相同分辨率的圖片,放在具備相同像素顯示的屏幕上顯示時。每個像素,屏幕上對應一個點顯示,此時 DPI = PPI。 好比:咱們把分辨率爲 m x n 的圖片,放在最大支持 m x n 像素的屏幕上時,DPI = PPI。

可是,實際上,咱們所須要顯示圖片的分辨率,跟屏幕參數匹配的機率仍是很小的。咱們來分析下,不匹配時的狀況:

當咱們把 1280 x 720 的圖片,放在 800 x 480 的 4 英寸的屏幕與 1920 x 1080 的 5.5 英寸的屏幕上顯示時的結果爲:

分辨率、DPI 與 PPI

PPI 是屏幕的顯示性能,因此與顯示的圖片沒有關係,是固定的值,可是 DPI 與顯示的圖片是有關係的。

  • 分辨率爲 1280 x 720 的圖片放在 800 x 480 的 4 英寸的屏幕上顯示

雖然圖片一行有 720 個像素,可是屏幕一行最多隻能顯示 480 個點,因此 DPI = PPI = 233,已經達到屏幕的最大顯示能力。

  • 分辨率爲 1280 x 720 的圖片放在 1920 x 1080 的 5.5 英寸的屏幕上顯示

雖然屏幕一行有 1080 個點,可是圖片一行最多隻能顯示 720 個像素,因此 DPI = 267 < PPI,並未達到屏幕的最大顯示能力,未達到屏幕的最佳顯示效果。

經過上面分析能夠獲得:

  • 分辨率:只能用來描述圖片的像素信息,不能描述圖片清晰度。
  • PPI:只能用來描述屏幕的顯示密度,也不能描述圖片的清晰度
  • DPI:才能用來描述圖片顯示的清晰度,表示圖片在屏幕上的顯示效果。

一句話總結下就是 DPI 表示印刷品點的密度,PPI 表示顯示設備點的密度。

PPI(左)和 DPI(右)的比較

PPI 計算公式

因爲顯示器的 DPI 是固定的,不像打印機那樣能夠調整,因此針對顯示器的設計時 DPI = PPI。

計算顯示器的每英寸像素值,須要肯定屏幕的尺寸和分辨率。

PPI 計算公式

其中:

  • W(Width):爲屏幕橫向分辨率。
  • H(Height):爲屏幕縱向分辨率。
  • inch:爲屏幕對角線的長度(單位爲英寸)。

基於 PPI 屏幕分級

根據屏幕每英寸像素值的不一樣,Android 中將平板電腦和手機的屏幕分爲下面幾類:

密度名稱 每英寸像素值 圖標尺寸
低密度(LDPI) ~120dpi 36 x 36 px
中密度(MDPI) 120dpi ~ 160dpi 48 x 48 px
高密度(HDPI) 160dpi ~ 240dpi 72 x 72 px
超高密度(XHDPI) 240dpi ~ 320dpi 96 x 96 px
超超高密度(XXHDPI) 320dpi ~ 480dpi 144 x 144 px
超超超高密度(XXXHDPI) 480dpi ~ 640dpi 192 x 192 px

密度無關像素(DP)

DP 或者 DIP,是 Android 開發用的單位。1dp 表示在屏幕點密度爲 160ppi 時 1px 長度。

因爲 Android 設備屏幕衆多,不可能爲每一個屏幕單獨開發,因此用下面公式計算在不一樣屏幕上的像素數。

PX 計算公式

同一圖標在分辨率不一樣的設備上顯示時,會出現以下效果:

同一圖標不一樣設備顯示效果

能夠看到上圖第 1 和第 2 個設備兩個屏幕尺寸相同,因爲它們分辨率的不一樣,同一個圖標在兩個設備上顯示的尺寸相差很大。

那麼,圖片顯示大小是由什麼決定的呢,屏幕尺寸嗎?上圖第 1 和第 2 個設備屏幕都是 4.3 英寸。

仍是由於分辨率呢?上圖第 2 和第 3 個設備屏幕都是 720 x 1280 的分辨率。

最後咱們找到了像素密度(density),也就是像素數和屏幕尺寸的比值。density 是每單位長度容納的像素數量,通常用像素/英寸,也就是 Pixel per inch(PPI)。

對比上圖能夠知道,PPI 越低圖片顯示的越大,PPI 越高圖片顯示的越小。

要讓不一樣屏幕顯示圖片的大小相同,就須要對圖片進行縮放,給高 PPI 屏提供更大的圖片。

兼容高 PPI 屏幕

高 PPI 屏幕須要更大的圖片才能獲得一樣的顯示效果,反之亦然。

PPI 和圖片 px 的關係,以下:

PPI 與 PX 計算公式

選定一個 PPI 值做爲基礎繪製圖片,用 PPI 的比值計算出圖片縮放比例,就能夠適配各類屏幕。

公式換算 1

Android 選定的這個基礎值就是 160ppi

公式換算 2

咱們已經解決了圖片放大縮小的問題,還須要一個單位用來描述長度。

由於 px 不固定,inch 不方便,因此 Android 創造了一個新的單位 dp,中文名密度無關像素。而且規定在 160ppi 的屏幕上 1dp = 1px。

設計師只須要針對 160ppi 的顯示屏設計並製圖,安卓會根據當前手機屏幕的 PPI 值來放大縮小圖片,在不一樣的屏幕上獲得相近的顯示效果。

公式換算 3

獨立比例像素(SP)

Android 設備的文字單位是 SP,簡單理解和 DP 是相同的。另外 SP 會隨着系統的字體大小改變,而 DP 則不會。

Google 推薦咱們使用 12sp 以上的大小,一般可使用 12sp、14sp、18sp、22sp。最好不要使用「奇數」和「小數」。

適配不一樣屏幕尺寸

佈局適配

wrap_content

設置視圖的寬高爲 wrap_content 時,視圖大小會根據內容自動增長。

match_parent

設置視圖寬高位 match_parent 時,視圖大小始終與父級同樣大。

LinerLayout

LinerLayout 是一個視圖組,用於使全部子視圖在單個方向(垂直或水平)保持對齊。 可使用 android:orientation 屬性指定佈局方向。

  • weight

使用線性佈局時,將某一個或者多個子視圖的寬或者高設置爲 0dp,設置 weight 值爲 1

weight 值爲 1 的子視圖將會充滿父視圖剩餘的空間,若是設置多個子視圖的 weight 都爲 1,那麼這些子視圖將平分並充滿父視圖剩餘的空間。

RelativeLayout

RelativeLayout 是一個視圖組,顯示相對位置的子視圖。使用 RelativeLayout 能夠將子視圖定位在任意位置。

限定符適配

尺寸限定符

  • small:提供給小屏幕設備的資源
  • normal:提供給中等屏幕設備的資源
  • large:提供給大屏幕設備的資源
  • xlarge:提供給超大屏幕的資源

使用方式以下:

|- layout // 無限定符,默認佈局
	|- main.xml
|- layout-small // 小屏幕設備
	|- main.xml
|- layout-normal // 中等屏幕設備
	|- main.xml
|- layout-large // 大屏幕設備
	|- main.xml
|- layout-xlarge // 超大屏幕設備
	|- main.xml
複製代碼

最小寬度限定符

最小寬度限定符就是設置設備屏幕大於或等於最小寬度時加載的視圖。

例如,7 英寸平板電腦最小寬度爲 600dp,若是但願的 UI 在這些屏幕上顯示兩列,但在較小屏幕上顯示單列,就可使用最小寬度限定符。

|- layout // 無限定符,默認佈局顯示單列
	|- main.xml
|- layout-sw600dp // 設備寬度爲 600dp 以上時顯示兩列
	|- main.xml
複製代碼

佈局別名

若是適配的屏幕設備比較多,爲了方便視圖文件的管理,咱們可使用佈局別名。

|- layout
	|- main.xml // 單列布局
	|- main_twopanes.xml // 雙列布局
|- values-large
	|- layout.xml
|- values-sw600dp
	|- layout.xml
複製代碼
  • res/values-large/layout.xml
<resources>
    <item name="main" type="layout">@layout/main_twopanes</item>
</resources>
複製代碼
  • res/values-sw600p/layout.xml
<resources>
    <item name="main" type="layout">@layout/main_twopanes</item>
</resources>
複製代碼

後兩個文件內容徹底相同,但它們實際上並未定義佈局, 而只是將 main 設置爲 main_twopanes 的別名。因爲這些文件具備 largesw600dp 選擇器,所以它們適用於任何 Android 版本的平板電腦和電視(低於 3.2 版本的平板電腦和電視匹配 large,高於 3.2 版本者將匹配 sw600dp)。

屏幕方向限定符

  • land:提供給橫屏設備的資源
  • port:提供給豎屏設備的資源
|- layout
	|- main.xml // 單列布局
	|- main_twopanes.xml // 雙列布局
|- values-large-land // 大屏幕橫向
	|- layout.xml
|- values-sw600dp-port // 最小寬度 600dp 橫向
	|- layout.xml
複製代碼

圖片適配

位圖

  • Logo 圖標

官方建議的圖標尺寸:

密度名稱 每英寸像素值 圖標尺寸 圖片資源目錄
LDPI ~120dpi 36 x 36 px mipmap-ldpi
MDPI(基準) 120dpi ~ 160dpi 48 x 48 px mipmap 或 mipmap-mdpi
HDPI(1.5倍) 160dpi ~ 240dpi 72 x 72 px mipmap-hdpi
XHDPI(2倍) 240dpi ~ 320dpi 96 x 96 px mipmap-xhdpi
XXHDPI(3倍) 320dpi ~ 480dpi 144 x 144 px mipmap-xxhdpi
XXXHDPI(4倍) 480dpi ~ 640dpi 192 x 192 px mipmap-xxxhdpi
  • 普通位圖和圖標

按照官方密度類型進行切圖便可,通常狀況下只須要根據主流設備選擇所須要的資源文件便可。

目前已知主流設備屏幕:

屏幕分辨率 屏幕尺寸 PPI 對應密度 圖片資源文件夾
240 x 320 2.5 160 MDPI(基準) drawable-mdpi
400 x 800 4.0 224 HDPI(1.5倍) drawable-hdpi
720 x 1280 4.7 313 XHDPI(2倍) drawable-xhdpi
1080 x 1920 5.5 401 XXHDPI(3倍) drawable-xxhdpi
1440 x 2560 6.0 490 XXXHDPI(4倍) drawable-xxxhdpi

九宮格位圖

  • 拉伸區域

拉伸區域

紅色框區域:表示縱向拉伸的區域,當圖片須要縱向拉伸的時候它會只指定拉伸紅色區域,其餘區域在縱向是不會拉伸。

綠色框區域:表示橫向拉伸的區域,當圖片須要橫向拉伸的時候它會只指定拉伸綠色區域,其餘區域在橫向是不會拉伸的。

顯然紅色和綠色相交的部分是既會進行橫向拉伸也會進行縱向拉伸。

  • 顯示區域

顯示區域

黃色區域:表示前景能顯示的橫向範圍。即前景的最左邊能夠顯示到什麼地方,最右邊能夠顯示的什麼地方。

藍色區域:表示前景能顯示的縱向範圍。即前景的最上面能夠顯示到什麼地方,最下面能夠顯示的什麼地方。

藍色和黃色相交部分:表示整個前景能顯示的區域。一個區域是矩形的,藍色規定了上下邊界,黃色規定了左右邊界,二者共同固然也就規定了一個矩形區域。

矢量圖

矢量圖是根據幾何特性來繪製圖形,矢量能夠是一個點或一條線,矢量圖只能靠軟件生成,文件佔用內在空間較小,由於這種類型的圖像文件包含獨立的分離圖像,能夠自由無限制的從新組合。

它的特色是放大後圖像不會失真,和分辨率無關,適用於圖形設計、文字設計和一些標誌設計、版式設計等。

  • SVG

SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)。用來定義用於網絡的基於矢量的圖形,SVG 使用 XML 格式定義圖形,圖像在放大或改變尺寸的狀況下其圖形質量不會有所損失。

Android 中對矢量圖的支持就是對 SVG 的支持。使用方式比較簡單,這裏再也不贅述。

ScaleType

ImageView 是 Android 中最經常使用的控件之一,而在使用 ImageView 時,必不可少的會使用到它的 scaleType 屬性。該屬性指定了你想讓 ImageView 如何顯示圖片,包括是否進行縮放、等比縮放、縮放後展現位置等。

Android 提供了八種 scaleType的 屬性值,每種都對應了一種展現方式。

  • center:保持原圖的大小,顯示在 ImageView 的中心。當原圖的尺寸大於 ImageView 的尺寸時,多出來的部分被裁切掉。
  • center_inside:以原圖正常顯示爲目的,若是原圖大小大於 ImageView 的尺寸,就按照比例縮小原圖的寬高,居中顯示在 ImageView 中。若是原圖尺寸小於 ImageView 的 size,則不作處理居中顯示圖片。
  • center_crop:以原圖填滿 ImageView 爲目的,若是原圖尺寸大於 ImageView 的尺寸,則與 center_inside 同樣,按比例縮小,居中顯示在 ImageView 上。若是原圖尺寸小於 ImageView 的尺寸,則按比例拉伸原圖的寬和高,填充 ImageView 居中顯示。
  • matrix:不改變原圖的大小,從 ImageView 的左上角開始繪製,超出部分作剪切處理。
  • fit_xy:把圖片按照指定的大小在 ImageView 中顯示,拉伸顯示圖片,不保持原比例,填滿ImageView。
  • fit_start:把原圖按照比例放大縮小到 ImageView的 高度,顯示在 ImageView 的 center(前部/上部)。
  • fit_center:把原圖按照比例放大縮小到 ImageView 的高度,顯示在 ImageView 的 center(中部/居中顯示)。
  • fit_end: 把原圖按照比例放大縮小到 ImageView 的高度,顯示在 ImageVIew 的 end(後部/尾部/底部)

scaleType

接口適配

本地加載圖片時,根據設備屏幕分辨率或像素密度,向服務器請求對應級別的圖片資源。

支持劉海屏幕

Android P 提供提供的劉海屏適配方案,詳見:Android 開發文檔 - 支持顯示切口

  • 對於有狀態欄的頁面,不會受到劉海屏特性的影響,由於劉海屏包含在狀態欄中了;
  • 全屏顯示的頁面,系統劉海屏方案會對應用界面作下移處理,避開劉海區顯示,這時會看到劉海區域變成一條黑邊,徹底看不到劉海了;
  • 已經適配 Android P 應用的全屏頁面能夠經過谷歌提供的適配方案使用劉海區,真正作到全屏顯示。

劉海屏適配思路:

  • Android P 之後的設備:若是有狀態欄不須要適配,由於劉海區域會包含在狀態欄中了(設置 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 可將劉海區域變成一條黑色邊)。若是全屏顯示,獲取到危險區域(劉海區域),讓操做區域避開危險區域便可。
  • Android P 以前的設備:根據主流廠商 華爲、vivo、OPPO、小米等所提供的方案進行適配。

增長不支持的屏幕

Google 建議咱們支持更多的設備,可是根據需求可能須要不支持某些設備。好比,僅支持手機不支持平板電腦。咱們就能夠增長不支持的屏幕設備。

若是應用僅支持小屏幕尺寸和標準屏幕尺寸,能夠這麼作:

<manifest ... >
  <compatible-screens>
    <!-- 全部的小尺寸屏幕 -->
    <screen android:screenSize="small" android:screenDensity="ldpi" />
    <screen android:screenSize="small" android:screenDensity="mdpi" />
    <screen android:screenSize="small" android:screenDensity="hdpi" />
    <screen android:screenSize="small" android:screenDensity="xhdpi" />
    <!-- 全部的標準尺寸屏幕 -->
    <screen android:screenSize="normal" android:screenDensity="ldpi" />
    <screen android:screenSize="normal" android:screenDensity="mdpi" />
    <screen android:screenSize="normal" android:screenDensity="hdpi" />
    <screen android:screenSize="normal" android:screenDensity="xhdpi" />
  </compatible-screens>
  ...
  <application ... >
    ...
  <application>
</manifest>
複製代碼

若是應用僅支持平板電腦或電視,能夠這麼作:

<manifest ... >
  <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="true" android:xlargeScreens="true"/>
  ...
</manifest>
複製代碼

今日頭條適配方案

2018 年 5 月,字節跳動技術團隊提出了一種低成本的屏幕適配方式,強烈推薦你們看一下:一種極低成本的 Android 屏幕適配方式

參考資料

個人 Github

github.com/jeanboydev/…

個人公衆號

歡迎關注個人公衆號,分享各類技術乾貨,各類學習資料,職業發展和行業動態。

Android 波斯灣

技術交流羣

歡迎加入技術交流羣,來一塊兒交流學習。

QQ 技術交流羣

QQ 技術交流羣
相關文章
相關標籤/搜索