在您的 Android 应用中添加图像

1. 开始之前

在本 Codelab 中,您将学习如何使用 Image 可组合项将图像添加到您的应用中。

先决条件

  • 了解如何在 Android Studio 中创建和运行应用的基本知识。
  • 了解如何添加 UI 元素(例如文本可组合项)的基本知识。

您将学习的内容

  • 如何将图像或照片添加到您的 Android 应用中。
  • 如何使用 Image 可组合项在您的应用中显示图像。
  • 使用 String 资源的最佳实践。

您将构建的内容

  • 增强 **生日快乐** 应用以包含图像。

您需要的内容

2. 设置您的应用

在 Android Studio 中打开您在上一个 Codelab 中的生日快乐项目。

运行应用时,它应该看起来像此屏幕截图。

2ff181d48325023c.png

将图像添加到您的项目

在此任务中,您将从互联网下载图像并将其添加到 **生日快乐** 应用中。

  1. 从此 链接 打开生日卡片应用的图像。
  2. 点击 **下载**。

1d731e32164fca8a.png

  1. 右键点击图像,然后将文件保存到您的计算机上,名为 androidparty.png
  2. 记下您保存图像的位置。

例如,您可能将其保存在 **下载** 文件夹中。

  1. 在 Android Studio 中,点击 **查看 > 工具窗口 > 资源管理器** 或点击 **项目** 窗口旁边的 **资源管理器** 选项卡。

318ae32952de3b49.png

2703cd334049774f.png

  1. 点击 **+(将资源添加到模块)> 导入可绘制对象**。

41054199d5299d08.png

  1. 在文件浏览器中,选择您下载的图像文件,然后点击 **打开**。

此操作将打开 **导入可绘制对象** 对话框。

727d06e96adc8b19.png

  1. Android Studio 将向您显示图像的预览。从 **限定符类型** 下拉列表中选择 **密度**。您将在后面的部分中了解为什么要这样做。

c8e37d10f3afb21d.png

  1. 从 **值** 列表中选择 **无密度**。

a8d0554a56c5a6e7.png

Android 设备具有不同的屏幕尺寸(包括手机、平板电脑和电视等),它们的屏幕也有不同的像素尺寸。也就是说,一台设备每平方英寸有 160 个像素,而另一台设备在相同空间内可以容纳 480 个像素。如果您不考虑这些像素密度的变化,系统可能会缩放您的图像,这会导致图像模糊,或占用过多内存的大图像,或尺寸不合适的图像。

当您调整尺寸大于 Android 系统可以处理的图像时,会抛出内存不足错误。对于照片和背景图像(例如当前图像 androidparty.png),您应将其放置在 drawable-nodpi 文件夹中,这将阻止调整大小行为。

有关像素密度的更多信息,请参阅 支持不同的像素密度

  1. 点击 **下一步**。
  2. Android Studio 将向您显示图像将被放置的文件夹结构。注意 drawable-nodpi 文件夹。
  3. 点击 **导入(C)**。

6fbeec4f4d4fa984.png

Android Studio 将创建一个 drawable-nodpi 文件夹并将您的图像放置在其中。在 Android Studio 项目视图中,资源名称显示为 androidparty.png (nodpi)。在计算机文件系统中,Android Studio 会创建一个名为 drawable-nodpi 的文件夹。

It is placed under the folder drawable no dpi

如果图像成功导入,Android Studio 会将图像添加到 **可绘制对象** 选项卡下的列表中。此列表包含您应用的所有图像和图标。现在您可以在应用中使用此图像。

305e34085badab89.png

  1. 切换回项目视图,点击 **查看 > 工具窗口 > 项目** 或点击最左侧的 **项目** 选项卡。
  2. 点击 **app > res > drawable** 以确认图像位于 drawable 文件夹中。

9ace033108aa748a.png

3. 添加 Image 可组合项

要在您的应用中显示图像,它需要一个可以显示的位置。就像您使用 Text 可组合项来显示文本一样,您可以使用 Image 可组合项来显示图像。

在此任务中,您将 Image 可组合项添加到您的应用中,将其图像设置为您下载的图像,定位它并调整其大小以使其充满屏幕。

添加一个可组合函数以添加图像

  1. MainActivity.kt 文件中,在 GreetingText() 函数之后添加一个 GreetingImage() 可组合函数。
  2. GreetingImage() 函数传递两个 String 参数:一个名为 message 的参数用于生日祝福,另一个名为 from 的参数用于您的签名。
