使用文本可组合项构建简单的应用

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

1. 准备工作

在本 Codelab 中,您将使用 Jetpack Compose 构建一个会在屏幕上显示生日祝福语的简单 Android 应用。

前提条件

  • 了解如何在 Android Studio 中创建应用。
  • 了解如何在模拟器中或在 Android 设备上运行应用。

学习内容

  • 了解如何编写可组合函数,例如 TextColumnRow 可组合函数。
  • 如何在布局中显示应用里的文本。
  • 如何设置文本格式,例如更改文本大小。

构建内容

  • 一个以文本格式显示生日祝福的 Android 应用,完成后应如下面的屏幕截图所示:

ca82241f560e8b99.png

所需条件

  • 一台安装了 Android Studio 的计算机

2. 观看配套代码演示视频(可选)

如果您想要观看某位课程教师完成此 Codelab 的过程,请播放以下视频。

建议将视频展开至全屏。您只需点击视频播放器中的全屏图标 此符号突出显示了方形中的 4 个角,用于指明全屏模式。,即可更清楚地查看 Android Studio 和相关代码。

这是可选步骤。您也可以跳过视频,立即开始按照此 Codelab 中的说明操作。

3. 设置 Happy Birthday 应用

在此任务中,您将在 Android Studio 中使用 Empty Compose Activity 模板设置一个项目,并将文本消息更改为个性化的生日祝福。

创建 Empty Compose Activity 项目

  1. Welcome to Android Studio 对话框中,选择 New Project
  2. New Project 对话框中,选择 Empty Compose Activity,然后点击 Next
  3. Name 字段中输入 Happy Birthday,在 Minimum SDK 字段中选择最低 API 级别 24 (Nougat),然后点击 Finish

6ecfe9ae1e5aa850.png

  1. 等待 Android Studio 创建项目文件并构建项目。
  2. 点击 fd26b2e3c2870c3.png Run ‘app'

应用应如下面的屏幕截图所示:

c67f06ea2afef154.png

在您使用 Empty Compose Activity 模板创建这个 Happy Birthday 应用时,Android Studio 会设置基本 Android 应用所需的各项资源,包括在屏幕上显示的 Hello Android! 消息。在本 Codelab 中,您将了解如何放置该消息、如何将其文本更改为生日祝福,以及如何添加额外的消息并为其设置格式。

什么是界面 (UI)?

应用的界面 (UI) 就是您在屏幕上所看到的内容(文本、图片、按钮和许多其他类型的元素)及其在屏幕上的布局方式。它既是应用向用户显示内容的方式,也是用户与应用展开互动的载体。

下面的图片包含一个可点击的按钮、一则文本消息和一个可供用户输入数据的文本输入字段。

e5abb8ad9f9ae2a7.png

可点击的按钮

fded30871a8e0eca.png

文本消息

aafb9c476f72d558.png

文本输入字段

上述各个元素就是所谓的界面组件。您在应用屏幕上看到的所有内容几乎均属于界面元素(也称为界面组件)。这些元素可以是互动元素(例如可点击的按钮或可修改的输入字段),也可以是装饰性图片。

在下面的应用中,尝试找到尽可能多的界面组件。

在本 Codelab 中,您将使用一种用于显示文本的界面元素,即“Text 元素”。

4. 什么是 Jetpack Compose?

Jetpack Compose 是用于构建 Android 界面的新款工具包。Compose 使用更少的代码、强大的工具和直观的 Kotlin 功能,可以帮助您简化并加快 Android 界面开发。借助 Compose,您可以通过定义一组函数来构建界面,这些函数称为可组合函数,它们会接受数据并发出界面元素。

可组合函数

在 Compose 中,可组合函数是界面的基本构建块。可组合函数:

  • 描述界面中的某一部分。
  • 不会返回任何内容。
  • 接受一些输入并生成屏幕上显示的内容。
  • 可能会发出多个界面元素。

注解

注解是用于在代码中附加额外信息的方式。此类信息可以帮助 Jetpack Compose 编译器等工具和其他开发者理解应用的代码。

若要应用注解,只需在您要注解的声明开头为其名称(注解)添加 @ 字符作为前缀即可。您可以为包括属性、函数和类在内的不同代码元素添加注解。本课程稍后会介绍类。

下图是一个带有注解的函数示例:

前缀字符是“@”,注解是“composable”,后跟函数声明

以下代码段中包含一些带有注解的属性示例。在后续 Codelab 中,您将用到这些属性。

// Example code, do not copy it over

@Json
val imgSrcUrl: String

@Volatile
private var INSTANCE: AppDatabase? = null

带形参的注解

