Compose 中的 Material Design 3

Jetpack Compose 提供了 Material Design 3 的实现,后者是 Material Design 的下一代产品。Material 3 包含更新的主题、组件和 Material You 个性化功能(如动态配色),旨在与 Android 12 及更高版本中的全新视觉样式和系统界面相得益彰。

下面,我们以 Reply 示例应用为例,演示了 Material Design 3 的实现。Reply 示例完全基于 Material Design 3

使用 Material Design 3 的 Reply 示例应用
图 1. 使用 Material Design 3 的 Reply 示例应用

依赖项

如需开始在 Compose 应用中使用 Material 3,请将 Compose Material 3 依赖项添加到 build.gradle 文件中:

implementation "androidx.compose.material3:material3:$material3_version"

添加依赖项后,您就可以开始向应用添加 Material Design 系统,包括颜色、排版和形状。

实验性 API

某些 M3 API 被视为实验性 API。在这种情况下,您需要使用 ExperimentalMaterial3Api 注解在函数或文件级别指定 OptIn:

// import androidx.compose.material3.ExperimentalMaterial3Api
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppComposable() {
    // M3 composables
}

Material 主题设置

M3 主题包含以下子系统:配色方案排版形状。当您自定义这些值时,您所做的更改会自动反映在您用于构建应用的 M3 组件中。

Material Design 的子系统:颜色、排版和形状
图 2. Material Design 的子系统:颜色、排版和形状

Jetpack Compose 使用 M3 MaterialTheme 可组合项实现以下概念:

