一、简介
通过此篇博客,你将学习:
- Material Design入门指南以及如何针对您的应用对其进行自定义
- Compose如何实现Material Design系统
- 如何在应用中定义和使用颜色、排版和形状
- 如何设置组件的样式
- 如何支持浅色主题和深色主题
二、Material主题设置
Jetpack Compose 提供了Material Design的实现,后者是一个用于创建数字化界面的综合设计系统。Material Design组件(按钮
卡片、开关等)在Material主题设置的基础上构建而成,Material主题设置是一种系统化的方法,用于自定义Material Design以更好地反映您产品的品牌。一个Material主题由颜色、排版和形状属性组成。如果您自定义这些属性,相应设置会自动反映在您用来构建应用的组件中。
了解Material主题设置有助于您了解如何为Jetpack Compose应用设置主题,因此我们会在下面简要介绍相关概念。
2.1、颜色
Material Design定义了一些从语义上命名的颜色,供您在整个应用中使用:
primary是主要品牌颜色,secondary用于提供强调色。您可以为形成对比的区域提供颜色更深、更浅的变体。background和surface这两种颜色用于那些容纳这些概念上主流在应用“Surface”的组件的容器。此外,Material还定义了“on”颜色,即针对具名颜色上层的内容使用的颜色;例如,“surface”色容器中的文本采用了“on surface”颜色,Material组件已配置为使用这些主题颜色。例如,悬浮操作按钮的默认颜色为secondary,卡片的默认颜色为surface,诸如此类。
通过定义具名颜色,可以提供备用调色板,例如浅色主题和深色主题:
2.2、排版
同样,Materialh还定义了一些从语义上命名的字体样式:
虽然您可能不会按主题来更改字体样式,但使用字体比例可提升应用内部的一致性。如果您提供自己的字体和其他字体自定义设置,相应设置将反应在您的应用中使用的Material组件中,例如,引用蓝默认使用h6
样式,按钮默认使用button
。
2.3、样式
Material支持系统地使用形状来呈现您的品牌并传达出品牌里面。它定义了3个类别:小型、中型和大型组件;每种组件都可以定义要使用的形状,从而自定义角的样式(切角和圆角)和大小。
如果您自定义形状主题,相应设置将反应在众多组件中,例如,默认情况下,按钮和文本字段使用小型形状主题,卡片和对话框使用中型形状主题,动作条使用大型形状主题。
2.4、基准
Material默认采用“基准”主题,即紫色的配色方案、Roboto字体比例,以及以上图片所示的略成圆形的形状。
三、定义主题
3.1、MaterialTheme
在Jetpack Compose中实现主题设置的核心元素是MaterialTheme
可组合项。如果将此可组合项放在Compose层次结构中,你就可以为其中的所有组件指定颜色、字体和形状的自定义设置。下面是此组合项在库中的定义方式:
@Composable
fun MaterialTheme(
colors: Colors,
typography: Typography,
shapes: Shapes,
content: @Composable () -> Unit
) {
...
}
以后,你可以使用MaterialTheme object
检索传递到此可组合项的形参,以公开colors
、typography
和shapes
属性。后面,我们将逐一进行深入介绍。
打开Home.kt
并找到Home
可组合函数 – 这是应用的注入口点。请注意,虽然我们声明了MaterialTheme
,但并未指定任何参数,因此会获得默认的”基准”样式:
@Composable
fun Home() {
...
MaterialTheme {
Scaffold( ... )
}
}
我们来创建颜色、字体和形状参数,为我们的应用实现主题。
3.2、创建主题
如需集中设置样式,我们建议您创建自己的可组合项,用于封装和配置MaterialTheme
。这样依赖,您就可以在一个位置指定自己的主题自定义设置,并在多个位置(例如跨多个屏幕或@Preview
)轻松地重复使用这些自定义设置。您可以根据需要创建多个主题可组合项。例如,如果您想针对应用的不同部分支持不同的样式,就可以这样做。
新建一个名为Theme.kt
的文件。添加一个名为JetnewsTheme
的新可组合函数,此函数可接受其他可组合项作为内容并封装MaterialTheme
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(content = content)
}
现在,切换回Home.kt
,并将MaterialTheme
替换为JetnewsTheme
(并将其导入)
- MaterialTheme {
+ JetnewsTheme {
...
在此屏幕上,@Preview
不会立即显示任何变化。更新PostItemPreview
和FeaturedPostPreview
以使用新的JetnewsTheme
可组合项来封装其内容,以便预览使用新的主题
@Preview("Featured Post")
@Composable
private fun FeaturePostPreview() {
val post = remember { PostRepo.getFeaturePost() }
+ JetnewsTheme {
FeaturedPost(post = post)
+ }
}
3.3、颜色
我们要在应用中实现的调色板如下所示(目前只是一个浅色调色板;我们很快会回来为设置深色主题提供支持)
Compose中的颜色是使用COlor
类定义的。借助多个构造函数,您可以将颜色指定为ULong
,也可以按单独的颜色通道来指定颜色。
注意 :若要从用于指定颜色的常用“#dd0d3c”格式进行转换,请将“#”替换为“0xff”,即Color(0xffdd0d3c)
,其中“ff”表示完整的Alpha值。
在theme
软件包中创建一个新文件Color.kt
。在此文件中将以颜色添加为顶级公共属性:
val Red700 = Color(0xffdd0D3c)
val Red800 = Color(0xffd00036)
val Red900 = Color(0xffc20029)
现在,我们已经定义了应用的颜色。接下来,我们将其合并到MaterialTheme
所需的Colors
对象中,从而将特定颜色分配到Material的具名颜色。切换回Theme.kt
,然后添加以下代码:
private val LightColors = lightColors(
primary = Red700,
primaryVariant = Red900,
onPrimary = Color.White,
secondary = Red700,
secondaryVariant = Red900,
onSecondary = Color.White,
error = Red800
)
下面,我们要使用lightColors
函数来构建Colors
,这样即可提供合理的默认值,让我们不必将构成Material调色板的所有颜色全都指定出来。例如,请注意,我们尚未指定background
颜色或许多”on”颜色,我们将使用默认值。
现在,让我们在应用中使用这些颜色。请更新JetnewsTheme
可组合项以使用新的Colors
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(
+ colors = LightColors,
content = content
)
}
打开Home.kt
并刷新预览。请注意,新的配色方案会反应在TopAppBar
等组件中。
3.4、排版
我们要在应用中实现的字体比例如下所示:
在Compose中,我们可以定义TextStyle
对象,以定义设置一些文本的样式所需的信息。下面是其属性的示例:
data class TextStyle(
val color: Color = Color.Unset,
val fontSize: TextUnit = TextUnit.Inherit,
val fontWeight: FontWeight? = null,
val fontStyle: FontStyle? = null,
val fontFamily: FontFamil? = null,
val letterSpacing: TextUnit = TextUnit.Inherit,
val background: Color = Colir.Unser,
val textAlign = TextAlign? = null,
val textDirection: TextDirection? = null,
val lineHeight: TextUnit = TextUnit.Inherit,
...
)
在theme
软件包中创建一个新文件Typography.kt
。首先,我们将定义FOntFamily
private val Montserrat = FontFamily(
Font(R.font.montserrat_regular),
Font(R.font.montserrat_medium, FontWeight.W500).
Font(R.font.montserrat_semibole, FontWeight.W600)
)
private val Domine = FontFamily(
Font(R.font.domine_regular),
Font(R.font.domine_bold, FontWeight.Bold)
)
现在创建一个MaterialTheme
接受的Typography
对象,为比例中的每个语义样式指定TextStyle
:
val JetnewsTypography = Typography(
h4 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 30.sp
),
h5 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 24.sp
),
h6 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 20.sp
),
subtitle1 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 16.sp
),
subtitle2 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
body1 = TextStyle(
fontFamily = Domine,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
body2 = TextStyle(
fontFamily = Montserrat,
fontSize = 14.sp
),
button = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
),
overline = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W500,
fontSize = 12.sp
)
)
打开Theme.kt
并更新JetnewsTheme
可组合项,以使用新的Typography
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(
color = LightColors,
+ typography = JetnewsTypography,
content = content
)
}
打开 Home.kt
并刷新预览,以查看显示精选博文的 Card
如何反映新应用的形状主题。
3.5、深色主题
在应用中支持深色主题不仅有助于您的应用在用户设备上更好地集成(从Android 10开始,设备上已提供全局深色主题切换开关),还有助于降低能耗以及满足无障碍功能需求提供支持。Material提供了关于如何创建深色主题的设计指南。以下是我们想为深色主题实现的备用调色板:
打开Color.kt
并添加以下颜色:
val Red200 = Color(0xfff297a4)
val Red300 = Color(0xffea6d7e)
现在,打开Theme.kt
并添加以下代码:
private val DarkColors = darkColors(
primary = Red300,
primaryVariant = Red700,
onPrimary = Color.Black,
secondary = Red300,
onSecondary = Color.Black,
error = Red200
)
现在,更新HetnewsTheme
:
@Composable
fun JetnewsTheme(
+ darkTheme: Boolean = isSystemInDakTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
+ colors = if (darkTheme) DarkColors else LightColors,
typography = JetnewsTypography,
shapes = JetnewsShapes,
content = content
)
}
此时,我们已经添加了用于判断是否使用深色主题的新形参,并将其默认设置为产讯设备的全局设置。这样即可为我们提供很实用的默认值,但如果我们要让特定屏幕始终/永不采用深色主题,或要让@Preview
采用深色主题,我们仍可轻松轻松替换。
打开Homt.kt
并为FeaturedPost
可组合项创新的预览,此预览能够以深色主题显示该可组合项:
@Preview("Featured Post · Dark")
@Composable
private fun FeaturedPostDarkPreview() {
val post = remember { PostRepo.getFeaturedPost() }
JetnewsTheme(darkTheme = true) {
FeaturedPost(post = post)
}
}
刷新预览窗格以查看深色主题预览。