Android 資源限定符命名規則

讀書筆記,如需轉載,請註明做者:Yuloran (t.cn/EGU6c76)php

吐槽

Android開發者真的挺苦逼的,不只要適配常規分辨率,如 480x85四、720x1280、1080x1920、1440x2560,還要適配各類奇葩分辨率,如 720x1440、1080x2160...html

適配緣由

不一樣分辨率和屏幕像素密度的手機,其寬高換算成dp後,有多是不一樣的。以下表(Google規定:在160dpi的手機上,1dp = 1px):java

注:dpi 跟 ppi 一個意思,都表示每英寸多少個像素點android

分辨率(像素) 像素密度(dpi) 寬(dp) 高(dp)
480x854 240 320 569.3
1080x1920 480 360 640

因此在1080x1920、480ppi的手機上,能夠完整顯示330dp寬的按鈕,而在480x85四、240ppi的手機上,卻顯示不下。如何解決呢?Google機智地設計了一套資源限定符,只需新建一個values-hdpi文件夾,再建一個dimens.xml(名字隨意),添加一句:數據庫

<dimen name="your_widget_name">300dp</dimen>
複製代碼

按鈕寬度便會自動調整爲300dp。bash

限定符優先級

限定符 示例 含義 優先級 since version
MCC 和 MNC 示例:
mcc310 mcc310-mnc004 mcc208-mnc00
...
移動國家代碼 (MCC),(可選)後跟設備 SIM 卡中的移動網絡代碼 (MNC)。

例如,mcc310 是指美國的任一運營商,mcc310-mnc004 是指美國的 Verizon 公司,mcc208-mnc00 是指法國的 Orange 公司。
API 1
語言和區域 示例:
en
fr
en-rUS
...
語言經過由兩個字母組成的 ISO 639-1 語言代碼定義,能夠選擇後跟兩個字母組成的 ISO 3166-1-alpha-2 區域碼(前帶小寫字母「r」)。

這些代碼不區分大小寫;r前綴用於區分區域碼。不能單獨指定區域。
API 1
佈局方向 ldrtl
ldltr
應用的佈局方向。

ldrtl 是指「佈局方向從右到左」;
ldltr 是指「佈局方向從左到右」,這是默認的隱式值。
API17
最小寬度 swdp

示例:
sw320dp
sw360dp
...
限定資源僅適用於最小屏幕寬度爲Ndp的手機(不考慮屏幕方向)。

好比1080x1920 480ppi的手機,最小寬度限定符就是sw360dp;
720x1280 320ppi的手機,最小寬度限定符也是sw360dp。
API 13
可用寬度 wdp
示例:
w320dp
...
限定資源僅適用於最小可用屏幕寬度爲Ndp的手機(考慮屏幕方向)。

通常狀況下,該限定符的值與swdp相同。
API 13
可用高度 hdp

示例:
h655dp
...
限定資源僅適用於最小可用屏幕高度爲Ndp的手機(考慮屏幕方向)。

計算N的時候必須減去StatusBar和NavigationBar的高度。

好比1080x2160 480ppi的手機,若StatusBar高度爲25dp,NavigationBar高度爲40dp,則N = 720 - 25 - 40 = 655,便可用高度限定符爲h655dp。
API 13
屏幕尺寸 small
normal
large
xlarge
small:QVGA,320x426(dp)
normal:HVGA,320x470(dp)
large:VGA,480x640(dp)
xlarge:720x960(dp)【API 9】
API 4
屏幕縱橫比 nong
notlong
long:寬屏,如 WQVGA、WVGA、FWVGA
notlong:非寬屏,如 QVGA、HVGA 和 VGA

