向 Dice Roller 应用添加图片

1. 准备工作

在此 Codelab 中,您将向现有 Dice Roller Android 应用添加骰子图片。请务必先完成之前关于构建 Dice Roller 应用基础的 Codelab。

您的应用不会在 TextView 中显示掷骰子的结果值,而会显示掷出的骰面数字的相应骰子图片。这样会让您的应用更加直观,并增强用户体验。

c7f0d42525da7431.png

我们将为您提供骰子图片的下载链接,您可以将其作为资源添加到应用中。为了编写要使用的骰子图片的代码,您需要使用 Kotlin 中的 when 语句。

前提条件

  • 完成了“创建交互式 Dice Roller 应用”Codelab。
  • 能够编写控制流语句(if / elsewhen 语句)。
  • 能够根据用户输入更新应用界面(修改 MainActivity.kt 文件)。
  • 能够向 Button. 添加点击事件监听器。
  • 能够向 Android 应用添加图片资源。

学习内容

  • 如何在应用运行时更新 ImageView
  • 如何(使用 when 语句)根据不同的条件自定义应用行为。

构建内容

  • 具有 Button 的 Dice Roller Android 应用,可用于掷骰子并更新屏幕上的图片。

所需工具

  • 一台安装了 Android Studio 的计算机。
  • 互联网连接,以便下载骰子图片。

2. 更新应用的布局

在此任务中,您会将布局中的 TextView 替换为显示掷骰子结果图片的 ImageView

打开 Dice Roller 应用

  1. 在 Android Studio 中打开之前 Codelab 中创建的 Dice Roller 应用并运行应用。您可以使用解决方案代码或您创建的代码。

应用应如下所示。

2e8416293e597725.png

  1. 打开 activity_main.xml (app > res > layout > activity_main.xml)。系统随即会打开布局编辑器

删除 TextView

  1. 布局编辑器中,选择 Component Tree 中的 TextView

a6fc189dac34ee71.png

  1. 右键点击并选择 Delete,或按 Delete 键。
  2. 请暂时忽略 Button 上的警告。您将在接下来的步骤中解决该问题。

向布局中添加 ImageView

  1. ImageViewPalette 拖动到 Design 视图上,并将其放置在 Button 上方。

91f6e2be0a01fbf.png

  1. Pick a Resource 对话框中,选择 Sample data 中的 avatars。在下一任务中添加骰子图片之前,将使用此临时图片。

824493e9927da401.png

  1. 点击 OK。应用的 Design 视图应如下所示。

f9d5ee87018baee.png

  1. Component Tree 中,您会发现两个错误。Button 不受垂直约束,而 ImageView 既不受垂直约束,也不受水平约束。

b8c3b83124c31ff.png

Button 不受垂直约束,是因为您移除了原先位于其上方的 TextView。现在,您需要将 ImageViewButton 放置到其下方。

确定 ImageView 和 Button 的位置

无论 Button 在哪个位置,您都需要让 ImageView 在屏幕上垂直居中。

  1. ImageView 添加水平约束条件。将 ImageView 的左侧与父级 ConstraintLayout 的左边缘连接起来。
  2. ImageView 的右侧与父级的右边缘连接起来。这会使 ImageView 在父级内水平居中。

9848bb6319e11777.png

  1. ImageView 添加垂直约束条件,将 ImageView 的顶部与父级的顶部连接起来。ImageView 会向上滑动到 ConstraintLayout 的顶部。

2d8d134e6292d48f.png

  1. Button 添加垂直约束条件,将 Button 的顶部与 ImageView 的底部连接起来。Button 会向上滑动到 ImageView 的下方。

b6d3dcee6c7a51fc.png

  1. 现在,再次选择 ImageView,然后添加垂直约束条件,将 ImageView 的底部与父级的底部连接起来。这会使 ImageViewConstraintLayout 内垂直居中。

现在,有关约束条件的所有警告都会消失。

完成上述所有操作后,Design 视图应如下所示,ImageView 居中,Button 位于其正下方。

1b05a6d2fd56459f.png

您可能会在 Component Tree 中的 ImageView 上看到一条警告,提示您向 ImageView 添加内容说明。现在不必担心这条警告,因为稍后在 Codelab 中,您将根据所显示的骰子图片设置 ImageView 的内容说明。您将在 Kotlin 代码中进行此更改。

3. 添加骰子图片

在此任务中,您将下载一些骰子图片,并将其添加到应用中。

下载骰子图片

  1. 打开此网址,将包含骰子图片的 ZIP 文件下载到您的计算机。等待下载完成。
  2. 在计算机上找到该文件(可能在 Downloads 文件夹中)。
  3. 双击 ZIP 文件进行解压缩。这将新建一个 dice_images 文件夹,其中包含 6 个骰子图片文件,分别显示从 1 到 6 的骰子值。