MaterialTheme(
    colorScheme = /* ...
    typography = /* ...
    shapes = /* ...
) {
    // M3 app content
}

如需为应用内容设置主题,请定义特定于您的应用的配色方案、排版和形状。

配色方案

配色方案的基础是五种主要颜色。这些颜色中的每种颜色都与 Material 3 组件使用的 13 色调调色板相关。例如,以下是 Reply 的浅色主题的配色方案:

Reply 示例应用浅色配色方案
图 3. Reply 示例应用浅色配色方案

详细了解配色方案和颜色角色

生成配色方案

虽然您可以手动创建自定义 ColorScheme,但使用品牌中的源颜色通常更容易生成配色方案。您可以使用 Material 主题构建器工具执行此操作,并且可以选择导出 Compose 主题代码。生成以下文件:

  • Color.kt 包含主题的颜色,以及为浅色和深色主题定义的所有角色。

val md_theme_light_primary = Color(0xFF476810)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFC7F089)
// ..
// ..

val md_theme_dark_primary = Color(0xFFACD370)
val md_theme_dark_onPrimary = Color(0xFF213600)
val md_theme_dark_primaryContainer = Color(0xFF324F00)
// ..
// ..

  • Theme.kt 包含浅色和深色配色方案以及应用主题。

private val LightColorScheme = lightColorScheme(
    primary = md_theme_light_primary,
    onPrimary = md_theme_light_onPrimary,
    primaryContainer = md_theme_light_primaryContainer,
    // ..
)
private val DarkColorScheme = darkColorScheme(
    primary = md_theme_dark_primary,
    onPrimary = md_theme_dark_onPrimary,
    primaryContainer = md_theme_dark_primaryContainer,
    // ..
)

@Composable
fun ReplyTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme =
        if (!darkTheme) {
            LightColorScheme
        } else {
            DarkColorScheme
        }
    MaterialTheme(
        colorScheme = colorScheme,
        content = content
    )
}

如需支持浅色主题和深色主题,请使用 isSystemInDarkTheme()。根据系统设置,定义要使用的配色方案:浅色或深色。

动态配色方案

动态配色是 Material You 的关键部分,使用此功能时,算法会从用户的壁纸中派生自定义颜色,以将其应用到其应用和系统界面。您可基于此调色板生成浅色和深色配色方案。

通过壁纸设置回复示例应用动态主题(左)和默认应用主题设置(右)
图 4. 通过壁纸回复示例应用动态主题(左)和默认应用主题设置(右)

动态颜色适用于 Android 12 及更高版本。如果动态颜色可用,您可以设置动态 ColorScheme。如果不可用,您应回退到使用自定义浅色或深色 ColorScheme

ColorScheme 提供了构建器函数来创建动态浅色深色配色方案:

// Dynamic color is available on Android 12+
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colors = when {
    dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
    dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
    darkTheme -> DarkColorScheme
    else -> LightColorScheme
}

颜色的使用

您可以通过 MaterialTheme.colorScheme 在应用中获取 Material 主题颜色:

Text(
    text = "Hello theming",
    color = MaterialTheme.colorScheme.primary
)

每种颜色角色都可以在各种位置使用,具体取决于组件的状态、显眼程度和强调效果。

  • 主色是基础颜色,用于主要组件,例如显眼的按钮、活动状态和提升的 Surface 的色调。
  • 辅助键颜色用于界面中不太显眼的组件(例如过滤组件),并增加了颜色表达的机会。
  • 第三色用于派生对比强调的作用,可用于平衡主色和辅色或增强元素的吸引力。

Reply 示例应用设计在 primary-container 之上使用 on-primary-container 颜色,以强调所选项。

具有主容器颜色的主要容器和文本字段。
图 5. 采用主容器颜色的主要容器和文本字段。

Card(
    colors = CardDefaults.cardColors(
        containerColor =
        if (isSelected) MaterialTheme.colorScheme.primaryContainer
        else
            MaterialTheme.colorScheme.surfaceVariant
    )
) {
    Text(
        text = "Dinner club",
        style = MaterialTheme.typography.bodyLarge,
        color =
        if (isSelected) MaterialTheme.colorScheme.onPrimaryContainer
        else MaterialTheme.colorScheme.onSurface,
    )
}

在这里,您可以在 Reply 抽屉式导航栏中看到,如何使用辅助和第三容器颜色创建强调效果和强调效果。

悬浮操作按钮的三级容器和第三容器级组合。
图 6. 悬浮操作按钮的三级容器和第三容器级组合。

排版

Material Design 3 定义了一个字体比例,包括改写自 Material Design 2 的文本样式。命名和分组已简化为:显示、大标题、标题、正文和标签,每个都有大号、中号和小号。

Material Design 3 的默认排版缩放
图 7. Material Design 3 的默认排版缩放
M3 默认字体大小/行高
displayLarge Roboto 57/64
displayMedium Roboto 45/52
displaySmall Roboto 36/44
headlineLarge Roboto 32/40
headlineMedium Roboto 28/36
headlineSmall Roboto 24/32
titleLarge New- Roboto Medium 22/28
titleMedium Roboto Medium 16/24
titleSmall Roboto Medium 14/20
bodyLarge Roboto 16/24
bodyMedium Roboto 14/20
bodySmall Roboto 12/16
labelLarge Roboto Medium 14/20
labelMedium Roboto Medium 12/16
labelSmall New Roboto Medium, 11/16

定义排版

Compose 提供了 M3 Typography 类以及现有的 TextStyle字体相关类,来为 Material 3 字体比例建模。Typography 构造函数提供了每种样式的默认值,因此您可以省略不想自定义的任何参数:

val replyTypography = Typography(
    titleLarge = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 22.sp,
        lineHeight = 28.sp,
        letterSpacing = 0.sp
    ),
    titleMedium = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.15.sp
    ),
    // ..
)
// ..

“body Large”(正文)、“body”(中等)和“label medium”(标签中等),用于不同排版用途。
图 8. “Body Large”(正文大)、“body medium”(正文中等)和“label medium”(标签中等),用于不同排版用途。

您的产品可能并不需要 Material Design 字体比例的全部 15 种默认样式。在此示例中,我们为缩减集合选择了五个大小,而忽略了其余大小。

您可以通过更改 TextStyle字体相关属性(如 fontFamilyletterSpacing)的默认值来自定义排版。

bodyLarge = TextStyle(
    fontWeight = FontWeight.Normal,
    fontFamily = FontFamily.SansSerif,
    fontStyle = FontStyle.Italic,
    fontSize = 16.sp,
    lineHeight = 24.sp,
    letterSpacing = 0.15.sp,
    baselineShift = BaselineShift.Subscript
),

定义 Typography 后,请将其传递给 M3 MaterialTheme

MaterialTheme(
    typography = replyTypography,
) {
    // M3 app Content
}

使用文本样式

您可以使用 MaterialTheme.typography 检索提供给 M3 MaterialTheme 可组合项的排版:

Text(
    text = "Hello M3 theming",
    style = MaterialTheme.typography.titleLarge
)
Text(
    text = "you are learning typography",
    style = MaterialTheme.typography.bodyMedium
)

您可以详细了解有关应用排版的 Material 准则。

形状

Material Surface 可以用不同的形状显示。形状能够引导用户注意力、区别组件、传达状态以及表达品牌。

形状缩放定义了容器角的样式,提供从方形到完全圆形的圆角范围。

定义形状

Compose 提供了带有扩展参数的 M3 Shapes 类,以支持新的 M3 形状。M3 形状比例更像是字体比例,能够在整个界面中呈现丰富多样的形状。

形状有不同大小:

  • 较小
  • 中等
  • 特大

默认情况下,每个形状都有一个默认值,但是您可以替换这些值:

val replyShapes = Shapes(
    extraSmall = RoundedCornerShape(4.dp),
    small = RoundedCornerShape(8.dp),
    medium = RoundedCornerShape(12.dp),
    large = RoundedCornerShape(16.dp),
    extraLarge = RoundedCornerShape(24.dp)
)

定义 Shapes 后,您可以将其传递给 M3 MaterialTheme

MaterialTheme(
    shapes = replyShapes,
) {
    // M3 app Content
}

使用形状

您可以在 MaterialTheme 中为所有组件自定义形状缩放,也可以按组件自定义。

使用默认值应用大中型形状:

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(
    shape = MaterialTheme.shapes.large,
    onClick = {
    }
) {
    /* fab content */
}

