Jetpack Compose 基础知识

教程取自于Google官方课程之Jetpack Compose 基础知识


1. 准备工作

Jetpack Compose 是一款新型工具包,旨在帮助简化界面开发。该工具包将响应式编程模型与简洁易用的 Kotlin 编程语言相结合,并采用完全声明式的代码编写方式,让您可以通过调用一系列函数来描述界面,这些函数会将数据转换为界面层次结构。当底层数据发生变化时,框架会自动重新执行这些函数,为您更新界面层次结构。

Compose 应用由可组合函数构成。可组合函数即带有 @Composable 标记的常规函数,这些函数可以调用其他可组合函数。使用一个函数就可以创建一个新的界面组件。该注解会告知 Compose 为函数添加特殊支持,以便后续更新和维护界面。借助 Compose,您可以将代码设计成多个小代码块。可组合函数通常简称为“可组合项”。

通过创建可重用的小型可组合项,您可以轻松构建应用中所用界面元素的库。每个可组合项对应于屏幕的一个部分,可以单独修改。

注意:在本 Codelab 中,“界面组件”、“可组合函数”和“可组合项”几个术语可以互换使用来指代同一个概念。

2. 启动新的 Compose 项目

如需启动新的 Compose 项目,请打开 Android Studio Bumblebee,然后选择 Start a new Android Studio project,如下所示:

新的 Compose 项目

如果系统未显示上述界面,请依次进入 File > New > New Project。
创建新项目时,请从可用模板中选择 Empty Compose Activity。
Empty Compose Activity

点击 Next,然后照常配置项目,并将其命名为 Basics Codelab。请确保您选择的 minimumSdkVersion 至少为 API 级别 21,这是 Compose 支持的最低 API 级别。

注意:如需详细了解如何使用空 activity 设置 Compose,或如何将其添加到现有项目,请查看此文档
选择 Empty Compose Activity 模板后,会在项目中为您生成以下代码:

  • 该项目已配置为使用 Compose。
  • 已创建 AndroidManifest.xml 文件。
  • build.gradle 和 app/build.gradle 文件包含 Compose 所需的选项和依赖项。

同步项目后,请打开 MainActivity.kt 并查看代码。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicsCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

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

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    BasicsCodelabTheme {
        Greeting("Android")
    }
}

警告:setContent 中使用的应用主题取决于项目名称。本 Codelab 假定项目名称为 LayoutsCodelab。如果您从 Codelab 中复制并粘贴代码,请记得使用 ui/Theme.kt 文件中提供的主题名称来更新 BasicsCodelabTheme。本 Codelab 后面会讲到如何设置主题。

本 Codelab 的解决方案
您可以从 GitHub 获取本 Codelab 的解决方案代码:https://github.com/googlecodelabs/android-compose-codelabs/archive/main.zip
您可以在 BasicsCodelab 项目中找到解决方案代码。建议您按照自己的节奏逐步完成 Codelab,必要时再查看解决方案。在此 Codelab 的学习过程中,我们会为您提供需要添加到项目的代码段。

3. Compose 使用入门

了解 Android Studio 为您生成的与 Compose 相关的各种类和方法。

可组合函数

可组合函数是带有 @Composable 注解的常规函数。这类函数自身可以调用其他 @Composable 函数。我们会展示如何为 Greeting 函数添加 @Composable 标记。此函数会生成一段显示给定输入 String 的界面层次结构。Text 是由库提供的可组合函数。

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

注意:可组合函数是带有 @Composable 注解的 Kotlin 函数,如上述代码段所示。

Android 应用中的 Compose
使用 Compose 时,Activities 仍然是 Android 应用的入口点。在我们的项目中,用户打开应用时会启动 MainActivity(如 AndroidManifest.xml 文件中所指定)。您可以使用 setContent 来定义布局,但不同于在传统 View 系统中使用 XML 文件,您将在该函数中调用可组合函数。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicsCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

BasicsCodelabTheme 是为可组合函数设置样式的一种方式。有关详细内容,请参阅设置应用主题部分。如需查看文本在屏幕上的显示效果,您可以在模拟器或设备上运行该应用,或使用 Android Studio 预览进行查看。

