Android佈局概述

jamesehng發表於2016-12-07

佈局

佈局定義使用者介面的視覺結構,如Activity或應用小部件的 UI。您可以通過兩種方式宣告佈局:

在 XML 中宣告 UI 元素。Android 提供了對應於 View 類及其子類的簡明 XML 詞彙,如用於小部件和佈局的詞彙;
執行時例項化佈局元素。您的應用可以通過程式設計建立 View 物件和 ViewGroup 物件(並操縱其屬性)。
Android 框架讓您可以靈活地使用以下一種或兩種方法來宣告和管理應用的 UI。例如,您可以在 XML 中宣告應用的預設佈局,包括將出現在佈局中的螢幕元素及其屬性。然後,您可以在應用中新增可在執行時修改螢幕物件(包括那些已在 XML 中宣告的物件)狀態的程式碼。

您還應嘗試使用層次結構檢視器工具來除錯佈局—當您在模擬器或裝置上進行除錯時,它會顯示佈局屬性值、繪製具有內邊距/外邊距指示符的線框以及完整渲染檢視。
您可以利用 layoutopt 工具快速分析佈局和層次結構中是否存在低效環節或其他問題。
在 XML 中宣告 UI 的優點在於,您可以更好地將應用的外觀與控制應用行為的程式碼隔離。您的 UI 描述位於應用程式碼外部,這意味著您在修改或調整描述時無需修改您的原始碼並重新編譯。例如,您可以建立適用於不同螢幕方向、不同裝置螢幕尺寸和不同語言的 XML 佈局。此外,在 XML 中宣告佈局還能更輕鬆地顯示 UI 的結構,從而簡化問題除錯過程。因此,本文將側重於示範如何在 XML 中宣告佈局。如果您對在執行時例項化 View 物件感興趣,請參閱 ViewGroup 類和 View 類的參考資料。

一般而言,用於宣告 UI 元素的 XML 詞彙嚴格遵循類和方法的結構和命名方式,其中元素名稱對應於類名稱,屬性名稱對應於方法。實際上,這種對應關係往往非常直接,讓您可以猜到對應於類方法的 XML 屬性,或對應於給定 XML 元素的類。但請注意,並非所有詞彙都完全相同。在某些情況下,在命名上略有差異。例如,EditText 元素具有的 text 屬性對應的類方法是 EditText.setText()。

提示:如需瞭解有關不同佈局型別的更多資訊,請參閱常見佈局物件。

編寫 XML

您可以利用 Android 的 XML 詞彙,按照在 HTML 中建立包含一系列巢狀元素的網頁的相同方式快速設計 UI 佈局及其包含的螢幕元素。

每個佈局檔案都必須只包含一個根元素,並且該元素必須是檢視物件或 ViewGroup 物件。定義根元素之後,即可再以子元素的形式新增其他佈局物件或小部件,從而逐步構建定義佈局的檢視層次結構。例如,以下這個 XML 佈局使用垂直 LinearLayout 來儲存一個 TextView 和一個 Button:

<?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:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>複製程式碼

在 XML 中宣告佈局後,請在您的 Android 專案 res/layout/ 目錄中以 .xml 副檔名儲存檔案,以便其能夠正確編譯。

佈局資源文件中提供了有關佈局 XML 檔案語法的更多資訊。

載入 XML 資源

當您編譯應用時,每個 XML 佈局檔案都會編譯到一個 View 資源中。 您應該在 Activity.onCreate() 回撥實現中從您的應用程式碼載入佈局資源。請通過呼叫 setContentView(),以 R.layout.layout_file_name 形式向其傳遞對佈局資源的引用來執行此操作。例如,如果您的 XML 佈局儲存為 main_layout.xml,則需要像下面這樣為您的 Activity 載入該佈局:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}複製程式碼

啟動您的 Activity 時,Android 框架會呼叫 Activity 中的 onCreate() 回撥方法(請參閱Activity文件中有關生命週期的闡述)。

屬性

每個檢視物件和 ViewGroup 物件都支援各自的各類 XML 屬性。某些屬性是檢視物件的專用屬性(例如,TextView 支援 textSize 屬性),但這些屬性也會被任何可以擴充套件此類的檢視物件繼承。某些屬性通用於所有 View 物件,因為它們繼承自根 View 類(如 id 屬性)。此外,其他屬性被視為“佈局引數”,即描述 View 物件特定佈局方向的屬性,如該物件的父 ViewGroup 物件所定義的屬性。

ID

任何檢視物件都可能具有關聯的整型 ID,此 ID 用於在結構樹中對 View 物件進行唯一標識。編譯應用後,此 ID 將作為整型數引用,但在佈局 XML 檔案中,通常會在 id 屬性中為該 ID 賦予字串值。這是所有 View 物件共用的 XML 屬性(由 View 類定義),您會經常用到它。XML 標記內部的 ID 語法是:

