1. 准备工作
从一开始,Jetpack Compose 就采用了具备 View 互操作性的设计,这意味着 Compose 和 View 系统可以共享资源并协同工作,从而显示界面。借助此功能,您可以将 Compose 添加到基于 View 的现有应用中。这意味着,Compose 和 View 可以在代码库中共存,直到整个应用完全使用 Compose。
在此 Codelab 中,您需要将 Juice Tracker 应用中基于 View 的列表项更改为 Compose。如果您愿意,可以自行转换 Juice Tracker 的其他视图。
如果您的应用界面是基于 View 系统,您可能不想一次全部重写整个界面。此 Codelab 将帮助您将基于 View 的界面中的单个 View 转换为 Compose 元素。
前提条件
- 熟悉基于 View 的界面。
- 具备有关如何使用基于 View 的界面构建应用的知识。
- 有使用 Kotlin 语法(包括 lambda)的经验。
- 具备有关如何在 Jetpack Compose 中构建应用的知识。
学习内容
- 如何将 Compose 添加到使用 Android View 构建的现有界面。
- 如何预览添加到基于 View 的应用的可组合项。
构建内容
- 您要将 Juice Tracker 应用中基于 View 的列表项转换为 Compose。
2. 起始应用概览
此 Codelab 将使用 View 构建 Android 应用中的 Juice Tracker 应用解决方案代码作为起始代码。起始应用已在使用 Room 持久性库保存数据。用户可以将果汁信息添加到应用数据库中,例如果汁名称、说明、颜色和评分。
在此 Codelab 中,您要将基于 View 的列表项转换为 Compose。
下载此 Codelab 的起始代码
首先,请下载起始代码:
或者,您也可以克隆该代码的 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 库
Recollect、Compose 和 View 可以同时存在于给定界面上;您可以使用 Compose 构建一些界面元素,并使用 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。您未在之前的 Codelab 中完成过此步骤,这是因为在您创建新的 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 界面内容的 Android View。使用 setContent
为 View 提供内容可组合函数。
- 打开
layout/list_item.xml
,然后在 Split 标签页中查看预览。
在此 Codelab 结束时,您需要将此 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
文件。 - 在 Delete 对话框中,选择 OK。
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
,以及用于果汁名称的 Text 可组合项和用于果汁说明的可组合项,如以下代码所示:
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 中创建星形可绘制对象,您需要创建星形矢量资源。
- 在 Project 窗格中,右键点击“drawable”,然后依次选择 New > Vector Asset。
- 在 Asset Studio 对话框中,搜索星形图标。选择已填充的星形图标。
- 将星形的颜色值更改为 625B71。
- 依次点击 Next > Finish。
- 请注意,可绘制对象会显示在
res/drawable
文件夹中。
- 添加一个预览可组合函数,用于预览
JuiceDetails
可组合函数。
@Preview
@Composable
fun PreviewJuiceDetails() {
JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}
创建“删除”按钮可组合项
- 在
JuiceListAdapter.kt
中,再添加一个名为DeleteButton()
的可组合函数,该函数接受 lambda 回调函数和修饰符。 - 将 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
可组合项绑定到 ViewHolder。当列表项被点击时,调用clickable()
lambda 函数中的onEdit(input)
,以便打开编辑对话框。
在 JuiceListViewHolder
类中的 bind()
函数内,您需要托管可组合项。您将使用 ComposeView
,这种 Android View 可以使用其 setContent
方法托管 Compose 界面内容。
fun bind(input: Juice) {
composeView.setContent {
ListItem(
input,
onDelete,
modifier = Modifier
.fillMaxWidth()
.clickable {
onEdit(input)
}
.padding(vertical = 8.dp, horizontal = 16.dp),
)
}
}
- 运行应用。添加您喜爱的果汁。注意酷炫的 Compose 列表项。
.
恭喜!您刚刚创建了您的第一个 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 开发者
- Interoperability API | Jetpack Compose | Android 开发者
- 迁移策略 | Jetpack Compose | Android 开发者
Codelab [中级]