43c95351759ada02.png

向应用中添加骰子图片

  1. 在 Android Studio 中,依次点击菜单中的 View > Tool Windows > Resource Manager,或者点击 Project 窗口左侧的 Resource Manager 标签页。
  2. 点击 Resource Manager 中左上角的 +,然后选择 Import Drawables。系统随即会打开一个文件浏览器。

67186ea5d631bc8a.png

  1. 查找并选中上述 6 个骰子图片文件。您可以先选中第一个骰子图片,然后在按住 Shift 键的同时选中其他图片。
  2. 点击 Open
  3. 点击 Next,然后点击 Import,以确认您要导入这 6 项资源。

a45dff94a19e2722.png

a7ad66d623ac73c2.png

  1. 如果文件已成功导入,那么应用的 Resource Manager (app>res>drawable) 中会显示这 6 张图片。

ab68f82b385fc83e.png

非常棒!在下一个任务中,您将在应用中使用这些图片。

重要提示!- 您将能够在 Kotlin 代码中通过这些图片的资源 ID 查看它们:

  • R.drawable.dice_1
  • R.drawable.dice_2
  • R.drawable.dice_3
  • R.drawable.dice_4
  • R.drawable.dice_5
  • R.drawable.dice_6

4. 使用骰子图片

替换示例头像

  1. Design Editor 中,选择 ImageView
  2. Declared Attributes 部分的 Attributes 中,找到设置为头像图片的工具 srcCompat 属性。

请注意,工具 srcCompat 属性仅在 Android Studio 的 Design 视图中使用提供的图片。图片仅在您构建应用时向开发者显示,但您在模拟器或设备上实际运行应用时不会看到该图片。

  1. 点击头像的小预览图。系统会打开一个对话框,供您选择要用于此 ImageView 的新资源。

d8a26941179b3bdf.png

  1. 选择 dice_1 可绘制对象,然后点击 OK

哇!ImageView 占据了整个屏幕。

1072e9fdd637afd9.png

接下来,您将调整 ImageView 的宽度和高度,以确保不会遮挡 Button

  1. Constraints Widget 下的 Attributes 窗口中,找到 layout_widthlayout_height 属性。它们当前设置为 wrap_content,这意味着 ImageView 的高度和宽度将与其中的内容(源图片)保持一致。
  2. ImageView 设置 160dp 的固定宽度和 200dp 的固定高度。按 Enter 键。

现在 ImageView 的尺寸小了很多。

9579582d8775e688.png

您可能会发现,Button 与图片的距离过近。

  1. Constraint Widget 中将按钮的上边距设置为 16dp。

8c647d6ae28ef3a6.png

Design 视图更新后,应用看起来更美观了!

b53f7379bfba8c27.png

点击按钮时改变骰子图片

虽然已更正布局,但需要更新 MainActivity 类才能使用骰子图片。

MainActivity.kt 文件中目前存在一个应用错误。如果您尝试运行该应用,会看到以下构建错误:

aaecce207cb5fc7.png

这是因为您的代码仍然在引用您已从布局中删除的 TextView

  1. 打开 MainActivity.kt (app > java > com.example.diceroller > MainActivity.kt)

代码引用了 R.id.textView,但 Android Studio 无法识别它。

3a923aa53fc3ba8a.png

  1. rollDice() 方法中,选择任何引用 TextView 的代码并将其删除。
// Update the TextView with the dice roll
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = dice.roll().toString()
  1. 还是在 rollDice() 中,创建一个类型为 ImageView 的新变量 diceImage。将其设置为与布局中的 ImageView 相同。使用 findViewById() 方法,并将 ImageView 的资源 ID R.id.imageView 作为输入参数传入。
val diceImage: ImageView = findViewById(R.id.imageView)

如果您想知道如何确定 ImageView 的确切资源 ID,请查看 Attributes 窗口顶部的 id

cbfc9d5e01a04e32.png

在 Kotlin 代码中引用此资源 ID 时,请确保输入的内容完全相同(小写的 i,大写的 V,没有空格)。否则,Android Studio 会显示错误。

  1. 添加以下代码行,以测试点击按钮时是否可以正确更新 ImageView。掷出的骰子并不总是“2”,我们只是使用 dice_2 图片进行测试。
diceImage.setImageResource(R.drawable.dice_2)

该代码会对 ImageView 调用 setImageResource() 方法,传递 dice_2 图片的资源 ID。这将更新屏幕上的 ImageView 以显示 dice_2 图片。

rollDice() 方法现在应如下所示:

private fun rollDice() {
    val dice = Dice(6)
    val diceRoll = dice.roll()
    val diceImage: ImageView = findViewById(R.id.imageView)
    diceImage.setImageResource(R.drawable.dice_2)
}
  1. 运行应用以验证它是否正常运行而不出现错误。启动应用时应显示空白屏幕(只有 Roll 按钮)。

