6.實現針對具體場景的佈局
6.1 問題
應用必須是通用的,能在各種尺寸和方向的螢幕上執行。你需要為各種不同的情況準備好相應的佈局資源。
6.2解決方案
(API Level 4)
構建多個佈局檔案,然後通過資源限定符讓Android選擇合適的佈局。下面看看如何使用資源構建特定於不同的大小和方向螢幕的佈局,還可以學習在多個配置使用同一佈局時如何使用佈局別名來減少重複程式碼。
6.3 實現機制
1.針對不同方向
用下面的限定符可以為Activity的橫屏和豎屏方向建立不同的資源:
- resource-land
- resource-port
這適用於所有型別的資源,但通常都是用於佈局。因此,在專案中不能只有res/layout/一個目錄,而是應該有res/layout-port和res/layout-land/兩個目錄。
注意:
在實際開發中最好有一個不帶識別符號的預設資源目錄。這樣在Android裝置不能匹配任何給定的設定時也能有可用的資源。
2.針對不同尺寸
螢幕尺寸識別符號(物理尺寸,不要與畫素密度混淆)可以用於針對平板電腦等大螢幕裝置的資源。大部分情況下,一個佈局就能滿足所有手機裝置的螢幕尺寸,但如果在平板電腦上,就可能需要專門的平板電腦佈局來填充使用者所面對的大螢幕。
在Android 3.2 (API Level 13)之前,下面的資源識別符號用於標識螢幕的物理尺寸:
- resource-small : 螢幕尺寸最小為426dp*320dp
- resource-medium : 螢幕尺寸最小為 470dp*320dp
- resource-large : 螢幕尺寸最小為640dp*480dp
- resource-xlarge : 螢幕尺寸最小為960dp"720dp
隨著手持裝置和平板電腦中大螢幕裝置越來越普及,顯然只有4種廣義的型別不能完成避免資源定義上的重疊。因此,在Android3.2中引入一個新的基於螢幕真實尺寸(單位為dp(density-independent pixels))的系統。通過這個新系統,可以對物理螢幕使用以下資源識別符號 : - 最小寬度(resource-sw_dp): 螢幕在最短方向上(即方向無關)至少具有指定的dp。640dp*480dp的螢幕通常最小寬度為480dp。
- 寬度(resource-w_dp):螢幕在當前水平方向上(即方向無關)至少具體指定的dp。640dp*480dp的螢幕橫屏時寬度為640dp,而豎屏時寬度為480dp。
- 高度(resource-h_dp): 螢幕在當前垂直方向上(即方向無關)至少具有指定的dp。640dp*480dp的螢幕豎屏時高度為640dp,而橫屏時高度為480dp。
所以,要想在整個應用程式中包含平板電腦專用的佈局,只需要為舊版本的平板電腦新增/res/layout-large/目錄,為新版本的平板電腦新增res/layout-sw720dp/目錄即可。
3.佈局別名
在建立通用的應用程式UI時,還有最後一個概念需要討論,那就是佈局的別名。通常同一個佈局要用於多個不同的裝置配置,但在同一個資源目錄中使用多個資源限定符(如最小寬度限定符和傳統的尺寸限定符)會產生問題。這種情況常常會導致開發人員要在不同的目錄中建立同一個佈局的多個副本,這維護起來非常困難。
我們可以通過別名解決這個問題。首先在預設資源目錄中建立一個佈局檔案,然後就可以在以“資源-限定符”命名的目錄下為這個檔案建立多個別名。下面的程式碼片段演示了res/layout/main_tablet.xml檔案的別名:
<resources> <item name="main" type="layout">@layout/main_tablet</item> </resources>
name屬性表示別名的名稱,表示這個別名代表的資源會用在特定的配置上。當在程式碼中使用R.layout.main時,這個別名就會連結到main_tablet.xml檔案。這段程式碼可以放到res/values-xlarge/layout.xml和res/values-sw720dp/layout.xml中,這樣這兩個配置都會連結到同一個佈局。
4.綜合示例
接下來看一個綜合使用這些技術的簡單示例。定義一個Activity,在其中用程式碼載入一個佈局資源。不過,在該資源中這個佈局被定義了三次,分別針對豎屏、橫屏和平板電腦產生不同的結果。先看看以下程式碼中的Activity:
載入一個佈局的簡單Activity
public class UniversalActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
現在我們將為該Activity的不同配置定義三個單獨的佈局。以下三段程式碼顯示了預設的佈局、橫屏時的佈局和平板電腦配置的UI佈局。
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?> <!-- 預設佈局 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is the default layout" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button One" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button Two" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button Three" /> </LinearLayout>
res/layout-land/main.xml
<?xml version="1.0" encoding="utf-8"?> <!-- 橫屏時的佈局--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is a horizontal layout for LANDSCAPE" /> <!-- 三個按鈕會平均大小地填滿整個螢幕--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Button One" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Button Two" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Button Three" /> </LinearLayout> </LinearLayout>
res/layout/main_tablet.xml
<?xml version="1.0" encoding="utf-8"?> <!-- 平板電腦的佈局 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <!-- 所有的使用者按鈕佔用25%的螢幕寬度 --> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is the layout for TABLETS" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button One" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button Two" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button Three" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button Four" /> </LinearLayout> <!-- 顯示詳細內容的額外檢視 --> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:text="Detail View" android:background="#CCC"/> </LinearLayout>
一種方式是建立具有相同檔名的三個檔案並把它們放到各自的配置目錄中,例如用於橫屏的res/layout-land目錄和用於平板電腦的res/layout-large目錄。如果每個佈局檔案只使用一次,這種方案是非常好的,但我們想要在多個配置下重用每個佈局,因此在這個示例中採用了為這三個佈局建立限定別名的方式。以下四段程式碼顯示瞭如何將每個佈局連結到正確的配置。
res/values-large-land/layout.xml
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> <item name="main" type="layout">@layout/main_tablet</item> </resources>
res/value-sw600dp-land/layout.xml
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> <item name="main" type="layout">@layout/main_tablet</item> </resources>
res/values-xlarge/layout.xml
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> <item name="main" type="layout">@layout/main_tablet</item> </resources>
res/values-sw720dp/layout.xml
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> <item name="main" type="layout">@layout/main_tablet</item> </resources>
我們定義了一組配置來適應三種類型的裝置:手機/7英寸平板裝置/10英寸平板裝置。手機裝置會在豎屏模式時載入預設的佈局,在螢幕旋轉時載入橫屏佈局。因此只有只有一種配置使用這些檔案,所以檔案會被直接分別放置到res/layout和res/layout-land目錄下。
7英寸的平板裝置在之前的尺寸方案中通常會被定義為超大螢幕裝置,而在新的方案中它們是最小寬度為大約600dp的裝置。豎屏時,我們決定讓應用程式都使用預設的佈局,而在橫屏時會得到更大的空間,因此我們改為載入平板電腦的佈局。要想實現該功能,我們為橫屏模式建立多個配置目錄來匹配裝置的型別。同時使用了最小寬度限定符和廣義的尺寸限定符,這樣就可以相容舊平板電腦和新平板電腦。
10英寸的平板裝置在之前的尺寸方案中通常會被定義為超大螢幕裝置,而在新的方案中它們是最小寬度為大約720dp的裝置。對於這些裝置,螢幕大到可以在兩個方向都使用平板電腦佈局,因此只通過螢幕尺寸定義了配置目錄。和小平板電腦一樣,同時使用最小寬度和廣義尺寸限定符來保證可以相容所有的平板電腦版本。
在所有引用平板電腦佈局的場景中,我們只需要建立一個佈局檔案即可管理,這多虧使用了別名。現在,執行應用程式後,會看到Android是如何讓選擇合適的佈局來匹配我們的配置的。下圖顯示了手機上預設的佈局和橫屏時的佈局。

手機上豎屏時的佈局

手機上橫屏時的佈局
同樣的應用程式執行在7英寸的平板裝置上會在豎屏時顯示預設的佈局,但在橫屏上會顯示全屏的平板電腦佈局。在更大的10英寸上橫屏和豎屏都會顯示全屏的平板電腦佈局。通過Android資源選擇系統的強大能力,大大減少了為不同裝置型別提供不同的優化UI佈局的難度。