@Composable
fun GreetingImage(message: String, from: String) {
}
  1. 每个可组合函数都应接受一个可选的 Modifier 参数。修饰符告诉 UI 元素如何在父布局中进行布局、显示或操作。在 GreetingImage() 可组合项中添加另一个参数。
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
}

Jetpack Compose 中的资源

资源是代码使用的附加文件和静态内容,例如位图、用户界面字符串、动画说明等等。有关 Android 中资源的更多信息,请参阅 应用资源概述

您应该始终将应用资源(例如图像和字符串)与代码分离,以便您可以独立维护它们。在运行时,Android 会根据当前配置使用适当的资源。例如,您可能希望根据屏幕尺寸提供不同的 UI 布局,或根据语言设置提供不同的字符串。

分组资源

您应该始终将每种类型的资源放置在项目的 res/ 目录的特定子目录中。例如,以下是一个简单项目的文件夹层次结构

MyProject/
    src/
        MyActivity.kt
    res/
        drawable/
            graphic.png
        mipmap/
            icon.png
        values/
            strings.xml

如您在此示例中所见,res/ 目录包含所有子目录中的资源,其中包括用于图像资源的 drawable/ 目录,用于启动器图标的 mipmap/ 目录,以及用于字符串资源的 values/ 目录。要详细了解应用资源的使用、格式和语法,请参阅 资源类型概述

访问资源

Jetpack Compose 可以访问 Android 项目中定义的资源。可以使用在项目的 R 类中生成的资源 ID 来访问资源。

Android 自动生成 R 类,其中包含项目中所有资源的 ID。在大多数情况下,资源 ID 与文件名相同。例如,可以使用以下代码访问上一个文件夹层次结构中的图像

R.drawable.graphic

R is a autogenerated class drawable is a sub directory in res folder graphic is resource ID

在下一任务中,您将使用在上一个任务中添加的图像 **androidparty.png** 文件。

  1. GreetingImage() 函数中,声明一个 val 属性并将其命名为 image
  2. 通过传入 androidparty 资源,调用 painterResource() 函数。将返回值分配给 image 变量。
val image = painterResource(R.drawable.androidparty)

Android Studio 会突出显示 .painterResource 代码,因为您需要导入该函数才能编译您的应用。

c00b0257f932d39e.png

  1. 点击 Android Studio 突出显示的 .painterResource
  2. 在弹出窗口中点击 **导入** 以添加 androidx.compose.ui.res.painterResource 的导入。

painterResource() 函数加载可绘制图像资源,并接受资源 ID(本例中为 R.drawable.androidparty)作为参数。

  1. 在调用 painterResource() 函数之后,添加一个 Image 可组合项,然后将 image 作为命名参数传递给 painter
Image(
    painter = image
)

Android Studio 会突出显示 Image 代码,因为您需要导入该函数才能编译您的应用。

2922caef87be79f.png

要解决此警告,请在 MainActivity.kt 文件的顶部添加以下导入

import androidx.compose.foundation.Image

初始警告现在已解决,但如果您将鼠标悬停在单词 Image 上,Android Studio 会显示一条新的警告,指出“以下函数中没有一个可以调用提供的参数”。这是因为提供的参数与任何 Image 函数签名都不匹配。

8b7c2d29c614414f.png

此警告将在下一部分中解决。

检查您的应用的可访问性

遵循可访问性编码实践时,您将让所有用户(包括有 残疾 的用户)更轻松地浏览和交互您的应用。

Android Studio 提供提示和警告以帮助您使应用更易于访问。内容描述定义了 UI 元素的用途,这使得您的应用更易于使用 TalkBack。

但是,此应用中的图像仅用于装饰目的。为图像添加内容描述将使在此特定情况下使用 TalkBack 变得更加困难。您不是设置向用户宣布的内容描述,而是可以将图像的 contentDescription 参数设置为 null,以便 TalkBack 跳过 Image 可组合项。

  • Image 可组合项中,添加另一个名为 contentDescription 的命名参数,并将其值设置为 null
Image(
    painter = image,
    contentDescription = null
)

预览 Image 可组合项

在本任务中,您将预览图像可组合项并在模拟器或设备上运行应用程序。

  1. BirthdayCardPreview() 函数中,用 GreetingImage() 函数调用替换 GreetingText() 函数调用。

您的函数应如下面的代码片段所示

