1. 开始之前
从一开始,Jetpack Compose 就是与 View 互操作性一起设计的,这意味着 Compose 和 View 系统可以共享资源并并排工作以显示 UI。此功能使您能够将 Compose 添加到现有的基于 View 的应用中。这意味着 Compose 和 View 可以共存于您的代码库中,直到您的整个应用完全使用 Compose。
在此代码实验室中,您将 **Juice Tracker** 应用中的基于 View 的列表项更改为 Compose。如果愿意,您可以自行转换 Juice Tracker 的其余 View。
如果您的应用使用基于 View 的 UI,您可能不想立即重写整个 UI。此代码实验室可帮助您将基于 View 的 UI 中的单个 View 转换为 Compose 元素。
先决条件
- 熟悉基于 View 的 UI。
- 了解如何使用基于 View 的 UI 构建应用。
- 了解 Kotlin 语法,包括 Lambda 表达式。
- 了解如何在 Jetpack Compose 中构建应用。
您将学到什么
- 如何将 Compose 添加到使用 Android View 构建的现有屏幕中。
- 如何预览添加到基于 View 的应用中的可组合项。
您将构建什么
- 您将在 **Juice Tracker** 应用中将基于 View 的列表项转换为 Compose。
2. 初始应用概述
此代码实验室使用来自 使用 View 构建 Android 应用 的 **Juice Tracker** 应用解决方案代码作为初始代码。初始应用已使用 Room 持久性库保存数据。用户可以将果汁信息添加到应用数据库中,例如果汁名称、描述、颜色和评分。
在此代码实验室中,您将基于 View 的列表项转换为 Compose。
下载此代码实验室的初始代码
要开始,请下载初始代码
或者,您可以克隆代码的 GitHub 存储库
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views
您可以在 JuiceTracker
GitHub 存储库中浏览代码。
3. 添加 Jetpack Compose 库
回想一下,Compose 和 View 可以一起存在于给定的屏幕上;您可以在 Compose 中放置一些 UI 元素,而在 View 系统中放置其他元素。例如,您可以在 Compose 中仅包含列表,而屏幕的其余部分则位于 View 系统中。
完成以下步骤,将 Compose 库添加到 **Juice Tracker** 应用中。
- 在 Android Studio 中打开 Juice Tracker。
- 打开应用级别的
build.gradle.kts
。 - 在
buildFeatures
块内,添加一个compose = true
标志。
buildFeatures {
//...
// Enable Jetpack Compose for this module
compose = true
}
此标志使 Android Studio 能够与 Compose 协同工作。在之前的代码实验室中,您没有执行此步骤,因为当您创建新的 Android Studio Compose 模板项目时,Android Studio 会自动生成此代码。
- 在
buildFeatures
下方,添加composeOptions
块。 - 在块内,将
kotlinCompilerExtensionVersion
设置为"1.5.1"
以设置 Kotlin 编译器版本。
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
- 在
dependencies
部分,添加 Compose 依赖项。您需要以下依赖项才能将 Compose 添加到基于 View 的应用中。这些依赖项有助于将 Compose 与 Activity 集成,添加 Compose 设计组件库,支持 Compose Jetpack 主题,并提供用于增强 IDE 支持的工具。
dependencies {
implementation(platform("androidx.compose:compose-bom:2023.06.01"))
// other dependencies
// Compose
implementation("androidx.activity:activity-compose:1.7.2")
implementation("androidx.compose.material3:material3")
implementation("com.google.accompanist:accompanist-themeadapter-material3:0.28.0")
debugImplementation("androidx.compose.ui:ui-tooling")
}
添加 ComposeView
ComposeView
是一个可以托管 Jetpack Compose UI 内容的 Android View。使用 setContent
为 View 提供内容可组合函数。
- 打开
layout/list_item.xml
并在 **拆分** 选项卡中查看预览。
在本代码实验室结束时,您将用可组合项替换此 View。
- 在
JuiceListAdapter.kt
中,从所有位置删除ListItemBinding
。在JuiceListViewHolder
类中,将binding.root
替换为composeView
。
import androidx.compose.ui.platform.ComposeView
class JuiceListViewHolder(
private val onEdit: (Juice) -> Unit,
private val onDelete: (Juice) -> Unit
): RecyclerView.ViewHolder(composeView)
- 在
onCreateViewHolder()
文件夹中,更新return()
函数以匹配以下代码
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JuiceListViewHolder {
return JuiceListViewHolder(
ComposeView(parent.context),
onEdit,
onDelete
)
}
- 在
JuiceListViewHolder
类中,删除所有private
变量并从bind()
函数中删除所有代码。现在,您的JuiceListViewHolder
类如下所示
class JuiceListViewHolder(
private val onEdit: (Juice) -> Unit,
private val onDelete: (Juice) -> Unit
) : RecyclerView.ViewHolder(composeView) {
fun bind(juice: Juice) {
}
}
- 此时,您可以删除
com.example.juicetracker.databinding.ListItemBinding
和android.view.LayoutInflater
导入项。
// Delete
import com.example.juicetracker.databinding.ListItemBinding
import android.view.LayoutInflater
- 删除
layout/list_item.xml
文件。 - 在 **删除** 对话框中选择 **确定**。
4. 添加可组合函数
接下来,您将创建一个发出列表项的可组合项。可组合项接受 Juice
和两个回调函数来编辑和删除列表项。
- 在
JuiceListAdapter.kt
中,在JuiceListAdapter
类定义之后,创建一个名为ListItem()
的可组合函数。 - 使
ListItem()
函数接受Juice
对象和用于删除的 Lambda 回调。
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun ListItem(
input: Juice,
onDelete: (Juice) -> Unit,
modifier: Modifier = Modifier
) {
}
查看要创建的列表项的预览。请注意,它具有果汁图标、果汁详细信息和删除按钮图标。您将很快实现这些组件。
创建果汁图标可组合项
- 在
JuiceListAdapter.kt
中,在ListItem()
可组合项之后,创建另一个名为JuiceIcon()
的可组合函数,该函数接受color
和Modifier
。
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
}
- 在
JuiceIcon()
函数内,添加color
和内容描述的变量,如下面的代码所示
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
val colorLabelMap = JuiceColor.values().associateBy { stringResource(it.label) }
val selectedColor = colorLabelMap[color]?.let { Color(it.color) }
val juiceIconContentDescription = stringResource(R.string.juice_color, color)
}
使用 colorLabelMap
和 selectedColor
变量,您将检索与用户选择关联的颜色资源。
- 添加一个
Box
布局,以将两个图标ic_juice_color
和ic_juice_clear
叠加在一起。ic_juice_color
图标具有色调,并对齐到中心。
import androidx.compose.foundation.layout.Box
Box(
modifier.semantics {
contentDescription = juiceIconContentDescription
}
) {
Icon(
painter = painterResource(R.drawable.ic_juice_color),
contentDescription = null,
tint = selectedColor ?: Color.Red,
modifier = Modifier.align(Alignment.Center)
)
Icon(painter = painterResource(R.drawable.ic_juice_clear), contentDescription = null)
}
由于您熟悉可组合项的实现,因此未提供有关其如何实现的详细信息。
- 添加一个函数以预览
JuiceIcon()
。将颜色作为Yellow
传递。
import androidx.compose.ui.tooling.preview.Preview
@Preview
@Composable
fun PreviewJuiceIcon() {
JuiceIcon("Yellow")
}
创建果汁详细信息可组合项
在 JuiceListAdapter.kt
中,您需要添加另一个可组合函数以显示果汁详细信息。您还需要一个列布局以显示名称和描述的两个 Text
可组合项以及评分指示器。为此,请完成以下步骤
- 添加一个名为
JuiceDetails()
的可组合函数,该函数接受Juice
对象和Modifier
,以及用于果汁名称的文本可组合项和用于果汁描述的可组合项,如下面的代码所示
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight
@Composable
fun JuiceDetails(juice: Juice, modifier: Modifier = Modifier) {
Column(modifier, verticalArrangement = Arrangement.Top) {
Text(
text = juice.name,
style = MaterialTheme.typography.h5.copy(fontWeight = FontWeight.Bold),
)
Text(juice.description)
RatingDisplay(rating = juice.rating, modifier = Modifier.padding(top = 8.dp))
}
}
- 要解决未解析的引用错误,请创建一个名为
RatingDisplay()
的可组合函数。
在 View 系统中,您有一个 RatingBar
用于显示以下评分栏。Compose 没有评分栏可组合项,因此您需要从头开始实现此元素。
- 定义
RatingDisplay()
函数以根据评分显示星级。此可组合函数根据评分显示星级数量。
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
@Composable
fun RatingDisplay(rating: Int, modifier: Modifier = Modifier) {
val displayDescription = pluralStringResource(R.plurals.number_of_stars, count = rating)
Row(
// Content description is added here to support accessibility
modifier.semantics {
contentDescription = displayDescription
}
) {
repeat(rating) {
// Star [contentDescription] is null as the image is for illustrative purpose
Image(
modifier = Modifier.size(32.dp),
painter = painterResource(R.drawable.star),
contentDescription = null
)
}
}
}
要在 Compose 中创建星形可绘制对象,您需要创建星形矢量资源。
- 在 **项目** 窗格中,右键单击 **drawable > 新建 > 矢量资源**。
- 在 **资源工作室** 对话框中,搜索星形图标。选择填充的星形图标。
- 将星形的颜色值更改为 **625B71**。
- 单击 **下一步 > 完成**。
- 请注意,可绘制对象出现在
res/drawable
文件夹中。
- 添加一个预览可组合项以预览
JuiceDetails
可组合项。
@Preview
@Composable
fun PreviewJuiceDetails() {
JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}
创建删除按钮可组合项
- 在
JuiceListAdapter.kt
中,添加另一个名为DeleteButton()
的可组合函数,该函数接受 Lambda 回调函数和 Modifier。 - 将 Lambda 设置为
onClick
参数,并将Icon()
作为参数传递,如下面的代码所示
import androidx.compose.ui.res.painterResource
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@Composable
fun DeleteButton(onDelete: () -> Unit, modifier: Modifier = Modifier) {
IconButton(
onClick = { onDelete() },
modifier = modifier
) {
Icon(
painter = painterResource(R.drawable.ic_delete),
contentDescription = stringResource(R.string.delete)
)
}
}
- 添加一个预览函数以预览删除按钮。
@Preview
@Composable
fun PreviewDeleteIcon() {
DeleteButton({})
}
5. 实现 ListItem 函数
现在您拥有了显示列表项所需的所有可组合项,您可以将它们排列在布局中。请注意您在上一步中定义的 ListItem()
函数。
@Composable
fun ListItem(
input: Juice,
onEdit: (Juice) -> Unit,
onDelete: (Juice) -> Unit,
modifier: Modifier = Modifier
) {
}
在 JuiceListAdapter.kt
中,完成以下步骤以实现 ListItem()
函数。
- 在
Mdc3Theme {}
Lambda 内添加一个Row
布局。
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import com.google.accompanist.themeadapter.material3.Mdc3Theme
Mdc3Theme {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween
) {
}
}
- 在
Row
Lambda 内,调用您创建的三个可组合项JuiceIcon
、JuiceDetails
和DeleteButton
作为子元素。
JuiceIcon(input.color)
JuiceDetails(input, Modifier.weight(1f))
DeleteButton({})
将 Modifier.
weight
(1f)
传递给 JuiceDetails()
可组合项可确保果汁详细信息在测量未加权子元素后占用剩余的水平空间。
- 将
onDelete(input)
Lambda 和具有顶部对齐的修饰符作为参数传递给DeleteButton
可组合项。
DeleteButton(
onDelete = {
onDelete(input)
},
modifier = Modifier.align(Alignment.Top)
)
- 编写一个预览函数以预览
ListItem
可组合项。
@Preview
@Composable
fun PreviewListItem() {
ListItem(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4), {})
}
- 将
ListItem
可组合项绑定到 View Holder。在clickable()
Lambda 函数内部调用onEdit(input)
以在单击列表项时打开编辑对话框。
在 JuiceListViewHolder
类中,在 bind()
函数内,您需要托管可组合项。您使用 ComposeView
,它是一个可以使用其 setContent
方法托管 Compose UI 内容的 Android View。
fun bind(input: Juice) {
composeView.setContent {
ListItem(
input,
onDelete,
modifier = Modifier
.fillMaxWidth()
.clickable {
onEdit(input)
}
.padding(vertical = 8.dp, horizontal = 16.dp),
)
}
}
- 运行应用。添加您最喜欢的果汁。请注意闪亮的可组合列表项。
.
恭喜!您刚刚创建了第一个 Compose 互操作性应用,该应用在基于 View 的应用中使用 Compose 元素。
6. 获取解决方案代码
要下载完成版 Codelab 的代码,您可以使用以下 Git 命令
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views-with-compose
或者,您可以将代码库下载为 zip 文件,解压缩,然后在 Android Studio 中打开。
如果您想查看解决方案代码,请在 GitHub 上查看。
7. 了解更多
Android 开发者文档
- Compose 工具 | Jetpack Compose | Android 开发者
- 互操作性 API | Jetpack Compose | Android 开发者
- 迁移策略 | Jetpack Compose | Android 开发者
Codelab [中级]