在您的 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 来访问资源。

一个 R 类是由 Android 自动生成的类,其中包含项目中所有资源的 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

更改不透明度

为了提高应用的对比度,请更改背景图像的不透明度。

Image 可组合项添加 alpha 参数并将其设置为 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() 函数的位置,并注意填充属性。
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 链接允许您在线浏览“生日快乐”项目文件或将它们下载到您的计算机上。

要下载完成的 codelab 代码,可以使用以下 git 命令

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

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

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

GitHub 中的分支

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

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

10. 结论

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

摘要

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

了解更多