@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        GreetingImage(
            message = "Happy Birthday Sam!",
            from = "From Emma"
        )
    }
}
  1. **设计** 面板应自动更新,如果未更新,请单击 609ccb451d05cf6b.png 进行构建。

请注意,您现在无法看到文本,因为新函数仅包含 Image 可组合项,而不包含 Text 可组合项。

acd47e25eb2a8d55.png

4. 添加 Box 布局

Compose 中的三个基本标准布局元素是 ColumnRowBox 可组合项。您已在之前的 Codelab 中了解了 ColumnRow 可组合项,现在您将进一步了解 Box 可组合项。

Box 布局是 Compose 中的标准布局元素之一。使用 Box 布局将元素堆叠在一起。 Box 布局还允许您配置其包含元素的特定对齐方式。

4d191637aaecf374.png

  1. GreetingImage() 函数中,在 Image 可组合项周围添加 Box 可组合项,如下所示
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
    val image = painterResource(R.drawable.androidparty)
    Box {
        Image(
            painter = image,
            contentDescription = null
        )
    }
}
  1. 在 Android Studio 提示时,导入 androidx.compose.foundation.layout.Box 函数。
  2. 添加代码以将 modifier 参数传递给 Box 可组合项。
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
    val image = painterResource(R.drawable.androidparty)
    Box(modifier) {
        Image(
            painter = image,
            contentDescription = null
        )
    }
}
  1. Box 可组合项的末尾,调用 GreetingText() 函数,并将生日消息、签名和修饰符传递给它,如下所示
@Composable
fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
    val image = painterResource(R.drawable.androidparty)
    Box(modifier) {
        Image(
            painter = image,
            contentDescription = null
        )
        GreetingText(
            message = message,
            from = from,
            modifier = Modifier
                .fillMaxSize()
                .padding(8.dp)
        )
    }
}
  1. 请注意 **设计** 面板中更新的预览。

您应该看到文本和图像。

The background image is anchored to the top

  1. 要使上述更改反映在模拟器或设备中,请在 onCreate() 函数中,用 GreetingImage() 函数调用替换 GreetingText() 函数调用。

您的 setContent 块应如下面的代码片段所示

setContent {
    HappyBirthdayTheme {
        // A surface container using the 'background' color from the theme
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            GreetingImage(
                message = "Happy Birthday Sam!",
                from = "From Emma"
            )
        }
    }
}

请注意,图像与屏幕一样宽,但图像锚定到屏幕顶部。屏幕底部有空白区域,看起来不太美观。在下一项任务中,您将填充屏幕的宽度和高度,并将图像缩放到充满整个屏幕。

5. 更改不透明度并缩放图像

在本任务中,您将使图像全屏显示以美化您的应用。为此,您将使用 ContentScale 参数。

缩放内容

您已将图像添加到应用并定位了图像。现在,您需要调整图像的缩放类型(即如何调整图像大小),以使其全屏显示。

有许多 ContentScale 类型 可用。您将使用 ContentScale.Crop 参数缩放,该缩放会均匀地缩放图像以保持纵横比,以便图像的宽度和高度等于或大于屏幕的相应尺寸。

  1. 将名为 ContentScale 的参数添加到图像。
Image(
    painter = image,
    contentDescription = null,
    contentScale = ContentScale.Crop
)
  1. 在 Android Studio 提示时,导入 androidx.compose.ui.layout.ContentScale 接口。
  2. 查看 **设计** 面板。

图像现在应该像在屏幕截图中看到的那样充满整个预览屏幕

ae1a5ec6b294f466.png

更改不透明度

要改善应用的对比度,请更改背景图像的不透明度。

alpha 参数添加到 Image 可组合项,并将其设置为 0.5F

Image(
    painter = image,
    contentDescription = null,
    contentScale = ContentScale.Crop,
    alpha = 0.5F
)

请注意图像不透明度的变化。

这么多代码!现在该预览一下您的辛勤成果了。

运行应用

在设备或模拟器上运行应用。

9d1416521733e8c.png

恭喜您成功创建全屏图像和文本消息。您还更改了图像的不透明度。

布局修饰符

修饰符用于装饰 Jetpack Compose UI 元素或添加行为。例如,您可以向行、文本或按钮添加背景、填充或行为。要设置它们,可组合项或布局需要接受一个修饰符作为参数。

在之前的 Codelab 中,您了解了修饰符,并使用了填充修饰符 (Modifier.padding) 向 Text 可组合项周围添加空间。修饰符的功能非常强大,您将在本路径和即将发布的路径中看到这一点。

