檢視區塊中的版面配置

試試 Compose
Jetpack Compose 是 Android 推薦的 UI 工具包。瞭解如何在 Compose 中處理版面配置。

版面配置定義了應用程式使用者介面的結構,例如活動。版面配置中的所有元素都使用 ViewViewGroup 物件階層建立。View 通常會擷取使用者可查看並互動的內容。ViewGroup 是隱藏容器,用於定義 View 和其他 ViewGroup 物件的版面配置結構,如圖 1 所示。

圖 1. 插圖:定義 UI 版面配置的檢視區塊階層。

View 物件通常稱為「小工具」,可能是多個子類別中的一個,例如 ButtonTextViewViewGroup 物件通常稱為「版面配置」,可能是提供不同版面配置結構的眾多類型中的一個,例如 LinearLayoutConstraintLayout

您可以透過下列兩種方式宣告版面配置:

  • 在 XML 中宣告 UI 元素。Android 提供符合 View 類別和子類別的簡單 XML 詞彙,例如用於小工具和版面配置的詞彙。您也可以利用 Android Studio 的版面配置編輯器,透過拖曳式介面建立 XML 版面配置。

  • 在執行階段將版面配置元素執行個體化。您的應用程式可以建立 ViewViewGroup 物件,並透過程式輔助方式控管其屬性。

透過 XML 宣告使用者介面,可以讓您分開應用程式呈現方式和控管其行為的程式碼。使用 XML 檔案也可以輕鬆為不同的螢幕大小和方向提供不同的版面配置。詳情請參閱「支援不同的螢幕大小」。

Android 架構讓您可以靈活運用其中一種或兩種方法,以構建應用程式使用者介面。舉例來說,您可以在 XML 中宣告應用程式的預設版面配置,然後在執行階段修改版面配置。

寫入 XML

使用 Android 的 XML 詞彙,您就可以利用一系列巢狀元素,快速設計使用者介面版面配置和其中包含的螢幕元素,方法和在 HTML 中建立網頁時一樣。

每個版面配置檔案都只能含有一個根元素 (必須是 ViewViewGroup 物件)。定義根元素後,您就可以將其他版面配置物件或小工具新增為子項元素,並逐步定義用於定義版面配置的 View 階層。例如,以下示範如何使用垂直 LinearLayout 以保存 TextViewButton 的 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: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 宣告版面配置後,請將檔案儲存到 .xml 擴充功能的 Android 專案的 res/layout/ 目錄中,即可正確編譯。

如要進一步瞭解版面配置 XML 檔案的語法,請參閱「版面配置資源」。

載入 XML 資源

編譯應用程式時,每個 XML 版面配置檔案都會編譯成 View 資源。在應用程式的 Activity.onCreate() 回呼實作中載入版面配置資源。方法是呼叫 setContentView(),並採用下列形式將版面配置資源參照傳送至 R.layout.layout_file_name:舉例來說,如果將 XML 版面配置儲存為 main_layout.xml,即可載入該版面配置進行 Activity,如下所示:

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

Activity 啟動時,Android 架構會呼叫 Activity 中的 onCreate() 回呼方法。如要進一步瞭解活動生命週期,請參閱「活動簡介」。

屬性

每個 ViewViewGroup 物件都支援各種 XML 屬性。部分屬性是View物件專屬,舉例來說,TextView 支援 textSize 屬性。不過,任何擴充此類別的 View 物件也會沿用這些屬性。有些對於所有 View 物件很常見,因為其是沿用自根 View 類別,例如 id 屬性。其他屬性會視為「版面配置參數」,這些屬性會說明 View 物件的特定版面配置方向,並由該物件的父項 ViewGroup 物件定義。

ID

任何 View 物件都有一個相關聯的整數 ID,以識別樹狀子目錄中的 View。應用程式經過編譯時,這個 ID 會參照為整數,但 ID 在版面配置 XML 檔案中通常會以 id 屬性指派為字串。這是所有 View 物件通用的 XML 屬性,由 View 類別定義。你經常使用這項功能。XML 標記中 ID 的語法如下:

android:id="@+id/my_button"

字串開頭的「at」符號 (@) 表示 XML 剖析器會剖析並展開其他 ID 字串,並將其識別為 ID 資源。加號 (+) 表示這是新的資源名稱,必須建立並新增至 R.java 檔案中的資源。

Android 架構提供許多其他 ID 資源。參照 Android 資源 ID 時,您不需要使用加號,但必須新增 android 套件命名空間,如下所示:

android:id="@android:id/empty"

android 套件命名空間表示您要參照 android.R 資源類別 (而非本機資源類別) 的 ID。

如要在應用程式中建立資料檢視並做為參照,可以使用下列常見模式:

  1. 在版面配置檔案中定義檢視區塊,並指派專屬 ID,如下列範例所示:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
  2. 建立檢視畫面物件的執行個體,並從版面配置擷取 (通常是在 onCreate() 方法中),如下列範例所示:

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)

    Java

    Button myButton = (Button) findViewById(R.id.my_button);

建立 RelativeLayout 時,請務必定義檢視畫面物件的 ID。在相對版面配置中,同層級檢視畫面可定義與另一個同層級檢視畫面對應的版面配置,該檢視畫面由專屬 ID 參照。

ID 在整個樹狀結構中不必不重複,但必須在搜尋樹狀結構中不重複。這通常是整個樹狀結構,因此盡可能不重複。

版面配置參數

名為 layout_something 的 XML 版面配置屬性為 View 定義了適合其所在的 ViewGroup 的版面配置參數。