注解可以接受形参。形参可以为处理它们的工具提供额外信息。以下是带形参和不带形参的 @Preview 注解的一些示例。

15169d39d744c179.png

不带参数的注解

992de02d7b5dbfda.png

用于预览背景的注解

fbc159107d248a84.png

带有预览标题的注解

您可以向注解传递多个参数,如下所示。

510f8443a174f972.png

带有预览标题和系统界面(手机屏幕)的注解

Jetpack Compose 中有各种各样的内置注解,截至目前为止,本课程中已经出现过 @Composable 注解和 @Preview 注解。在本课程后面的部分,您将了解更多注解及其用法。

可组合函数示例

可组合函数带有 @Composable 注解。所有可组合函数都必须带有此注解。此注解可告知 Compose 编译器:此函数用于将数据转换为界面。请注意,编译器是一种特殊的程序,它会接受您编写的代码,逐行查看,然后将其转换成计算机可以理解的指令(机器语言)。

以下代码段是一个简单的可组合函数示例,该函数接受传递的数据(name 函数参数)并用其在屏幕上渲染文本元素。

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

关于可组合函数的几点说明:

  • 可组合函数可以接受一些参数,用来让应用逻辑描述或修改界面。在本例中,界面元素接受一个 String,以便在问候用户时称呼姓名。
  • 此函数不会返回任何内容。用于发出界面的可组合函数无需返回任何内容,因为它们描述所需的屏幕状态,而不是构造界面元素。换言之,可组合函数只描述界面而不构造或创建界面,因此没有要返回的内容。

注意代码中的可组合函数

  1. 在 Android Studio 中,打开 MainActivity.kt 文件。
  2. 滚动到 DefaultPreview() 函数并将其删除。添加一个新的可组合函数 BirthdayCardPreview(),用于预览 Greeting() 函数,如下所示。按照最佳做法,您应当始终对函数命名或重命名,以描述其功能。
@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        Greeting("Android")
    }
}

可组合函数可以调用其他可组合函数。在此代码段中,预览函数调用了 Greeting() 可组合函数。

请注意,在上一个函数中,@Composable 注解前面还有另一个带参数的注解,即 @Preview 注解。本课程稍后会详细介绍传递给 @Preview 注解的参数。

可组合函数名称

不返回任何内容且带有 @Composable 注解的 Compose 函数必须使用 Pascal 大小写命名。Pascal 大小写是指一种命名惯例,采用这种命名法时,复合词中每个单词的首字母大写。Pascal 大小写与驼峰式大小写之间的区别在于:在 Pascal 大小写中,所有单词的首字母都大写;而在驼峰式大小写中,第一个单词的首字母不大写。

Compose 函数:

  • 必须是名词:DoneButton()
  • 不能是动词或动词短语:DrawTextField()
  • 不能是名词性介词:TextFieldWithLink()
  • 不能是形容词:Bright()
  • 不能是副词:Outside()
  • 名词可以添加描述性形容词作为前缀:RoundIcon()

无论函数是否发出界面元素,此准则都适用。如需了解详情,请参阅为可组合函数命名

示例代码。请勿复制

// Do: This function is a descriptive PascalCased noun as a visual UI element
@Composable
fun FancyButton(text: String) {}

// Do: This function is a descriptive PascalCased noun as a non-visual element
// with presence in the composition
@Composable
fun BackButtonHandler() {}

// Don't: This function is a noun but is not PascalCased!
@Composable
fun fancyButton(text: String) {}

// Don't: This function is PascalCased but is not a noun!
@Composable
fun RenderFancyButton(text: String) {}

// Don't: This function is neither PascalCased nor a noun!
@Composable
fun drawProfileImage(image: ImageAsset) {}

5. Android Studio 中的“Design”窗格

Android Studio 允许您在 IDE 中预览可组合函数,无需将应用安装到 Android 设备或模拟器中。正如您在之前的在线课程中学到的,您可以在 Android Studio 的 Design 窗格中预览应用的外观。

c284448a820d577c.png

可组合函数必须为任何参数提供默认值才能对其进行预览。因此,您无法直接预览 Greeting() 函数,而是需要添加另一个函数(在本例中为 BirthdayCardPreview() 函数),由该函数使用适当的形参调用 Greeting() 函数。

@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        Greeting("Android")
    }
}

如需查看预览,请执行以下操作:

  1. 构建代码。

预览会自动更新。

更新或刷新预览的另一种方式是点击 Design 窗格中的 fdd133641cfac2b3.png Build & Refresh

