Jetpack Compose 提供了 Material Design 的实现,这是一个用于创建数字界面的综合设计系统。Material Design 组件(按钮、卡片、开关等)构建于 Material Theming 之上,这是一种系统化的方式,可根据您的产品品牌更好地自定义 Material Design。Material Theme 包含颜色、排版和形状属性。当您自定义这些属性时,您的更改将自动反映在您用于构建应用的组件中。
Jetpack Compose 使用 MaterialTheme
可组合项实现这些概念
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
配置您传递给 MaterialTheme
的参数,以对您的应用进行主题化。
图 1. 第一个屏幕截图显示了一个未配置 MaterialTheme
的应用,因此它使用默认样式。第二个屏幕截图显示了一个向 MaterialTheme
传递参数以自定义样式设置的应用。
颜色
Compose 中使用 Color
类来建模颜色,这是一个简单的数据持有类。
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
虽然您可以随意组织这些颜色(作为顶层常量、在单例中或内联定义),但我们强烈建议您在主题中指定颜色并从中检索颜色。这种方法可以轻松支持深色主题和嵌套主题。
图 2. Material 颜色系统。
Compose 提供了 Colors
类来建模 Material 颜色系统。Colors
提供了构建器函数来创建浅色或深色颜色集
private val Yellow200 = Color(0xffffeb46) private val Blue200 = Color(0xff91a4fc) // ... private val DarkColors = darkColors( primary = Yellow200, secondary = Blue200, // ... ) private val LightColors = lightColors( primary = Yellow500, primaryVariant = Yellow400, secondary = Blue700, // ... )
定义 Colors
后,您可以将其传递给 MaterialTheme
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
使用主题颜色
您可以使用 MaterialTheme.colors
检索提供给 MaterialTheme
可组合项的 Colors
。
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
表面和内容颜色
许多组件接受一对颜色和内容颜色
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
这使您不仅可以设置可组合项的颜色,还可以为其内容(其中包含的可组合项)提供默认颜色。许多可组合项默认使用此内容颜色。例如,Text
基于其父级的内容颜色来设置其颜色,而 Icon
则使用该颜色来设置其色调。
图 3. 设置不同的背景颜色会产生不同的文本和图标颜色。
方法会为任何主题颜色检索相应的“on”颜色。例如,如果您在 Surface
上设置primary
背景颜色,它会使用此函数将onPrimary
设置为内容颜色。如果您设置了非主题背景颜色,则还应指定适当的内容颜色。使用LocalContentColor
在层级结构中的给定位置检索当前背景的首选内容颜色。
内容 Alpha 值
通常,您希望改变强调内容的程度,以传达重要性并提供视觉层级。Material Design 文本易读性建议建议采用不同级别的不透明度来传达不同的重要性级别。
Jetpack Compose 通过 LocalContentAlpha
实现这一点。您可以通过为该 CompositionLocal
提供值来为层级结构指定内容 Alpha 值。嵌套的可组合项可以使用此值来对其内容应用 Alpha 处理。例如,Text
和 Icon
默认使用 LocalContentColor
与 LocalContentAlpha
结合。Material 指定了一些标准 Alpha 值(high
、medium
、disabled
),这些值由 ContentAlpha
对象建模。
// By default, both Icon & Text use the combination of LocalContentColor & // LocalContentAlpha. De-emphasize content by setting content alpha CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( // ... ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon( // ... ) Text( // ... ) }
要详细了解 CompositionLocal
,请查阅使用 CompositionLocal 处理局部作用域数据指南。
图 4. 对文本应用不同程度的强调,以视觉方式传达信息层级。第一行文本是标题,包含最重要的信息,因此使用 ContentAlpha.high
。第二行包含次要的元数据,因此使用 ContentAlpha.medium
。
深色主题
在 Compose 中,您可以通过向 MaterialTheme
可组合项提供不同的 Colors
集来实现浅色和深色主题
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
在此示例中,MaterialTheme
被封装在其自己的可组合函数中,该函数接受一个参数来指定是否使用深色主题。在这种情况下,函数通过查询设备主题设置来获取 darkTheme
的默认值。
您可以使用以下代码检查当前的 Colors
是浅色还是深色
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
海拔叠加层
在 Material Design 中,深色主题中具有更高海拔的表面会接收海拔叠加层,这会使其背景变亮。表面的海拔越高(使其更接近隐含的光源),该表面就越亮。
这些叠加层在使用深色时由 Surface
可组合项自动应用,也适用于任何其他使用表面的 Material 可组合项
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
图 5. 卡片和底部导航都使用 surface
颜色作为其背景。由于卡片和底部导航位于背景上方不同的海拔高度,因此它们的颜色略有不同——卡片比背景更亮,底部导航比卡片更亮。
对于不涉及 Surface
的自定义场景,请使用 LocalElevationOverlay
,这是一个包含由 Surface
组件使用的 ElevationOverlay
的 CompositionLocal
// Elevation overlays // Implemented in Surface (and any components that use it) val color = MaterialTheme.colors.surface val elevation = 4.dp val overlaidColor = LocalElevationOverlay.current?.apply( color, elevation )
要禁用海拔叠加层,请在可组合层次结构中的所需位置提供 null
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
有限的颜色强调
Material 建议为深色主题应用有限的颜色强调,在大多数情况下优先使用 surface
颜色而不是 primary
颜色。Material 可组合项,如 TopAppBar
和 BottomNavigation
默认实现此行为。
图 6. 带有有限颜色强调的 Material 深色主题。顶部应用栏在浅色主题中使用主色,在深色主题中使用表面色。
对于自定义场景,请使用 primarySurface
扩展属性
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
排版
Material 定义了一个类型系统,鼓励您使用少量具有语义名称的样式。
图 7. Material 类型系统。
Compose 使用 Typography
、TextStyle
和字体相关的类来实现类型系统。Typography
构造函数为每种样式提供了默认值,因此您可以省略任何您不想自定义的样式
val raleway = FontFamily( Font(R.font.raleway_regular), Font(R.font.raleway_medium, FontWeight.W500), Font(R.font.raleway_semibold, FontWeight.SemiBold) ) val myTypography = Typography( h1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W300, fontSize = 96.sp ), body1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W600, fontSize = 16.sp ) /*...*/ ) MaterialTheme(typography = myTypography, /*...*/) { /*...*/ }
如果您想在整个应用中使用相同的字体,请指定 defaultFontFamily 参数
,并省略任何 TextStyle
元素的 fontFamily
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
使用文本样式
TextStyle
s 通过 MaterialTheme.typography
访问。按如下方式检索 TextStyle
s
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
图 8. 使用精选的字体和样式来表达您的品牌。
形状
Material 定义了一个形状系统,允许您为大型、中型和小型组件定义形状。
图 9. Material 形状系统。
Compose 使用 Shapes
类实现形状系统,该类允许您为每个尺寸类别指定一个 CornerBasedShape
val shapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(0f), large = CutCornerShape( topStart = 16.dp, topEnd = 0.dp, bottomEnd = 0.dp, bottomStart = 16.dp ) ) MaterialTheme(shapes = shapes, /*...*/) { /*...*/ }
许多组件默认使用这些形状。例如,Button
、TextField
和 FloatingActionButton
默认使用小尺寸,AlertDialog
默认使用中尺寸,ModalDrawer
默认使用大尺寸 — 有关完整的映射,请参阅形状方案参考。
使用形状
Shape
s 通过 MaterialTheme.shapes
访问。使用如下代码检索 Shape
s
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
图 10. 使用形状来表达品牌或状态。
默认样式
Compose 中没有与 Android Views 中的默认样式等效的概念。您可以通过创建自己的“重载”可组合函数来包装 Material 组件,从而提供类似的功能。例如,要创建按钮样式,请将按钮包装在您自己的可组合函数中,直接设置您希望更改的参数,并将其他参数作为参数暴露给包含的可组合项。
@Composable fun MyButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary ), onClick = onClick, modifier = modifier, content = content ) }
主题叠加
通过嵌套 MaterialTheme
可组合项,您可以在 Compose 中实现与 Android Views 中的主题叠加等效的功能。由于 MaterialTheme
默认将颜色、排版和形状设置为当前主题值,如果一个主题只设置其中一个参数,则其他参数将保留其默认值。
此外,在将基于视图的屏幕迁移到 Compose 时,请注意 android:theme
属性的用法。在该 Compose UI 树部分中,您可能需要一个新的 MaterialTheme
。
在此示例中,详情屏幕的大部分使用 PinkTheme
,而相关部分则使用 BlueTheme
。请参见下面的屏幕截图和代码。
图 11. 嵌套主题。
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
组件状态
可交互的 Material 组件(可点击、可切换等)可以处于不同的视觉状态。状态包括启用、禁用、按下等。
可组合项通常具有一个 enabled
参数。将其设置为 false
会阻止交互,并更改颜色和海拔等属性以视觉方式传达组件状态。
图 12. enabled = true
的按钮(左)和 enabled = false
的按钮(右)。
在大多数情况下,您可以依赖颜色和海拔等值的默认设置。如果您希望配置在不同状态下使用的值,则可以使用类和便利函数。请参见下面的按钮示例
Button( onClick = { /* ... */ }, enabled = true, // Custom colors for different states colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary, disabledBackgroundColor = MaterialTheme.colors.onBackground .copy(alpha = 0.2f) .compositeOver(MaterialTheme.colors.background) // Also contentColor and disabledContentColor ), // Custom elevation for different states elevation = ButtonDefaults.elevation( defaultElevation = 8.dp, disabledElevation = 2.dp, // Also pressedElevation ) ) { /* ... */ }
图 13. enabled = true
的按钮(左)和 enabled = false
的按钮(右),已调整颜色和海拔值。
涟漪效果
Material 组件使用涟漪效果来指示它们正在被交互。如果您在层级结构中使用 MaterialTheme
,则在 clickable
和 indication
等修饰符内部,Ripple
将用作默认的Indication
。
在大多数情况下,您可以依赖默认的 Ripple
。如果您希望配置其外观,可以使用 RippleTheme
来更改颜色和 Alpha 值等属性。
您可以扩展 RippleTheme
并利用 defaultRippleColor
和 defaultRippleAlpha
实用函数。然后,您可以使用 LocalRippleTheme
在您的层级结构中提供自定义涟漪主题
@Composable fun MyApp() { MaterialTheme { CompositionLocalProvider( LocalRippleTheme provides SecondaryRippleTheme ) { // App content } } } @Immutable private object SecondaryRippleTheme : RippleTheme { @Composable override fun defaultColor() = RippleTheme.defaultRippleColor( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) @Composable override fun rippleAlpha() = RippleTheme.defaultRippleAlpha( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) }
图 14. 通过 RippleTheme
提供不同涟漪值的按钮。
了解详情
要详细了解 Compose 中的 Material Theming,请查阅以下其他资源。
Codelabs
视频
为您推荐
- 注意:当 JavaScript 关闭时显示链接文本
- Compose 中的自定义设计系统
- 在 Compose 中从 Material 2 迁移到 Material 3
- Compose 中的无障碍功能