android:id="@+id/my_button"
字串開頭處的 @ 符號指示 XML 解析程式應該解析並展開 ID 字串的其餘部分,並將其標識為 ID 資源。加號 (+) 表示這是一個新的資源名稱,必須建立該名稱並將其新增到我們的資源(在 R.java 檔案中)內。Android 框架還提供了許多其他 ID 資源。 引用 Android 資源 ID 時,不需要加號,但必須新增 android 軟體包名稱空間,如下所示:

android:id="@android:id/empty"
新增 android 軟體包名稱空間之後,現在,我們將從 android.R 資源類而非本地資源類引用 ID。

要想建立檢視並從應用中引用它們,常見的模式是:

在佈局檔案中定義一個檢視/小部件,併為其分配一個唯一的 ID:

<Button android:id="@+id/my_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/my_button_text"/>複製程式碼

然後建立一個 view 物件例項,並從佈局中捕獲它(通常使用 onCreate() 方法):
Button myButton = (Button) findViewById(R.id.my_button);
建立 RelativeLayout 時,為 view 物件定義 ID 非常重要。在相對佈局中,同級檢視可以定義其相對於其他同級檢視的佈局,同級檢視通過唯一的 ID 進行引用。

ID 不需要在整個結構樹中具有唯一性,但在您要搜尋的結構樹部分應具有唯一性(要搜尋的部分往往是整個結構樹,因此最好儘可能具有全域性唯一性)。

佈局引數

名為 layout_something 的 XML 佈局屬性可為檢視定義與其所在的 ViewGroup 相適的佈局引數。

每個 ViewGroup 類都會實現一個擴充套件 ViewGroup.LayoutParams 的巢狀類。此子類包含的屬性型別會根據需要為檢視組的每個子檢視定義尺寸和位置。 正如您在圖 1 中所見,父檢視組為每個子檢視(包括子檢視組)定義佈局引數。

這裡寫圖片描述
這裡寫圖片描述

圖 以視覺化方式表示的檢視層次結構,其中包含與每個檢視關聯的佈局引數。

請注意,每個 LayoutParams 子類都有自己的值設定語法。 每個子元素都必須定義適合其父元素的 LayoutParams,但父元素也可為其子元素定義不同的 LayoutParams。

所有檢視組都包括寬度和高度(layout_width 和 layout_height),並且每個檢視都必須定義它們。許多 LayoutParams 還包括可選的外邊距和邊框。

您可以指定具有確切尺寸的寬度和高度,但您多半不想經常這樣做。 在更多的情況下,您會使用以下常量之一來設定寬度或高度:

wrap_content 指示您的檢視將其大小調整為內容所需的尺寸。
match_parent 指示您的檢視儘可能採用其父檢視組所允許的最大尺寸。
一般而言,建議不要使用絕對單位(如畫素)來指定佈局寬度和高度, 而是使用相對測量單位,如密度無關畫素單位 (dp)、wrap_content 或 match_parent,這種方法更好,因為它有助於確保您的應用在各類尺寸的裝置螢幕上正確顯示。可用資源文件中定義了可接受的測量單位型別。

佈局位置

檢視的幾何形狀就是矩形的幾何形狀。檢視具有一個位置(以一對水平向左和垂直向上座標表示)和兩個尺寸(以寬度和高度表示)。 位置和尺寸的單位是畫素。

可以通過呼叫方法 getLeft() 和方法 getTop() 來檢索檢視的位置。前者會返回表示檢視的矩形的水平向左(或稱 X 軸) 座標。後者會返回表示檢視的矩形的垂直向上(或稱 Y 軸)座標。 這些方法都會返回檢視相對於其父項的位置。 例如,如果 getLeft() 返回 20,則意味著檢視位於其直接父項左邊緣向右 20 個畫素處。

此外,系統還提供了幾種便捷方法來避免不必要的計算,即 getRight() 和 getBottom()。 這些方法會返回表示檢視的矩形的右邊緣和下邊緣的座標。 例如,呼叫 getRight() 類似於進行以下計算:getLeft() + getWidth()。

尺寸、內邊距和外邊距

檢視的尺寸通過寬度和高度表示。檢視實際上具有兩對寬度和高度值。

第一對稱為測量寬度和測量高度。 這些尺寸定義檢視想要在其父項內具有的大小。 這些測量尺寸可以通過呼叫 getMeasuredWidth() 和 getMeasuredHeight() 來獲得。

第二對簡稱為寬度和高度,有時稱為繪製寬度和繪製高度。 這些尺寸定義檢視在繪製時和佈局後在螢幕上的實際尺寸。 這些值可以(但不必)與測量寬度和測量高度不同。 寬度和高度可以通過呼叫 getWidth() 和 getHeight() 來獲得。

要想測量其尺寸,檢視需要將其內邊距考慮在內。內邊距以檢視左側、頂部、右側和底部各部分的畫素數表示。 內邊距可用於以特定數量的畫素彌補檢視的內容。 例如,左側內邊距為 2,會將檢視的內容從左邊緣向右推 2 個畫素。 可以使用 setPadding(int, int, int, int) 方法設定內邊距,並通過呼叫 getPaddingLeft()、getPaddingTop()、getPaddingRight() 和 getPaddingBottom() 進行查詢。