Reply 示例应用中针对卡片的中等形状和针对悬浮操作按钮的大形状。
图 9. Reply 示例应用中卡片的中等形状和悬浮操作按钮的大形状

Compose 中还有另外两种形状:RectangleShapeCircleShape。矩形没有边框半径,而圆形则会显示完整的圆角边缘:

Card(shape = RectangleShape) { /* card content */ }
Card(shape = CircleShape) { /* card content */ }

以下示例展示了应用了默认形状值的一些组件:

所有 Material 3 组件的默认形状值。
图 10. 所有 Material 3 组件的默认形状值。

您可以详细了解有关应用形状的 Material 准则。

强调效果

M3 中的强调效果是使用颜色变体及其颜色组合提供的。在 M3 中,可以通过两种方式为界面添加强调效果:

  • 同时使用 Surface、surface-variant 和背景以及扩展 M3 颜色系统中的 on-surface、on-surface-variants 颜色。例如,Surface 可与 on-surface-variant 一起使用,surface-variant 可与 on-surface 一起使用,以提供不同级别的强调效果。
使用中性色组合进行强调。
图 11. 使用中性色组合进行强调。
  • 为文本使用不同粗细的字体。如上所示,您可以为我们的字体比例提供自定义粗细,以提供不同的强调效果。

bodyLarge = TextStyle(
    fontWeight = FontWeight.Bold
),
bodyMedium = TextStyle(
    fontWeight = FontWeight.Normal
)

Z 轴高度

Material 3 主要使用色调颜色叠加叠加层来表示高度。这是一种用于区分容器和 Surface 的新方法,增加的色调高度除了使用阴影外,还使用更突出的色调。

色调高度搭配阴影高度
图 12. 使用阴影 ElevationE 的色调高度