5cec5263ba04ea1.png

  1. BirthdayCardPreview() 函数中,将 Greeting() 函数中的 "Android" 实参更改为您的名字。
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        Greeting("James")
    }
}
  1. 点击 Design 窗格中的 fdd133641cfac2b3.png Build & Refresh

您应该会看到更新后的预览。

4c1634ec586ca3ba.png

6. 添加新的文本元素

在此任务中,您将移除 Hello $name! 问候语并添加生日祝福。

添加新的可组合函数

  1. MainActivity.kt 文件中,删除 Greeting() 函数定义。在本 Codelab 稍后,您将添加自己的函数,用于显示生日祝福。
@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}
  1. 将鼠标悬停在 Greeting() 函数上。请注意,Android Studio 突出显示了 Greeting() 函数调用,那么请将光标悬停在此函数调用上,确定出现了什么错误。

46fed08f63f1b3c6.png

  1. onCreate()BirthdayCardPreview() 函数中删除 Greeting() 函数调用及其实参。此时 MainActivity.kt 文件将如下所示:
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            HappyBirthdayTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                }
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview(){
    HappyBirthdayTheme {
    }
}
  1. BirthdayCardPreview() 函数前,添加一个名为 BirthdayGreetingWithText() 的新函数。不要忘记在该函数前添加 @Composable 注解,因为此函数是一个将发出 Text 可组合项的 Compose 函数。
@Composable
fun BirthdayGreetingWithText() {
}
  1. 最佳实践是让可组合项接受 Modifier 形参,并将该 modifier 传递给其发出界面元素的第一个子项。在后续任务和 Codelab 中,您将详细了解 Modifier 和子元素。现在,请向 BirthdayGreetingWithText() 函数添加 Modifier 形参。
@Composable
fun BirthdayGreetingWithText(modifier: Modifier = Modifier) {
}
  1. 将类型为 Stringmessage 形参添加到 BirthdayGreetingWithText() 可组合函数中。
@Composable
fun BirthdayGreetingWithText(message: String, modifier: Modifier = Modifier) {
}
  1. BirthdayGreetingWithText() 函数中,添加一个 Text 可组合项并传入文本消息作为具名实参。
@Composable
fun BirthdayGreetingWithText(message: String, modifier: Modifier = Modifier) {
    Text(
       text = message
    )
}

BirthdayGreetingWithText() 函数用于在界面中显示文本。为此,它会调用 Text() 可组合函数。

预览函数

在此任务中,您将在 Design 窗格中预览 BirthdayGreetingWithText() 函数。

  1. BirthdayCardPreview() 函数中调用 BirthdayGreetingWithText() 函数。
  2. 将一个 String 参数(其内容是对朋友的生日祝福)传递给 BirthdayGreetingWithText() 函数。如果愿意,您可以使用朋友的名字进行自定义,例如 "Happy Birthday Sam!"
@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        BirthdayGreetingWithText(message = "Happy Birthday Sam!")
    }
}
  1. Design 窗格中,点击 ea3433426a37f49b.png Build & Refresh,然后等待构建完成以预览函数。

eadbd65191fd4632.png

7. 更改字体大小

您已将文本添加到界面中,但它看起来和最终应用还不太一样。在此任务中,您将了解如何更改大小、文本颜色以及影响文本元素外观的其他属性。此外,您还可以尝试使用不同的字体大小和颜色。

可缩放像素

可缩放像素 (SP) 是字体大小的度量单位。Android 应用中的界面元素使用两种不同的度量单位:一种是您稍后将为布局使用的密度无关像素 (DP),另一种就是可缩放像素 (SP)。默认情况下,SP 单位与 DP 单位大小相同,但前者的大小会根据用户在手机设置下的首选文本大小进行调整。

  1. MainActivity.kt 文件中,滚动到 BirthdayGreetingWithText() 函数中的 Text() 可组合项。
  2. Text() 函数传递 fontSize 实参作为第二个具名实参,并将其值设为 36.sp
Text(
   text = message,
   fontSize = 36.sp
)

Android Studio 突出显示了 .sp 代码,因为您需要导入一些类或属性来编译应用。

6b6e60b13e085a13.png

  1. 点击 Android Studio 突出显示的 .sp
  2. 点击弹出式窗口中的 Import 来导入 androidx.compose.ui.unit.sp,以使用 .sp 扩展属性。

a1a623c584e7e6dc.png

  1. 滚动到文件顶部,您会发现一些 import 语句,此处应包含一个 import androidx.compose.ui.unit.sp 语句,这表示 Android Studio 已将相应软件包添加到您的文件中。

80ae3819ddfc7306.png

  1. 点击 Design 窗格中的 Build & Refresh 以查看更新后的预览。请注意生日祝福预览中的字体大小变化。