例如,此 Text 可组合项有一个 Modifier 参数,用于将背景颜色更改为绿色。

// Example
Text(
    text = "Hello, World!",
    // Solid element background color
    modifier = Modifier.background(color = Color.Green)
)

类似于上面的示例,您可以向布局添加修饰符,以使用排列和对齐属性定位子元素。

要设置子元素在 Row 中的位置,请设置 horizontalArrangementverticalAlignment 参数。对于 Column,请设置 verticalArrangementhorizontalAlignment 参数。

排列属性用于在布局大小大于其子元素大小之和时排列子元素。

例如:当 Column 的大小大于其子元素大小之和时,可以指定 verticalArrangement 来定义子元素在 Column 中的位置。以下是不同垂直排列的说明

equal height, space between, space around, space evenly, top, center and bottom

类似地,当 Row 的大小大于其子元素大小之和时,可以指定 horizontalArrangement 来定义子元素在 Row 中的位置。以下是不同水平排列的说明

equal weight, space between, space around, space evenly, end, center and start

对齐属性用于在布局的开头、中心或结尾处对齐子元素。

6. 对齐和排列文本

在本任务中,您将观察在之前的 Codelab 中添加的代码,以排列应用中的文本。

  1. MainActivity.kt 文件中,滚动到 GreetingText() 函数。列中的 verticalArrangement 属性设置为 Arrangement.Center。因此,文本内容将居中显示在屏幕上。
@Composable
fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
    Column(
        verticalArrangement = Arrangement.Center,
        modifier = modifier
    ) {
        Text(
            text = message,
            fontSize = 100.sp,
            lineHeight = 116.sp,
            textAlign = TextAlign.Center
        )
        Text(
            text = from,
            fontSize = 36.sp,
            modifier = Modifier
                .padding(16.dp)
                .align(alignment = Alignment.End)
        )
    }
}

填充

UI 元素会围绕其内容进行包装。为了防止它包装得太紧,您可以指定每一侧的填充量。

Text composable without padding

Text composable with padding

填充用作修饰符,这意味着您可以将其应用于任何可组合项。对于可组合项的每一侧,padding 修饰符都接受一个可选参数,该参数定义填充量。

Diagram shows top start bottom and end padding

// This is an example.
Modifier.padding(
    start = 16.dp,
    top = 16.dp,
    end = 16.dp,
    bottom = 16.dp
)
  1. 轮到您了!在 MainActivity.kt 文件中,滚动到调用 GreetingText() 函数的位置,并注意 padding 属性。
modifier = Modifier
    .fillMaxSize()
    .padding(8.dp)
  1. 类似地,请注意在 GreetingText() 函数内部,签名 Text 可组合项的填充。
modifier = Modifier
    .padding(16.dp)
    .align(alignment = Alignment.End)

7. 采用良好的代码实践

翻译

编写应用时,请务必记住,它们可能在某个时候被翻译成另一种语言。正如您在之前的 Codelab 中了解到的那样,String 数据类型是一系列字符,例如 "Happy Birthday Sam!"

硬编码字符串是在应用代码中直接编写的字符串。硬编码字符串使将应用翻译成其他语言以及在应用的不同位置重复使用字符串变得更加困难。您可以将字符串提取到资源文件中以解决这些问题。不要在代码中硬编码字符串,而是将字符串放在文件中,命名字符串资源,并在需要使用字符串时使用这些名称。即使更改字符串或将其翻译成另一种语言,名称也保持不变。

  1. MainActivity.kt 文件中,滚动到 onCreate() 函数。选择生日问候语 Happy Birthday Sam! 字符串(不带引号)。
  2. 单击屏幕左侧的灯泡。
  3. 选择 **提取字符串资源**。

bd8451ea9a2aee25.png

Android Studio 将打开 **提取资源** 对话框。在此对话框中,您可以自定义字符串资源的名称以及有关如何存储它的某些详细信息。**资源名称** 字段是您输入字符串名称的位置。**资源值** 字段是您输入实际字符串的位置。

  1. 在 **提取资源** 对话框中,将 **资源名称** 更改为 happy_birthday_text

字符串资源应使用小写名称,多个单词应使用下划线分隔。保留其他设置的默认值。

c110d39102e88e4.png

  1. 单击 **确定**。
  2. 请注意代码的变化。

硬编码字符串现在被对 getString() 函数的调用替换。

