使用 Material 3 在 Compose 中设置主题
- 官方文档:https://developer.android.google.cn/codelabs/jetpack-compose-theming?hl=zh_cn#0
1、配色方案
(1)基本介绍
配色方案的基础是五种关键颜色,其中每种颜色都对应 Material 3 组件使用的 13 色调调色板之一
每种强调色(主色、辅色、第三色)都会提供四种不同色调的兼容色,以便进行配对、定义强调效果和视觉表现
中性色也分兼容的色调,用于 Surface 和背景。当这些颜色被应用于任何 Surface 时,它们对于突出文本图标也非常重要
除了主色主色、辅色、第三色,其他颜色都是中性色(Neutral)
它主要用来做背景(Background)、卡片(Surface)、文字(Text)
例如,为了让悬浮操作按钮(FAB)成为引人注目的按钮,可以使用第三色
(2)生成配色方案
可以手动创建自定义 ColorScheme
也可以使用 Material 主题构建器,并且选择导出 Compose 主题代码:https://material-foundation.github.io/material-theme-builder/
生成的 Theme.kt 文件包含浅色和深色配色方案以及应用主题,同时还包含主要主题可组合函数 AppTheme
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable() () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> darkScheme
else -> lightScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
content = content
)
}
1. useDarkTheme:此参数会与函数 isSystemInDarkTheme 相关联,用于观察系统主题设置并应用浅色或深色主题
如果想手动让应用保持浅色或深色主题,可以将布尔值传递给 useDarkTheme
2. content:主题将被应用到的内容
// Theme.kt 中的关键配置代码
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable() () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> darkScheme
else -> lightScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
content = content
)
}
(3)颜色角色
主色是基础颜色,用于主要组件,例如,显眼的按钮、活动状态
辅色用于界面中不太显眼的组件,例如,过滤组件
第三色用于提供对比鲜明的强调色
中性色用于应用中的背景和 Surface
(4)色调和阴影高度
在界面上,高度(elevation)就是一个组件浮在背景上有多高
传统做法是用阴影(shadow)来表示高度,越高,阴影越大越深
色调高度是用颜色来暗示高度,而不是只靠阴影
当一个组件的高度提高时,它除了阴影变重,它的背景颜色也会发生微妙变化。这个颜色变化是从主色里提取的色调叠加层
简单来说,组件浮得越高,它就越染上一点主色的色调
@Composable
fun TonalElevation_ShadowElevation_Demo() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Surface(
shadowElevation = 8.dp,
tonalElevation = 0.dp,
modifier = Modifier.fillMaxWidth()
) {
Text("阴影 8.dp,无色调", modifier = Modifier.padding(16.dp))
}
Surface(
shadowElevation = 0.dp,
tonalElevation = 8.dp,
modifier = Modifier.fillMaxWidth()
) {
Text("色调 8.dp,无阴影", modifier = Modifier.padding(16.dp))
}
Surface(
shadowElevation = 8.dp,
tonalElevation = 8.dp,
modifier = Modifier.fillMaxWidth()
) {
Text("阴影 + 色调 都是 8.dp", modifier = Modifier.padding(16.dp))
}
}
}
@Preview(showBackground = true)
@Composable
fun TonalElevation_ShadowElevation_Demo_Preview() {
TonalElevation_ShadowElevation_Demo()
}
2、动态配色
使用动态颜色,算法会从用户的壁纸中派生自定义颜色,以将其应用到其应用和系统界面
动态颜色适用于 Android 12 及更高版本
如果动态颜色可用,可以使用 dynamicLightColorScheme 函数或 dynamicDarkColorScheme 函数设置动态配色方案
如果不可用,应回退采用默认的浅色或深色的 ColorScheme
状态栏的样式能根据用于为应用设置主题的配色方案进行动态变化
// Theme.kt 中的关键配置代码
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable() () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> darkScheme
else -> lightScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
content = content
)
}
3、排版
Material Design 3 定义了一个字体比例。命名和分组已简化为:显示、大标题、标题、正文、标签。每个都有大号、中号和小号
Compose 提供了 M3 Typography 类以及现有的 TextStyle 和 font-related 类,用以对 Material 3 字体比例进行建模
class Typography(
val displayLarge: TextStyle = TypographyTokens.DisplayLarge,
val displayMedium: TextStyle = TypographyTokens.DisplayMedium,
val displaySmall: TextStyle = TypographyTokens.DisplaySmall,
val headlineLarge: TextStyle = TypographyTokens.HeadlineLarge,
val headlineMedium: TextStyle = TypographyTokens.HeadlineMedium,
val headlineSmall: TextStyle = TypographyTokens.HeadlineSmall,
val titleLarge: TextStyle = TypographyTokens.TitleLarge,
val titleMedium: TextStyle = TypographyTokens.TitleMedium,
val titleSmall: TextStyle = TypographyTokens.TitleSmall,
val bodyLarge: TextStyle = TypographyTokens.BodyLarge,
val bodyMedium: TextStyle = TypographyTokens.BodyMedium,
val bodySmall: TextStyle = TypographyTokens.BodySmall,
val labelLarge: TextStyle = TypographyTokens.LabelLarge,
val labelMedium: TextStyle = TypographyTokens.LabelMedium,
val labelSmall: TextStyle = TypographyTokens.LabelSmall,
)
Typography 把整个 App 里所有要用到的文字样式都打包好了
例如,headlineLarge(大号大标题)、titleMedium(中号标题)、bodyLarge(大号正文)、labelSmall(小号标签)
不需要一个一个地去设置字体大小、粗细、行高,只要定义一个全局的 Typography,整个 App 的主题就会自动应用这些样式,保证设计统一
TextStyle 是具体的样式类,它包含了所有关于文字的细节属性,例如
color(颜色)、fontSize(字号)、fontWeight(粗细)
fontFamily(字体家族)、letterSpacing(字间距)、lineHeight(行高)
Typography 里面其实就是一个一个的 TextStyle。例如,Typography.bodyLarge 本质上就是一个被预设好参数的 TextStyle 对象
font-related 类指的是和字体相关的独立工具类
FontFamily,定义字体,例如,衬线体、无衬线体或自定义下载字体
FontWeight,定义粗细,例如,Bold、Light
FontStyle,定义风格,例如,正常、斜体
val typography = Typography(
headlineSmall = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 24.sp,
lineHeight = 32.sp,
letterSpacing = 0.sp
),
titleLarge = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 18.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
bodyLarge = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.15.sp
),
bodyMedium = TextStyle(
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.25.sp
),
labelMedium = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
)
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
...
MaterialTheme(
colorScheme = colors,
typography = typography,
content = content
)
}
4、形状
Compose 提供带有扩展参数的 Shapes 类来实现新的 M3 形状
形状比例中包含不同大小的形状:特小、小、中、大、特大
class Shapes(
val extraSmall: CornerBasedShape = ShapeDefaults.ExtraSmall,
val small: CornerBasedShape = ShapeDefaults.Small,
val medium: CornerBasedShape = ShapeDefaults.Medium,
val large: CornerBasedShape = ShapeDefaults.Large,
val extraLarge: CornerBasedShape = ShapeDefaults.ExtraLarge,
)
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
...
MaterialTheme(
colorScheme = colors,
typography = typography,
shapes = shapes,
content = content
)
}
val shapes = Shapes(
extraSmall = RoundedCornerShape(4.dp),
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(16.dp),
large = RoundedCornerShape(24.dp),
extraLarge = RoundedCornerShape(32.dp)
)
5、强调方式
强调,即突出显示,例如,标题要比正文显眼
M3 提供了两种方式做强调
用颜色组合,例如,深色背景配浅色文字,浅色背景配深色文字
用字体粗细,例如,标题用粗体,正文用细体
补充学习
1、切换状态栏图标颜色
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
(1)获取当前视图
val view = LocalView.current
- LocalView.current 获取当前 Compose 界面依附的 View 对象,即 Android 传统的 View 系统实例
(2)避免预览崩溃
if (!view.isInEditMode) { ... }
isInEditMode 用于判断当前是否处于 Android Studio 的预览模式
在预览模式下没有真实的 Activity 和 Window,如果执行下面的代码会抛出异常
这个判断确保逻辑只在真机或模拟器运行时生效
(3)副作用执行
SideEffect { ... )
SideEffect 是 Compose 中的一个副作用 API
SideEffect 保证里面的代码每次重组完成后都会执行
修改状态栏属于外部命令式操作,要确保在 UI 绘制到屏幕上之后立即同步执行,避免界面闪烁
(4)切换状态栏图标颜色
val window = (view.context as Activity).window
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
通过 view.context 强制转为 Activity,拿到当前窗口对象
通过 WindowCompat.getInsetsController 方法获取 AndroidX 兼容库提供的控制器,用于管理状态栏和导航栏的显示
isAppearanceLightStatusBars 属性,true 为浅色状态栏(图标为黑色),false 为深色状态栏(图标为白色)
!darkTheme 表示,当主题为浅色时,使用黑色图标;当主题为深色时,使用白色图标
2、切换状态栏颜色
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat
.getInsetsController(window, view)
.isAppearanceLightStatusBars = useDarkTheme
}
}
(1)获取当前视图
val view = LocalView.current
- LocalView.current 获取当前 Compose 界面依附的 View 对象,即 Android 传统的 View 系统实例
(2)避免预览崩溃
if (!view.isInEditMode) { ... }
isInEditMode 用于判断当前是否处于 Android Studio 的预览模式
在预览模式下没有真实的 Activity 和 Window,如果执行下面的代码会抛出异常
这个判断确保逻辑只在真机或模拟器运行时生效
(3)副作用执行
SideEffect { ... )
SideEffect 是 Compose 中的一个副作用 API
SideEffect 保证里面的代码每次重组完成后都会执行
修改状态栏属于外部命令式操作,要确保在 UI 绘制到屏幕上之后立即同步执行,避免界面闪烁
(4)切换状态栏颜色
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat
.getInsetsController(window, view)
.isAppearanceLightStatusBars = useDarkTheme
通过 view.context 强制转为 Activity,拿到当前窗口对象
将状态栏的背景色设置为 colors.primary。如果是深色主色,背景色较深;如果是浅色主色,背景色较亮
通过 WindowCompat.getInsetsController 方法获取 AndroidX 兼容库提供的控制器,用于管理状态栏和导航栏的显示
isAppearanceLightStatusBars 属性,true 为浅色状态栏(图标为黑色),false 为深色状态栏(图标为白色)
useDarkTheme 表示,当主题为深色时,使用黑色图标;当主题为浅色时,使用白色图标
网硕互联帮助中心







评论前必须登录!
注册