讀書筆記,如需轉載,請註明做者: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 |
能夠同時使用多個限定符,用"-"鏈接。如values-sw360dp-h655dp-port;網絡
不區分大小寫;app
同一類型的限定符只能出現一次;ide
限定符的使用順序必須嚴格按照上表規定的優先級,不然會直接編譯出錯。如:佈局
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
那麼該手機最終會採用哪一個文件夾下的資源呢?
淘汰與設備配置衝突的資源文件: drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/
查表,從最高優先級(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/
繼續淘汰非port文件夾: drawable-en/ drawable-en-port/ drawable-en-notouch-12key/
僅剩一個文件夾了!因此該手機採用的就是 drawable-en-port 下的資源!
流程圖:
注:限定符的優先順序比與設備徹底匹配的限定符數量更加劇要。本例中,drawable-port-notouch-12key 包含更多相匹配的限定符仍然被淘汰了,就是由於它使用的限定符優先級比較低。
挑出須要特殊適配的資源。這裏的資源主要指drawable、layout 和dimens;
設計師根據所需適配的分辨率分別切圖和標註(像素爲單位);
開發人員將設計師提供的切圖放至對應的drawable文件夾下;
開發人員根據標註計算出對應的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...
複製代碼