每個 ViewGroup 類別都會導入可擴充 ViewGroup.LayoutParams 的巢狀類別。這個子類別包含定義每個子檢視畫面大小與位置的資源類型,適用於檢視區塊群組。如圖 2 所示,父項檢視區塊群組會為每個子項檢視畫面 (包括子項檢視區塊群組) 定義版面配置參數。

圖 2. 以視覺化方式呈現檢視區塊階層,以及與每個檢視畫面相關的版面配置參數。

每個 LayoutParams 子類別對於設定值有自己的語法。每個子項元素都必須定義適用於其父項的 LayoutParams,不過也可能為本身的子項定義不同的 LayoutParams

所有檢視區塊群組都包含寬度和高度 (使用 layout_widthlayout_height),而且每個檢視畫面都必須定義這些群組。許多 LayoutParams 包含選用邊界和邊框。

您可以針對確切的測量指定寬度和高度,但您通常不會這麼做。您通常會使用以下常數來設定寬度或高度:

  • wrap_content:指示檢視區塊將自身調整至內容所需的大小。
  • match_parent:可讓您的檢視畫面佔據父項檢視區塊群組允許的範圍。

一般來說,我們不建議使用絕對單位 (例如像素) 指定版面配置的寬度和高度。較好的做法是使用相對測量,例如密度獨立像素單位 (dp)、wrap_contentmatch_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() 進行查詢。

雖然檢視畫面可以定義邊框間距,但並不支援邊界。不過,檢視區塊群組支援邊界。詳情請參閱 ViewGroupViewGroup.MarginLayoutParams

如要進一步瞭解維度,請參閱維度一文。

除了以程式輔助方式設定邊界和邊框間距,您也可以在 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:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

上例顯示套用的邊界和邊框間距。TextView 的邊界和邊框間距都相同,Button 則顯示如何分別套用至不同邊緣。

常見版面配置

ViewGroup 類別的每個子類別都提供了特定方式,讓您查看其中的巢狀結構。最彈性的版面配置類型是 ConstraintLayout,也是提供最佳工具的類型,可讓版面配置階層保持淺層。

以下是 Android 平台內建的一些常見版面配置類型。

建立線性版面配置

將子項版面配置整理成單一水平或垂直列,如果視窗長度超出螢幕長度,則會建立捲軸。

建立動態清單

如果版面配置的內容是動態或尚未預先決定,可以使用 RecyclerViewAdapterView 的子類別。RecyclerView 通常是較好的選擇,因為它比 AdapterView 更有效率地使用記憶體。

使用 RecyclerViewAdapterView 可用的常見版面配置如下:

清單

顯示捲動單一欄清單。

格線

顯示欄和列的捲動網格。

RecyclerView 提供更多可能性,並可建立自訂版面配置管理工具

在轉接程式檢視畫面中填入資料

您可以將 AdapterView 執行個體繫結至 Adapter,以填入 ListViewGridViewAdapterView,這樣就能從外部來源擷取資料並建立 View,以代表每個資料項目。

Android 提供 Adapter 的多個子類別,可用於擷取 AdapterView 的各種類型資料及建立檢視畫面。兩種最常見的轉接程式如下:

ArrayAdapter
當資料來源是陣列時,請使用這個轉接程式。根據預設,ArrayAdapter 會為每個項目呼叫 toString() 並將內容放入 TextView,藉此為每個陣列項目建立檢視畫面。

舉例來說,如果您想要在 ListView 中顯示字串陣列,請使用建構函式初始化新的 ArrayAdapter,以指定每個字串和字串陣列的版面配置:

Kotlin

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    

Java

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, myStringArray);
    

這個建構函式的引數如下:

  • 您的應用程式 Context
  • 包含陣列中每個字串的 TextView 的版面配置
  • 字串陣列

然後在 ListView 上呼叫 setAdapter()

Kotlin

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    

Java

    ListView listView = (ListView) findViewById(R.id.listview);
    listView.setAdapter(adapter);
    

如要自訂每個項目的外觀,您可以覆寫陣列中物件的 toString() 方法。或者,如要針對 TextView 以外的每個項目建立檢視畫面 (例如,每個陣列項目需要 ImageView),請擴充 ArrayAdapter 類別,然後覆寫 getView() 即可為每個項目傳回您想要的檢視畫面類型。

SimpleCursorAdapter
如果資料來自 Cursor,請使用這個轉接程式。使用 SimpleCursorAdapter 時,請針對 Cursor 中的每個列指定要使用的版面配置,以及要插入版面配置檢視畫面的 Cursor 欄。舉例來說,假設要建立一份包含使用者名稱和電話號碼的清單,您可以執行一次查詢,該查詢會傳回一個 Cursor,其中包含每個人的列以及名稱和數字的欄。然後建立字串陣列,指定每個結果版面配置中來自 Cursor 的欄,以及指定各欄應放置在對應檢視畫面的整數陣列:

Kotlin

    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                              ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    

Java

    String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                            ContactsContract.CommonDataKinds.Phone.NUMBER};
    int[] toViews = {R.id.display_name, R.id.phone_number};
    

SimpleCursorAdapter 執行個體化時,請傳遞每個結果要使用的版面配置、包含結果的 Cursor,以及下列兩個陣列:

Kotlin

    val adapter = SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    

Java

    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 中每個項目的點擊事件。例如:

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click.
}

Java

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

listView.setOnItemClickListener(messageClickedHandler);

其他資源

如要瞭解版面配置的用法,請參閱 GitHub 上的 Sunflower 試用版應用程式