添加可滚动列表

1. 准备工作

在此 Codelab 中,您将学习如何使用 Jetpack Compose 在应用中创建可滚动列表。

您将使用 Affirmations 应用,该应用会显示配有精美图片的自我肯定话语列表,为您的日常生活带来满满正能量!

数据已经有了,您只需获取数据并将其显示在界面中即可。

前提条件

  • 熟悉以 Kotlin 编写的列表
  • 拥有使用 Jetpack Compose 构建布局的经验
  • 拥有在设备或模拟器上运行应用的经验

学习内容

  • 如何使用 Jetpack Compose 创建 Material Design 卡片?
  • 如何使用 Jetpack Compose 创建可滚动列表?

构建内容

  • 您将使用一个现有应用,在其界面中添加可滚动列表

完成后的效果将如下所示:

286f5132aa155fa6.png

所需条件

  • 一台连接到互联网并安装了网络浏览器和 Android Studio 的计算机
  • 能够访问 GitHub

下载起始代码

在 Android Studio 中,打开 basic-android-kotlin-compose-training-affirmations 文件夹。

根据 starter 分支代码构建应用时,应用应显示空白屏幕。

3beea0789e2eeaba.png

2. 创建列表项数据类

为 Affirmation 创建数据类

在 Android 应用中,列表由列表项组成。如果包含单项数据,列表项可以是字符串或整数等简单内容。对于包含多项数据的列表项(例如图片和文本),您需要一个包含所有这些属性的类。数据类是一种仅包含属性的类,可以提供一些可与这些属性配合使用的实用程序方法。

  1. com.example.affirmations 下创建一个新的软件包。

89c8d8485c685fac.png

将新软件包命名为 model。“model”软件包会包含将由一个数据类表示的数据模型。该数据类由表示“自我肯定话语”内容相关信息的属性组成;“自我肯定话语”将包含字符串资源和图片资源。软件包是包含类以及其他目录的目录。

b54fb6bf57de44c8.png

  1. com.example.affirmations.model 软件包中创建一个新类。

58510a651bd49100.png

将新类命名为 Affirmation,并将其设为 Data class

7f94b65ee3d8407f.png

  1. 每个 Affirmation 均包含一张图片和一个字符串。在 Affirmation 数据类中创建两个 val 属性,将其中一个属性命名为 stringResourceId,另一个命名为 imageResourceId。这两个属性的值都应为整数。

Affirmation.kt

data class Affirmation(
    val stringResourceId: Int,
    val imageResourceId: Int
)
  1. stringResourceId 属性添加 @StringRes 注解,并为 imageResourceId 添加 @DrawableRes 注解。stringResourceId 表示存储在字符串资源中的自我肯定话语文本的 ID。imageResourceId 表示存储在可绘制资源中的自我肯定话语图片的 ID。

Affirmation.kt

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes

data class Affirmation(
    @StringRes val stringResourceId: Int,
    @DrawableRes val imageResourceId: Int
)
  1. com.example.affirmations.data 软件包中,打开 Datasource.kt 文件并取消注释两个 import 语句和 Datasource 类的内容。

Datasource.kt

import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

class Datasource() {
    fun loadAffirmations(): List<Affirmation> {
        return listOf<Affirmation>(
            Affirmation(R.string.affirmation1, R.drawable.image1),
            Affirmation(R.string.affirmation2, R.drawable.image2),
            Affirmation(R.string.affirmation3, R.drawable.image3),
            Affirmation(R.string.affirmation4, R.drawable.image4),
            Affirmation(R.string.affirmation5, R.drawable.image5),
            Affirmation(R.string.affirmation6, R.drawable.image6),
            Affirmation(R.string.affirmation7, R.drawable.image7),
            Affirmation(R.string.affirmation8, R.drawable.image8),
            Affirmation(R.string.affirmation9, R.drawable.image9),
            Affirmation(R.string.affirmation10, R.drawable.image10))
    }
}

3. 向应用中添加列表

创建列表项卡片

