實戰 | 在應用中使用 Compose Material 3

Android開發者發表於2022-02-18

Material You 是下一代 Material Design 的發展方向,也是一種全新的設計願景: 方便您打造個性化的樣式設計、滿足各種需求並自適應各種螢幕;Jetpack Compose 是用於構建原生 Android 介面的新款現代工具包,可以幫助您更快地構建更出色的應用。

您可能對現有的 Compose Material 庫十分了解,它基於 Material Design 2 規範,其中包括了 Material 主題、Material 元件和深色主題等功能。新的 Compose Material 3 Jetpack 庫 現已釋出 Alpha 版,它基於 Material Design 3 規範,包括了更新後的主題、元件以及動態配色這類 Material You 個性化功能,旨在與新的 Android 12 視覺樣式和系統介面相得益彰。接下來,我們將使用 Jetchat 來說明如何應用 Material Design 3 和 Material You。

如果您更喜歡通過視訊瞭解此內容,請在此處檢視:

https://www.bilibili.com/vide...

△ 在應用中使用 Compose Material 3

Jetchat 是一款使用 Jetpack Compose 構建的示例聊天應用,目前使用 Material Design 2 中的主題和元件。我們將在 Jetchat 中,應用由我們的設計人員提供的 Compose Material 3 庫的更新,其中包括更廣泛的色調顏色、對元件的最新更新,甚至包括動態配色以使應用更加個性化,從而使其更加美觀。

△ Jetchat 應用

在開始前,我們首先要將 Material 3 的依賴項新增到模組的 build.gradle 檔案中:

implementation 'androidx.compose.material3:material3:1.0.0-alpha01'

MaterialTheme

我們先來看看 MaterialTheme。現有的 MaterialTheme 可組合項是 Material Design 2 的實現,它通過調整顏色、排版和形狀系統,可以在整個應用內實現對 Material 2 元件進行主題設定。我們為 Material Design 3 引入了新版本的 MaterialTheme,可以通過調整配色方案和排版系統對 Material 3 元件的主題進行設定,而更新 Shape 的功能也會在不久之後加入。

import androidx.compose.material3.MaterialTheme
 
@Composable
fun MaterialTheme (
    colorScheme: ColorScheme,
    typography: Typography,
    // 更新 Shape 的功能即將到來
    content: @Composable () -> Unit
)

首先,我們看一下配色方案。Material Design 3 將顏色細分到特定名稱的顏色槽中。比如 Material 3 元件使用的 Primary、Background 和 Error,這些顏色槽共同形成一種配色方案。部分顏色槽來自 Material Design 2,同時也引入了一些新的顏色槽以擴充整體調色盤。這些顏色槽都包含了美觀的全新預設基準顏色,在淺色和深色主題上都可以應用。

△ 綠色框為 Material You 中新加入的顏色槽

△ 綠色框為 Material You 中新加入的顏色槽

上面這些顏色取自一組色調調色盤,例如,我們來看一下 Primary 顏色槽。該顏色槽使用的顏色值來自 Primary 色調調色盤中的不同色調,並根據淺色和深色主題選擇相應的色調,以滿足無障礙功能要求。

△ Primary 顏色槽

△ Primary 顏色槽

Compose 使用新的 ColorScheme 類對此進行建模,其引數以 Material Design 3 配色方案中的顏色槽命名。您可以使用 lightColorScheme 函式建立具有淺色基準值的 ColorScheme 例項;也可以使用自定義顏色覆蓋預設值,或者使用 darkColorScheme 設定深色預設基準值;您還可以使用 isSystemInDarkTheme 工具函式,根據系統設定在淺色和深色配色方案之間切換。

val AppLightColorScheme = lightColorScheme (
    primary = Color(...),
    // secondary、tertiary 等等
    // 具有淺色基準值的 ColorScheme 例項
)
 
val AppDarkColorScheme = darkColorScheme(
    // primary、secondary、tertiary 等等
    // 具有深色基準值的 ColorScheme 例項
)
 
val dark = isSystemInDarkTheme()
val colorScheme = if (dark) AppDarkColorScheme else AppLightColorScheme
 
// 將 colorScheme 作為引數傳遞給 MaterialTheme。
MaterialTheme (
    colorScheme = colorScheme,
    // 字型
) {
    // 應用內容
}

接下來,我們來看看 Jetchat 的配色方案。Jetchat 的配色方案由 MaterialTheme Builder 工具生成,我們使用 Jetchat 品牌顏色中的藍色和黃色作為 Primary 顏色、Secondary 顏色和 Tertiary 顏色的來源,生成了非常適合 Jetchat 的 Material 3 配色方案,其中涵蓋了用於淺色和深色主題的顏色。Jetchat 所使用的品牌顏色取自 MaterialTheme Builder 工具生成的一組自定義色調調色盤,下圖中顯示了 Primary 顏色,即藍色的色調調色盤,以及配色方案中匹配的 Primary 顏色槽。