在 Material 3 中,深色主题中的高度叠加层也已更改为色调颜色叠加层。叠加层颜色来自主要颜色槽。

Material Design 3 中的阴影高度与色调高度
图 13. Material Design 3 中的阴影高度与色调高度对比

M3 Surface 是大多数 M3 组件的后备可组合项,同时支持色调和阴影高度:

Surface(
    modifier = Modifier,
    tonalElevation = /*...
    shadowElevation = /*...
) {
    Column(content = content)
}

Material 组件

Material Design 附带了一组丰富的 Material 组件(例如按钮、条状标签、卡片、导航栏),这些组件已遵循 Material 主题设置,可帮助您构建精美的 Material Design 应用。您可以立即开始使用具有默认属性的组件。

Button(onClick = { /*..*/ }) {
    Text(text = "My Button")
}

M3 提供了同一组件的多个版本,可根据强调和关注度以不同的角色使用。

按钮强调效果从 FAB 变为“主要向下”按钮
图 14. 按钮强调效果从 FAB 变为“主要”按钮,改为“文本”按钮
  • 用于进行最高强调度操作的扩展悬浮操作按钮:

ExtendedFloatingActionButton(
    onClick = { /*..*/ },
    modifier = Modifier
) {
    Icon(
        imageVector = Icons.Default.Edit,
        contentDescription = stringResource(id = R.string.edit),
    )
    Text(
        text = stringResource(id = R.string.add_entry),
    )
}

  • 高强调度操作的实心按钮:

Button(onClick = { /*..*/ }) {
    Text(text = stringResource(id = R.string.view_entry))
}

  • 用于低强调度操作的文本按钮:

TextButton(onClick = { /*..*/ }) {
    Text(text = stringResource(id = R.string.replated_articles))
}

您可以详细了解 Material 按钮和其他组件。 Material 3 提供了各种组件套件,例如按钮、应用栏、导航组件,这些组件专为不同的用例和屏幕尺寸而设计。

Material 还提供了多个导航组件,可帮助您根据不同的屏幕尺寸和状态实现导航。

当您想要定位 5 个或更少的目的地时,NavigationBar 适用于紧凑型设备:

NavigationBar(modifier = Modifier.fillMaxWidth()) {
    Destinations.entries.forEach { replyDestination ->
        NavigationBarItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { }
        )
    }
}

NavigationRail 适用于处于横屏模式的中小型平板电脑或手机。它可以为用户提供人体工学,并改善这些设备的用户体验。

NavigationRail(
    modifier = Modifier.fillMaxHeight(),
) {
    Destinations.entries.forEach { replyDestination ->
        NavigationRailItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { }
        )
    }
}

底部导航栏(左侧)和侧边导航栏(右侧)的回复展示
图 15. BottomNavigationBar(左)和 NavigationRail(右)的回复展示

在默认主题中同时使用这两种主题进行回复,为所有尺寸的设备提供沉浸式用户体验。

NavigationDrawer 适用于具有足够空间来显示细节的大中型平板电脑。您可以将 PermanentNavigationDrawerModalNavigationDrawerNavigationRail 结合使用。

PermanentNavigationDrawer(modifier = Modifier.fillMaxHeight(), drawerContent = {
    Destinations.entries.forEach { replyDestination ->
        NavigationRailItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { },
            label = { }
        )
    }
}) {
}

永久性抽屉式导航栏的回复展示
图 16. 永久性抽屉式导航栏的回复展示

导航选项可提升用户体验、人体工学设计和单手操作性。 您可以在 Compose 自适应 Codelab 中详细了解 Material 导航组件。

自定义组件的主题设置

M3 鼓励个性化和灵活性。所有组件都应用了默认颜色,但如有需要,可以使用灵活的 API 自定义其颜色。

大多数组件(如卡片和按钮)都提供了一个默认对象,用于显示颜色和高度界面,用户可以修改这些界面以自定义组件:

