在基于 View 的应用中添加 Compose

1. 开始之前

从一开始,Jetpack Compose 的设计就考虑了与 View 的互操作性,这意味着 Compose 和 View 系统可以共享资源并并排工作以显示 UI。此功能允许您将 Compose 添加到现有的基于 View 的应用中。这意味着 Compose 和 View 可以共存于您的代码库中,直到您的整个应用完全使用 Compose。

在这个 Codelab 中,您将**Juice Tracker** 应用中的基于 View 的列表项更改为 Compose。如果您愿意,可以自行转换 Juice Tracker 的其余 View。

如果您有一个使用基于 View 的 UI 的应用,您可能不想一次性重写其整个 UI。此 Codelab 可帮助您将基于 View 的 UI 中的单个 View 转换为 Compose 元素。

先决条件

  • 熟悉基于 View 的 UI。
  • 了解如何使用基于 View 的 UI 构建应用。
  • 具有 Kotlin 语法经验,包括 lambda 表达式。
  • 了解如何在 Jetpack Compose 中构建应用。

您将学习什么

  • 如何将 Compose 添加到使用 Android View 构建的现有屏幕。
  • 如何预览添加到基于 View 的应用中的 Composable。

您将构建什么

  • 您将在**Juice Tracker** 应用中将基于 View 的列表项转换为 Compose。

2. 初始应用概述

此 Codelab 使用来自使用 View 构建 Android 应用的**Juice Tracker** 应用解决方案代码作为初始代码。初始应用已使用Room 持久性库保存数据。用户可以向应用数据库添加果汁信息,例如果汁名称、描述、颜色和评分。

36bd5542e97fee2e.png

在这个 Codelab 中,您将基于 View 的列表项转换为 Compose。

List item with juice details

下载此 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 库

记住,Compose 和 View 可以共存于同一屏幕上;您可以将一些 UI 元素放在 Compose 中,而其他元素放在 View 系统中。例如,您可以在 Compose 中只有列表,而屏幕的其余部分在 View 系统中。

完成以下步骤,将 Compose 库添加到**Juice Tracker** 应用。

  1. 在 Android Studio 中打开 Juice Tracker。
  2. 打开应用级别的build.gradle.kts
  3. buildFeatures块内,添加compose = true标志。
buildFeatures {
    //...
    // Enable Jetpack Compose for this module
    compose = true
}

此标志使 Android Studio 可以与 Compose 协同工作。在之前的 Codelab 中您没有执行此步骤,因为当您创建新的 Android Studio Compose 模板项目时,Android Studio 会自动生成此代码。

  1. buildFeatures下方,添加composeOptions块。
  2. 在块内,将kotlinCompilerExtensionVersion设置为"1.5.1"以设置 Kotlin 编译器版本。
composeOptions {
    kotlinCompilerExtensionVersion = "1.5.1"
}
  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是一个 Android View,可以托管 Jetpack Compose UI 内容。使用setContent为视图提供内容可组合函数。

  1. 打开layout/list_item.xml并在**拆分**选项卡中查看预览。

在本 Codelab 结束时,您将用可组合项替换此视图。

7a2df616fde1ec56.png

  1. 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) 
  1. onCreateViewHolder()文件夹中,更新return()函数以匹配以下代码
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JuiceListViewHolder {
   return JuiceListViewHolder(
       ComposeView(parent.context),
       onEdit,
       onDelete
   )
}
  1. JuiceListViewHolder类中,删除所有private变量并从bind()函数中删除所有代码。JuiceListViewHolder类现在看起来像以下代码
class JuiceListViewHolder(
    private val onEdit: (Juice) -> Unit,
    private val onDelete: (Juice) -> Unit
) : RecyclerView.ViewHolder(composeView) {

   fun bind(juice: Juice) {

   }
}
  1. 此时,您可以删除com.example.juicetracker.databinding.ListItemBindingandroid.view.LayoutInflater导入。
// Delete
import com.example.juicetracker.databinding.ListItemBinding
import android.view.LayoutInflater
  1. 删除layout/list_item.xml文件。
  2. 在**删除**对话框中选择**确定**。

2954ed44c5827571.png

4. 添加可组合函数

接下来,您将创建一个发出列表项的可组合项。Composable 接受Juice和两个回调函数来编辑和删除列表项。

  1. JuiceListAdapter.kt中,在JuiceListAdapter类定义之后,创建一个名为ListItem()的可组合函数。
  2. 使ListItem()函数接受Juice对象和用于删除的 lambda 回调。
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier


@Composable
fun ListItem(
    input: Juice,
    onDelete: (Juice) -> Unit,
    modifier: Modifier = Modifier
) {
}

查看您要创建的列表项的预览。请注意,它有一个果汁图标、果汁详细信息和一个删除按钮图标。您将很快实现这些组件。

4ec7f82371c6bc15.png

