使用 Jetpack Compose 实现 Material 主题效果

1. 前言

Material Design 是 Google 打造的设计系统,旨在帮助开发者针对 Android 以及其他移动平台和网络平台打造优质的数字体验。它深受物质世界及其纹理(包括物体如何反射光和投射阴影)启发构建而成。它提供了一些准则,指导如何以具有可读性、吸引力和一致性的方式构建应用界面。

在此 Codelab 中,您将学习 Material 主题设置,获得自定义颜色、排版和形状方面的指导,以便在您的应用中使用 Material Design。您可以根据应用的需要进行自定义。您还将学习如何添加顶部应用栏,以显示应用的名称和图标。

前提条件

  • 熟悉 Kotlin 语言,包括语法、函数和变量。
  • 能够在 Compose 中构建布局,包括带内边距的行和列。
  • 能够在 Compose 中创建简单列表。

学习内容

  • 如何将 Material 主题设置应用于 Compose 应用。
  • 如何为您的应用添加自定义字体。
  • 如何为您的应用添加自定义调色板。
  • 如何向应用中的元素添加自定义形状。
  • 如何向应用中添加顶部应用栏。

构建内容

  • 您将构建一个遵循 Material Design 最佳做法的精美应用。

所需条件

  • 最新版本的 Android Studio。
  • 用于下载起始代码和字体的互联网连接。

2. 应用概览

在此 Codelab 中,您将创建 Woof,该应用会显示狗狗列表,并使用 Material Design 来打造出色的应用体验。

此图显示了 Woof 应用,以及采用浅色主题时的自定义状态栏。

对于 Woof 应用,我们邀请了一位设计师帮助我们设计这款应用,其中包括选择应用的调色板、排版和形状。我们知道,并非所有开发者都可以接触到设计师,但在此 Codelab 中,我们将向您展示使用 Material 主题实现各种设计的可能性。通过此 Codelab,您可以了解到日后如何使用 Material 主题设置来改进应用的外观和风格。

下面是该设计师的设计规范,其中详细介绍了应用的浅色主题和深色主题的视觉设计。

应用设计:

浅色主题

深色主题

此图显示了 Woof 应用,以及采用浅色主题时的自定义状态栏。

调色板

以下是由设计师选择的浅色和深色主题的调色板。

浅色:

颜色

名称

十六进制颜色

此图片描绘的是 Grey50 这种颜色。

Grey50

#F8F9FA

主要

此图片描绘的是 Green50 这种颜色。

Green50

#E6F4EA

surface

此图片描绘的是 Green100 这种颜色。

Green100

#CEEAD6

background

此图片描绘的是 Grey700 这种颜色。

Grey700

#5F6368

辅助

此图片描绘的是 Grey900 这种颜色。

Grey900

#202124

onSurface

此图片描绘的是 Grey900 这种颜色。

Grey900

#202124

onPrimary

深色:

颜色

名称

十六进制颜色

此图片描绘的是 White 这种颜色。

White

#FFFFFF

主要

此图片描绘的是 Grey100 这种颜色。

Grey100

#F1F3F4

onSurface

此图片描绘的是 Grey100 这种颜色。

Grey100

#F1F3F4

onPrimary

此图片描绘的是 Cyan700 这种颜色。

Cyan700

#129EAF

surface

此图片描绘的是 Cyan900 这种颜色。

Cyan900

#007B83

background

此图片描绘的是 Grey900 这种颜色。

Grey900

#202124

辅助

排版

以下是此应用的字体(由设计师选择)。

标题

字体

字体粗细

字号

h1

此图片描绘的是 Abril Fatface 这种字体。

Normal

30sp

h2

此图片描绘的是 Montserrat 这种字体。

Bold

20sp

h3

此图片描绘的是 Montserrat 这种字体。

Bold

14sp

body1

此图片描绘的是 Montserrat 这种字体。

Normal

14sp

主题文件

Theme.kt 会存储所有关于应用主题的信息,这些信息通过颜色、排版和形状进行定义。在此 Codelab 中,您只对此文件进行一次修改,但这是一个需要您了解的重要文件。该文件内是可组合项 WoofTheme(),用于设置应用的颜色、排版和形状。

@Composable
fun WoofTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
   val colors = if (darkTheme) {
       DarkColorPalette
   } else {
       LightColorPalette
   }

   MaterialTheme(
       colors = colors,
       typography = Typography,
       shapes = Shapes,
       content = content
   )
}

MainActivity.kt 中,添加了 WoofTheme() 来提供 Material 主题设置。

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           WoofTheme {
               WoofApp()
           }
       }
   }
}

查看 DefaultPreview()。此外,还会添加 WoofTheme(),以提供您在 Preview 标签页中看到的 Material 主题设置。

@Preview
@Composable
fun DefaultPreview() {
   WoofTheme(darkTheme = false) {
       WoofApp()
   }
}

3. 获取起始代码

  1. 进入为此项目提供的 GitHub 代码库页面。
  2. 点击 Code 按钮。系统随即会打开一个对话框。

1debcf330fd04c7b.png

  1. 在对话框中,点击 HTTPS 标签页,然后点击网址旁边的 d80cda5fcac5cc83.png 以复制。您可以在 Android Studio 中使用此网址。

52117cef9280e2c3.png

  1. 如果 Android Studio 未打开,请将其打开,然后在 Welcome to Android Studio 窗口中点击 Get from VCS

38e3cb13f6cbf3b8.png

  1. 如果 Android Studio 已经打开,请依次选择 File > New > Project from Version Control

eb859f5f2f920118.png

  1. Get from Version Control 窗口中,从 Version Control 菜单中选择 Git