val customCardColors = CardDefaults.cardColors(
    contentColor = MaterialTheme.colorScheme.primary,
    containerColor = MaterialTheme.colorScheme.primaryContainer,
    disabledContentColor = MaterialTheme.colorScheme.surface,
    disabledContainerColor = MaterialTheme.colorScheme.onSurface,
)
val customCardElevation = CardDefaults.cardElevation(
    defaultElevation = 8.dp,
    pressedElevation = 2.dp,
    focusedElevation = 4.dp
)
Card(
    colors = customCardColors,
    elevation = customCardElevation
) {
    // m3 card content
}

您可以详细了解如何自定义 Material 3

系统界面

Material You 的某些方面来自 Android 12 及更高版本中的全新视觉样式和系统界面。发生变化的两个关键方面是涟漪和滚动。您无需执行任何额外操作即可实施这些更改。

涟漪

现在,按下时,涟漪会使用微小的火花照射表面。Compose Material 涟漪在 Android 的底层使用 RippleDrawable,因此 Android 12 及更高版本上为所有 Material 组件提供火花涟漪。

M2 与 M3 中的涟漪效果对比
图 17. M2 与 M3 中的涟漪

滚动

滚动现在会在滚动容器的边缘使用拉伸效果。无论 API 级别如何,在 Compose Foundation 1.1.0 及更高版本中,滚动容器可组合项(例如 LazyColumnLazyRowLazyVerticalGrid)中的拉伸滚动默认处于启用状态。

在容器边缘使用拉伸效果滚动回弹
图 18. 在容器边缘使用拉伸效果进行滚动

无障碍功能

Material 组件中内置的无障碍功能标准旨在为具有包容性的产品设计奠定基础。了解产品的无障碍功能有助于提高所有用户的易用性,包括弱视、失明、听觉障碍、认知障碍、运动障碍或情境性障碍(如手臂骨折)的用户。

颜色无障碍功能

动态配色旨在满足色彩对比度的无障碍功能标准。色调调色板系统对于使任何配色方案在默认情况下都易于访问至关重要。

Material 的颜色系统提供标准色调值和测量值,可用于满足无障碍对比度。

Reply 示例应用:主要、次要和第三色调面板(从上到下)
图 19. Reply 示例应用:主要、次要和第三色调调色板(从上到下)

所有 Material 组件和动态主题都已使用一组色调调色板中的上述颜色角色,这些颜色角色经过精心挑选,用于满足无障碍功能要求。不过,如果您要自定义组件,请务必使用适当的颜色角色并避免不匹配。

在主色上使用 on-primary,在主容器上使用 on-primary-container,并且对其他强调色和中性色也采用相同的方法,以向用户提供能够达到的对比度。

在主要容器之上使用第三容器会导致用户的对比度按钮较差:

// ✅ Button with sufficient contrast ratio
Button(
    onClick = { },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.primary,
        contentColor = MaterialTheme.colorScheme.onPrimary
    )
) {
}

// ❌ Button with poor contrast ratio
Button(
    onClick = { },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.tertiaryContainer,
        contentColor = MaterialTheme.colorScheme.primaryContainer
    )
) {
}

对比度足够高(左侧)与对比度低(右侧)
图 20. 鲜明对比(左侧)与低对比度(右侧)

排版无障碍功能

M3 字型比例更新了静态字型梯度和值,以提供经过简化但动态且尺寸类别可跨设备扩展的框架。

例如,在 M3 中,可以根据设备上下文(例如手机或平板电脑)为小显示屏分配不同的值。

大屏设备

Material 提供了有关自适应布局和可折叠设备的指南,以便用户能够无障碍地使用您的应用,并改善持有大型设备的用户的工效学设计。

Material 提供不同类型的导航,以帮助您为大型设备提供更好的用户体验。

您可以详细了解 Android 大屏设备应用质量指南,并参阅我们的 Reply 示例,了解自适应和无障碍设计。

了解详情

如需详细了解 Compose 中的 Material 主题设置,请参阅以下资源:

示例应用

文档

API 参考文档和源代码

视频