c29b50554a31d30f.png

点按按钮后,会出现显示值 2 的骰子图片。太好了!!

7df72d671b22853f.png

您可以根据按钮点按操作来改变图片了!您离目标更近了!

5. 根据掷骰子结果显示正确的骰子图片

显然,骰子结果并不总是 2。利用在针对不同的掷骰子结果添加条件行为 Codelab 中学到的控制流逻辑,以便根据掷骰子的随机结果在屏幕上显示适当的骰子图片。

在开始输入代码之前,请先编写一些伪代码来描述应该发生的情况,从概念上思考应用的行为方式。例如:

如果用户掷出 1,就显示 dice_1 图片。

如果用户掷出 2,就显示 dice_2 图片。

等等...

在 Kotlin 中,可以根据掷骰子的结果值,使用 if / else 语句编写上述伪代码。

if (diceRoll == 1) {
   diceImage.setImageResource(R.drawable.dice_1)
} else if (diceRoll == 2) {
   diceImage.setImageResource(R.drawable.dice_2)
}
 ...

但针对每一种情况编写 if / else 会做大量重复工作。使用 when 语句可以简单地表达相同的逻辑。它更简洁(使用的代码更少)!请在您的应用中使用此方法。

when (diceRoll) {
   1 -> diceImage.setImageResource(R.drawable.dice_1)
   2 -> diceImage.setImageResource(R.drawable.dice_2)
   ...

更新 rollDice() 方法

  1. rollDice() 方法中,删除每次将图片资源 ID 设置为 dice_2 图片的代码行。
diceImage.setImageResource(R.drawable.dice_2)
  1. 将其替换为 when 语句,该语句可根据 ImageView 值更新 diceRoll
   when (diceRoll) {
       1 -> diceImage.setImageResource(R.drawable.dice_1)
       2 -> diceImage.setImageResource(R.drawable.dice_2)
       3 -> diceImage.setImageResource(R.drawable.dice_3)
       4 -> diceImage.setImageResource(R.drawable.dice_4)
       5 -> diceImage.setImageResource(R.drawable.dice_5)
       6 -> diceImage.setImageResource(R.drawable.dice_6)
   }

完成更改后,rollDice() 方法应如下所示。

private fun rollDice() {
   val dice = Dice(6)
   val diceRoll = dice.roll()

   val diceImage: ImageView = findViewById(R.id.imageView)

   when (diceRoll) {
       1 -> diceImage.setImageResource(R.drawable.dice_1)
       2 -> diceImage.setImageResource(R.drawable.dice_2)
       3 -> diceImage.setImageResource(R.drawable.dice_3)
       4 -> diceImage.setImageResource(R.drawable.dice_4)
       5 -> diceImage.setImageResource(R.drawable.dice_5)
       6 -> diceImage.setImageResource(R.drawable.dice_6)
   }
}
  1. 运行应用。点击 Roll 按钮会将骰子图片更改为除 2 以外的值。成功啦!

ec209952f84b81bd.png 32fc8979b1984e00.png

优化您的代码

如果要编写更简洁的代码,可以进行以下代码更改。这不会对您的应用用户产生任何明显影响,但会缩短代码,减少重复。

您可能已经注意到,在 when 语句中对 diceImage.setImageResource() 调用了 6 次。

when (diceRoll) {
    1 -> diceImage.setImageResource(R.drawable.dice_1)
    2 -> diceImage.setImageResource(R.drawable.dice_2)
    3 -> diceImage.setImageResource(R.drawable.dice_3)
    4 -> diceImage.setImageResource(R.drawable.dice_4)
    5 -> diceImage.setImageResource(R.drawable.dice_5)
    6 -> diceImage.setImageResource(R.drawable.dice_6)
}

每种情况之间唯一改变的是所使用的资源 ID。这意味着您可以创建一个变量,用于存储要使用的资源 ID。然后,在代码中只调用 diceImage.setImageResource() 一次并传递正确的资源 ID 即可。

  1. 将上述代码替换为以下代码。
val drawableResource = when (diceRoll) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   6 -> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)

这里运用的新概念是,when 表达式实际上可以返回一个值。使用此新代码段后,when 表达式会返回正确的资源 ID,该 ID 将存储在 drawableResource 变量中。然后,您可以使用该变量更新显示的图片资源。

  1. 请注意,when 现在带红色的下划线。如果将指针悬停在它的上面,您会看到一条错误消息:‘when' expression must be exhaustive, add necessary ‘else' branch

出现该错误的原因是 when 表达式的值被赋给 drawableResource,因此 when 必须涵盖所有情况,它必须处理所有可能的情况,这样,即使您更改为 12 面的骰子,也始终会返回值。Android Studio 建议添加一个 else 分支。您可以通过将 6 的情况更改为 else 来解决此问题。从 15 的情况不变,但包括 6 在内的所有其他情况都通过 else 处理。