△ MaterialTheme Builder 工具中生成的 Jetchat 配色方案

△ MaterialTheme Builder 工具中生成的 Jetchat 配色方案

要實現 Jetchat 配色方案,首先使用 Color 類宣告這些顏色。MaterialTheme Builder 工具還可以為您匯出生成的程式碼。接下來,便可以使用相應的顏色值宣告 Jetchat 淺色和深色配色方案。

// 來自名為'Blue'的色調調色盤的 Primary 顏色
val Blue10 = Color (0xFF000965)
val Blue20 = Color (0xFF00159E)
val Blue30 = Color (0xFF0023DA)
val Blue40 = Color (0xFF1E40FF)
val Blue80 = Color (0xFFBBC3FF)
val Blue90 = Color (0xFFDDE0FF)
 
val JetchatLightColorScheme = lightColorScheme (
    primary = Blue40,
    onPrimary = Color.White,
    primaryContainer = Blue90,
    onPrimaryContainer = Blue10,
    // secondary、tertiary、surface 等等
)
 
val JetchatDarkColorScheme = darkColorScheme (
    primary = Blue80,
    onPrimary = Blue20,
    primaryContainer = Blue30,
    onPrimaryContainer = Blue90,
    // secondary、tertiary、surface 等等
)

我們為 Jetchat 主題建立了一個新的可組合函式,該函式接收一個用於判斷深色主題的引數和一個應用內容引數,從而使我們可以在 Jetchat 的淺色和深色配色方案之間切換。接下來,我們將 colorScheme 值和 content 傳遞給內部的 MaterialTheme 可組合項,這使我們能夠封裝 Jetchat 內容併為應用提供主題。

@Composable
fun JetchatTheme (
    dark: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (dark) JetchatDarkColorScheme else JetchatLightColorScheme
    MaterialTheme (
        colorScheme = colorScheme,
        content = content,
    )
}

下面讓我們看一下 Jetchat 對話介面,介面中的不同部分使用了配色方案中的不同顏色槽。例如,根據使用者不同,訊息頭像的邊框顏色使用 Primary 顏色或 Tertiary 顏色。這裡使用 MaterialTheme.colorScheme 訪問主題顏色值。

△ Jetchat 對話介面

△ Jetchat 對話介面

@Composable
fun Message(...) {
    val avatarBorderColor = if (isUserMe) {
        MaterialTheme.colorScheme.primary
    } else {
        MaterialTheme.colorScheme.tertiary
    }
    ...
}

動態配色

接下來,讓我們來了解什麼是動態配色。動態配色是 Material You 的重要部分,即用演算法從使用者的桌布中提取自定義顏色並應用於應用和系統介面,您可將此作為起點來生成完整的淺色和深色配色方案。

△ Jetchat 的配色方案隨使用者設定的桌布變化

△ Jetchat 的配色方案隨使用者設定的桌布變化

動態配色可在 Android 12 及更高版本中使用,要在 Compose 中實現動態 ColorScheme,需要首先檢查 Build.VERSION.SDK。如果動態配色可用,我們便可以設定動態 ColorScheme;如果不可用,則可以回退到像以前一樣使用 lightColorScheme 或 darkColorScheme:

val dynamic = Build.VERSION.SOK_INT >= Build.VERSION_CODES.S
val colorScheme = if (dynamic) {
    val context = LocalContext.current
    // 使用 dynamicLightColorScheme 函式建立具有淺色動態值的 ColorScheme 例項
    // 或使用 dynamicDarkColorScheme 建立具有深色動態值的例項
    // 傳入 Context 以便從 Android 系統獲取動態配色資源
    if (dark) dynamiclightColorScheme(context) else dynamicDarkColorScheme(context)
} else {
    // 使用 lightColorScheme 或者 darkColorScheme
}

目前,Jetchat 一直在使用品牌的藍色配色方案,但我們希望增加對基於桌布的動態配色方案的支援,以配合使用者的個性化調整。在本例中,色調調色盤基於桌布中的顏色生成,而動態配色方案則派生自這些色調調色盤,其中包括用於淺色和深色主題的顏色。

為了在 Jetchat 中實現這一點,我們首先更新 JetchatTheme 為動態配色新增一個新引數,然後使用該動態配色引數設定動態 ColorScheme,或者在不可用時回退到品牌的藍色配色方案。與前面一樣將 colorScheme 值和 content 傳遞給內部的 MaterialTheme 可組合項。