创建果汁图标可组合项

  1. JuiceListAdapter.kt中,在ListItem()可组合项之后,创建另一个名为JuiceIcon()的可组合函数,该函数接受colorModifier
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {

}
  1. 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)

}

使用colorLabelMapselectedColor变量,您将检索与用户选择关联的颜色资源。

  1. 添加一个Box布局,以彼此叠加显示两个图标ic_juice_coloric_juice_clearic_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)
}

由于您熟悉可组合实现,因此未提供有关其实现方式的详细信息。

  1. 添加一个函数来预览JuiceIcon()。将颜色作为Yellow传递。
import androidx.compose.ui.tooling.preview.Preview


@Preview
@Composable
fun PreviewJuiceIcon() {
    JuiceIcon("Yellow")
}

c016198f82a5d199.png

创建果汁详细信息可组合项

JuiceListAdapter.kt中,您需要添加另一个可组合函数来显示果汁详细信息。您还需要一个列布局来显示果汁名称和描述的两个Text可组合项,以及一个评分指示器。为此,请完成以下步骤

  1. 添加一个名为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))
   }
}
  1. 要解决未解析的引用错误,请创建一个名为RatingDisplay()的可组合函数。

536030e2ecb01a4e.png

在 View 系统中,您有一个RatingBar来显示以下评分栏。Compose 没有评分栏可组合项,因此您需要从头实现此元素。

  1. 定义RatingDisplay()函数以根据评分显示星级。此可组合函数根据评分显示星数。

Rating bar with four stars

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,您需要创建星形矢量资源。

  1. 在**项目**窗格中,右键单击**drawable > 新建 > 矢量资源**。

201431ca3d212113.png

  1. 在**资源工作室**对话框中,搜索星形图标。选择填充的星形图标。

9956ed24371f61ac.png

5a79bac6f3982b72.png

  1. 将星形的颜色值更改为**625B71**。

44d4bdfa93bc369a.png

  1. 单击**下一步 > 完成**。
  2. 请注意,drawable 出现于res/drawable文件夹中。

64bb8d9f05019229.png

  1. 添加一个预览可组合项来预览JuiceDetails可组合项。
@Preview
@Composable
fun PreviewJuiceDetails() {
    JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}

with juice name juice description and star rating bar

创建删除按钮可组合项

  1. JuiceListAdapter.kt中,添加另一个名为DeleteButton()的可组合函数,该函数接受一个 lambda 回调函数和一个 Modifier。
  2. 将 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)
        )
    }
}
  1. 添加一个预览函数来预览删除按钮。
@Preview
@Composable
fun PreviewDeleteIcon() {
    DeleteButton({})
}

Android studio preview of the delete icon

5. 实现 ListItem 函数

现在您已经拥有了显示列表项所需的所有可组合项,您可以将它们排列在布局中。请注意您在上一步中定义的ListItem()函数。

@Composable
fun ListItem(
   input: Juice,
   onEdit: (Juice) -> Unit,
   onDelete: (Juice) -> Unit,
   modifier: Modifier = Modifier
) {
}

JuiceListAdapter.kt中,完成以下步骤以实现ListItem()函数。

  1. 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
   ) {

   }
}
  1. Row lambda 内,调用您创建的三个可组合项JuiceIconJuiceDetailsDeleteButton作为子元素。
JuiceIcon(input.color)
JuiceDetails(input, Modifier.weight(1f))
DeleteButton({})

Modifier.weight(1f)传递给JuiceDetails()可组合项可确保果汁详细信息在测量非加权子元素后占用剩余的水平空间。

  1. onDelete(input) lambda 和具有顶部对齐的修饰符作为参数传递给DeleteButton可组合项。
DeleteButton(
   onDelete = {
       onDelete(input)
   },
   modifier = Modifier.align(Alignment.Top)
)
  1. 编写一个预览函数来预览ListItem可组合项。
@Preview
@Composable
fun PreviewListItem() {
   ListItem(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4), {})
}

Android Studio list item preview with sweet beet juice details

  1. ListItem可组合项绑定到视图持有者。在clickable() lambda 函数内调用onEdit(input)以在单击列表项时打开编辑对话框。

JuiceListViewHolder类中,在bind()函数内,您需要托管可组合项。您可以使用ComposeView,它是一个 Android View,可以使用其setContent方法托管 Compose UI 内容。

fun bind(input: Juice) {
    composeView.setContent {
        ListItem(
            input,
            onDelete,
            modifier = Modifier
                .fillMaxWidth()
                .clickable {
                    onEdit(input)
                }
                .padding(vertical = 8.dp, horizontal = 16.dp),
       )
   }
}
  1. 运行应用。添加您最喜欢的果汁。注意闪亮的 Compose 列表项。

aadccf32ab952d0f.png. 8aa751f4cf63bf98.png

恭喜!您刚刚创建了第一个 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 开发者文档

代码实验室 [中级]