此应用旨在显示自我肯定话语列表。将界面配置为显示列表的第一步是,创建列表项。每个自我肯定话语项都包含一张图片和一个字符串。每个项的数据都随附起始代码,您将创建界面组件来显示这样的项。

该项将由一个 Card 可组合项组成,后者包含一个 Image 和一个 Text 可组合项。在 Compose 中,Card 是一种在单个容器中显示内容和操作的 Surface。自我肯定话语卡片在预览中将如下所示:

4f657540712a069f.png

该卡片会显示一张图片并在其下方显示一些文本。这种垂直布局可以使用封装在 Card 可组合项中的 Column 可组合项来实现。您可以自己尝试创建该布局,也可以按照以下步骤操作来实现它。

  1. 打开 MainActivity.kt 文件。
  2. AffirmationsApp() 方法下创建一个名为 AffirmationCard() 的新方法,并使用 @Composable 注解对其进行注解。

MainActivity.kt

@Composable
fun AffirmationsApp() {
}

@Composable
fun AffirmationCard() {

}
  1. 修改方法签名,以接受 Affirmation 对象作为形参。Affirmation 对象来自 model 软件包。

MainActivity.kt

import com.example.affirmations.model.Affirmation

@Composable
fun AffirmationCard(affirmation: Affirmation) {

}
  1. 向签名添加 modifier 参数。将该参数的默认值设置为 Modifier

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {

}
  1. AffirmationCard 方法内,调用 Card 可组合项。传入 modifier 参数。

MainActivity.kt

import androidx.compose.material3.Card

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {

    }
}
  1. Card 可组合项内添加一个 Column 可组合项。Column 可组合项中包含的项会自行在界面中垂直排列。这样一来,您就可以在相关文本上方放置图片。相反,Row 可组合项会以水平方式排列其包含的项。

MainActivity.kt

import androidx.compose.foundation.layout.Column

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {

        }
    }

}
  1. Column 可组合项的 lambda 正文内添加 Image 可组合项。之前提到,Image 可组合项始终需要一项用于显示的资源和一个 contentDescription。该资源应该是传递给 painter 参数的 painterResourcepainterResource 方法会加载矢量可绘制对象或光栅化资源格式(例如 PNG)。另外,为 contentDescription 参数传递 stringResource

MainActivity.kt

import androidx.compose.foundation.Image
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
            )
        }
    }
}
  1. 除了 paintercontentDescription 参数之外,还应传递 modifiercontentScalecontentScale 用于决定图片应如何缩放和显示。Modifier 对象应设置 fillMaxWidth 属性,并将高度设置为 194.dpcontentScale 应为 ContentScale.Crop

MainActivity.kt

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.unit.dp
import androidx.compose.ui.layout.ContentScale

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
        }
    }
}
  1. Column 内,在 Image 可组合项之后创建一个 Text 可组合项。将 affirmation.stringResourceIdstringResource 传递给 text 形参,传递 padding 属性已设置为 16.dpModifier 对象,并通过将 MaterialTheme.typography.headlineSmall 传递给 style 形参来设置文本主题。

MainActivity.kt

import androidx.compose.material3.Text
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.platform.LocalContext

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
            Text(
                text = LocalContext.current.getString(affirmation.stringResourceId),
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.headlineSmall
            )
        }
    }
}

预览 AffirmationCard 可组合项

卡片是 Affirmations 应用界面的核心,通过您的辛勤努力,它已经创建好了!如要检查卡片能否正确显示,您可以创建一个无需启动整个应用即可预览的可组合项。

  1. 创建一个名为 AffirmationCardPreview() 的私有方法。使用 @Preview@Composable 为该方法添加注解。

MainActivity.kt

import androidx.compose.ui.tooling.preview.Preview

@Preview
@Composable
private fun AffirmationCardPreview() {

}
  1. 在该方法内,调用 AffirmationCard 可组合项,并向其传递一个新的 Affirmation 对象,同时 R.string.affirmation1 字符串资源和 R.drawable.image1 可绘制资源已传递到该对象的构造函数中。

MainActivity.kt