@Composable
fun JetchatTheme (
    dark: Boolean = isSystemInDarkTheme (),
    dynamic: Boolean = Build. VERSION.SDK_INT >= Build.VERSION_CODES.S,
    content: @Composable () -> Unit
) {
    // ColorScheme 配置以及 MaterialTheme
    val colorScheme = if (dynamic) {
        val context = LocalContext.current
        if (dark) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
    } else {
        if (dark) JetchatDarkColor Scheme else Jetchat Light Color Scheme
    }
 
    MaterialTheme(
        colorScheme = colorScheme, 
        content = content,
    )
}

現在,在 Android 12 及更高版本上,Jetchat 介面可根據使用者桌布自動調整配色,無論是淺色主題還是深色主題都可提供適合品牌的美觀體驗。

△ 自動適配深淺色主題的動態配色

△ 自動適配深淺色主題的動態配色

排版

現在我們已經瞭解了配色方案,接下來讓我們來看看排版。Material Design 3 有了新的字型規格,包括了由 Material Design 2 適配而來的文字樣式。樣式的命名和分組簡化為顯示、大標題、標題、正文和標籤;每個分組都有大號、中號和小號字型。

△ Material 3 與 Material 2 的字型樣式分組

△ Material 3 與 Material 2 的字型樣式分組

Compose 使用新的 Typography 類對字型規格進行建模,其引數以 Material Design 3 字型規格中的樣式命名。我們可以使用 Roboto 基準值建立一個 Typography 例項,用自定義文字樣式覆蓋預設值,最後將 Typography 作為引數傳遞給 MaterialTheme。

import androidx.compose.material3.Typography
 
class Typography (
    val displayLarge: TextStyle,
    val displayMedium: TextStyle,
    val displaySmall: TextStyle,
    // headlineLarge、titleMedium、bodySmall 等等
)
 
val AppTypography = Typography (
    bodyLarge = TextStyle(...),
    // displayLarge、titleMedium、labelSmall 等等
    // 使用預設的 Roboto 基準值
)
 
MaterialTheme (
    typography = AppTypography,
    // colorScheme
) {
    //App content
}

我們再來看看 Jetchat 的排版。設計人員為我們提供了新的品牌字型規格,用到了自定義字型 Montserrat 和 Karla:

△ Jetchat 所使用的字型規格

△ Jetchat 所使用的字型規格

我們首先使用 FontFamily 類宣告這些字型,該類將儲存 Font 類的例項。我們可以使用字型資源 ID 和字型粗細構造 Font 類,然後使用 Typography 類宣告 Jetchat 字型樣式,並使用 TextStyle 類覆蓋每個文字樣式,包括我們的字型、字號、字型粗細等其他排版值。最後,同樣的,將 Typography 作為引數傳遞給 MaterialTheme:

val MontserratFontFamily = FontFamily ( 
    Font(R.font.montserrat_regular),
    Font(R.font montserrat_light, FontWeight Light),
    Font(R.font.montserrat_semibold, FontWeight. SemiBold)
)
 
val KarlaFontFamily = FontFamily (
    Font(R.font.karla_regular),
    Font(R.font.karla_bold, FontWeight. Bold)
)
 
val JetchatTypography = Typography( 
    bodyLarge = TextStyle(
        fontFamily = KarlaFontFamily,
        fontWeight = FontWeight. Normal,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.15.sp
    ),
 
    // titleMedium、labelSmall 等等
)
 
MaterialTheme (
    typography = JetchatTypography,
    // colorScheme、content
)

我們來看一下 Jetchat 對話介面,介面中的每個部分使用了 Jetchat 字型規格中的不同文字樣式。例如,訊息中的聯絡人和時間戳,分別使用了 titleMedium 和 labelSmall 樣式。它們通過 MaterialTheme.typography 表示訪問主題字型值。

△ 對話介面的字型設定

△ 對話介面的字型設定

@Composable
fun Message(...) {
    …
    Text (style = MaterialTheme.typography.titleMedium, ...)
    …
    Text (style = MaterialTheme.typography.labelSmall, ...)
}

高度

在瞭解了 Material 3 主題相關的更新後,接下來讓我們看看 Material Design 另一個關鍵更新——高度。概括來說,Material 2 中使用陰影表示高度,而 Material 3 中改為使用色調顏色疊加層表示高度。這是一種區分容器和表面的新方式,增加色調高度會使色調變得更為突出。

在 Material 2 中高度疊加層是深色主題的一部分,在 Material 3 中也已更改為色調顏色疊加層。

△ M2 與 M3 中高度系統效果對比

△ M2 與 M3 中高度系統效果對比

我們以 Surface 元件為例,Surface 是用於支援大多數 Material 元件的可組合項,現有的 Surface 可組合項實現的是 Material Design 2 的高度系統。在 Material Design 2 中 Surface 接收一個 elevation 引數並處理深色主題中的陰影和疊加層渲染。我們為 Material Design 3 引入了新版 Surface,它接受一個 tonalElevation 引數,並會在淺色和深色主題中處理色調顏色疊加層渲染。讓我們看看前後有何不同:

△ Material 2 中的 Surface

△ Material 2 中的 Surface

△ Material 3 中的 Surface

△ Material 3 中的 Surface

元件更新

Material 3 對許多元件進行了更新,比如按鈕、應用欄、對話方塊、FAB 和導航元件。此類更新利用了新的 Material 3 主題設定值,幷包含了對每個元件規範的最新更新。

△ Material 3 中更新的元件

△ Material 3 中更新的元件

例如 Material 2 中的 BottomNavigation。它符合 Material Design 2 規範,並接受 backgroundColor 和 elevation 等引數。在 Material 3 中該可組合項更名為 NavigationBar,它符合 Material Design 3 規範,其中的引數更改為 containerColor 和 tonalElevation,以更準確地反映各自的用途。

// Materail 2 中的 NavigationBar
import androidx.compose.material.BottomNavigation
 
@Composable
fun BottomNavigation (
    // M2 預設值
    backgroundColor: Color, 
    elevation: Dp,
    …
)
 
 
// Materail 3 中的 NavigationBar
import androidx.compose.material3.NavigationBar
 
@Composable
fun NavigationBar (
    // M3 預設值
    containerColor: Color, 
    tonalElevation: Dp,
    …
)

△ 更新前後的樣式改變

△ 更新前後的樣式改變

Compose Material 3 中的元件進行了很多更新,為了讓您全面瞭解所有的元件以及它們的實現方式,我們更新了 Compose Material Catalog 應用,並新增了 Material 3 部分。請在 AOSP 上檢視 原始碼 並在 Google Play 中下載該應用

下面我們來看看 Jetchat 中的一個例子。在個人資料介面上有一個用於撰寫訊息的擴充套件 FAB,該元件已從 Material 2 更新為 Material 3 版本。這是 Material 2 版本的一個簡單實現,使用了 ExtendedFloatingActionButton 可組合項,內部使用了 Icon 和 Text、可組合項以及自定義的 Primary 背景顏色。

import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.Text
 
ExtendedFloatingActionButton(
    icon = { Icon(...) },
    text = { Text(...) },
    backgroundColor = MaterialTheme.colors.primary,
    ...
)

Material 3 對該元件的更新如這裡所示,可組合項的依賴匯入已更改為 Material 3,我們使用更名後的 containerColor 引數和 Material 3 配色方案中的 Tertiary 顏色。

import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
 
ExtendedFloatingActionButton(
    icon = { Icon(...) },
    text = { Text(...) },
    containerColor = MaterialTheme.colorScheme.tertiary,
    ...
)

視覺效果

Material You 的某些方面來自新的 Android 12 視覺樣式和系統介面,其中的兩個重要變化是波紋和滾動效果。現在,波紋效果會在按下時使用細微的閃光照亮表面,滾動效果則會在滾動容器的邊緣使用拉伸效果。實現這些更改不需要額外的工作,在 Compose Foundation 1.1 及更高版本的滾動容器可組合項中拉伸滾動預設處於開啟狀態;Android 12 上提供的閃光波紋適用於所有 Material 元件。

△ M2 與 M3 中的波紋效果

△ M2 與 M3 中的波紋效果

// 拉伸滾動
// 適用於 LazyColumn、Lazy Row、LazyVerticalGrid 等元件
// ComposeFoundation 1.1.0+ 可用
 
// 閃光波紋
// 適用於所有 Material 2 和 Material 3 元件
// Android 12+ 可用

與 Android View 的互操作性改進

與 Android 檢視的互操作性是使用 Compose 開發應用的一個重要部分,我們已經在 Material 3 中進行了一些更新來支援這一點。MDC-AndroidCompose Theme Adapter 庫 是一款支援重用 Android XML 主題的 Material 元件,以方便我們在 Jetpack Compose 中設定主題。

現有的 MdcTheme 可組合項與 Material 2 XML 主題相容,我們還引入了一個新的 Mdc3Theme 可組合項,它與 Material 3 XML 主題相容。

△ MDC-AndroidCompose Theme Adapter 是 XML 主題與 MaterialTheme 之間的橋樑

△ MDC-AndroidCompose Theme Adapter 是 XML 主題與 MaterialTheme 之間的橋樑

尾聲

現在是在您的 Android 應用中試用 Compose Material 3 的好時機,我們準備了一系列資源來幫助您順利完成旅程。我們提供了新的關於 Compose Material 3 的 API 文件,並在 Android Studio 中提供了新的 Empty Compose Activity 模板,其中包含有關 Material 3 的更新。此外,我們還更新了 Compose 中的主題設定 指南,以及在前面看到的 Jetchat 示例和 Compose Material Catalog 應用,以及 MDC-Android ComposeTheme Adapter 互操作性庫。

相關文章