1. 准备工作
在此 Codelab 中,您将向现有 Dice Roller Android 应用添加骰子图片。请务必先完成之前关于构建 Dice Roller 应用基础的 Codelab。
您的应用不会在 TextView
中显示掷骰子的结果值,而会显示掷出的骰面数字的相应骰子图片。这样会让您的应用更加直观,并增强用户体验。
我们将为您提供骰子图片的下载链接,您可以将其作为资源添加到应用中。为了编写要使用的骰子图片的代码,您需要使用 Kotlin 中的 when
语句。
前提条件
- 完成了“创建交互式 Dice Roller 应用”Codelab。
- 能够编写控制流语句(
if / else
、when
语句)。 - 能够根据用户输入更新应用界面(修改
MainActivity.kt
文件)。 - 能够向
Button.
添加点击事件监听器。 - 能够向 Android 应用添加图片资源。
学习内容
- 如何在应用运行时更新
ImageView
。 - 如何(使用
when
语句)根据不同的条件自定义应用行为。
构建内容
- 具有
Button
的 Dice Roller Android 应用,可用于掷骰子并更新屏幕上的图片。
所需工具
- 一台安装了 Android Studio 的计算机。
- 互联网连接,以便下载骰子图片。
2. 更新应用的布局
在此任务中,您会将布局中的 TextView
替换为显示掷骰子结果图片的 ImageView
。
打开 Dice Roller 应用
- 在 Android Studio 中打开之前 Codelab 中创建的 Dice Roller 应用并运行应用。您可以使用解决方案代码或您创建的代码。
应用应如下所示。
- 打开
activity_main.xml
(app > res > layout > activity_main.xml)。系统随即会打开布局编辑器。
删除 TextView
- 在布局编辑器中,选择 Component Tree 中的
TextView
。
- 右键点击并选择 Delete,或按
Delete
键。 - 请暂时忽略
Button
上的警告。您将在接下来的步骤中解决该问题。
向布局中添加 ImageView
- 将
ImageView
从 Palette 拖动到 Design 视图上,并将其放置在Button
上方。
- 在 Pick a Resource 对话框中,选择 Sample data 中的 avatars。在下一任务中添加骰子图片之前,将使用此临时图片。
- 点击 OK。应用的 Design 视图应如下所示。
- 在 Component Tree 中,您会发现两个错误。
Button
不受垂直约束,而ImageView
既不受垂直约束,也不受水平约束。
Button
不受垂直约束,是因为您移除了原先位于其上方的 TextView
。现在,您需要将 ImageView
和 Button
放置到其下方。
确定 ImageView 和 Button 的位置
无论 Button
在哪个位置,您都需要让 ImageView
在屏幕上垂直居中。
- 为
ImageView
添加水平约束条件。将ImageView
的左侧与父级ConstraintLayout
的左边缘连接起来。 - 将
ImageView
的右侧与父级的右边缘连接起来。这会使ImageView
在父级内水平居中。
- 为
ImageView
添加垂直约束条件,将ImageView
的顶部与父级的顶部连接起来。ImageView
会向上滑动到ConstraintLayout
的顶部。
- 为
Button
添加垂直约束条件,将Button
的顶部与ImageView
的底部连接起来。Button
会向上滑动到ImageView
的下方。
- 现在,再次选择
ImageView
,然后添加垂直约束条件,将ImageView
的底部与父级的底部连接起来。这会使ImageView
在ConstraintLayout
内垂直居中。
现在,有关约束条件的所有警告都会消失。
完成上述所有操作后,Design 视图应如下所示,ImageView
居中,Button
位于其正下方。
您可能会在 Component Tree 中的 ImageView
上看到一条警告,提示您向 ImageView
添加内容说明。现在不必担心这条警告,因为稍后在 Codelab 中,您将根据所显示的骰子图片设置 ImageView
的内容说明。您将在 Kotlin 代码中进行此更改。
3. 添加骰子图片
在此任务中,您将下载一些骰子图片,并将其添加到应用中。
下载骰子图片
- 打开此网址,将包含骰子图片的 ZIP 文件下载到您的计算机。等待下载完成。
- 在计算机上找到该文件(可能在 Downloads 文件夹中)。
- 双击 ZIP 文件进行解压缩。这将新建一个
dice_images
文件夹,其中包含 6 个骰子图片文件,分别显示从 1 到 6 的骰子值。
向应用中添加骰子图片
- 在 Android Studio 中,依次点击菜单中的 View > Tool Windows > Resource Manager,或者点击 Project 窗口左侧的 Resource Manager 标签页。
- 点击 Resource Manager 中左上角的 +,然后选择 Import Drawables。系统随即会打开一个文件浏览器。
- 查找并选中上述 6 个骰子图片文件。您可以先选中第一个骰子图片,然后在按住
Shift
键的同时选中其他图片。 - 点击 Open。
- 点击 Next,然后点击 Import,以确认您要导入这 6 项资源。
- 如果文件已成功导入,那么应用的 Resource Manager (app>res>drawable) 中会显示这 6 张图片。
非常棒!在下一个任务中,您将在应用中使用这些图片。
重要提示!- 您将能够在 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. 使用骰子图片
替换示例头像
- 在 Design Editor 中,选择
ImageView
。 - 在 Declared Attributes 部分的 Attributes 中,找到设置为头像图片的工具 srcCompat 属性。
请注意,工具 srcCompat 属性仅在 Android Studio 的 Design 视图中使用提供的图片。图片仅在您构建应用时向开发者显示,但您在模拟器或设备上实际运行应用时不会看到该图片。
- 点击头像的小预览图。系统会打开一个对话框,供您选择要用于此
ImageView
的新资源。
- 选择
dice_1
可绘制对象,然后点击 OK。
哇!ImageView
占据了整个屏幕。
接下来,您将调整 ImageView
的宽度和高度,以确保不会遮挡 Button
。
- 在 Constraints Widget 下的 Attributes 窗口中,找到 layout_width 和 layout_height 属性。它们当前设置为 wrap_content,这意味着
ImageView
的高度和宽度将与其中的内容(源图片)保持一致。 - 为
ImageView
设置 160dp 的固定宽度和 200dp 的固定高度。按 Enter 键。
现在 ImageView
的尺寸小了很多。
您可能会发现,Button
与图片的距离过近。
- 在 Constraint Widget 中将按钮的上边距设置为 16dp。
Design 视图更新后,应用看起来更美观了!
点击按钮时改变骰子图片
虽然已更正布局,但需要更新 MainActivity
类才能使用骰子图片。
MainActivity.kt
文件中目前存在一个应用错误。如果您尝试运行该应用,会看到以下构建错误:
这是因为您的代码仍然在引用您已从布局中删除的 TextView
。
- 打开
MainActivity.kt
(app > java > com.example.diceroller > MainActivity.kt)
代码引用了 R.id.textView
,但 Android Studio 无法识别它。
- 在
rollDice()
方法中,选择任何引用TextView
的代码并将其删除。
// Update the TextView with the dice roll
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = dice.roll().toString()
- 还是在
rollDice()
中,创建一个类型为ImageView
的新变量diceImage
。将其设置为与布局中的ImageView
相同。使用findViewById()
方法,并将ImageView
的资源 IDR.id.imageView
作为输入参数传入。
val diceImage: ImageView = findViewById(R.id.imageView)
如果您想知道如何确定 ImageView
的确切资源 ID,请查看 Attributes 窗口顶部的 id。
在 Kotlin 代码中引用此资源 ID 时,请确保输入的内容完全相同(小写的 i,大写的 V,没有空格)。否则,Android Studio 会显示错误。
- 添加以下代码行,以测试点击按钮时是否可以正确更新
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)
}
- 运行应用以验证它是否正常运行而不出现错误。启动应用时应显示空白屏幕(只有 Roll 按钮)。
点按按钮后,会出现显示值 2 的骰子图片。太好了!!
您可以根据按钮点按操作来改变图片了!您离目标更近了!
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() 方法
- 在
rollDice()
方法中,删除每次将图片资源 ID 设置为dice_2
图片的代码行。
diceImage.setImageResource(R.drawable.dice_2)
- 将其替换为
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)
}
}
- 运行应用。点击 Roll 按钮会将骰子图片更改为除 2 以外的值。成功啦!
优化您的代码
如果要编写更简洁的代码,可以进行以下代码更改。这不会对您的应用用户产生任何明显影响,但会缩短代码,减少重复。
您可能已经注意到,在 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 即可。
- 将上述代码替换为以下代码。
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
变量中。然后,您可以使用该变量更新显示的图片资源。
- 请注意,
when
现在带红色的下划线。如果将指针悬停在它的上面,您会看到一条错误消息:‘when' expression must be exhaustive, add necessary ‘else' branch。
出现该错误的原因是 when
表达式的值被赋给 drawableResource
,因此 when
必须涵盖所有情况,它必须处理所有可能的情况,这样,即使您更改为 12 面的骰子,也始终会返回值。Android Studio 建议添加一个 else
分支。您可以通过将 6
的情况更改为 else
来解决此问题。从 1
到 5
的情况不变,但包括 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 到 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()
}
添加代码注释
在代码中添加一些注释,以描述您编写的代码会执行什么操作。
完成所有这些更改后,您的 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 中打开它,请执行以下操作。
获取代码
- 点击提供的网址。此时,项目的 GitHub 页面会在浏览器中打开。
- 在项目的 GitHub 页面上,点击 Code 按钮,这时会出现一个对话框。
- 在对话框中,点击 Download ZIP 按钮,将项目保存到计算机上。等待下载完成。
- 在计算机上找到该文件(很可能在 Downloads 文件夹中)。
- 双击 ZIP 文件进行解压缩。系统将创建一个包含项目文件的新文件夹。
在 Android Studio 中打开项目
- 启动 Android Studio。
- 在 Welcome to Android Studio 窗口中,点击 Open an existing Android Studio project。
注意:如果 Android Studio 已经打开,请依次选择 File > New > Import Project 菜单选项。
- 在 Import Project 对话框中,前往解压缩的项目文件夹所在的位置(很可能在 Downloads 文件夹中)。
- 双击该项目文件夹。
- 等待 Android Studio 打开项目。
- 点击 Run 按钮 以构建并运行应用。请确保该应用按预期构建。
- 在 Project 工具窗口中浏览项目文件,了解应用的设置方式。
8. 总结
- 使用
setImageResource()
更改ImageView
中显示的图片 - 使用
if / else
表达式或when
表达式等控制流语句来处理应用中的不同情况,例如,在不同情况下显示不同的图片。
9. 了解详情
10. 自行练习
请进行以下练习:
- 向应用添加另一个骰子,使得一个 Roll 按钮提供 2 个掷骰子结果。您的布局中需要多少个
ImageViews
?这会对MainActivity.kt
代码产生什么影响?
检查您的作品:
创建好的应用应正常运行而不出现错误,并显示两个骰子。