Jetpack Compose 提供了 Material Design 的实现,这是一个用于创建数字界面的综合设计系统。 Material Design 组件(按钮、卡片、开关等)建立在 Material Theming 之上,这是一种系统化方法,用于自定义 Material Design 以更好地反映产品的品牌。Material 主题包含 颜色、字体 和 形状 属性。当您自定义这些属性时,您的更改会自动反映在您用于构建应用程序的组件中。
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
基于其父级的 content color 设置其颜色,而 Icon
使用该颜色来设置其色调。
图 3. 设置不同的背景色会产生不同的文本和图标颜色。
contentColorFor()
方法检索任何主题颜色的适当“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 中,深色主题中具有较高海拔的表面会收到 海拔叠加,这会使其背景变亮。表面的海拔越高(使其更靠近隐含的光源),该表面就越亮。
当使用深色时,这些叠加会由 Surface
可组合项自动应用,以及使用表面的任何其他 Material 可组合项。
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
图 5. 卡片和底部导航都使用 surface
颜色作为其背景。由于卡片和底部导航在背景之上具有不同的海拔级别,因此它们具有略微不同的颜色 - 卡片比背景更亮,而底部导航比卡片更亮。
对于不涉及 Surface
的自定义场景,请使用 LocalElevationOverlay
,这是一个包含 ElevationOverlay
的 CompositionLocal
,该 ElevationOverlay
由 Surface
组件使用。
// 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
通过 MaterialTheme.typography
访问。像这样检索 TextStyle
。
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
可通过 MaterialTheme.shapes
访问。使用类似以下代码检索 Shape
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
将颜色、排版和形状默认设置为当前主题值,如果主题只设置了其中一个参数,则其他参数将保留其默认值。
此外,在将基于 View 的屏幕迁移到 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
,则 Ripple
将用作 Indication
的默认值,在 clickable
和 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 主题,请参阅以下其他资源。
Codelabs
视频
为您推荐
- 注意:当 JavaScript 关闭时,将显示链接文本
- Compose 中的自定义设计系统
- 将 Compose 中的 Material 2 迁移到 Material 3
- Compose 中的无障碍性