Jetpack Compose 建築分層

本頁面會概略說明構成 Jetpack Compose 的架構層和基礎原則,並提供這項設計的重要概念。

Jetpack Compose 並非單一單體式專案;是由多個模組結合而成,以組成完整的堆疊。瞭解構成 Jetpack Compose 的各種不同模組,您就能:

  • 使用適當的抽象層建構您的應用程式或程式庫
  • 瞭解何時可以「下滑」到層級較低的位置,進一步自訂或自訂
  • 盡可能減少依附元件

圖層

Jetpack Compose 的主要層如下:

圖 1. Jetpack Compose 的主要層。

每一層都是以較低層級建構而成,結合多項功能以建立較高層級的元件。每一層都會建構在較低層的公開 API 上,以驗證模組邊界,並視需求取代任何圖層。現在,讓我們從左下角開始分析這些圖層。

執行階段
這個模組提供 Compose 執行階段的基礎知識,例如 remembermutableStateOf}@Composable 註解和 SideEffect。如果您只需要撰寫 樹狀結構的樹狀結構功能 (而非 UI 功能),考慮直接在這個層建構。
UI
UI 層由多個模組 (ui-textui-graphicsui-tooling 等) 組成。這些模組實作 UI 工具包的基礎知識,例如 LayoutNodeModifier、輸入處理常式、自訂版面配置和繪圖。如果您只需要 UI 工具包的基本概念,可以考慮在這個層上建構內容。
Foundation
此模組為 Compose UI 提供跨越設計系統的構成元素,例如 RowColumnLazyColumn,以及可辨識特定手勢等。您可以考慮在基礎層上進行建構,建立自己的設計系統。
材料
這個模組提供適用於 Compose UI 的質感設計系統實作程序,提供質感系統、樣式化元件、波浪圖示、圖示。在應用程式中使用 Material Design 時,請建構這個圖層。

設計原則

Jetpack Compose 的指導原則是提供可聚焦 (或組合) 的一系列細微功能,而非數個單體式元件。這個方法有許多優點。

控制

層級元件越高,您就越能執行更多操作,但會限制您能夠擁有的直接控制權。如果您需要進一步控管,可以「放置」到較低層級的元件。

舉例來說,如要讓元件的顏色能夠建立動畫,您可以使用 animateColorAsState API:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

不過,如果您需要該元件一律顯示為灰色,便無法使用這個 API 執行。您可以改為使用較低層級的 Animatable API:

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

較高層級的 animateColorAsState API 本身是在較低層級的 Animatable API 上建構。使用較低層級 API 較為複雜,但可控制更多設定。請選擇最符合需求的抽象層級。

自訂

從較小的建構區塊組成層級較高的元件,就能更輕鬆地自訂所需的元件。舉例來說,假設 Material 層提供了 Button實作

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button 是由 4 個元件組合而成:

  1. 提供背景、形狀、點擊處理等作業的 Surface 資料。

  2. CompositionLocalProvider 會在啟用或停用按鈕時變更內容的 Alpha 值

  3. ProvideTextStyle 可設定要使用的預設文字樣式

  4. Row 會提供按鈕內容的預設版面配置政策

我們省略了部分參數和註解,以更清楚地顯示結構,不過整個元件只約 40 行程式碼,只是要組合這些 4 個元件來實作按鈕。像 Button 這類元件則會注意自己所公開的參數,讓可以廣泛使用的自訂參數相輔相成,讓元件更容易使用。舉例來說,材料元件提供質感設計系統中指定的自訂項目,讓您輕鬆遵循質感設計原則。

不過,如果您想要自訂元件參數以外的自訂項目,則可以「捨棄」層級和建立元件分支。舉例來說,質感設計會指定按鈕應使用純色背景。如果您需要漸層背景,Button 參數不支援這個選項。在這種情況下,您可以使用 Material Button 實作做為參考資料,並建構自己的元件:

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

上述實作仍繼續使用 Material 層的元件,例如 Material 的目前內容 Alpha 版概念和目前文字樣式。但用來取代素材 SurfaceRow,並設定樣式,呈現理想的外觀。

如果您完全不想使用 Material 概念,例如自行建構專屬設計系統,則可選擇使用基礎層元件:

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

Jetpack Compose 會保留最高階元件的最簡單名稱。舉例來說,androidx.compose.material.Text 是以 androidx.compose.foundation.text.BasicText 為基礎。如此一來,如果想取代較高層級,則可透過自己的可偵測名稱提供自己的實作項目。

挑選合適的抽象層

不過,Compose 建構了可重複使用的可重複使用元件的理念,因此,您未必會觸及較低等級的建構模塊。許多更高層級的元件不僅提供更多功能,而且通常會實作無障礙設計等最佳做法。

舉例來說,如果您想在自訂元件中新增手勢支援,可以使用 Modifier.pointerInput 從頭開始建構,但還有其他較高等級的元件可能會提供更好的起點,例如 Modifier.draggableModifier.scrollableModifier.swipeable

建議您以「最高層級」元件做為建構依據,以提供所需的功能,以享有其中包含的最佳做法。

瞭解詳情

如需建構自訂設計系統的範例,請參閱 Jetsnack 範例