c1ff21b37a87da88.png

  1. URL 文本框中,输入您从 GitHub 代码库页面复制的链接。
  2. Directory 菜单中,选择要从 GitHub 代码库克隆源代码的目录。

d872acd8fb88d532.png

  1. 点击 Clone
  2. 等待 Android Studio 下载完毕,然后打开项目。
  3. 在 Android Studio 的导航菜单中,点击 8de56cba7583251f.png ‘Run app’,然后确保应用按预期构建。
  4. 在导航菜单中,依次点击 Git > 分支…

3b3d9c44dcc1327a.png

  1. Git Branches 对话框中的 Remote Branches 部分下,选择之前提供的分支名称,然后点击 Checkout

4d189e9e70df0234.png

  1. 如果您需要切换分支,请依次点击 Git > Branches,选择要切换到的分支,然后点击 Smart Checkout

596d8337aba9e937.png

查看起始代码

  1. 在 Android Studio 中打开起始代码。
  2. 依次打开 com.example.woof > data > Dog.kt。其中包含用于代表狗狗的照片、名字、年龄和爱好的 Dog data class。它还包含狗狗列表以及您将在应用中用作数据的信息。
  3. 依次打开 res > drawable。此文件包含此项目所需的所有图片资源,包括应用图标、狗狗图片和图标。
  4. 依次打开 res > values > strings.xml。此文件包含您在此应用中使用的字符串,包括应用名称、狗狗名字、说明等。
  5. 打开 MainActivity.kt。此文件包含创建简单的列表的代码,该列表用于显示小狗的照片、狗狗的名字以及狗狗的年龄。
  6. WoofApp() 包含用于显示 DogItemsLazyColumn
  7. DogItem() 包含一个 Row,用于显示小狗的照片和相关信息。
  8. DogIcon() 显示小狗的照片。
  9. DogInformation() 包含狗狗的名字和年龄。

确保您的模拟器/设备采用浅色主题

在此 Codelab 中,您将使用浅色主题和深色主题,不过,此 Codelab 中的大部分主题都采用浅色主题。开始之前,请确保您的设备/模拟器使用的是浅色主题。

如需在浅色主题下查看您的应用,请在模拟器或实体设备上执行以下操作:

  1. 进入设备的设置应用。
  2. 搜索深色主题,然后点击进入该主题。
  3. 如果深色主题处于开启状态,请将其关闭。

运行起始代码以查看应用的起始状态;这是一个列表,其中会显示狗狗的照片、名字和年龄。它能正常运行,但外观不好看,所以我们会解决该问题。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。

4. 添加颜色

在 Woof 应用中,您首先要修改的就是调色板。

调色板是您的应用使用的颜色组合。不同的颜色组合会激发不同的心情,这会影响用户使用您的应用时的感受。由于我们就这个应用与设计师进行了合作,因此设计师为我们选择了调色板。不过,Material Design 网站提供了颜色系统指南,以便您详细了解调色板以及如何自行生成调色板。

之前您在 Android 应用中添加颜色时,使用了 Color.REDColor.PINK 等元素。如果要添加更具体的颜色,如天蓝色或玉绿色,该怎么办?