儘管檢視可以定義內邊距,但它並不支援外邊距。 不過,檢視組可以提供此類支援。如需瞭解更多資訊,請參閱 ViewGroup 和 ViewGroup.MarginLayoutParams。

常見佈局

ViewGroup 類的每個子類都提供了一種獨特的方式來顯示您在其中巢狀的檢視。以下是 Android 平臺中內建的一些較為常見的佈局型別。

注:儘管您可以通過將一個或多個佈局巢狀在另一個佈局內來實現您的 UI 設計,但應該使您的佈局層次結構儘可能簡略。佈局的巢狀佈局越少,繪製速度越快(扁平的檢視層次結構優於深層的檢視層次結構)。

線性佈局

一種使用單個水平行或垂直行來組織子項的佈局。它會在視窗長度超出螢幕長度時建立一個滾動條。

相對佈局

讓您能夠指定子物件彼此之間的相對位置(子物件 A 在子物件 B 左側)或子物件與父物件的相對位置(與父物件頂部對齊)。

網頁檢視

顯示網頁。

使用介面卡構建佈局
如果佈局的內容是屬於動態或未預先確定的內容,您可以使用這樣一種佈局:在執行時通過子類 AdapterView 用檢視填充佈局。 AdapterView 類的子類使用 Adapter 將資料與其佈局繫結。Adapter 充當資料來源與 AdapterView 佈局之間的中間人—Adapter(從陣列或資料庫查詢等來源)檢索資料,並將每個條目轉換為可以新增到 AdapterView 佈局中的檢視。

介面卡支援的常見佈局包括:

列表檢視

顯示滾動的單列列表。

網格檢視

顯示滾動的行列網格。

使用資料填充介面卡檢視
您可以通過將 AdapterView 例項與 Adapter 繫結來填充 AdapterView(如 ListView 或 GridView),此操作會從外部來源檢索資料,並建立表示每個資料條目的 View。

Android 提供了幾個 Adapter 子類,用於檢索不同種類的資料和構建 AdapterView 的檢視。 兩種最常見的介面卡是:

ArrayAdapter

請在資料來源為陣列時使用此介面卡。預設情況下,ArrayAdapter 會通過在每個專案上呼叫 toString() 並將內容放入 TextView 來為每個陣列項建立檢視。
例如,如果您具有想要在 ListView 中顯示的字串陣列,請使用建構函式初始化一個新的 ArrayAdapter,為每個字串和字串陣列指定佈局:

ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, myStringArray);
此建構函式的引數是:

您的應用 Context
包含陣列中每個字串的 TextView 的佈局
字串陣列
然後,只需在您的 ListView 上呼叫 setAdapter():

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
要想自定義每個項的外觀,您可以重寫陣列中各個物件的 toString() 方法。或者,要想為 TextView 之外的每個項建立檢視(例如,如果您想為每個陣列項建立一個 ImageView),請擴充套件 ArrayAdapter 類並重寫 getView() 以返回您想要為每個項獲取的檢視型別。

SimpleCursorAdapter

請在資料來自 Cursor 時使用此介面卡。使用 SimpleCursorAdapter 時,您必須指定要為 Cursor 中的每個行使用的佈局,以及應該在哪些佈局檢視中插入 Cursor 中的哪些列。 例如,如果您想建立人員姓名和電話號碼列表,則可以執行一個返回 Cursor(包含對應每個人的行,以及對應姓名和號碼的列)的查詢。 然後,您可以建立一個字串陣列,指定您想要在每個結果的佈局中包含 Cursor 中的哪些列,並建立一個整型陣列,指定應該將每個列放入的對應檢視:
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER};
int[] toViews = {R.id.display_name, R.id.phone_number};
當您例項化 SimpleCursorAdapter 時,請傳遞要用於每個結果的佈局、包含結果的 Cursor 以及以下兩個陣列:

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
ListView listView = getListView();
listView.setAdapter(adapter);
然後,SimpleCursorAdapter 會使用提供的佈局,將每個 fromColumns 項插入對應的 toViews 檢視,為 Cursor 中的每個行建立一個檢視。

如果您在應用的生命週期中更改了介面卡讀取的底層資料,則應呼叫 notifyDataSetChanged()。此操作會通知附加的檢視,資料發生了變化,它應該自行重新整理。

處理點選事件

您可以通過實現 AdapterView.OnItemClickListener 介面來響應 AdapterView 中每一項上的點選事件。 例如:

// Create a message handling object as an anonymous class.
private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
// Do something in response to the click
}
};

listView.setOnItemClickListener(mMessageClickedHandler);

                        我的文章首發於公眾號
                        網際網路學術(IT-paper)
                    掃碼上車瞭解最新網際網路學術熱點複製程式碼

掃碼上車
掃碼上車

相關文章