显示“Build & Refresh”选项

现在,您可以尝试使用不同的字体大小。

8. 再添加一个文本元素

在前面的任务中,您添加了一句对朋友的生日祝福语。在此任务中,您将在生日卡上签上自己的名字。

  1. MainActivity.kt 文件中,滚动到 BirthdayGreetingWithText() 函数。
  2. 向该函数传递一个类型为 Stringfrom 参数作为您的签名。
fun BirthdayGreetingWithText(message: String, from: String, modifier: Modifier = Modifier)
  1. 在生日祝福语 Text 可组合项之后,再添加一个接受 text 参数(其值设为 from)的 Text 可组合项。
Text(
   text = from
)
  1. 添加一个 fontSize 命名参数,并将其值设为 24.sp
Text(
   text = from,
   fontSize = 24.sp
)
  1. 滚动到 BirthdayCardPreview() 函数。
  2. 再添加一个 String 参数来为生日卡签名,例如 "- from Emma"
BirthdayGreetingWithText(message = "Happy Birthday Sam!", from ="- from Emma")
  1. 点击“Design”窗格中的 Build & Refresh
  2. 注意预览结果。

c59bb2754ab5db49.png

一个可组合函数可能会发出多个界面元素。不过,如果您未提供有关如何排列这些元素的指导,Compose 可能会以您不喜欢的方式排列它们。例如,上面的代码生成了两个相互重叠的文本元素,因为您没有提供有关如何排列这两个可组合项的指导。

在下一个任务中,您将了解如何将可组合项排列成一行或一列。

9. 将文本元素排列成一行或一列

界面层次结构

界面层次结构基于包含机制,意即一个组件可以包含一个或多个组件,有时会用“父级”和“子级”这两个词来表述。这种说法是指,父界面元素包含子界面元素,而子界面元素还可以继续包含子界面元素。在此部分中,您将了解可用作父界面元素的 ColumnRowBox 可组合项。

9270b7e10f954dcb.png

Compose 中的 3 个基本标准布局元素是 ColumnRowBox 可组合项。下一个 Codelab 会详细介绍 Box 可组合项。

Column 显示了垂直排列的三个元素,Row 显示了水平排列的三个元素

ColumnRowBox 都是接受可组合内容作为参数的可组合函数,因此您可以在这些布局元素内放置项目。例如,Row 可组合项中的各个子元素彼此相邻地水平放置成一行。

// Don't copy.
Row {
    Text("First Column")
    Text("Second Column")
}

这些文本元素会在屏幕上并排显示,如下图所示。

蓝色边框仅用于演示目的,不会实际显示。

6f74a11c03564a61.png

尾随 lambda 语法

请注意,在上一个代码段中,Row 可组合函数中使用的是花括号而不是圆括号。这称为尾随 Lambda 语法。本课程稍后会详细介绍 lambda 和尾随 lambda 语法。现在,您只需要熟悉这个常用的 Compose 语法。

当最后一个形参是函数时,Kotlin 提供了一种特殊语法来将函数作为形参传递给函数。

函数形参是最后一个形参

如果要将函数作为形参传递,您可以使用尾随 lambda 语法。此语法不是将函数正文连同函数名称一起放在括号 ({}) 内,而是将括号和函数正文放在函数名称后面。这是 Compose 中的一种常见做法,因此您需要熟悉代码的格式。

例如,Row() 可组合函数中的最后一个参数是 content 参数,它是一个发出子界面元素的函数。假设您想要创建一个包含三个文本元素的行。以下代码行得通,但它非常繁琐:

Row(
    content = {
        Text("Some text")
        Text("Some more text")
        Text("Last text")
    }
)

由于 content 形参是函数签名中的最后一个形参,并且您要将其值作为 lambda 表达式传递(目前,如果您不知道 lambda 是什么也没关系,只需要熟悉一下语法即可),因此您可以移除 content 形参和括号,如下所示:

