Android多終端适配是我們在實際開發中必然會遇到也必然要解決的問題,解決多終端适配的方法有很多,比如使用百分比布局庫(percent-support-lib)、在res目錄下生成不同檔案下的dimen值、盡量的使用wrap_content/match_parent/weight權重、盡量用dp替代px 文字使用sp做為機關等。
Android為什麼會出現螢幕适配的問題
由于Android系統的開放性,任何使用者、開發者、OEM廠商、營運商都可以對Android進行定制,修改成他們想要的樣子。但是這種“碎片化”到底到達什麼程度呢?
在2012年,OpenSignalMaps(以下簡稱OSM)釋出了第一份Android碎片化報告,統計資料表明,
2012年,支援Android的裝置共有3997種。
2013年,支援Android的裝置共有11868種。
2014年,支援Android的裝置共有18796種到
到目前為止就更多了,千千萬萬的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,以此類推。
mdpi、hdpi、xdpi、xxdpi
mdpi、hdpi、xdpi、xxdpi用來修飾Android中的drawable檔案夾及values檔案夾,用來區分不同像素密度下的圖檔和dimen值。
在進行開發的時候,我們需要把合适大小的圖檔放在合适的檔案夾裡面
1、使用百分比布局庫(Android-percent-support)
兩種布局供大家使用
PercentRelativeLayout、PercentFragmentLayout,顧名思義這是繼承自RelativeLayout和FragmentLayou兩個容器類
支援的屬性有:
layout_widthPercent:占父控件寬度百分百
layout_heightPercent:占父控件高度百分百
layout_marginPercent
、
layout_marginLeftPercent
、
layout_marginTopPercent
、
layout_marginRightPercent
、
layout_marginBottomPercent
、
layout_marginStartPercent
、
layout_marginEndPercent
這些屬性一看就明白它的意思
使用:它的使用比較簡單,github上也有源碼Android-percent-support-lib-sample
首先在build.gradle中添加
compile 'com.android.support:percent:22.2.0'
然後就可以用它來進行布局,看下面的效果圖,簡單明了
2、在res目錄下生成不同分辨率下的dimens檔案
目錄結構大緻如下,你可以點選我下載下傳這個工具
這個時候我們就可以盡情的寫具體的數值了,比如:layout_marginLeft="@dimen/dimen_30_dip",這個基準尺寸就是設計圖尺寸,如果UI設計師給你的設計圖就是480*800你用工具生成的基準也是480*800的話就無需要對标注進行換算,設計圖傷标注的是多少,layout中就直接寫具體的數值也是沒問題的
存在的弊端:
對于沒有考慮到螢幕尺寸,可能會出現意外的情況
apk的大小會增加;
3、使用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,這樣就可以根據各元件之間的特殊關系指定布局了。例如,我們可以将某個子視圖對齊到螢幕左側,同時将另一個視圖對齊到螢幕右側。
下面的代碼以官方Demo為例說明。
在上面的代碼中我們使用了相對布局,并且使用alignXXX等屬性指定了子控件的位置,下面是這種布局方式在應對螢幕變化時的表現
在小尺寸螢幕的顯示
在平闆的大尺寸上的顯示效果
雖然控件的大小由于螢幕尺寸的增加而發生了改變,但是我們可以看到,由于使用了相對布局,是以控件之前的位置關系并沒有發生什麼變化,這說明我們的适配成功了。