Compose 版面配置基本概念

Jetpack Compose 可讓您在設計及建構應用程式 UI 時更加得心應手。Compose 會透過下列方式將狀態轉換為 UI 元素:

  1. 元素組合
  2. 元素的版面配置
  3. 繪製元素

Compose 透過組合、版面配置和繪圖將狀態轉換為 UI

本文件將重點放在元素的版面配置,說明 Compose 提供的部分建構區塊,以協助您安排 UI 元素的版面配置。

Compose 的版面配置目標

Jetpack Compose 導入版面配置系統有兩個主要目標:

可組合函式的基本概念

可組合函式是 Compose 的基本構成要素。可組合函式是會發出 Unit 的函式,用於描述使用者介面的部分元件。這種函式會接受部分輸入內容,並產生畫面上顯示的內容。如要進一步瞭解可組合項,請參閱 Compose 心理模型說明文件。

可組合函式可能會輸出多個 UI 元素。不過,如果您未提供如何安排這些元素的指引,Compose 可能會以您不想要的方式安排元素位置。舉例來說,以下程式碼會產生兩個文字元素:

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

如果您沒有提供想要的排列方式,Compose 會將文字元素互相堆疊在一起,以致文字無法閱讀:

兩個文字元素以互相重疊的形式繪製,以致文字無法閱讀

Compose 提供一系列現成可用的版面配置,協助您安排 UI 元素,進而自行定義更特殊的版面配置。

標準版面配置元件

在多數情況下,您可以直接使用 Compose 的標準版面配置元素

使用 Column 將項目直向排列在畫面上。

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

兩個文字元素排列在欄版面配置中,因此文字清楚易讀

同樣地,使用 Row 將項目橫向排列在畫面上。ColumnRow 都支援所含元素的對齊設定。

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

顯示較複雜的版面配置,畫面中文字元素的欄旁邊有一張小圖片

如要將元素放在另一個元素上,請使用 BoxBox 也支援所含元素的特定對齊設定。

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

顯示兩個互相堆疊的元素

您通常只需要使用這些建構區塊。您可以自行編寫可組合函式,將這些版面配置結合成更精巧的版面配置,以符合應用程式的需求。

比較三個簡單的版面配置可組合項:欄、列和方塊

如要在 Row 內設定子項的位置,請設定 horizontalArrangementverticalAlignment 引數。如要針對 Column 進行這項設定,則應設定 verticalArrangementhorizontalAlignment 引數:

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

所有項目靠右對齊

版面配置模型

在版面配置模型中,UI 樹狀結構是透過單次傳遞完成版面配置。系統會先要求每個節點自我測量,接著對任何子項進行遞迴測量,並將大小限制向下傳遞至樹狀結構的子項。接下來,系統會設定分葉節點的大小和位置,並將解析後的大小和位置指示向上傳遞回樹狀結構。

簡單來說,測量順序是父項先子項後,但設定大小和位置的順序則是子項先父項後。

我們來深入分析下列 SearchResult 函式。

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

這個函式會產生以下 UI 樹狀結構。

SearchResult
  Row
    Image
    Column
      Text
      Text

SearchResult 範例中,系統會按照以下順序安排 UI 樹狀結構的版面配置:

  1. 系統要求根節點 Row 進行測量。
  2. 根節點 Row 要求其第一個子項 Image 進行測量。
  3. Image 是分葉節點 (也就是不含任何子項),因此會回報大小並傳回位置指示。
  4. 根節點 Row 要求其第二個子項 Column 進行測量。
  5. Column 節點要求其第一個 Text 子項進行測量。
  6. 第一個 Text 節點是分葉節點,因此會回報大小並傳回位置指示。
  7. Column 節點要求其第二個 Text 子項進行測量。
  8. 第二個 Text 節點是分葉節點,因此會回報大小並傳回位置指示。
  9. Column 節點完成測量並設定其子項的大小和位置後,現在可以決定本身的大小和位置。
  10. 根節點 Row 完成測量並設定其子項的大小和位置後,現在可以決定本身的大小和位置。

在搜尋結果 UI 樹狀結構中,測量及設定大小和位置的順序

成效

Compose 僅會測量子項一次,藉此提升效能。單次傳遞測量有助於提升效能,可讓 Compose 有效處理深層 UI 樹狀結構。如果某個元素測量其子項兩次,而該子項要測量其每個子項兩次 (以此類推),則每次要配置整個 UI 都必須執行大量工作,導致難以維持應用程式效能。

如果版面配置基於某個理由需要多次測量,Compose 為此提供了「內建函式測量」特殊系統。如要進一步瞭解這項功能,請參閱「Compose 版面配置中的內建函式測量資料」。

由於測量和位置設定是版面配置傳遞的不同子階段,因此僅會影響項目位置 (而不是測量) 的任何變更都能獨立執行。

在版面配置中使用修飾符

如「Compose 修飾符」一文中所述,您可以使用修飾符裝飾或擴增可組合項。修飾符是自訂版面配置的必要選項。舉例來說,我們在此透過鏈結數個修飾符來自訂 ArtistCard

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

這是較複雜的版面配置,在其中使用修飾符來變更圖形的排列方式以及回應使用者輸入內容的區域

請注意,以上程式碼將多個不同的修飾符函式搭配使用。

  • clickable 會讓可組合項對使用者輸入內容產生回應並顯示漣漪效果。
  • padding 會在元素周圍放置空格。
  • fillMaxWidth 提供可組合的父項寬度上限。
  • size() 會指定元素的偏好寬度和高度。

可捲動版面配置

如要進一步瞭解可捲動版面配置,請參閱 Compose 手勢說明文件

如要查看各種清單和 Lazy 清單,請參閱 Compose 清單說明文件

回應式版面配置

設計版面配置時應將不同螢幕方向和板型規格大小列入考量。Compose 提供一些內建機制,方便您配合各種螢幕設定調整可組合的版面配置。

限制

為了得知來自父項的限制,並據以設計版面配置,建議您使用 BoxWithConstraints測量限制可以在內容 lambda 的範圍中取得。您可以利用這些測量限制,針對不同的螢幕設定組合不同的版面配置:

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

以版位為基礎的版面配置

Compose 提供各式各樣以Material Design為基礎,且包含 androidx.compose.material:material 依附元件的可組合項 (在 Android Studio 中建立 Compose 專案時提供),讓您輕鬆建構 UI。DrawerFloatingActionButtonTopAppBar 等元素應有盡有。

Material 元件會大量利用「Slot API」,Compose 會採用這個模式將自訂圖層帶入可組合項上方。這種方法使得元件更具彈性,因為元件會接受可自行設定的子項元素,而不須揭露子項的所有設定參數。版位會在 UI 中保留空白空間,讓開發人員視需要填入項目。舉例來說,您可以在 TopAppBar 中自訂下列版位:

圖表顯示 Material 元件應用程式列中的可用版位

可組合函式通常會採用 content 可組合 lambda ( content: @Composable () -> Unit)。Slot API 會公開特定用途適用的多個 content 參數。例如,您可以利用 TopAppBar 提供 titlenavigationIconactions 的內容。

舉例來說,Scaffold 可讓您使用基本的 Material Design 版面配置結構來實作 UI。Scaffold 為最常用的頂層 Material 元件提供版位,例如 TopAppBarBottomAppBarFloatingActionButtonDrawer。只要使用 Scaffold,即可輕鬆確保這些元件放置在正確的位置,且能互相搭配正常運作。

JetNews 範例應用程式使用 Scaffold 來設定多個元素的位置

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}