原文地址:http://android.xsoftlab.net/training/multiscreen/index.html
Android運行于數(shù)以百計不同尺寸的裝備上。范圍小得手持移動電話,大到電視裝備。因此,在設(shè)計APP時應(yīng)當(dāng)統(tǒng)籌到盡量多的屏幕尺寸。這樣才能照顧到較多的潛伏用戶。
但是僅僅斟酌不同的裝備類型還不夠。每種尺寸為用戶提供了不同的可能性與挑戰(zhàn),所以為了使用戶感到滿意,利用程序需要做的不單單是支持多樣的屏幕:它還必須對每種屏幕結(jié)構(gòu)將用戶體驗優(yōu)化到最好。
這節(jié)課將會學(xué)習(xí)如何實現(xiàn)針對屏幕結(jié)構(gòu)優(yōu)化的用戶界面。
Note: 這節(jié)課與相干示例程序均使用的是support library。
這節(jié)課將會學(xué)習(xí)通過以下方式來支持不同的屏幕尺寸:
為了使布局可以靈活的適配不同的屏幕尺寸,應(yīng)當(dāng)對某些View組件的width,height屬性使用”wrap_content”或”match_parent”。如果使用了”wrap_content”,那末View的高寬會被設(shè)置為View內(nèi)容所需的最小尺寸。但是”match_parent”會使View的高寬擴大到父布局的尺寸大小。
通過使用”wrap_content”或”match_parent”可使View高寬擴大到View所需要的大小或擴大到父布局的可用空間:
<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”的。這可使布局正確的適配不同的屏幕尺寸及方向。
下圖是布局在垂直及水平方向的示例。注意View的尺寸會自動適配屏幕的高寬:
你可使用LinearLayout結(jié)合”wrap_content”或”match_parent”構(gòu)造相對復(fù)雜的布局。但是,LinearLayout不能夠精確的控制子View的相對關(guān)系。在LinearLayout中View只能簡單的被線性排列。如果需要調(diào)劑View間的相對關(guān)系,1種較好的解決方式就是使用RelativeLayout,它允許指定View間的相對關(guān)系。下面的示例中,你可以指定1個View靠著另外一個View的左側(cè),而另外一個View的右側(cè)則靠著屏幕的右側(cè)。
<?xml version="1.0" encoding="utf⑻"?>
<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>
下圖是該布局在QVGA屏幕中的顯示效果:
下圖是該布局在大屏幕中的顯示效果:
要注意雖然這些View的尺寸產(chǎn)生了改變,但是其它之間的相對關(guān)系還是保存了下來。
上面我們學(xué)習(xí)了如何利用靈活布局或相對布局來匹配不同的屏幕,但是這對匹配任何屏幕來講還不夠好。因此,利用程序不單單只是實現(xiàn)靈活的布局,還應(yīng)當(dāng)對不同的屏幕配置提供相應(yīng)的布局??梢酝ㄟ^configuration qualifiers中所描寫的內(nèi)容學(xué)習(xí)具體細(xì)節(jié),它可使程序在運行時根據(jù)當(dāng)前的屏幕配置來自動選擇對應(yīng)的資源。
比如說,很多利用程序針對大屏幕實現(xiàn)了”two pane”的模式。平板與電視大到足以同時顯示兩個面板,但是移動電話只能同時顯示其中1個。所以,要實現(xiàn)這類布局,項目中應(yīng)當(dāng)含有以下文件:
<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>
<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>
要注意第2個布局的目錄路徑的large標(biāo)識符。這個布局會在屏幕類型為large時被采取(比如,7英寸的平板或更大的裝備)。其它布局則會被小型裝備所采取。
開發(fā)者會遇到的困難之1就是在3.2之前Android的裝備只有”large”屏幕尺寸,這包括了Dell Streak、Galaxy Tab和常規(guī)的7英寸平板。但是,很多利用程序希望可以在這個范圍下不同尺寸的裝備中展現(xiàn)不同的布局,比如5英寸的裝備或7英寸的裝備,乃至是所有的”large”裝備都想斟酌在內(nèi)。這就是為何Android會3.2的版本中引入”最小寬度(Smallest-width)”標(biāo)識符的緣由。
最小寬度限定符允許將最小寬度為給定的dp寬度的裝備作為目標(biāo)。比如說,經(jīng)典的7英寸平板的最小寬度為600dp,如果希望可以在這塊屏幕上同時放置兩個面板的話,可以直接使用上臉部分中所介紹的雙面板布局。不過這里則不是large尺寸標(biāo)識符,而是使用sw600dp尺寸標(biāo)識符,用于指明該布局運行于最小寬度為600dp的裝備上。
<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>
<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時才會選擇layout-sw600dp/main.xml,再略微小點的布局則會選擇layout/main.xml。
但是,以上部份在3.2之前其實不會有甚么效果,由于3.2之前的系統(tǒng)辨認(rèn)不出sw600dp這類尺寸標(biāo)識符,所以最好還是保存large標(biāo)識符,所以會有1個名為res/layout-large/main.xml的布局文件,其中的內(nèi)容與res/layout-sw600dp/main.xml保持1致。下面的部份將會介紹1種技術(shù)來避免重復(fù)的布局文件。
最小寬度限定符只在Android 3.2上開始可用。因此,開發(fā)者還應(yīng)當(dāng)繼續(xù)使用抽象尺寸標(biāo)志(small, normal, large及xlarge)來兼容較早的版本。所以,如果希望在移動電話中顯示單面板UI,在其它較大的屏幕中采取多面板UI,那末項目中應(yīng)當(dāng)含有以下文件:
這后面兩個文件是完全相同的,由于其中1個是用來匹配Android 3.2的裝備的,而另外一個是用來匹配較早版本的裝備的。
為了不存在這類重復(fù)的文件,可使用別名文件技術(shù)。比如,你可以定義以下布局文件:
然后添加兩個文件:
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
后面這兩個文件含有相同的內(nèi)容,但是它們實際上并沒有定義布局。它們只是將main_twopanes的別名設(shè)置為了main而已。1旦這些文件包括了large 或sw600dp,那末所有的系統(tǒng)則不會再專門辨別版本。
有些布局在垂直及水平方向上均表現(xiàn)良好。但在新聞瀏覽示例APP中,針對每種屏幕尺寸與方向均專門定義了布局:
上面所有的布局文件都被放置在res/layout/目錄下。為了使每種布局與相干的屏幕配置產(chǎn)生關(guān)聯(lián),App使用布局別名的方式來匹配每項配置:
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>
以上對所有可能的布局均作了定義,它們會與相干的屏幕配置產(chǎn)生映照關(guān)系:
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>
支持不同的屏幕尺寸一樣意味著圖片資源一樣也需要自動適配不同的尺寸。比如,1張按鈕的背景圖必須匹配按鈕的形狀。
如果要將1張簡圖片利用在組件中,必須敏銳的意想到結(jié)果可能不是想象中那樣,由于在運行時將會拉伸或緊縮圖片。解決辦法就是使用9宮格位圖,它是1種特殊的PNG格式的文件,它內(nèi)部指明了哪部份區(qū)域可以被拉伸,哪部份不可以。
因此,在設(shè)計位圖時應(yīng)當(dāng)首先選用9宮格。為了將位圖轉(zhuǎn)化為9宮格位圖,你可以從1張有規(guī)律的圖片開始(下圖被放大了4倍)。
然后通過draw9patch工具將該圖片打開,該工具位于tools/目錄下,它可以用來標(biāo)記哪塊區(qū)域可以被拉伸。拉伸標(biāo)記位于圖片的左側(cè)和頂部。你也能夠通過在右側(cè)及底部繪點的方式來定義內(nèi)容區(qū)域,以下圖所示:
注意邊上那些黑色的像素點。左側(cè)和頂部的點指明了圖象可以被拉伸的區(qū)域,右側(cè)和頂部的點指明了內(nèi)容區(qū)域。
最后還要注意.9.png的擴大名。必須使用該擴大名,由于這是框架將其與普通圖片辨別的1種方式。
當(dāng)在使用這張圖片作為背景時,框架會將圖片拉伸以適應(yīng)按鈕的尺寸,以下圖所示:
上一篇 線性表-順序表、鏈表類模板的實現(xiàn)(數(shù)據(jù)結(jié)構(gòu)基礎(chǔ) 第2周)
下一篇 Jersey構(gòu)建Java RestFul Webservice基礎(chǔ)學(xué)習(xí)教程