若要使用 Android Studio 预览,您只需使用 @Preview 注解标记所有无参数可组合函数或采用默认参数的函数,然后构建您的项目即可。现在 MainActivity.kt 文件中已经包含了一个 Preview Composable 函数。您可以在同一个文件中包含多个预览,并为它们指定名称。

@Preview(showBackground = true, name = "Text preview")
@Composable
fun DefaultPreview() {
    BasicsCodelabTheme {
        Greeting(name = "Android")
    }
}
Preview

注意:在此项目中导入与 Jetpack Compose 相关的类时,请从以下位置导入:
androidx.compose.*(针对编译器和运行时类)

androidx.compose.ui.*(针对界面工具包和库)

4. 调整界面

首先,为 Greeting 设置不同的背景颜色。为此,您可以用 Surface 包围 Text 可组合项。Surface 会采用一种颜色,因此请使用 MaterialTheme.colors.primary。

注意:SurfaceMaterialTheme 是与 Material Design 相关的概念。Material Design 是 Google 提供的一个设计系统,旨在帮助您构建界面和体验。

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colors.primary) {
        Text (text = "Hello $name!")
    }
}

嵌套在 Surface 内的组件将在该背景颜色之上绘制。
将上述代码添加到项目后,您会在 Android Studio 的右上角看到 Build & Refresh 按钮。点按该按钮或构建项目即可在预览中查看新更改。



您可以在预览中查看新更改:



您可能忽略了一个重要的细节:文字现在是白色的。我们是何时对此进行定义的?

我们并没有对此进行过定义!Material 组件(例如 androidx.compose.material.Surface)旨在提供应用中可能需要的常见功能(例如为文本选择适当的颜色),让您获得更好的体验。我们之所以说 Material 很实用,是因为它提供在大多数应用中都会用到的实用默认值和模式。Compose 中的 Material 组件是在其他基础组件(位于 androidx.compose.foundation 中)的基础上构建的。如果您需要更高的灵活性,也可以从您的应用组件中访问这些组件。

在这种情况下,Surface 会了解,当该背景设置为 primary 颜色后,其上的任何文本都应使用 onPrimary 颜色,此颜色也在主题中进行了定义。如需了解详情,请参阅设置应用主题部分。

注意:如需查看 Compose 中 Material 组件的交互式列表,请查看 Compose Material Catalog 应用。
修饰符
大多数 Compose 界面元素(例如 Surface 和 Text)都接受可选的 modifier 参数。修饰符会指示界面元素如何在其父级布局中放置、显示或表现。

例如,padding 修饰符会在其修饰的元素周围应用一定的空间。您可以使用 Modifier.padding() 创建内边距修饰符。

现在,为屏幕上的 Text 添加内边距:

import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
...
点击 Build & Refresh 即可查看新更改。
@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colors.primary) {
        Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
    }
}

点击 Build & Refresh 即可查看新更改。


5. 重复使用可组合项

您添加到界面的组件越多,创建的嵌套层级就越多。如果函数变得非常大,可能会影响可读性。通过创建可重用的小型组件,可以轻松构建应用中所用界面元素的库。每个组件对应于屏幕的一个部分,可以单独修改。

创建一个名为 MyApp 的可组合项,该组合项中包含问候语。

@Composable
private fun MyApp() {
    Surface(color = MaterialTheme.colors.background) {
        Greeting("Android")
    }
}

这样一来,由于现在可以重复使用 MyApp 可组合项,您就可以省去 onCreate 回调和预览,从而避免重复编写代码。您的 MainActivity.kt 文件应如下所示:

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.padding
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.dp
import com.codelab.basicstep1.ui.theme.BasicsCodelabTheme

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

@Composable
private fun MyApp() {
    Surface(color = MaterialTheme.colors.background) {
        Greeting("Android")
    }
}

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colors.primary) {
        Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
    }
}

@Preview(showBackground = true)
@Composable
private fun DefaultPreview() {
    BasicsCodelabTheme {
        MyApp()
    }
}

6. 创建列和行