徹底基於屏幕的縱橫比(寬屏較寬),與屏幕方向無關。
API 4
圓形屏幕 round
notround
round:圓形屏幕,例如圓形可穿戴式設備
notround:方形屏幕,例如手機或平板電腦
API 23
屏幕方向 port
land
port:豎屏
land:橫屏
API 1
UI 模式 car
desk
television
appliance
watch
car:設備正在車載手機座上顯示
desk:設備正在桌面手機座上顯示
television:設備正在電視上顯示,爲用戶提供「十英尺」體驗,其 UI 位於遠離用戶的大屏幕上,主要面向方向鍵或其餘非指針式交互【API 13】
appliance:設備用做不帶顯示屏的裝置
watch:設備配有顯示屏,戴在手腕上【API 20】
API 8
夜間模式 night
notnight
night:晚上
notnight:白天
API 8
屏幕像素密度 (dpi) ldpi
mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
nodpi
tvdpi
anydpi
ldpi:120dpi
mdpi: 160dpi
hdpi:240dpi
xhdpi:320dpi【API 8】
xxhdpi:480dpi【API 16】
xxxhdpi:640dpi【API 18】
nodpi:放不想縮放的位圖
tvdpi:213dpi【API 13】
anydpi:此限定符適合全部屏幕密度,其優先級高於其餘限定符。 這對於矢量可繪製對象頗有用。【API 21】

六個主要密度之間的縮放比爲 3:4:6:8:12:16(忽略 tvdpi 密度)。所以,9x9 (ldpi) 位圖至關於 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位圖,依此類推。

若是您認爲圖像資源在電視或其餘某些設備上呈現的效果不夠好,而想嘗試使用 tvdpi 資源,則縮放比例爲 1.33*mdpi。例如,mdpi 屏幕的 100px x 100px 圖像應該至關於 tvdpi 的133px x 133px。
API 1
觸摸屏類型 notouch
finger
notouch:有觸摸屏
finger:設備有一個專供用戶經過手指直接與其交互的觸摸屏
API 1
鍵盤可用性 keysexposed
keyshidden
keyssoft
keysexposed:設備具備可用的鍵盤。若是設備啓用了軟鍵盤(不無可能),那麼即便硬鍵盤沒有展現給用戶,哪怕設備沒有硬鍵盤,也可使用此限定符。 若是沒有提供或已經禁用軟鍵盤,則只有在顯示硬鍵盤時纔會使用此限定符。
keyshidden:設備具備可用的硬鍵盤,但它處於隱藏狀態,且設備沒有啓用軟鍵盤。
keyssoft:設備已經啓用軟鍵盤(不管是否可見)。

若是提供了 keysexposed 資源,但未提供 keyssoft 資源,那麼只要系統已經啓用軟鍵盤,就會使用 keysexposed 資源,而不考慮鍵盤是否可見。
API 1
主要文本輸入法 nokeys
qwerty
12keys
nokeys:設備沒有用於文本輸入的硬按鍵。
qwerty:設備具備標準硬鍵盤(不管是否對用戶可見)。
12key:設備具備 12 鍵硬鍵盤(不管是否對用戶可見)。
API 1
導航鍵可用性 navexposed
navhidden
navexposed:導航鍵可供用戶使用。
navhidden:導航鍵不可用(例如,位於密封蓋子後面)。

不知道指的啥,反正不是底部的虛擬按鍵。那玩意兒須要根據各個廠商的定製方案去適配(監聽廣播或者讀數據庫)。
API 1
主要非觸摸導航方法 nonav
dpad
trackball
wheel
nonav:除了使用觸摸屏之外,設備沒有其餘導航設施。
dpad:設備具備用於導航的方向鍵。
trackball:設備具備用於導航的軌跡球,如HTC G2。
wheel:設備具備用於導航的方向盤(不常見)。
API 1
平臺版本(API 級別) 示例:
v3
v4
v7
...
從哪一個版本開始支持此限定符 API 1
  • 目前共有19種限定符,固然經常使用的很少。適配時,根據業務須要,查表適配便可。
  • 上表限定符適用於全部資源,而不只僅是values。drawable、layout也可使用。

限定符命名規則

  1. 能夠同時使用多個限定符,用"-"鏈接。如values-sw360dp-h655dp-port;網絡

  2. 不區分大小寫;app

  3. 同一類型的限定符只能出現一次;ide

  4. 限定符的使用順序必須嚴格按照上表規定的優先級,不然會直接編譯出錯。如:佈局

D:\HardwareDetection\app\src\main\res\values-port-sw360dp: Error: Invalid resource directory name

緣由:sw360dp優先級大於port
複製代碼

