屏幕適配問題的本質(zhì):
使得“布局”优构、“布局組件”、“圖片資源”雁竞、“用戶界面流程”匹配不同的屏幕尺寸
使得布局钦椭、布局組件自適應(yīng)屏幕尺寸;
根據(jù)屏幕的配置來加載相應(yīng)的UI布局、用戶界面流程
使得“圖片資源”匹配不同的屏幕密度
解決方案
1:使得布局元素自適應(yīng)屏幕尺寸
開發(fā)中彪腔,我們使用的布局一般有:
- 線性布局(Linearlayout)
- 相對布局(RelativeLayout)
- 幀布局(FrameLayout)
- 表格布局(TabLayout)
- 絕對布局(AbsoluteLayout)
常用的就是線性布局(Linearlayout)侥锦、相對布局(RelativeLayout)和幀布局(FrameLayout)需要根據(jù)需求進行選擇,但要記椎抡酢:
RelativeLayout
布局的子控件之間使用相對位置的方式排列捎拯,因為RelativeLayout講究的是相對位置,即使屏幕的大小改變盲厌,視圖之前的相對位置都不會變化,與屏幕大小無關(guān)祸泪,靈活性很強LinearLayout
通過多層嵌套LinearLayout和組合使
用"wrap_content"和"match_parent"以及"weight"來構(gòu)建布局吗浩。但是LinearLayout無法準確地控制子視圖之間的位置關(guān)系,只能簡單的一個挨著一個地排列
所以没隘,對于屏幕適配來說懂扼,使用相對布局(RelativeLayout)將會是更好的解決方案
2:根據(jù)屏幕的配置來加載相應(yīng)的UI布局
做法:使用限定符
應(yīng)用場景:需要為不同屏幕尺寸的設(shè)備設(shè)計不同的布局
- 作用:通過配置限定符使得程序在運行時根據(jù)當前設(shè)備的配置(屏幕尺寸)自動加載合適的布局資源
。限定符類型:
右蒲。尺寸(size)限定符
阀湿。最小寬度(Smallest-width)限定符
。布局別名
瑰妄。屏幕方向(Orientation)限定符
尺寸(size)限定符
- 使用場景:當一款應(yīng)用顯示的內(nèi)容較多陷嘴,希望進行以下設(shè)置:
1.在平板電腦和電視的屏幕(>7英寸)上:實施“雙面板”模式以同時顯示更多內(nèi)容
2.在手機較小的屏幕上:使用單面板分別顯示內(nèi)容
因此,我們可以使用尺寸限定符(layout-large)通過創(chuàng)建一個文件來完成上述設(shè)定:
res/layout-large/main.xml
- 讓系統(tǒng)在屏幕尺寸>7英寸時采用適配平板的雙面板布局
反之(默認情況下)采用適配手機的單面板布局
文件配置如下:適配手機的單面板(默認)布局:res/layout/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="match_parent" />
</LinearLayout>
- 適配尺寸>7寸平板的雙面板布局::res/layout-large/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="400dp"
android:layout_marginRight="10dp"/>
<fragment android:id="@+id/article"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.ArticleFragment"
android:layout_width="fill_parent" />
</LinearLayout>
注意:
1.兩個布局名稱均為main.xml间坐,只有布局的目錄名不同:第一個布局的目錄名為:layout灾挨,第二個布局的目錄名為:layout-large,包含了尺寸限定符(large)
2.被定義為大屏的設(shè)備(7寸以上的平板)會自動加載包含了large限定符目錄的布局竹宋,而小屏設(shè)備會加載另一個默認的布局
但要注意的是劳澄,這種方式只適合Android 3.2版本之前。
于是3.2之后推出了最小寬度(Smallest-width)限定符
- 背景:上述提到的限定符“l(fā)arge”具體是指多大呢蜈七?似乎沒有一個定量的指標秒拔,這便意味著可能沒辦法準確地根據(jù)當前設(shè)備的配置(屏幕尺寸)自動加載合適的布局資源
- 例子:比如說large同時包含著5寸和7寸,這意味著使用“l(fā)arge”限定符的話我沒辦法實現(xiàn)為5寸和7寸的平板電腦分別加載不同的布局
于是飒硅,在Android 3.2及之后版本砂缩,引入了最小寬度(Smallest-width)限定符
定義:通過指定某個最小寬度(以 dp 為單位)來精確定位屏幕從而加載不同的UI資源
- 使用場景
你需要為標準 7 英寸平板電腦匹配雙面板布局(其最小寬度為 600 dp),在手機(較小的屏幕上)匹配單面板布局
解決方案:您可以使用上文中所述的單面板和雙面板這兩種布局狡相,但您應(yīng)使用 sw600dp 指明雙面板布局僅適用于最小寬度為 600 dp 的屏幕梯轻,而不是使用 large 尺寸限定符。
- sw xxxdp尽棕,即small width的縮寫喳挑,其不區(qū)分方向,即無論是寬度還是高度,只要大于 xxxdp伊诵,就采用次此布局
- 例子:使用了layout-sw 600dp的最小寬度限定符单绑,即無論是寬度還是高度,只要大于600dp曹宴,就采用layout-sw 600dp目錄下的布局
例子
- 適配手機的單面板(默認)布局:res/layout/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="match_parent" />
</LinearLayout>
- 適配尺寸>7寸平板的雙面板布局:res/layout-sw600dp/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="400dp"
android:layout_marginRight="10dp"/>
<fragment android:id="@+id/article"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.ArticleFragment"
android:layout_width="fill_parent" />
</LinearLayout>
對于最小寬度≥ 600 dp 的設(shè)備搂橙,系統(tǒng)會自動加載 layout-sw600dp/main.xml(雙面板)布局,否則系統(tǒng)就會選擇 layout/main.xml(單面板)布局(這個選擇過程是Android系統(tǒng)自動選擇的)
然而笛坦,使用早于Android 3.2系統(tǒng)的設(shè)備將無法識別sw600dp這個限定符区转,所以你還是同時需要使用large限定符。這樣你就需要在res/layout-large和res/layout-sw600dp目錄下都添加一個相同的main.xml版扩。
使用布局別名(也就是最后的解決辦法)
Smallest-width限定符僅在Android 3.2及之后的系統(tǒng)中有效废离。因而,你也需要同時使用Size限定符(small, normal, large和xlarge)來兼容更早的系統(tǒng)礁芦。例如蜻韭,你想手機上顯示single-pane界面,而在7寸平板和更大屏的設(shè)備上顯示multi-pane界面柿扣,你需要提供以下文件:
- res/layout/main.xml: single-pane布局
- res/layout-large: multi-pane布局
- res/layout-sw600dp: multi-pane布局
最后的兩個文件是完全相同的肖方,為了要解決這種重復(fù),你需要使用別名技巧未状。例如俯画,你可以定義以下布局:
- 適配手機的單面板(默認)布局 :res/layout/main.xml, single-pane布局
- 適配尺寸>7寸平板的雙面板布局: res/layout/main_twopanes.xml, two-pane布局
然后加入以下兩個文件,以便進行Android 3.2前和Android 3.2后的版本雙面板布局適配:
1.res/values-large/layout.xml(Android 3.2之前的雙面板布局)
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
2.res/values-sw600dp/layout.xml(Android 3.2及之后的雙面板布局)
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
- 最后兩個文件有著相同的內(nèi)容司草,但是它們并沒有真正去定義布局活翩,它們僅僅只是給main定義了一個別名main_twopanes。這樣兩個layout.xml都只是引用了@layout/main_twopanes翻伺,就避免了重復(fù)定義布局文件的情況材泄。
- 由于這些文件包含 large 和 sw600dp 選擇器,因此吨岭,系統(tǒng)會將此文件匹配到不同版本的>7寸平板上:
a. 版本低于 3.2 的平板會匹配 large的文件
b. 版本高于 3.2 的平板會匹配 sw600dp的文件
屏幕方向(Orientation)限定符
- 使用場景:根據(jù)屏幕方向進行布局的調(diào)整
小屏幕, 豎屏: 單面板
小屏幕, 橫屏: 單面板
7 英寸平板電腦拉宗,縱向:單面板,帶操作欄
7 英寸平板電腦辣辫,橫向:雙面板旦事,寬,帶操作欄
10 英寸平板電腦急灭,縱向:雙面板姐浮,窄,帶操作欄
10 英寸平板電腦葬馋,橫向:雙面板卖鲤,寬肾扰,帶操作欄
電視,橫向:雙面板蛋逾,寬集晚,帶操作欄
分2步來完成:
1.先定義類別:單/雙面板、是否帶操作欄区匣、寬/窄
定義在 res/layout/ 目錄下的某個 XML 文件中
2.再進行相應(yīng)的匹配:屏幕尺寸(小屏偷拔、7寸、10寸)亏钩、方向(橫莲绰、縱)
使用布局別名進行匹配
具體
1.在 res/layout/ 目錄下的某個 XML 文件中定義所需要的布局類別
(單/雙面板、是否帶操作欄姑丑、寬/窄)
res/layout/onepane.xml:(單面板)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="match_parent" />
</LinearLayout>
res/layout/onepane_with_bar.xml:(單面板帶操作欄)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:id="@+id/linearLayout1"
android:gravity="center"
android:layout_height="50dp">
<ImageView android:id="@+id/imageView1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/logo"
android:paddingRight="30dp"
android:layout_gravity="left"
android:layout_weight="0" />
<View android:layout_height="wrap_content"
android:id="@+id/view1"
android:layout_width="wrap_content"
android:layout_weight="1" />
<Button android:id="@+id/categorybutton"
android:background="@drawable/button_bg"
android:layout_height="match_parent"
android:layout_weight="0"
android:layout_width="120dp"
style="@style/CategoryButtonStyle"/>
</LinearLayout>
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="match_parent" />
</LinearLayout>
res/layout/twopanes.xml:(雙面板钉蒲,寬布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="400dp"
android:layout_marginRight="10dp"/>
<fragment android:id="@+id/article"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.ArticleFragment"
android:layout_width="fill_parent" />
</LinearLayout>
res/layout/twopanes_narrow.xml:(雙面板,窄布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="200dp"
android:layout_marginRight="10dp"/>
<fragment android:id="@+id/article"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.ArticleFragment"
android:layout_width="fill_parent" />
</LinearLayout>
2.使用布局別名進行相應(yīng)的匹配(屏幕尺寸(小屏彻坛、7寸、10寸)踏枣、方向(橫昌屉、縱)),可為resources設(shè)置bool,通過獲取其值來動態(tài)判斷目前已處在哪個適配布局
- res/values/layouts.xml:(默認布局)
<resources>
<item name="main_layout" type="layout">@layout/onepane_with_bar</item>
<bool name="has_two_panes">false</bool>
</resources>
- res/values-sw600dp-land/layouts.xml(大屏茵瀑、橫向间驮、雙面板、寬-Andorid 3.2版本后)
<resources>
<item name="main_layout" type="layout">@layout/twopanes</item>
<bool name="has_two_panes">true</bool>
</resources>
- res/values-large-land/layouts.xml(大屏马昨、橫向竞帽、雙面板、寬-Andorid 3.2版本前)
<resources>
<item name="main_layout" type="layout">@layout/twopanes</item>
<bool name="has_two_panes">true</bool>
</resources>
- res/values-large-port/layouts.xml(大屏鸿捧、縱向屹篓、單面板帶操作欄-Andorid 3.2版本前)
<resources>
<item name="main_layout" type="layout">@layout/onepane</item>
<bool name="has_two_panes">false</bool>
</resources>
使用自動拉伸位圖:Nine-Patch的圖片類型(也就是我們常說的.9png圖片)
支持不同屏幕大小通常情況下也意味著,你的圖片資源也需要有自適應(yīng)的能力匙奴。例如堆巧,一個按鈕的背景圖片必須能夠隨著按鈕大小的改變而改變。
如果你想使用普通的圖片來實現(xiàn)上述功能泼菌,你很快就會發(fā)現(xiàn)結(jié)果是令人失望的谍肤,因為運行時會均勻地拉伸或壓縮你的圖片。解決方案是使用nine-patch圖片哗伯,它是一種被特殊處理過的PNG圖片荒揣,你可以指定哪些區(qū)域可以拉伸而哪些區(qū)域不可以。
因而焊刹,當你設(shè)計需要在不同大小的控件中使用的圖片時系任,最好的方法就是用nine-patch圖片
注意:
- 必須要使用.9.png后綴名恳蹲,因為系統(tǒng)就是根據(jù)這個來區(qū)別nine-patch圖片和普通的PNG圖片的;
- 必須放在drawable下面
- 當你需要在一個控件中使用nine-patch圖片時,如
android:background="@drawable/button"系統(tǒng)就會根據(jù)控件的大小自動地拉伸你想要拉伸的部分