Android自適應(yīng)不同屏幕大小的全部方法
本文講述了Android自適應(yīng)不同屏幕大小的全部方法。分享給大家供大家參考,具體如下:
本文將告訴你如何讓你的應(yīng)用程序支持各種不同屏幕大小,主要通過(guò)以下幾種辦法:
讓你的布局能充分的自適應(yīng)屏幕;
根據(jù)屏幕的配置來(lái)加載合適的UI布局;
確保正確的布局應(yīng)用在正確的設(shè)備屏幕上;
提供可以根據(jù)屏幕大小自動(dòng)伸縮的圖片;
使用 "wrap_content" 和 "match_parent"。
為了確保你的布局能夠自適應(yīng)各種不同屏幕大小,你應(yīng)該在布局的視圖中使用"wrap_content"和"match_parent"來(lái)確定它的寬和高。如果你使用了"wrap_content",相應(yīng)視圖的寬和高就會(huì)被設(shè)定成剛好能夠包含視圖中內(nèi)容的最小值。而如果你使用了"match_parent"(在Android API 8之前叫作"fill_parent"),就會(huì)讓視圖的寬和高延伸至充滿整個(gè)父布局。
通過(guò)使用"wrap_content"和"match_parent"來(lái)替代硬編碼的方式定義視圖大小,你的視圖要么僅僅使用了需要的那邊一點(diǎn)空間,要么就會(huì)充滿所有可用的空間。例如:
<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>
注意上面的例子中是如何使用"wrap_content"和"match_parent"來(lái)給控件定義寬高的,這讓整個(gè)布局可以正確地適應(yīng)不同屏幕的大小,甚至是橫屏。
下圖是這個(gè)布局分別在豎屏和橫屏?xí)r顯示的結(jié)果,注意控件的寬和高是根據(jù)屏幕自適應(yīng)的。
使用RelativeLayout
通過(guò)多層嵌套LinearLayout和組合使用"wrap_content"和"match_parent"已經(jīng)可以構(gòu)建出足夠復(fù)雜的布局。但是LinearLayout無(wú)法允許你準(zhǔn)確地控制子視圖之前的位置關(guān)系,所有LinearLayout中的子視圖只能簡(jiǎn)單的一個(gè)挨著一個(gè)地排列。如果你需要讓子視圖能夠有更多的排列方式,而不是簡(jiǎn)單地排成一行或一列,使用RelativeLayout將會(huì)是更好的解決方案。RelativeLayout允許布局的子控件之間使用相對(duì)定位的方式控制控件的位置,比如你可以讓一個(gè)子視圖居屏幕左側(cè)對(duì)齊,讓另一個(gè)子視圖居屏幕右側(cè)對(duì)齊。
例如:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/label" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Type here:"/> <EditText android:id="@+id/entry" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/label"/> <Button android:id="@+id/ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/entry" android:layout_alignParentRight="true" android:layout_marginLeft="10dp" android:text="OK" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toLeftOf="@id/ok" android:layout_alignTop="@id/ok" android:text="Cancel" /> </RelativeLayout>
下圖展示了這個(gè)布局在QVGA屏幕上顯示的結(jié)果。
下圖展示了這個(gè)布局在一個(gè)更大的屏幕上顯示的結(jié)果。
可以注意到,即使屏幕的大小改變,視圖之前的相對(duì)位置都沒(méi)有改變。
使用Size限定符
雖然使用以上幾種方式可以解決屏幕適配性的問(wèn)題,但是那些通過(guò)伸縮控件來(lái)適應(yīng)各種不同屏幕大小的布局,未必就是提供了最好的用戶體驗(yàn)。你的應(yīng)用程序應(yīng)該不僅僅實(shí)現(xiàn)了可自適應(yīng)的布局,還應(yīng)該提供一些方案根據(jù)屏幕的配置來(lái)加載不同的布局,可以通過(guò)配置限定符(configuration qualifiers)來(lái)實(shí)現(xiàn)。配置限定符允許程序在運(yùn)行時(shí)根據(jù)當(dāng)前設(shè)備的配置自動(dòng)加載合適的資源(比如為不同尺寸屏幕設(shè)計(jì)不同的布局)。
現(xiàn)在有很多的應(yīng)用程序?yàn)榱酥С执笃猎O(shè)備,都會(huì)實(shí)現(xiàn)“two pane”模式(程序會(huì)在左側(cè)的面板上展示一個(gè)包含子項(xiàng)的List,在右側(cè)面板上展示內(nèi)容)。平板和電視設(shè)備的屏幕都很大,足夠同時(shí)顯示兩個(gè)面板,而手機(jī)屏幕一次只能顯示一個(gè)面板,兩個(gè)面板需要分開(kāi)顯示。所以,為了實(shí)現(xiàn)這種布局,你可能需要以下文件:
res/layout/main.xml,single-pane(默認(rèn))布局:
<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-large/main.xml,two-pane布局:
<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>
請(qǐng)注意第二個(gè)布局的目錄名中包含了large限定符,那些被定義為大屏的設(shè)備(比如7寸以上的平板)會(huì)自動(dòng)加載此布局,而小屏設(shè)備會(huì)加載另一個(gè)默認(rèn)的布局。
使用Smallest-width限定符
使用Size限定符有一個(gè)問(wèn)題會(huì)讓很多程序員感到頭疼,large到底是指多大呢?很多應(yīng)用程序都希望能夠更自由地為不同屏幕設(shè)備加載不同的布局,不管它們是不是被系統(tǒng)認(rèn)定為"large"。這就是Android為什么在3.2以后引入了"Smallest-width"限定符。
Smallest-width限定符允許你設(shè)定一個(gè)具體的最小值(以dp為單位)來(lái)指定屏幕。例如,7寸的平板最小寬度是600dp,所以如果你想讓你的UI在這種屏幕上顯示two pane,在更小的屏幕上顯示single pane,你可以使用sw600dp來(lái)表示你想在600dp以上寬度的屏幕上使用two pane模式。
res/layout/main.xml,single-pane(默認(rèn))布局:
<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-sw600dp/main.xml,two-pane布局:
<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>
這意味著,那些最小屏幕寬度大于600dp的設(shè)備會(huì)選擇layout-sw600dp/main.xml(two-pane)布局,而更小屏幕的設(shè)備將會(huì)選擇layout/main.xml(single-pane)布局。
然而,使用早于Android 3.2系統(tǒng)的設(shè)備將無(wú)法識(shí)別sw600dp這個(gè)限定符,所以你還是同時(shí)需要使用large限定符。這樣你就需要在res/layout-large和res/layout-sw600dp目錄下都添加一個(gè)相同的main.xml。下節(jié)你將會(huì)看到如何避免重復(fù)定義這種布局的技巧。
使用布局別名
Smallest-width限定符僅在Android 3.2及之后的系統(tǒng)中有效。因而,你也需要同時(shí)使用Size限定符(small, normal, large和xlarge)來(lái)兼容更早的系統(tǒng)。例如,你想手機(jī)上顯示single-pane界面,而在7寸平板和更大屏的設(shè)備上顯示multi-pane界面,你需要提供以下文件:
res/layout/main.xml: single-pane布局
res/layout-large: multi-pane布局
res/layout-sw600dp: multi-pane布局
最后的兩個(gè)文件是完全相同的,為了要解決這種重復(fù),你需要使用別名技巧。例如,你可以定義以下布局:
res/layout/main.xml, single-pane布局
res/layout/main_twopanes.xml, two-pane布局
加入以下兩個(gè)文件:
res/values-large/layout.xml:
<resources> <item name="main" type="layout">@layout/main_twopanes</item> </resources>
res/values-sw600dp/layout.xml:
<resources> <item name="main" type="layout">@layout/main_twopanes</item> </resources>
最后兩個(gè)文件有著相同的內(nèi)容,但是它們并沒(méi)有真正去定義布局,它們僅僅只是給main定義了一個(gè)別名main_twopanes。這樣兩個(gè)layout.xml都只是引用了@layout/main_twopanes,就避免了重復(fù)定義布局文件的情況。
使用Orientation限定符
有些布局會(huì)在橫屏和豎屏的情況下都顯示的很好,但是多數(shù)情況下這些布局都可以再調(diào)整的。在News Reader示例程序中,布局在不同屏幕尺寸和不同屏幕方向中是這樣顯示的:
小屏幕, 豎屏: 單面板, 顯示logo;
小屏幕, 橫屏: 單面板, 顯示logo;
7寸平板, 豎屏: 單面板, 顯示action bar;
7寸平板, 橫屏: 雙面板, 寬, 顯示action bar;
10寸平板, 豎屏: 雙面板, 窄, 顯示action bar;
10寸平板, 橫屏: 雙面板, 寬, 顯示action bar;
電視, 橫屏: 雙面板, 寬, 顯示action bar;
所有這些布局都是定義在 res/layout/ 這個(gè)目錄下,為了要讓設(shè)備根據(jù)屏幕配置來(lái)加載正確的布局,程序需要使用布局別名來(lái)實(shí)現(xiàn)。
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>
現(xiàn)在所有需要的布局都已經(jīng)定義好了,剩下的只要使用限定符來(lái)讓各個(gè)設(shè)備根據(jù)屏幕配置加載正確的布局了。你現(xiàn)在就可以使用布局別名技術(shù):
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:
<resources> <item name="main_layout" type="layout">@layout/twopanes</item> <bool name="has_two_panes">true</bool> </resources>
res/values-sw600dp-port/layouts.xml:
<resources> <item name="main_layout" type="layout">@layout/onepane</item> <bool name="has_two_panes">false</bool> </resources>
res/values-large-land/layouts.xml:
<resources> <item name="main_layout" type="layout">@layout/twopanes</item> <bool name="has_two_panes">true</bool> </resources>
res/values-large-port/layouts.xml:
<resources> <item name="main_layout" type="layout">@layout/twopanes_narrow</item> <bool name="has_two_panes">true</bool> </resources>
使用Nine-Patch圖片
支持不同屏幕大小通常情況下也意味著,你的圖片資源也需要有自適應(yīng)的能力。例如,一個(gè)按鈕的背景圖片必須能夠隨著按鈕大小的改變而改變。
如果你想使用普通的圖片來(lái)實(shí)現(xiàn)上述功能,你很快就會(huì)發(fā)現(xiàn)結(jié)果是令人失望的,因?yàn)檫\(yùn)行時(shí)會(huì)均勻地拉伸或壓縮你的圖片。解決方案是使用nine-patch圖片,它是一種被特殊處理過(guò)的PNG圖片,你可以指定哪些區(qū)域可以拉伸而哪些區(qū)域不可以。
因而,當(dāng)你設(shè)計(jì)需要在不同大小的控件中使用的圖片時(shí),最好的方法就是用nine-patch圖片。為了將圖片轉(zhuǎn)換成nine-patch圖片,你可以從一張普通的圖片開(kāi)始:
然后通過(guò)SDK中帶有的draw9patch工具打開(kāi)這張圖片(工具位置在SDK的tools目錄下),你可以在圖片的左邊框和上邊框繪制來(lái)標(biāo)記哪些區(qū)域可以被拉伸。你也可以在圖片的右邊框和下邊框繪制來(lái)標(biāo)記內(nèi)容需要放置在哪個(gè)區(qū)域。結(jié)果如下圖所示:
注意圖片邊框上的黑色像素,在上邊框和左邊框的部分表示當(dāng)圖片需要拉伸時(shí)就拉伸黑點(diǎn)標(biāo)記的位置。在下邊框和右邊框的部分表示內(nèi)容將會(huì)被放置的區(qū)域。
同時(shí)需要注意,這張圖片的后綴名是 .9.png。你必須要使用這個(gè)后綴名,因?yàn)橄到y(tǒng)就是根據(jù)這個(gè)來(lái)區(qū)別nine-patch圖片和普通的PNG圖片的。
當(dāng)你需要在一個(gè)控件中使用nine-patch圖片時(shí)(如android:background="@drawable/button"),系統(tǒng)就會(huì)根據(jù)控件的大小自動(dòng)地拉伸你想要拉伸的部分,效果如下圖所示:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android異步方法以同步方式實(shí)現(xiàn)
- Android多線程之同步鎖的使用
- Android seekbar(自定義)控制音量同步更新
- android-獲取網(wǎng)絡(luò)時(shí)間、獲取特定時(shí)區(qū)時(shí)間、時(shí)間同步的方法
- android中ListView數(shù)據(jù)刷新時(shí)的同步方法
- Android實(shí)現(xiàn)歌曲播放時(shí)歌詞同步顯示具體思路
- Android獲取點(diǎn)擊屏幕的位置坐標(biāo)
- Android 5.0+ 屏幕錄制實(shí)現(xiàn)的示例代碼
- Android6.0開(kāi)發(fā)中屏幕旋轉(zhuǎn)原理與流程分析
- Android手機(jī)屏幕同步工具asm.jar
相關(guān)文章
Android編程實(shí)現(xiàn)TCP客戶端的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)TCP客戶端的方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)TCP客戶端的原理及數(shù)據(jù)通信的相關(guān)技巧,需要的朋友可以參考下2016-04-04Android學(xué)習(xí)教程之2D繪圖基礎(chǔ)及繪制太極圖
這篇文章主要給大家介紹了Android中2D繪圖基礎(chǔ)的相關(guān)資料,文中介紹了繪圖的基礎(chǔ)內(nèi)容,以及通過(guò)Canvas和Paint實(shí)現(xiàn)繪制太極圖的詳細(xì)過(guò)程,對(duì)各位Android新手開(kāi)發(fā)者們具有一定的參考價(jià)值,需要的朋友下面來(lái)一起看看吧。2017-04-04Flutter使用socketIo實(shí)現(xiàn)實(shí)時(shí)通訊
本文主要介紹了Flutter使用socketIo實(shí)現(xiàn)實(shí)時(shí)通訊,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07android中圖片的三級(jí)緩存cache策略(內(nèi)存/文件/網(wǎng)絡(luò))
實(shí)現(xiàn)圖片緩存也不難,需要有相應(yīng)的cache策略。這里我采用 內(nèi)存-文件-網(wǎng)絡(luò) 三層cache機(jī)制,其中內(nèi)存緩存包括強(qiáng)引用緩存和軟引用緩存(SoftReference),其實(shí)網(wǎng)絡(luò)不算cache,這里姑且也把它劃到緩存的層次結(jié)構(gòu)中2013-06-06Android開(kāi)發(fā)實(shí)現(xiàn)TextView顯示豐富的文本
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)TextView顯示豐富的文本,涉及Android中TextView的使用技巧,需要的朋友可以參考下2015-12-12