实战 | 在应用中使用 Compose Material 3
Material You 是下一代 Material Design 的发展方向,也是一种全新的设计愿景: 方便您打造个性化的样式设计、满足各种需求并自适应各种屏幕;Jetpack Compose 是用于构建原生 Android 界面的新款现代工具包,可以帮助您更快地构建更出色的应用。
Material You
https://m3.material.io/Jetpack Compose
https://developer.android.google.cn/jetpack/compose
您可能对现有的 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。
Compose Material 3 Jetpack 库
https://developer.android.google.cn/jetpack/androidx/releases/compose-material3Jetchat
https://github.com/android/compose-samples/tree/main/JetNews
△ 在应用中使用 Compose Material 3
Bilibili 视频链接
https://www.bilibili.com/video/BV14i4y1f71K/
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 中新加入的颜色槽
上面这些颜色取自一组色调调色板,例如,我们来看一下 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 配色方案
要实现 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 访问主题颜色值。
@Composable
fun Message(...) {
val avatarBorderColor = if (isUserMe) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.tertiary
}
...
}
动态配色
接下来,让我们来了解什么是动态配色。动态配色是 Material You 的重要部分,即用算法从用户的壁纸中提取自定义颜色并应用于应用和系统界面,您可将此作为起点来生成完整的浅色和深色配色方案。
动态配色可在 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 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 所使用的字体规格
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
)
@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 中也已更改为色调颜色叠加层。
我们以 Surface 组件为例,Surface 是用于支持大多数 Material 组件的可组合项,现有的 Surface 可组合项实现的是 Material Design 2 的高度系统。在 Material Design 2 中 Surface 接收一个 elevation 参数并处理深色主题中的阴影和叠加层渲染。我们为 Material Design 3 引入了新版 Surface,它接受一个 tonalElevation 参数,并会在浅色和深色主题中处理色调颜色叠加层渲染。让我们看看前后有何不同:
△ Material 2 中的 Surface
△ Material 3 中的 Surface
组件更新
Material 3 对许多组件进行了更新,比如按钮、应用栏、对话框、FAB 和导航组件。此类更新利用了新的 Material 3 主题设置值,并包含了对每个组件规范的最新更新。
△ Material 3 中更新的组件
// 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,
…
)
源代码
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/integration-tests/material-catalog/在 Google Play 中下载该应用
https://play.google.com/store/apps/details?id=androidx.compose.material.catalog
import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.Text
ExtendedFloatingActionButton(
icon = { Icon(...) },
text = { Text(...) },
backgroundColor = MaterialTheme.colors.primary,
...
)
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
ExtendedFloatingActionButton(
icon = { Icon(...) },
text = { Text(...) },
containerColor = MaterialTheme.colorScheme.tertiary,
...
)
视觉效果
// 拉伸滚动
// 适用于 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 主题兼容。
尾声
现在是在您的 Android 应用中试用 Compose Material 3 的好时机,我们准备了一系列资源来帮助您顺利完成旅程。我们提供了新的关于 Compose Material 3 的 API 文档,并在 Android Studio 中提供了新的 Empty Compose Activity 模板,其中包含有关 Material 3 的更新。此外,我们还更新了 Compose 中的主题设置指南,以及在前面看到的 Jetchat 示例和 Compose Material Catalog 应用,以及 MDC-Android ComposeTheme Adapter 互操作性库。
Compose Material 3 的 API 文档
https://developer.android.google.cn/jetpack/androidx/releases/compose-material3Compose 中的主题设置
https://developer.android.google.cn/jetpack/compose/themesCompose Material Catalog
https://github.com/android/compose-samplesMDC-Android ComposeTheme Adapter
https://material-components.github.io/material-components-android-compose-theme-adapter/
推荐阅读