Compose 中的三个基本标准布局元素是 ColumnRowBox 可组合项。


它们是接受可组合内容的可组合函数,因此您可以在其中放置内容。例如,Column 中的每个子级都将垂直放置。

// Don't copy over
Column {
    Text("First row")
    Text("Second row")
}

现在尝试更改 Greeting,使其显示包含两个文本元素的列,如以下示例中所示:



请注意,您可能需要移动周围的内边距。

将您的结果与此解决方案进行比较:

import androidx.compose.foundation.layout.Column
...

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colors.primary) {
        Column(modifier = Modifier.padding(24.dp)) {
            Text(text = "Hello,")
            Text(text = name)
        }
    }
}

Compose 和 Kotlin
可组合函数可以像 Kotlin 中的其他函数一样使用。这会使界面构建变得非常有效,因为您可以添加语句来影响界面的显示方式。

例如,您可以使用 for 循环向 Column 中添加元素:

@Composable
fun MyApp(names: List<String> = listOf("World", "Compose")) {
    Column {
        for (name in names) {
            Greeting(name = name)
        }
    }
}

您尚未设置可组合项的尺寸,也未对可组合项的大小添加任何限制,因此每一行仅占用可能的最小空间,预览时的效果也是如此。让我们更改预览效果,以模拟小屏幕手机的常见宽度 320dp。按如下所示向 @Preview 注解添加 widthDp 参数:

@Preview(showBackground = true, widthDp = 320)
@Composable
fun DefaultPreview() {
    BasicsCodelabTheme {
        MyApp()
    }
}


修饰符在 Compose 中使用得非常广泛,现在我们来练习更高级的用法:尝试使用 fillMaxWidthpadding 修饰符复制以下布局。

现在,将您的代码与解决方案进行比较:

@Composable
fun MyApp(names: List<String> = listOf("World", "Compose")) {
    Column(modifier = Modifier.padding(vertical = 4.dp)) {
        for (name in names) {
            Greeting(name = name)
        }
    }
}

@Composable
private fun Greeting(name: String) {
    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
        Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
            Text(text = "Hello, ")
            Text(text = name)
        }
    }
}

请注意:

修饰符可以包含重载,因而具有相应的优势,例如您可以指定不同的方式来创建内边距。
若要向一个元素添加多个修饰符,您只需要将它们链接起来即可。
有多种方式可以实现此结果,因此,如果您的代码与此代码段不同,并不表示您的代码就是错的。不过,为了继续完成此 Codelab,仍请复制并粘贴此代码。

添加按钮
接下来,您将添加一个用于展开 Greeting 的可点击元素,因此需要先添加对应的按钮。您的目标是要创建以下布局:


Button 是 Material 软件包提供的一种可组合项,它采用可组合项作为最后一个参数。由于尾随 lambda 可以移到括号之外,因此您可以向按钮添加任何内容作为子级,例如 Text

// Don't copy yet
Button(
    onClick = { } // You'll learn about this callback later
) {
    Text("Show less")
}

注意:Compose 根据 Material Design 按钮规范提供了不同类型的 ButtonButtonOutlinedButtonTextButton。在本示例中,您将使用包围 Text 作为 Button 内容的 OutlinedButton

为了实现这一点,您需要学习如何在行尾放置可组合项。由于没有 alignEnd 修饰符,因此您需要在开始时为该可组合项赋予一定的 weight。weight 修饰符会让元素填满所有可用空间,使其“具有弹性”,也就是会推开其他没有权重的元素(即“无弹性”元素)。该修饰符还会使 fillMaxWidth 修饰符变得多余。

现在尝试添加该按钮,并按照上述图片中所示放置该按钮。

下面列出了对应的解决方案代码:

import androidx.compose.material.Button
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
...

@Composable
private fun Greeting(name: String) {

    Surface(
        color = MaterialTheme.colors.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
        Row(modifier = Modifier.padding(24.dp)) {
            Column(modifier = Modifier.weight(1f)) {
                Text(text = "Hello, ")
                Text(text = name)
            }
            OutlinedButton(
                onClick = { /* TODO */ }
            ) {
                Text("Show more")
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容