注意點

  • 不帶任何限定符的資源文件夾,如values,是默認文件夾。該文件夾對應160ppi,即mdpi。當系統在其餘資源文件夾下找不到對應資源時,便會使用默認文件夾中的資源。若默認文件夾也找不到,則拋出異常。

  • 無需特殊適配的資源應當置於默認文件夾中。

  • 屏幕像素密度限定符(如values-hdpi、values-xxhdpi),是惟一一個不會因衝突而被淘汰的文件夾。什麼意思呢?就是說只要對應的資源存在,那麼不論該資源放在哪一個dpi文件夾,系統都不會拋出異常(系統會優先選擇高dpi文件夾下的資源)。而其餘限定符則沒有這個特權,若系統淘汰了與當前設備不匹配的限定符後找不到對應的資源,則會直接拋出異常:

Caused by: java.lang.RuntimeException: Binary XML file line #10: You must supply a layout_width attribute.
   at android.content.res.TypedArray.getLayoutDimension(TypedArray.java:606)
複製代碼

限定符淘汰規則

  • 假設res下存在如下目錄: drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/

  • 用戶手機的配置是: 語言區域 = en-GB 屏幕方向 = port 屏幕像素密度 = hdpi 觸摸屏類型 = notouch 主要文本輸入法 = 12key

那麼該手機最終會採用哪一個文件夾下的資源呢?

  1. 淘汰與設備配置衝突的資源文件: drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/

  2. 查表,從最高優先級(MCC)開始,若該限定符存在,則淘汰其它全部不含此限定符的文件夾,不然順位取下一個優先級的限定符,本例先淘汰非en文件夾(en-GB > port > hdpi > notouch > 12key): drawable/ drawable-en/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/

  3. 繼續淘汰非port文件夾: drawable-en/ drawable-en-port/ drawable-en-notouch-12key/

僅剩一個文件夾了!因此該手機採用的就是 drawable-en-port 下的資源!

流程圖:

限定符淘汰規則.png

:限定符的優先順序比與設備徹底匹配的限定符數量更加劇要。本例中,drawable-port-notouch-12key 包含更多相匹配的限定符仍然被淘汰了,就是由於它使用的限定符優先級比較低。

適配套路

  1. 挑出須要特殊適配的資源。這裏的資源主要指drawable、layout 和dimens;

  2. 設計師根據所需適配的分辨率分別切圖和標註(像素爲單位);

  3. 開發人員將設計師提供的切圖放至對應的drawable文件夾下;

  4. 開發人員根據標註計算出對應的dp值,編寫dimens.xml,並放至對應的values文件夾中。

小技巧

有人問,這麼多分辨率,我怎麼知道是xxdpi仍是xxxdpi的文件夾?

So easy!

public class DisplayMetrics {
    public static final int DENSITY_260 = 260;
    public static final int DENSITY_280 = 280;
    public static final int DENSITY_300 = 300;
    public static final int DENSITY_340 = 340;
    public static final int DENSITY_360 = 360;
    public static final int DENSITY_400 = 400;
    public static final int DENSITY_420 = 420;
    public static final int DENSITY_560 = 560;
    public static final int DENSITY_DEFAULT = 160;
    public static final int DENSITY_DEVICE_STABLE = 0;
    public static final int DENSITY_HIGH = 240;
    public static final int DENSITY_LOW = 120;
    public static final int DENSITY_MEDIUM = 160;
    public static final int DENSITY_TV = 213;
    public static final int DENSITY_XHIGH = 320;
    public static final int DENSITY_XXHIGH = 480;
    public static final int DENSITY_XXXHIGH = 640;
...省略若干行
複製代碼
  • Android Studio中,雙擊shift,輸入DisplayMetrics,查看class文件(不要查看java源碼,註釋太多了!),看看是否是應有盡有?

  • 什麼?你不知道手機的ppi是多少?easy!仍是這個類,用下面這個操做便可:

int densityDpi = Resources.getSystem().getDisplayMetrics().densityDpi; // 像素密度,值爲320,480...
float density = Resources.getSystem().getDisplayMetrics().density; // dp 跟 px 的換算關係,值爲1.5,2,3...
複製代碼

Google官方適配教程

相關文章
相關標籤/搜索