在 Android 系统中,颜色用十六进制颜色值表示。十六进制颜色代码以井号 (#) 字符开头,后跟六个字母和/或数字(代表该颜色的红色、绿色和蓝色 [RGB] 分量)。前两个字母/数字表示红色,后面的两个表示绿色,最后两个表示蓝色。

这显示的是用于创建颜色的十六进制数字。

颜色还可以包含 Alpha 值(字母和/或数字),用于表示颜色的透明度(#00 表示不透明度为 0% [完全透明],#FF 表示不透明度为 100% [完全不透明])。若添加 alpha 值,则该值为井号 (#) 字符后的十六进制颜色代码的前两个字符。如果未添加 alpha 值,系统会假定它是 #FF,即 100% 不透明(完全不透明)。

以下是一些十六进制颜色值的示例。

颜色

名称

十六进制颜色

此图片描绘的是黑色。

黑色

#000000

此图片描绘的是绿色。

绿色

#72D98C

此图片描绘的是蓝色。

蓝色

#4285F4

此图片描绘的是 White 这种颜色。

White

#FFFFFF

您当然不需要记住颜色的十六进制颜色代码。我们提供了用于选择颜色的工具,这些工具可为您生成数字。在此 Codelab 中,我们提供了颜色。

下面是我们在应用中使用的调色板。请注意,没有 alpha 值;这意味着颜色为 100% 不透明度。

浅色:

颜色

名称

十六进制颜色

此图片描绘的是 Grey50 这种颜色。

Grey50

#F8F9FA

主要

此图片描绘的是 Green50 这种颜色。

Green50

#E6F4EA

surface

此图片描绘的是 Green100 这种颜色。

Green100

#CEEAD6

background

此图片描绘的是 Grey700 这种颜色。

Grey700

#5F6368

辅助

此图片描绘的是 Grey900 这种颜色。

Grey900

#202124

onSurface

此图片描绘的是 Grey900 这种颜色。

Grey900

#202124

onPrimary

您可能想知道什么是以及它们是如何分配的。

  • 主要颜色是应用在屏幕和组件中最常显示的颜色。
  • 辅助颜色为应用提供了更多强调和区分的方式。
  • surface 颜色会影响卡片、工作表和菜单等组件的 Surface。
  • background 颜色显示在可滚动的内容后面。
  • on 颜色元素显示在调色板中其他颜色的上层,主要应用于文本、图标和描边。在调色板中,我们有一个 onSurface 颜色(显示在 surface 颜色的上层)和一个 onPrimary 颜色(显示在主要颜色的上层)。

有了这些插槽可以实现统一的设计系统,相关组件的颜色也相似。

某些组件会自动映射到颜色槽。例如,Surface 可组合项会自动将 background 颜色映射到 surface 槽。这意味着,您无需为 Surface 可组合项明确分配 surface 颜色;当您在应用中设置颜色主题时,它会自动显示。除这 6 个颜色槽之外,颜色槽的数量还多。不过,也并非必须全部分配。未在应用主题中指定的所有颜色都将采用基准 Material 颜色主题(用于定义应用的默认主题)中定义的颜色。

关于颜色的理论已足够 - 现在可以向应用中添加这个漂亮的调色板了!

向主题添加调色板

  1. 打开 Color.kt 文件。此文件用于为应用的调色板添加颜色。已经添加了一些映射到默认调色板的颜色。在已包含的颜色下方添加 Woof 应用调色板所需的这些新颜色。
//Light Theme
val Grey50 = Color(0xFFF8F9FA)
val Grey900 = Color(0xFF202124)
val Grey700 = Color(0xFF5F6368)
val Green50 = Color(0xFFE6F4EA)
val Green100 = Color(0xFFCEEAD6)
  1. 打开 Theme.kt 文件,并将 LightColorPalette 替换为以下代码,将颜色添加到它们的槽中。
private val LightColorPalette = lightColors(
   background = Green100,
   surface = Green50,
   onSurface = Grey900,
   primary = Grey50,
   onPrimary = Grey900,
   secondary = Grey700
)

WoofTheme() 会检查 darkTheme 是否设置为 true,并将 MaterialTheme object 中的 colors 设置为浅色调色板(如果设置为 false)或深色调色板(如果设置为 true)。

@Composable
fun WoofTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
   val colors = if (darkTheme) {
       DarkColorPalette
   } else {
       LightColorPalette
   }

   MaterialTheme(
       colors = colors,
       typography = Typography,
       shapes = Shapes,
       content = content
   )
}

向应用添加颜色

现在,颜色已添加,可以在应用的不同区域中使用它们了。您将使用:

  • 针对列表背景使用 background 颜色。
  • 针对列表项的背景使用 surface 颜色,使其在列表的背景颜色中显得更醒目。
  • 针对文本使用 onSurface 颜色,使其在列表项颜色下更清晰易读。

其余两种颜色将在此 Codelab 的后续内容中使用。

  1. 打开 MainActivity.kt。在 WoofApp() 中,向 LazyColumn 添加背景修饰符,使列表的背景变为绿色。将背景颜色设为您在 Theme.kt 中设置为 background 的颜色。您可以通过 MaterialTheme.colors.background 访问设置为应用主题的 background 颜色的颜色。

点击 Preview 标签页中的 Build & Refresh

import androidx.compose.material.MaterialTheme
import androidx.compose.foundation.background

LazyColumn(modifier =
   Modifier.background(MaterialTheme.colors.background))

太棒了!背景现在的颜色为 Green100

此图片在 WoofPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含背景颜色。

  1. DogItem() 中,将 background() 扩展添加到 Row 并传入 MaterialTheme.colors.surface。这会将 Row 的背景颜色设置为使用应用主题中指定的 surface 颜色。
import androidx.compose.ui.unit.dp

Row(
   modifier = Modifier
       .fillMaxWidth()
       .padding(8.dp)
       .background(MaterialTheme.colors.surface)
)

预览标签页中,您会看到列表项现在与应用背景的颜色不同,并且每个列表项更加明显。

此图片在 DefaultPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色,以及列表项的背景颜色。

  1. DogInformation() 中,将 color 添加到狗狗名字文本可组合项和狗狗年龄文本可组合项。此操作会更改这两个文本可组合项的字体颜色。使用 onSurface 颜色。
Column {
   Text(
       text = stringResource(dog.name),
       color = MaterialTheme.colors.onSurface,
       modifier = Modifier.padding(top = smallPadding)
   )
   Text(
       text = stringResource(R.string.years_old, dog.age.toString()),
       color = MaterialTheme.colors.onSurface
   )
}
  1. 构建和刷新应用。显示狗狗名字和年龄的文字与背景形成了更明显的对比,更易于阅读,但在浅色主题中并不那么明显。

浅色主题:

此图片在 WoofPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。

下图显示了文本与深色主题的背景对比度,我们将在下一部分中实现此效果:

没有文本颜色的深色主题

有文本颜色的深色主题

此图片在 WoofPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色,以及列表项的背景颜色。这是深色主题下的效果。

此图片在 WoofPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。这是深色主题下的效果。

深色主题

在 Android 系统中,可以选择将设备切换为深色主题。深色主题使用更暗、更柔和的颜色,并且:

  • 可以大幅减少耗电量(具体取决于设备的屏幕技术)。
  • 为弱视以及对强光敏感的用户提高可视性。
  • 让所有人都可以在光线较暗的环境中更轻松地使用设备。

您的应用可以选择启用 Force Dark,这意味着系统会为您实现深色主题。不过,如果您实现深色主题,可为用户提供更好的体验,以便您继续完全控制应用主题。

在选择自己的深色主题时,请务必注意,深色主题的颜色必须符合无障碍功能对比度标准。深色主题使用较深的界面颜色,且色彩强度有限。

以下是该应用的深色主题的颜色:

深色:

颜色

名称

十六进制颜色

此图片描绘的是 White 这种颜色。

White

#FFFFFF

主要

此图片描绘的是 Grey100 这种颜色。

Grey100

#F1F3F4

onSurface

此图片描绘的是 Grey100 这种颜色。

Grey100

#F1F3F4

onPrimary

此图片描绘的是 Cyan700 这种颜色。

Cyan700

#129EAF

surface

此图片描绘的是 Cyan900 这种颜色。

Cyan900

#007B83

background

此图片描绘的是 Grey900 这种颜色。

Grey900

#202124

辅助

添加深色主题

  1. Color.kt 中,添加深色主题的颜色。请注意,您只能添加四种颜色,因为已经为浅色主题添加了 Grey900
//Dark Theme
val White = Color(0xFFFFFFFF)
val Grey100 = Color(0xFFF1F3F4)
val Cyan900 = Color(0xFF007B83)
val Cyan700 = Color(0xFF129EAF)
  1. 移除默认提供的颜色,因为我们不再需要这些颜色。现在,文件中应包含下列值。
//Light Theme
val Grey50 = Color(0xFFF8F9FA)
val Grey900 = Color(0xFF202124)
val Grey700 = Color(0xFF5F6368)
val Green50 = Color(0xFFE6F4EA)
val Green100 = Color(0xFFCEEAD6)

//Dark Theme
val White = Color(0xFFFFFFFF)
val Grey100 = Color(0xFFF1F3F4)
val Cyan900 = Color(0xFF007B83)
val Cyan700 = Color(0xFF129EAF)
  1. Theme.kt 文件中,将现有的 DarkColorPalette 替换为下面显示的颜色。
private val DarkColorPalette = darkColors(
   background = Cyan900,
   surface = Cyan700,
   onSurface = White,
   primary = Grey900,
   onPrimary = White,
   secondary = Grey100
)

在预览中查看深色主题

如需查看深色主题的实际效果,您需要向 MainActivity.kt 添加另一个 Preview()。这样,当您更改代码中的界面布局时,就能同时看到浅色主题和深色主题的预览效果。

  1. DefaultPreview() 下,新建一个名为 DarkThemePreview() 的函数,并为其添加 @Preview@Composable 注解。
@Preview
@Composable
fun DarkThemePreview() {

}
  1. DarkThemePreview() 内,添加 WoofTheme()。如果不添加 WoofTheme(),您将看不到我们在应用中添加的任何样式。将 darkTheme 参数设置为 true
@Preview
@Composable
fun DarkThemePreview() {
   WoofTheme(darkTheme = true) {

   }
}
  1. WoofTheme() 内调用 WoofApp()
@Preview
@Composable
fun DarkThemePreview() {
   WoofTheme(darkTheme = true) {
       WoofApp()
   }
}

现在,当您点击 Preview 部分中的 Build & Refresh 并向下滚动时,会看到应用采用深色主题,包括颜色较深的应用/列表项背景和白色文字。您可以比较深色主题与浅色主题的区别。

深色主题

浅色主题

此图片在 WoofPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。这是深色主题下的效果。

此图片在 WoofPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。

在设备或模拟器上查看深色主题

如需在模拟器或实体设备上以深色主题背景查看应用,请执行以下操作:

  1. 进入设备的设置应用。
  2. 搜索深色主题,然后点击进入该主题。
  3. 开启深色主题
  4. 重新打开 Woof 应用后,该应用将采用深色主题

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。这是深色主题下的效果。

此 Codelab 将重点介绍浅色主题,因此,请在继续设置此应用之前关闭深色主题。

  1. 进入设备的设置应用。
  2. 选择显示
  3. 关闭深色主题

比较应用在本部分开始时的外观与现在的外观。列表项和文本的定义更明确,配色方案更具视觉吸引力。

没有颜色

有颜色(浅色主题)

有颜色(深色主题)

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。

此图片在 DefaultPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。这是深色主题下的效果。

5. 添加形状

默认情况下,应用中的所有形状都是矩形。不过,应用形状可能会给可组合项的外观和风格带来很大的变化。形状能够引导用户注意力、区别组件、传达状态以及表达品牌。

许多形状都是使用 RoundedCornerShape 定义的,后者描述的是圆角矩形。传入的数字定义角的圆度。如果使用 RoundedCornerShape(percent = 0),则矩形没有圆角;如果使用 RoundedCornerShape(percent = 50),角将变为完全圆形。如果您想尝试更复杂的形状,Material 网站可提供形状自定义工具

百分之 0

百分之 25

百分之 50

这张图片描绘的是 0% 的 RoundedCornerShape。

这张图片描绘的是 25% 的 RoundedCornerShape。

这张图片描绘的是 50% 的 RoundedCornerShape。

Shape.kt 文件用于定义 Compose 中组件的形状。组件分为三种类型:小、中和大。在本部分中,您将修改定义为 medium 大小的 Card 组件。系统会根据组件的大小将组件分组为形状类别

由于 Image 不是组件,因此您需要在 MainActivity.kt 中添加其形状。

在此部分中,您要将狗狗的图片设置为圆形,并修改列表项的形状。

将狗狗的图片的形状设为圆形

  1. 首先,您要将带狗狗照片的图标修改为圆形。打开 MainActivity.kt。在 DogIcon() 中,将 clip 属性添加到 Imagemodifier;这会将图片裁剪为某种形状。传入 RoundedCornerShape() 并传入 50,使角完全变为圆形。
import androidx.compose.ui.draw.clip
import androidx.compose.foundation.shape.RoundedCornerShape

@Composable
fun DogIcon(@DrawableRes dogIcon: Int, modifier: Modifier = Modifier) {
   Image(
       modifier = modifier
           .size(64.dp)
           .padding(8.dp)
           .clip(RoundedCornerShape(50)),

查看 DefaultPreview 时,您会注意到狗狗图标已变为圆形!不过,有些照片的侧边会被截断,而不是显示为完整的圆形。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色,狗狗照片是圆形的。

  1. 要将所有照片设为圆形,请添加 ContentScaleCrop 属性;这会剪裁图片。请注意,contentScaleImage 的一个属性,不是 modifier 的一部分。
import androidx.compose.ui.layout.ContentScale

@Composable
fun DogIcon(dogIcon: Int, modifier: Modifier = Modifier) {
   Image(
       modifier = modifier
           .size(64.dp)
           .padding(8.dp)
           .clip(RoundedCornerShape(50)),
       contentScale = ContentScale.Crop,

现在,当您运行应用时,图标会变成圆形。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色,狗狗照片是圆形的。

向列表项添加形状

在此部分中,您将向列表项添加形状。列表项已显示在 Row 中,但 Row 无法完成形状设置。而应将 Row 添加到 Card 中。Card 是可以包含一个可组合项并包含装饰选项的 Surface。可通过边框、形状和高度等添加装饰。在本部分中,您将使用 Card 向列表项添加形状。

  1. DogItem() 中,为 Row 添加 Card
import androidx.compose.material.Card

@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   Card() {
       Row(
  1. 打开 Shape.kt 文件。Card 是媒介组件,因此您将更新 Shapes 对象的媒介属性。对于此应用,您想将列表的角设置为圆角,但不要使其完全成为圆形。为此,请将 16.dp 传递给 medium 属性。
val Shapes = Shapes(
   small = RoundedCornerShape(4.dp),
   medium = RoundedCornerShape(16.dp),
   large = RoundedCornerShape(0.dp)
)
  1. 由于 Card 已默认使用中等形状,因此您无需将其显式设置为中型。刷新预览,您将看到圆角!但是,列表项之间没有内边距,并且不清楚一个列表项的结束位置以及下一个列表项的开始位置。现在,您将在列表项之间添加内边距,从而在列表项之间创建定义。

此图片显示了狗狗列表的预览,其中包含狗狗的名字、照片和年龄。此应用包含自定义颜色,列表项的角为圆角。

如果您返回到 WoofTheme() 中的 Theme.kt 文件并查看 MaterialTheme(),会看到 shapes 属性设置为您刚刚更新的 Shapes val

MaterialTheme(
   colors = colors,
   typography = Typography,
   shapes = Shapes,
   content = content
)

更新修饰符并添加内边距

  1. 由于 Card 现在是 DogItem() 中显示的第一个可组合项,因此传递给 DogItem 可组合项的修饰符应该转发到 Card,而不是转发到 RowRow 现在使用 Modifier 的新实例。
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   Card(
       modifier = modifier
   ) {
       Row(
           modifier = Modifier
               .fillMaxWidth()
               .padding(8.dp)
               .background(MaterialTheme.colors.surface)
       )
  1. padding 添加到 Card modifier 并传入 8.dp 以在每个列表项周围添加内边距。
Card(
   modifier = modifier.padding(8.dp)
)

现在,当您刷新预览时,由于列表项之间有内边距,所以您的列表项会更清晰地呈现出圆角。

此图片显示了狗狗列表的预览,其中包含狗狗的名字、照片和年龄。此应用包含自定义颜色,列表项的角为圆角。

不明确颜色

CardSurface,在 Theme.kt 文件中,我们为 surface 槽明确设置了颜色。因此,我们可以从 Row 中移除颜色,而无需明确将其设置为 surface 颜色。

  1. DogItem() 中,移除背景颜色的明确设置。
Row(
   modifier = Modifier
       .fillMaxWidth()
       .padding(8.dp)
)

刷新预览,您会发现 Row 仍采用相同的背景颜色,尽管我们移除了明确的颜色分配。

此图片显示了狗狗列表的预览,其中包含狗狗的名字、照片和年龄。此应用包含自定义颜色,列表项的角为圆角。

  1. 由于狗的名字和年龄的 Text 位于 Surface 之上,因此其颜色默认为 onSurface。移除 DogInformation() 中的 Text 项的明确颜色设置。

现在,该可组合项将如下所示:

@Composable
fun DogInformation(@StringRes dogName: Int, dogAge: Int, modifier: Modifier = Modifier) {
   Column {
       Text(
           text = stringResource(dogName),
           modifier = modifier.padding(top = 8.dp)
       )
       Text(
           text = stringResource(R.string.years_old, dogAge)
       )
   }
}

为列表项添加高度

列表项的形状调整后,应用看起来非常漂亮,但我们要添加一些额外的设置,让列表项与背景形成更高的对比度。您已经使用 Card shape 属性设置了列表项,现在您将使用 Card elevation 属性为卡片添加高度。高度基本上是在 Card 与背景之间添加一种对比,即通过添加阴影来使应用看起来更真实且更有趣。

  1. DogItem() 中,添加 list_item_elevation 作为 Cardelevation 属性。
Card(
   modifier = Modifier.padding(8.dp),
   elevation = 4.dp
)
  1. 刷新预览。高度会增加应用的阴影和深度,使其看起来更逼真!

此图片显示了狗狗列表的预览,其中包含狗狗的名字、照片和年龄。此应用包含自定义颜色,列表项的角为圆角,并且背景和列表项之间存在高度。

下图并排展示了该应用在调整形状前后的效果。请注意,如果添加形状,应用的外观会更具吸引力。

不调整形状

调整形状

此图片在 DefaultPreview 中显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含应用的背景颜色、列表项的背景颜色以及文本颜色。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色和形状。

6. 添加排版

Material Design 字体比例

字体缩放是一系列字体样式的选择,可在应用中使用,确保字体既灵活又一致。Material Design 字体比例包含字型系统支持的 13 种字体样式。只有在您想自定义自己的应用时,才需要使用这些选项。如果您不知道为每个字体比例类别设置什么,请注意,您可以使用一个默认的排版缩放。

此图片是字体比例,显示了字型系统支持的所有 13 种样式。

字体比例包含可重复使用的文本类别,每个类别都有预期的应用和含义。在我们的应用中,我们只会使用标题和正文类别。

标题

在字体比例中,标题的范围为 1 到 6。标题是屏幕上最大的文字,仅供重要文字或数字使用。

正文

正文文本的范围介于 1 到 2 之间,通常用于长篇幅撰写,因为它们适合较小的文本。

字体

Android 平台提供了一些字体,但您可能会想使用非默认提供的字体来对您的应用进行真正意义上的自定义。自定义字体可以增添个性,并可用于品牌塑造。

在本部分中,您将添加名为 Abril FatfaceMontserrat BoldMontserrat Regular 的自定义字体。您将使用 H1 和 H2 标题以及 Material 字型系统中的 body1 文本,并将它们添加到应用中的文本中。

创建字体 Android 资源目录。

在向应用添加字体之前,您需要添加一个字体目录。

  1. 在 Android Studio 的项目视图中,右键点击 res 文件夹。
  2. 依次选择 New > Android Resource Directory

此图片显示了将文件结构导航到 Android 资源目录的情况。

  1. 将目录命名为 font,将资源类型设为 font,然后点击 OK

此图片显示了使用新字体资源目录添加字体目录的过程。

  1. 打开位于 res > drawable > font 的新字体资源目录。

下载自定义字体

由于您使用的不是 Android 平台提供的字体,因此您需要下载自定义字体。

  1. 访问 https://fonts.google.com/
  2. 搜索 Montserrat,然后点击 Download family
  3. 解压缩该 ZIP 文件。
  4. 打开下载的 Montserrat 文件夹。在 static 文件夹中,找到 Montserrat-Bold.ttfMontserrat-Regular.ttfttf 代表 TrueType 字体,即字体文件的格式)。选择两种字体,将它们拖动到 Android Studio 中项目的字体资源目录中。

此图片显示了 Montserrat 字体的静态文件夹的内容。

  1. 在字体文件夹中将 Montserrat-Bold.ttf 重命名为 montserrat_bold.ttf,并将Montserrat-Regular.ttf 重命名为 montserrat_regular.ttf
  2. 搜索 Abril Fatface,然后点击 Download family
  3. 打开下载的 Abril_Fatface 文件夹。选择 AbrilFatface-Regular.ttf 并将其拖动到字体资源目录中。
  4. 在字体文件夹中,将 Abril_Fatface.ttf 重命名为 abril_fatface_regular.ttf

项目中的字体资源目录和三个自定义字体文件应如下所示:

此图片显示了添加到字体文件夹的字体文件。

初始化字体

  1. 在项目窗口中,依次打开 ui.theme > Type.kt,然后删除 Typography 变量的内容。
// Set of Material typography styles to start with
val Typography = Typography(

)
  1. 在 import 语句下方和 Typography val 上方初始化下载的字体。首先,初始化 Abril Fatface,方法是将其设为 FontFamily 并使用字体文件 abril_fatface_regular 传入 Font
val AbrilFatface = FontFamily(
   Font(R.font.abril_fatface_regular)
)
  1. Abril Fatface 下方初始化 Montserrat,方法是将其设为 FontFamily 并使用字体文件 montserrat_regular 传入 Font。对于 montserrat_bold,还应添加 FontWeight.Bold。即使您传入了字体文件的粗体版本,Compose 也不知道该文件是粗体文件,因此您需要明确地将此文件关联到 FontWeight.Bold
val AbrilFatface = FontFamily(
   Font(R.font.abril_fatface_regular)
)

val Montserrat = FontFamily(
   Font(R.font.montserrat_regular),
   Font(R.font.montserrat_bold, FontWeight.Bold)
)

接下来,将不同类型的标题设为您刚刚添加的字体。Typography 对象具有上面讨论的 13 种不同字体的参数。您可以根据需要定义任意数量。在此应用中,我们将设置 h1(标题 1)、h2(标题 2)和 body1。在此应用的下一部分中,您将使用 h3(标题 3),因此需在此处添加。

下表列出了应用概览部分中的表格,其中显示了您添加的每个标题的字体和字号。

标题

字体

字体粗细

字号

h1

此图片描绘的是 AbrilFatface 这种字体。

Normal

30sp

h2

此图片描绘的是 Montserrat 这种字体。

Bold

20sp

h3

此图片描绘的是 Montserrat 这种字体。

Bold

14sp

body1

此图片描绘的是 Montserrat 这种字体。

Normal

14sp

  1. 使用上表填写 Typography val。对于 h1 属性,应将其设置为 TextStyle,并使用上表中的信息填写 fontFamilyfontWeightfontSize。这意味着所有设置为 h1 的文本都将使用 Abril Fatface 作为字体,字体粗细正常,fontSize30.sp

h2h3body1 重复此过程。

val Typography = Typography(
   h1 = TextStyle(
       fontFamily = AbrilFatface,
       fontWeight = FontWeight.Normal,
       fontSize = 30.sp
   ),
   h2 = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Bold,
       fontSize = 20.sp
   ),
   h3 = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Bold,
       fontSize = 14.sp
   ),
   body1 = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Normal,
       fontSize = 14.sp
   )
)

如果您返回到 WoofTheme() 中的 Theme.kt 文件并查看 MaterialTheme(),会看到 typography 属性设置为您刚刚更新的 Typography val

MaterialTheme(
   colors = colors,
   typography = Typography,
   shapes = Shapes,
   content = content
)

向应用文字添加排版

现在,您要为应用中的每一个文本实例添加标题类型。

  1. 添加 h2(标题 2)作为 dog name 的样式,因为它是一种简短的重要信息。将 body1 添加为 dog age 的样式,因为它适合较小的文本。
@Composable
fun DogInformation(@StringRes dogName: Int, dogAge: Int, modifier: Modifier = Modifier) {
   Column {
       Text(
           text = stringResource(dogName),
           style = MaterialTheme.typography.h2,
           modifier = modifier.padding(top = 8.dp)
       )
       Text(
           text = stringResource(R.string.years_old, dogAge),
           style = MaterialTheme.typography.body1
       )
   }
}
  1. 刷新应用。现在,狗狗的名字会使用 20.sp 的粗体 Montserrat 字体显示,狗狗的年龄会使用 14.sp 的正常 Montserrat 字体显示。

此图片显示了狗狗列表的预览,其中包含狗狗的名字、照片和年龄。此应用包含自定义颜色、形状和排版。

下图并排展示了该应用在添加排版前后的效果。请注意狗狗的名字和年龄之间的字体差异。

排版前

排版后

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色和形状。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色、形状和排版。

7. 添加顶部栏

Scaffold 是一种布局,可为各种组件和屏幕元素(如 ImageRowColumn)提供槽。Scaffold 还为 topBar 提供了槽,您将在本部分中使用。

topBar 可用于许多用途,但在本例中,您会将其用于品牌宣传以及彰显应用个性。您将创建一个类似于以下屏幕截图的可组合项,并将其放入 ScaffoldtopBar 部分。

此图显示了 Woof 顶部栏,其中包含图片和文字。

对于此应用,顶部栏由一个包含徽标和应用名称的行组成。徽标包含可爱的渐变色的爪子和应用名称!

此图片显示了顶部栏所含的组件,它是一个包含图片和文字的行。

向顶部栏添加图片和文字

  1. MainActivity.kt 中,创建一个名为 WoofTopAppBar() 且接受 modifier 的可组合项。
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {

}
  1. WoofTopAppBar() 中,添加 Row()
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
   Row() {

   }
}
  1. Image 添加到 Row 中。使用 painterImage 设置为可绘制对象文件夹中的 ic_woof_logo,并将 contentDescription 设置为 null。在这种情况下,应用徽标不会为有视觉障碍的用户添加任何语义信息,因此我们无需添加内容说明。
Image(
   painter = painterResource(R.drawable.ic_woof_logo),
   contentDescription = null
)
  1. 接下来,在 Row 中的 Image 后添加一个 Text 可组合项,并使用 stringResource() 将其设为 app_name 的值。这会将文本设置为存储在 strings.xml 中的应用的名称。
Text(
   text = stringResource(R.string.app_name)
)
  1. 现在,您已将图标和应用名称添加到 TopAppBar(),接下来需要将 TopAppBar() 添加到布局中。在 WoofApp() 中,添加围绕 LazyColumnScaffold
import androidx.compose.material.Scaffold

@Composable
fun WoofApp() {
   Scaffold(

   ) {
       LazyColumn(modifier =
        Modifier.background(MaterialTheme.colors.background)) {
           items(dogs) {
               DogItem(dog = it)
           }
       }
   }
}
  1. Scaffold 中,添加 topBar 属性并将其设置为 WoofTopAppBar()
Scaffold(
   topBar = {
       WoofTopAppBar()
   }
)

WoofApp() 可组合项将如下所示:

@Composable
fun WoofApp() {
   Scaffold(
       topBar = {
           WoofTopAppBar()
       }
   ) {
       LazyColumn(modifier = Modifier.background(MaterialTheme.colors.background)) {
           items(dogs) {
               DogItem(dog = it)
           }
       }
   }
}
  1. 刷新预览,然后查看顶部应用栏。它显示的是图标和文字,但看起来不像我们预期的成品。在下一部分中,您将使用颜色、排版和内边距等进行美化。

此图显示了 Woof 应用,其中包含一个包含图片和文字的顶部栏。

美化顶部栏

  1. 首先,我们要在顶部栏中添加一种背景颜色,以便与应用的其他部分区分开来。在 WoofTopAppBar() 中,通过向 Row 添加 modifier 并将背景颜色设置为 primary,添加主应用主题颜色作为背景颜色。
Row(
   modifier = modifier
       .background(color = MaterialTheme.colors.primary)
){ }

此图显示了 Woof 应用,其顶部栏包含图片和文本以及部分背景。

  1. 背景颜色仅出现在图标和文本后面,但您希望它覆盖应用的整个宽度。为此,请将 Row 上的 modifier 设置为 fillMaxWidth()
Row(
   modifier = modifier
       .fillMaxWidth()
       .background(color = MaterialTheme.colors.primary)
){ }

此图显示了 Woof 应用,其顶部栏包含图片和文本以及完整的背景。

  1. 接下来,更新文本以遵循您之前定义的 h1(标题 1)样式。由于背景设置为 primary 颜色,因此文本会自动设置为 onPrimary 颜色。
Text(
   text = stringResource(R.string.app_name),
   style = MaterialTheme.typography.h1
)
  1. 效果看起来好多了,不过图片太小,可以在其周围使用一些内边距。为 Image 添加修饰符,并将图片尺寸设置为 R.dimen.image_size,并将图片周围的内边距设置为 R.dimen.padding_small
Image(
   modifier = Modifier
       .size(64.dp)
       .padding(8.dp),
   painter = painterResource(R.drawable.ic_woof_logo),
   contentDescription = null
)

此图显示了 Woof 应用,其顶部栏包含图片和文本以及白色背景。文本的样式为 h1,图片周围有内边距。

  1. 就快完成了!现在,将垂直对齐方式设置为 Alignment.CenterVertically,这样可以垂直居中行中所有项。
import androidx.compose.ui.Alignment

Row(
   modifier = modifier
       .fillMaxWidth()
       .background(color = MaterialTheme.colors.primary),
   verticalAlignment = Alignment.CenterVertically
){ }

此图显示了 Woof 应用,其顶部栏包含图片和文本以及白色背景。文本的样式为 h1,图片周围有内边距,并且项垂直居中排列。

运行应用,欣赏 TopAppBar 将该应用关联在一起是多么美观。

不含顶部应用栏

带有顶部应用栏

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色、形状和排版。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色、形状和排版,并且包含顶部栏。

现在,在深色主题下运行您的应用。由于栏使用 primary 颜色,而文本使用 onSurface 颜色,因此您无需为深色主题更新任何内容。

此图片显示了一款应用,其中包含狗狗的列表,列表中包含狗狗的名字、照片和年龄。此应用包含自定义颜色、形状和排版,并且包含顶部栏。这是深色模式下的显示效果。

恭喜,您已完成了此 Codelab 的学习!

8. [可选] 更改状态栏

为了进一步提升用户体验,您可以更新状态栏的颜色,其中包含时间、互联网连接、电池状态等信息。

此图显示了 Woo 应用,并在状态栏周围有一个框。

  1. 依次进入 app > res > values > colors.xml
  2. 删除 <resources> 标记的内容。
<resources>

</resources>
  1. 添加 grey_50,将其设置为浅色主题状态栏的 #FFF8F9FA,然后添加 grey_900 并将其设置为深色主题状态栏的 #FF202124
<resources>
   <color name="grey_50">#FFF8F9FA</color>
   <color name="grey_900">#FF202124</color>
</resources>
  1. 依次进入 app > res > values > themes.xml,并将 android:statusBarColor 中的颜色替换为 grey_50
<resources>
   <style name="Theme.Woof" parent="android:Theme.Material.Light.NoActionBar">
       <item name="android:statusBarColor">@color/grey_50</item>
   </style>
</resources>
  1. 在设备或模拟器上运行应用。

此图显示了 Woof 应用,以及采用浅色主题时的自定义状态栏。

状态栏现在与顶部应用栏匹配,这使得配色方案看起来更统一,但现在很难看到图标。

  1. themes.xml 文件中,在 statusBarColor 下添加 windowLightStatusBar 并将其设为 true
<resources>
   <style name="Theme.Woof" parent="android:Theme.Material.Light.NoActionBar">
       <item name="android:statusBarColor">@color/grey_50</item>
       <item name="android:windowLightStatusBar">true</item>
   </style>
</resources>
  1. 您会收到错误消息。将光标悬停在消息上,然后按 Override Resources in values-v23

此图片是 Android Studio 中弹出的错误。

  1. 这将创建一个名为 v23/themes.xml 的新 themes.xml 文件,此文件用于 API 级别 23 及更高级别。
  2. 在模拟器上运行应用。请注意,您现在可以看到这些图标了!

此图显示了 Woof 应用,以及采用浅色主题时的自定义状态栏。

在深色主题中添加状态栏

现在,您可以自定义深色主题的状态栏。

  1. res 文件夹中,添加一个名为 values-night 的新 Android 资源目录

cdc98bf466706a30.png

  1. 切换到 Project Source Files 文件视图。

此图片显示了所选的项目源文件视图。

  1. 依次进入 app > src > main > res > values-night
  2. values-night 中,添加一个名为 themes.xmlValues Resource File
  3. 添加名称为 Theme.Woof 且父元素为 android:style/Theme.Material.NoActionBar 的样式标记。
<resources>
   <style name="Theme.Woof" parent="android:style/Theme.Material.NoActionBar">

   </style>
</resources>
  1. 将状态栏颜色添加为 grey_900。由于默认图标为白色图标,因此您无需添加 windowsStatusLightBar
<resources>
   <style name="Theme.Woof" parent="android:style/Theme.Material.NoActionBar">
       <item name="android:statusBarColor">@color/grey_900</item>
   </style>
</resources>
  1. 使用深色主题运行应用,即可查看状态栏的更新。

此图显示了 Woof 应用,以及采用深色主题时的自定义状态栏。

9. 获取解决方案代码

如需下载完成后的 Codelab 代码,您可以使用以下 Git 命令:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git
$ cd basic-android-kotlin-compose-training-woof
$ git checkout material

或者,您也可以下载 ZIP 文件形式的代码库,将其解压缩并在 Android Studio 中打开。

如果您想查看解决方案代码,请前往 GitHub 查看

10. 总结

您刚刚创建了您的第一个 Material 应用!您为浅色和深色主题添加了自定义调色板,为不同的组件创建了形状,并下载了字体并将其添加到应用中,还创建了美观的顶部栏将它们统一在一起。运用您在此 Codelab 中学到的技能,并更改颜色、形状和排版,打造完全属于您自己的应用!

摘要

  • 借助 Material Theming,您可以依据关于自定义颜色、排版和形状的指南,在应用中使用 Material Design。
  • 主题是在 Theme.kt 文件中通过名为 [您的应用名称]+Theme() 的可组合项定义(在此应用中是 WoofTheme())。在该函数内,MaterialTheme 对象用于设置应用的 colortypographyshapescontent
  • 您可以在 Colors.kt 中列出您在应用中使用的颜色。然后,在 Theme.kt 中,将 LightColorPaletteDarkColorPalette 中的颜色分配给特定槽。并非所有槽都需要分配。
  • 您的应用可以选择启用 Force Dark,这意味着系统会为您实现深色主题。不过,如果您实现深色主题,可为用户提供更好的体验,以便您继续完全控制应用主题。
  • Shapes.kt 是定义应用形状的位置。形状尺寸有三种(小、中、大),您可以指定角的圆角程度。
  • 形状能够引导用户注意力、区别组件、传达状态以及表达品牌。
  • Types.kt 用于初始化字体并为 Material Design 字体比例分配 fontFamilyfontWeightfontSize
  • Material Design 字体比例包含一系列对比鲜明的样式,可支持您的应用及其内容的需求。字体比例是字型系统支持的 13 种样式的组合。

11. 了解详情