GreetingImage(
    message = getString(R.string.happy_birthday_text),
    from = "From Emma",
    modifier = Modifier.padding(8.dp)
)
  1. 在 **项目** 面板中,从路径 **app > res > values > strings.xml** 打开 **strings.xml** 文件,并注意 Android Studio 创建了一个名为 happy_birthday_text 的字符串资源。
<resources>
    <string name="app_name">Happy Birthday</string>
    <string name="happy_birthday_text">Happy Birthday Sam!</string>
</resources>

**strings.xml** 文件包含用户在您的应用中看到的字符串列表。请注意,您的应用名称也是字符串资源。通过将所有字符串放在一个地方,您可以更轻松地翻译应用中的所有文本,并更轻松地在应用的不同部分重复使用字符串。

  1. 按照相同的步骤提取签名 Text 可组合项的文本,但这次在 **资源名称** 字段中输入 signature_text

完成后的文件应如下面的代码片段所示

<resources>
    <string name="app_name">Happy Birthday</string>
    <string name="happy_birthday_text">Happy Birthday Sam!</string>
    <string name="signature_text">From Emma</string>
</resources>
  1. 更新 BirthdayCardPreview() 以使用 stringResource() 和提取的字符串。
@Preview(showBackground = true)
@Composable
fun BirthdayCardPreview() {
    HappyBirthdayTheme {
        GreetingImage(
            message = stringResource(R.string.happy_birthday_text),
            from = stringResource(R.string.signature_text)
        )
    }
}
  1. 再次运行您的应用以确保它仍然有效。

8. 尝试此挑战

恭喜您将图像添加到您的应用中。这是一个挑战,供您参考

  1. 将签名文本可组合项排列或对齐,使其与屏幕中心对齐。

您的应用应该看起来像这样

b681900fe13e5598.png

以下是供您参考的 GreetingText() 函数的解决方案代码

@Composable
fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
    Column(
        verticalArrangement = Arrangement.Center,
        modifier = modifier
    ) {
        Text(
            text = message,
            fontSize = 100.sp,
            lineHeight = 116.sp,
            textAlign = TextAlign.Center
        )
        Text(
            text = from,
            fontSize = 36.sp,
            modifier = Modifier
                .padding(16.dp)
                .align(alignment = Alignment.CenterHorizontally)
        )
    }
}

9. 获取解决方案代码

**生日快乐** 应用的解决方案代码位于 GitHub 上。

GitHub 是一种服务,允许开发人员管理其软件项目的代码。它使用 Git,这是一种版本控制系统,可以跟踪对每个版本代码所做的更改。如果您曾经在 Google Docs 中看到过文档的版本历史记录,您就可以看到进行了哪些编辑以及编辑时间。同样,您可以跟踪项目中代码的版本历史记录。这在您独自或与团队一起进行项目时非常有用。

GitHub 还提供一个网站,允许您查看和管理您的项目。此 GitHub 链接允许您在线浏览 **生日快乐** 项目文件或将它们下载到您的计算机上。

要下载已完成的代码实验室的代码,可以使用以下 git 命令

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-birthday-card-app.git

或者,您可以将存储库下载为 zip 文件,解压缩它,然后在 Android Studio 中打开它。

如果您想查看解决方案代码,请 在 GitHub 上查看

GitHub 中的分支

在您了解分支是什么之前,请先了解什么是存储库或 repo。存储库是您在计算机上克隆(复制)的整个项目(目录和文件)。分支是存储库的版本,换句话说,它是独立的开发线路。例如,在本课程中,starter 分支可能是您在代码实验室期间用于构建的项目的版本。mainsolution 分支是代码实验室结束时项目的版本,包含完整的解决方案代码。

存储库可以包含多个分支,这意味着存储库中存在多个版本的代码。

10. 结论

您已将图像添加到您的 **生日快乐** 应用中,使用修饰符对齐文本,遵循可访问性指南,并使其更易于翻译成其他语言!更重要的是,您已完成创建自己的 **生日快乐** 应用!在社交媒体上分享您的作品,并使用标签 **#AndroidBasics** 以便我们能看到它!

摘要

  • Android Studio 中的 **资源管理器** 选项卡可帮助您添加和组织图像和其他资源。
  • Image 可组合项是一种在您的应用中显示图像的 UI 元素。
  • Image 可组合项应该有一个内容描述,以使您的应用更易于访问。
  • 显示给用户的文本(例如生日问候语)应提取到字符串资源中,以使其更易于将您的应用翻译成其他语言。

了解更多