UI适配方案
Android的ui适配一般有兩種解決方案,一種是通過系統的資源适配機制(layout和drawable),另一種是通過代碼來适配(fragment和view),兩種方案也經常結合起來用。
1 資源适配
Android的資源适配機制的原理就是在一個app工程中為不同分辨率,不同尺寸和不同方向的螢幕準備相對應的layout檔案和圖檔,在運作時系統會自動找到相應的檔案,而要使系統正确地找到最優的資源檔案,開發者必須遵守android的資源适配規則。
1.1 需掌握的概念
1.11 Screen size(螢幕尺寸)
android定義了small,
normal, large, xlarge四種尺寸,其中normal是以G1
3.5寸的螢幕為基準,一種尺寸标準一般對應一個範圍的螢幕大小,具體如下:
Android 3.2(level
13)之後,新加入了最小dp的概念,有swdp,wdp,hdp,n代表最小的數值,其中wdp指螢幕寬度>=ndp,hdp指螢幕高度>=ndp,swdp指高度和寬度都要>=ndp,比如w600dp意味着當運作在寬度>=600dp的螢幕上時才會加載此級别的資源檔案。
1.12 Orientation(螢幕方向)
Android定義了port(橫向),land(縱向)兩種方向,如果應用支援app橫向模式,一般都會針對layout提供一個land方向的資源檔案,以對layout重新布局。
1.13 Density-independent pixel (dp)
dp是用來衡量密度和像素之間關系的機關,以mdpi(160dpi)為基準,1dp=1px,其他按比例變化,比如hdpi(240dpi)中,1dp=1.5px。dp主要用來設定view的大小,如果用px來設定view的大小,在相同大小的螢幕上,高分辨率的螢幕下view的實體大小會變小(因為一個像素的實體大小變小了),而如果使用dp,比如高度為100dp,則view的大小幾乎沒有變化(因為雖然一個像素變小了,但在高分辨率的螢幕上,100dp>100px,最終像素數量變多了,實體大小=像素大小*像素數量,互相抵消後,view的實體大小幾乎不變),也就是說,隻要我們使用dp,然後在mdpi的基準下調整一個最佳的數值,在所有裝置上都可以呈現相同的實體大小。
1.14 Scale-independent pixel (sp)
sp的原理和dp類似,使用在字型大小的設定上,除了受螢幕的參數影響外,還受系統字型設定的影響(很多機型沒有這個設定功能),當把系統字型的大小設定更大時,字型也會在用來的基礎上變大。
以上的幾個概念都應用在layout上,下面的Screen
density應用在drawable上。
1.15 Screen density(螢幕密度)
Android定義了low,
medium, high, extra-high, extra-extra-high,
extra-extra-extra-high 6種密度,簡寫為ldpi(120dpi),
mdpi(160dpi), hdpi,(240dpi), xhdpi(320dpi), xxhdpi(480dpi),
xxxhdpi(640dpi),
主要用來衡量一定的螢幕面積内像素的數量,和我們平時說的分辨率相似,在螢幕大小相同時,螢幕密度越高,分辨率也越高,而為了使圖檔不失真,所需要的圖檔分辨率也要越高,這就是android适配圖檔的基礎。
1.2 layout
layout适配要解決的問題一般有兩種,一種是布局的變化,比如在大螢幕上或者橫向螢幕上我們希望布局有所變化來充分利用螢幕的空間,另一種是view的大小适配問題,我們要避免view會出現過大或過小的情況。
1.21 布局的變化适配
在android項目工程中,通過在不同的資源檔案夾放入相應的layout檔案,系統會自動幫我們找到适應的layout,如下:
檔案夾的命名方式需遵守android的命名規範,通過IDE的提示可完成。
Android這套機制最适合的場景就是在小的螢幕下(比如手機),布局是一個清單,而在大的螢幕下(平闆)或者橫向螢幕下,布局除了顯示一個清單,還顯示詳細資訊,如下:
要實作這種适配,我們隻需定義兩個布局檔案(single-pane和multi-pane),并取相同的檔案名,放到相應的檔案夾就行,如下:
一般還會結合fragment來實作這種場景,會更加靈活,封裝性也更好。
附注:在layout中,google建議不要用AbsoluteLayout。
1.22 View的大小适配
在設定view的寬度和高度時,不要用px作為機關,而是用dp和sp(字型),或者是wrap_content,match_parent這種自動适應機關,常用的還有按比例配置設定大小。
1.221 dp和sp
在用dp設定view的大小時,一般在mdpi的螢幕上調節一個最佳的數值,再用dp作為機關,sp原理一樣,隻是用于字型大小的設定。
1.222 wrap_content
wrap_content主要适用于對view的大小沒有要求,隻是希望能完整的顯示出内容,或者内容是動态變化的,比如textview或button的字型内容。wrap_content由于需要計算出内容的大小,是以相比match_parent,wrap_content更加耗時。
1.223 match_parent
match_parent主要适用于view的parent已經确定好大小,而view需要占滿整個parent。
1.224 按比例
按比例配置設定大小主要适用于parent的大小已經确定,多個子view大小要按比例配置設定,要實作按比例配置設定,一種是直接在代碼擷取parent的大小,再按比例計算出子view的大小,另一種是使用LinearLayout作為parent,子view的大小設為0,通過設定weight的值來設定比例。
1.3 drawable
drawable的适配也是通過資源檔案夾來配置設定的,如下:
1.31 drawable分辨率
圖檔的分辨率比例為ldpi : mdpi
: hdpi : xhdpi : xxhdpi : xxxhdpi =
3:4:6:8:12:16,比如在設定啟動圖示時,mdpi的分辨率為48 * 48,其他的則按比例調整。
1.32 資源查找及項目實踐
在實際項目中,一般不會準備所有分辨率的圖檔,因為在目前density的資源檔案夾找不到相應的資源時,系統會到其他的檔案夾找,再做相應的比例縮放,根據google官方文檔,系統查找的順序并不确定,一般會到預設的資源檔案夾(沒有dpi字尾)找,但如果目前螢幕是ldpi的,系統會優先使用hdpi而不是mdpi的,因為hdpi到ldpi要縮小0.5倍,而mdpi到ldpi需要0.75倍,縮小0.5倍效率更高。
在之前的測試中(android
4.4),發現系統是從最高density檔案夾往低找,同時結合縮放比例來确定查找結果,這和layout缺失時有點不同,layout的查找順序是從目前size往低找,因為對于layout來說,可以相容小的,但更大的layout可能就會出問題了(如果隻有更大級别的layout,系統會報錯),而對于drawable來說,分辨率更大,縮放後圖檔也不會失真。
因為系統的查找政策對開發者來說是透明的,是以在項目中,一般會為mdpi,hdpi,xhdpi
.......準備相應的圖檔資源,而對于高分辨率圖檔(背景圖),則一般會準備一套,再由系統去縮放。
2 代碼适配
為了符合MVC模式,并讓界面和代碼盡可能地分離,采用上述的資源适配方案更好,但在有些場景下卻需要在代碼裡來适配ui,比如需要根據螢幕的寬度或高度來決定view的大小,或者在運作時需要動态調整view的大小。同時,fragment的引入也使xml和代碼的融合更加靈活(但複雜性也相應變大)。
2.1 view
在代碼中設定view的大小時最常用的一個參數就是density,在mdpi下density為1,因為設定view大小的api的參數是以像素為機關的,是以我們隻需将mdpi下的基準數值乘以density就可以實作xml下dp的效果,
擷取density的方法如下:
getResources().getDisplayMetrics().density
2.2 fragment
Fragment是android
3.0引入的,可以通過support
v4包來相容3.0之前的版本。個人覺得fragment更像是一個容器,相比view,fragment除了可以綁定一個layout外,還有自己的生命周期,也可以管理自己的資料,更利于代碼的封裝,但複雜性就變大了。
一般來說fragment适用于動态布局的場景下,比如上述的雙清單(平闆)和單清單(手機),fragment可以直接在layout檔案中作為view來聲明,如下:
Fragment的具體用法請看開發者文檔。
3 其他方案
3.1 support screen
通過在mianifest裡加上标簽可以聲明應用支援或不支援多大的螢幕,google
play等商店也會通過此标簽來判斷app支援的螢幕尺寸,此标簽常用于螢幕相容模式中。
可設定的值如下:
請見:www.developer.android.com/guide/topics/manifest/supports-screens-element.html
3.2 Screen Compatibility Mode(螢幕相容模式)
相容模式的目的是為沒有做任何UI适配的app在平闆上運作提供一種UI優化,有兩種版本的相容模式,android
1.6之前是version 1,1.6之後是version 2,下面介紹的是基于version 2。
google官方文檔不建議使用此功能,而是采用ui适配方案。
個人覺得目前此功能很少用,都是通過android的資源适配機制或者fragment來解決UI适配的問題。
3.21 開啟相容模式
當運作在android
3.2及以上的平闆上時,以下情況下會讓使用者選擇是否使用相容模式:
1)android:minSdkVersion和android:targetSdkVersion
都設定為10以下,且沒有設定來說明app支援大螢幕。
2)android:minSdkVersion和android:targetSdkVersion
都設定為11及以上,并且設定來說明沒有适配大螢幕。
3.22 關閉相容模式
目前targetSdkVersion
都大于11,是以隻要沒有設定就不會出現相容模式。
詳細資訊見開發者文檔:www.developer.android.com/guide/practices/screen-compat-mode.html
3.3 Nine-patch Bitmaps
Nine-patch
Bitmaps是android針對圖檔拉伸的一種優化技術,通過在一張普通的圖檔最外層相應位置加上像素點,可以使圖檔在拉伸時隻拉伸像素點所在的方向,而其他位置(有形狀的位置)則不會被拉伸導緻變形。如下:
拉伸後:
可以通過sdk自帶的draw9patch工具來生成Nine-patch
Bitmap,生成的圖檔字尾為xx.9.png。
參考文檔:www.developer.android.com/training/multiscreen/index.html
www.developer.android.com/guide/practices/screens_support.html