@Preview
@Composable
private fun AffirmationCardPreview() {
    AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
  1. 打开 Split 标签页,您将看到 AffirmationCard 的预览。如有必要,请点击 Design 窗格中的 Build & Refresh,使系统显示预览。

924a4df2c1db236c.png

创建列表

列表项组件是列表的构建块。创建列表项后,您可以利用它来创建列表组件。

  1. 创建一个名为 AffirmationList() 的函数,使用 @Composable 注解对其进行注解,并在方法签名中将 Affirmation 对象的 List 声明为形参。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>) {

}
  1. 在方法签名中将 modifier 对象声明为形参,并将默认值设为 Modifier

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {

}
  1. 在 Jetpack Compose 中,可以使用 LazyColumn 可组合项创建可滚动列表。LazyColumnColumn 之间的区别在于,当要显示的项数量较少时,应使用 Column,因为 Compose 会一次性加载所有项。Column 只能保存预定义或固定数量的可组合项。LazyColumn 可以按需添加内容,因此非常适合较长的列表,尤其是当列表长度未知时。LazyColumn 还提供默认滚动行为,无需添加其他代码。在 AffirmationList() 函数内声明一个 LazyColumn 可组合项。将 modifier 对象作为参数传递给 LazyColumn

MainActivity.kt

import androidx.compose.foundation.lazy.LazyColumn

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {

    }
}
  1. LazyColumn 的 lambda 正文中,调用 items() 方法并传入 affirmationListitems() 方法用于向 LazyColumn 添加项。此方法对该可组合项有些许独特,对于大多数可组合项来说并不常见。

MainActivity.kt

import androidx.compose.foundation.lazy.items

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) {

        }
    }
}
  1. 调用 items() 方法需要 lambda 函数。在该函数中,指定一个 affirmation 形参,用于表示 affirmationList 中的一个自我肯定话语项。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->

        }
    }
}
  1. 针对列表中的每个自我肯定话语,调用 AffirmationCard() 可组合项。向其传递 affirmation,以及一个将 padding 属性设置为 8.dpModifier 对象。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->
            AffirmationCard(
                affirmation = affirmation,
                modifier = Modifier.padding(8.dp)
            )
        }
    }
}

显示列表

  1. AffirmationsApp 可组合项中,检索当前的布局方向并将其保存在变量中。它们将用于稍后配置内边距。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
}
  1. 现在,创建一个 Surface 可组合项。此可组合项将设置 AffirmationsList 可组合项的内边距。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface() {
    }
}
  1. Modifier 传递给 Surface 可组合项,该可组合项可填充其父项的最大宽度和高度、设置状态栏内边距,以及将开头和结尾内边距设置为 layoutDirection。以下示例展示了如何将 LayoutDirection 对象转换为内边距:WindowInsets.safeDrawing.asPaddingValues().calculateStartPadding(layoutDirection)

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
    }
}
  1. Surface 可组合项的 lambda 中,调用 AffirmationList 可组合项,并将 DataSource().loadAffirmations() 传递给 affirmationList 参数。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
        AffirmationsList(
            affirmationList = Datasource().loadAffirmations(),
        )
    }
}

在设备或模拟器上运行 Affirmations 应用,并查看最终效果!

286f5132aa155fa6.png

4. 获取解决方案代码

如需下载完成后的 Codelab 代码,您可以使用以下 Git 命令:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-affirmations.git
$ cd basic-android-kotlin-compose-training-affirmations
$ git checkout intermediate

或者,您也可以下载 ZIP 文件形式的代码库,将其解压缩并在 Android Studio 中打开。

如果您想查看解决方案代码,请前往 GitHub 查看

5. 总结

现在,您已了解如何使用 Jetpack Compose 创建卡片、列表项和可滚动列表!请注意,这些只是用于创建列表的基本工具。您可以尽情挥洒创意,按自己的喜好自定义列表项!

总结

  • 使用 Card 可组合项创建列表项。
  • 修改 Card 可组合项中包含的界面。
  • 使用 LazyColumn 可组合项创建可滚动列表。
  • 使用自定义列表项构建列表。