Row {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

将文本元素排列成一行

在此任务中,您要将应用中的文本元素排列成一行,以免相互重叠。

  1. MainActivity.kt 文件中,滚动到 BirthdayGreetingWithText() 函数。
  2. 添加 Row 可组合项将文本元素括起来,以显示包含两个文本元素的一行。

现在,该函数应如下面的代码段所示:

@Composable
fun BirthdayGreetingWithText(message: String, from: String, modifier: Modifier = Modifier)  {
    Row{
        Text(
            text = message,
            fontSize = 36.sp,
       )
        Text(
            text = from,
            fontSize = 24.sp,
       )
   }
}
  1. 点击代码段中突出显示的 Row
  2. 请注意,Android Studio 针对 Row 提供了多个导入选择。
  3. 点击 Import

突出显示 Row 函数,并显示了两个弹出式窗口,一个指出未解析错误,另一个提示导入

  1. androidx.compose.foundation.layout 软件包中,选择以 Row(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.Argument.Horizontal, androidx.... 开头的函数

dfad9fcfae49aa7a.png

  1. Design 窗格中,点击 Build & Refresh 更新预览。

生日祝福和签名并排显示为一行。

现在,因为元素不再重叠,预览的效果大为改善。不过,这仍然不是您想要的效果,因为没有充足的空间显示签名。在下一个任务中,您要将文本元素排列成一列来解决此问题。

将文本元素排列成一列

在此任务中,该由您更改 BirthdayGreetingWithText() 函数来将文本元素排成一列了。完成后,别忘了点击 Build & Refresh 更新预览,结果应如下面的屏幕截图所示:

生日祝福和签名一上一下地显示为一列。

既然您已尝试自行完成了此任务,那么现在您可以对照以下代码段中的解决方案代码检查您自己的代码:

@Composable
fun BirthdayGreetingWithText(message: String, from: String, modifier: Modifier = Modifier) {
   Column {
       Text(
           text = message,
           fontSize = 36.sp,
       )
       Text(
           text = from,
           fontSize = 24.sp,
       )
   }
}

当 Android Studio 提示时,导入以下软件包:

import androidx.compose.foundation.layout.Column
  1. 回想一下,您需要将修饰符形参传递给可组合项中的子元素。这意味着,您需要将修饰符形参传递给 Column 可组合项。
@Composable
   fun BirthdayGreetingWithText(message: String, from: String, modifier: Modifier = Modifier) {
   Column(modifier = modifier) {
       Text(
           text = message,
           fontSize = 36.sp,
       )
       Text(
           text = from,
           fontSize = 24.sp,
       )
   }
}

10. 在设备上显示

当您对预览的效果感到满意后,即可在设备上或模拟器中运行应用。

  1. MainActivity.kt 文件中,滚动到 onCreate() 函数。
  2. Surface 代码块调用 BirthdayGreetingWithText() 函数。
  3. 传递 BirthdayGreetingWithText() 函数、您的生日祝福和签名。

完成后的 onCreate() 函数应该会如同下面的代码段所示:

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           HappyBirthdayTheme {
               // A surface container using the 'background' color from the theme
               Surface(
                   modifier = Modifier.fillMaxSize(),
                   color = MaterialTheme.colors.background
               ) {
                   BirthdayGreetingWithText( message = "Happy Birthday Sam!", from = "- from Emma")
               }
           }
       }
   }
}
  1. 在模拟器中构建并运行您的应用。

ca82241f560e8b99.png

11. 获取解决方案代码

完成后的 MainActivity.kt

package com.example.happybirthday

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import com.example.happybirthday.ui.theme.HappyBirthdayTheme

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           HappyBirthdayTheme {
               // A surface container using the 'background' color from the theme
               Surface(
                   modifier = Modifier.fillMaxSize(),
                   color = MaterialTheme.colors.background
               ) {
                   BirthdayGreetingWithText( message = "Happy Birthday Sam!", from = "- from Emma")
               }
           }
       }
   }
}

@Composable
   fun BirthdayGreetingWithText(message: String, from: String, modifier: Modifier = Modifier) {
   Column(modifier = modifier) {
       Text(
           text = message,
           fontSize = 36.sp,
       )
       Text(
           text = from,
           fontSize = 24.sp,
       )
   }
}

@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
   HappyBirthdayTheme {
       BirthdayGreetingWithText(message = "Happy Birthday Sam!", from ="- from Emma")
   }
}

12. 总结

您已成功创建 Happy Birthday 应用。

在下一个 Codelab 中,您将向应用中添加一张图片,并更改文本元素的对齐方式来进行美化。

要点总结

  • Jetpack Compose 是用于构建 Android 界面的新款工具包。它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助您简化并加快 Android 界面开发。
  • 应用的界面 (UI) 就是您在屏幕上所看到的内容,包括文本、图片、按钮和许多其他类型的元素。
  • 可组合函数是 Compose 的基本构建块。可组合函数是用于描述界面中某一部分的函数。
  • 可组合函数带有 @Composable 注解;此注解可告知 Compose 编译器:此函数用于将数据转换为界面。
  • Compose 中的三个基本标准布局元素是 ColumnRow,Box。它们是接受可组合内容的可组合函数,因此您可以在其中放置项目。例如,Row 中的各个子项将彼此相邻地水平放置。

了解详情