val drawableResource = when (diceRoll) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   else -> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)
  1. 运行应用以确保它仍然可以正常运行。请务必进行充分的测试,以确保看到带有骰子图片 1 到 6 的所有数字。

在 ImageView 上设置适当的内容说明

现在,您已用图片替换掷出的数字,屏幕阅读器无法再判断掷出的数字了。为修复此问题,更新图片资源后,请更新 ImageView 的内容说明。内容说明应为 ImageView 中显示的文字说明,以便屏幕阅读器可以对其进行描述。

diceImage.contentDescription = diceRoll.toString()

屏幕阅读器可以朗读此内容说明,因此如果屏幕上显示掷骰子结果为“6”的骰子图片,屏幕阅读器就可以朗读“6”的内容说明。

6. 采用规范的编码做法

打造更实用的启动体验

当用户首次打开应用时,应用是空的(只显示 Roll 按钮),这看起来很奇怪。用户可能不知道会发生什么,因此,当您首次启动应用并创建 Activity 时,请更改界面以显示随机骰子。这样用户就更有可能知道点按 Roll 按钮会掷骰子。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val rollButton: Button = findViewById(R.id.button)
    rollButton.setOnClickListener { rollDice() }

    // Do a dice roll when the app starts
    rollDice()
}

ec209952f84b81bd.png

添加代码注释

在代码中添加一些注释,以描述您编写的代码会执行什么操作。

完成所有这些更改后,您的 rollDice() 方法可能如下所示。

/**
* Roll the dice and update the screen with the result.
*/
private fun rollDice() {
    // Create new Dice object with 6 sides and roll the dice
    val dice = Dice(6)
    val diceRoll = dice.roll()

    // Find the ImageView in the layout
    val diceImage: ImageView = findViewById(R.id.imageView)

    // Determine which drawable resource ID to use based on the dice roll
    val drawableResource = when (diceRoll) {
        1 -> R.drawable.dice_1
        2 -> R.drawable.dice_2
        3 -> R.drawable.dice_3
        4 -> R.drawable.dice_4
        5 -> R.drawable.dice_5
        else -> R.drawable.dice_6
    }

    // Update the ImageView with the correct drawable resource ID
    diceImage.setImageResource(drawableResource)

    // Update the content description
    diceImage.contentDescription = diceRoll.toString()
}

如需完整的 MainActivity.kt 文件,请参阅下一步中链接的 GitHub 上的解决方案代码。

恭喜您完成 Dice Roller 应用的创建!现在,您可以与好友一起玩这款游戏,乐享下一个游戏之夜!

7. 解决方案代码

此 Codelab 的解决方案代码位于下方所示的项目和模块中。

如需获取此 Codelab 的代码并在 Android Studio 中打开它,请执行以下操作。

获取代码

  1. 点击提供的网址。此时,项目的 GitHub 页面会在浏览器中打开。
  2. 在项目的 GitHub 页面上,点击 Code 按钮,这时会出现一个对话框。

5b0a76c50478a73f.png

  1. 在对话框中,点击 Download ZIP 按钮,将项目保存到计算机上。等待下载完成。
  2. 在计算机上找到该文件(很可能在 Downloads 文件夹中)。
  3. 双击 ZIP 文件进行解压缩。系统将创建一个包含项目文件的新文件夹。

在 Android Studio 中打开项目

  1. 启动 Android Studio。
  2. Welcome to Android Studio 窗口中,点击 Open an existing Android Studio project

36cc44fcf0f89a1d.png

注意:如果 Android Studio 已经打开,请依次选择 File > New > Import Project 菜单选项。

21f3eec988dcfbe9.png

  1. Import Project 对话框中,前往解压缩的项目文件夹所在的位置(很可能在 Downloads 文件夹中)。
  2. 双击该项目文件夹。
  3. 等待 Android Studio 打开项目。
  4. 点击 Run 按钮 11c34fc5e516fb1c.png 以构建并运行应用。请确保该应用按预期构建。
  5. Project 工具窗口中浏览项目文件,了解应用的设置方式。

8. 总结

  • 使用 setImageResource() 更改 ImageView 中显示的图片
  • 使用 if / else 表达式或 when 表达式等控制流语句来处理应用中的不同情况,例如,在不同情况下显示不同的图片。

9. 了解详情

10. 自行练习

请进行以下练习

  1. 向应用添加另一个骰子,使得一个 Roll 按钮提供 2 个掷骰子结果。您的布局中需要多少个 ImageViews?这会对 MainActivity.kt 代码产生什么影响?

检查您的作品

创建好的应用应正常运行而不出现错误,并显示两个骰子。