上一篇:【在 Kotlin 中添加条件行为】
在此文章中,您将向现有 Dice Roller Android 应用添加骰子图片。请务必先完成之前关于构建 Dice Roller 应用基础的文章。
您的应用不会在 TextView
中显示掷骰子的结果值,而会显示掷出的骰面数字的相应骰子图片。这样会让您的应用更加直观,并增强用户体验。
我们将为您提供骰子图片的下载链接,您可以将其作为资源添加到应用中。为了编写要使用的骰子图片的代码,您需要使用 Kotlin 中的 when
语句。
前提条件
if / else
、when
语句)。MainActivity.kt
文件)。Button.
添加点击事件监听器。学习内容
ImageView
。when
语句)根据不同的条件自定义应用行为。您将构建的内容
Button
的 Dice Roller Android 应用,可用于掷骰子并更新屏幕上的图片。所需工具
在此任务中,您会将布局中的 TextView
替换为显示掷骰子结果图片的 ImageView
。
打开 Dice Roller 应用
应用应如下所示。
activity_main.xml
(app > res > layout > activity_main.xml)。系统随即会打开布局编辑器。删除 TextView
TextView
。提示: 您在添加更多界面组件以及添加和移除约束条件时,有时可能会发现
View
之间相互重叠,导致很难选择下面的视图。在这种情况下,您可以在 Component Tree 中选择View
。
Delete
键。Button
上的警告。您将在接下来的步骤中解决该问题。向布局中添加 ImageView
ImageView
从 Palette 拖动到 Design 视图上,并将其放置在 Button
上方。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
添加内容说明。现在不必担心这条警告,因为稍后在文章中,您将根据所显示的骰子图片设置 ImageView
的内容说明。您将在 Kotlin 代码中进行此更改。
在此任务中,您将下载一些骰子图片,并将其添加到应用中。
下载骰子图片
dice_images
文件夹,其中包含 6 个骰子图片文件,分别显示从 1 到 6 的骰子值。向应用中添加骰子图片
Shift
键的同时选中其他图片。非常棒!在下一个任务中,您将在应用中使用这些图片。
重要提示!- 您将能够在 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
替换示例头像
ImageView
。请注意,工具 srcCompat 属性仅在 Android Studio 的 Design 视图中使用提供的图片。图片仅在您构建应用时向开发者显示,但您在模拟器或设备上实际运行应用时不会看到该图片。
ImageView
的新资源。dice_1
可绘制对象,然后点击 OK。哇!ImageView
占据了整个屏幕。
接下来,您将调整 ImageView
的宽度和高度,以确保不会遮挡 Button
。
ImageView
的高度和宽度将与其中的内容(源图片)保持一致。ImageView
设置 160dp 的固定宽度和 200dp 的固定高度。按 Enter 键。现在 ImageView
的尺寸小了很多。
您可能会发现,Button 与图片的距离过近。
Design 视图更新后,应用看起来更美观了!
注意: 请使用密度无关像素 (dp) 作为这些尺寸的单位,以便在具有不同像素分辨率的设备上适当缩放图片。
点击按钮时改变骰子图片
虽然已更正布局,但需要更新 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
的资源 ID R.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)
}
点按按钮后,会出现显示值 2 的骰子图片。太好了!!
您可以根据按钮点按操作来改变图片了!您离目标更近了!
显然,骰子结果并不总是 2。利用在针对不同的掷骰子结果添加条件行为文章中学到的控制流逻辑,以便根据掷骰子的随机结果在屏幕上显示适当的骰子图片。
在开始输入代码之前,请先编写一些伪代码来描述应该发生的情况,从概念上思考应用的行为方式。例如:
如果用户掷出 1,就显示 dice_1 图片。
如果用户掷出 2,就显示 dice_2 图片。
等等…
注意: 伪代码是关于一些代码运作方式的非正式说明。它使用一些计算机语言元素,例如
if / else
,但以人类可以理解的方式进行描述。如果要在确定所有细节之前规划正确的方法,伪代码很有用。
在 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)
}
}
优化您的代码
如果要编写更简洁的代码,可以进行以下代码更改。这不会对您的应用用户产生任何明显影响,但会缩短代码,减少重复。
您可能已经注意到,在 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)
在 ImageView 上设置适当的内容说明
现在,您已用图片替换掷出的数字,屏幕阅读器无法再判断掷出的数字了。为修复此问题,更新图片资源后,请更新 ImageView
的内容说明。内容说明应为 ImageView
中显示的文字说明,以便屏幕阅读器可以对其进行描述。
diceImage.contentDescription = diceRoll.toString()
屏幕阅读器可以朗读此内容说明,因此如果屏幕上显示掷骰子结果为“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()
方法可能如下所示。
/**
* 掷骰子并用结果更新屏幕。
*/
private fun rollDice() {
// 创建一个新的骰子对象,有6个面,然后掷骰子
val dice = Dice(6)
val diceRoll = dice.roll()
// 在布局中查找 ImageView
val diceImage: ImageView = findViewById(R.id.imageView)
// 根据掷骰子决定使用哪个可绘制资源 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
else -> R.drawable.dice_6
}
// 使用正确的可绘制资源 ID 更新 ImageView
diceImage.setImageResource(drawableResource)
// 更新内容描述
diceImage.contentDescription = diceRoll.toString()
}
如需完整的 MainActivity.kt
文件,请参阅下一步中链接的 GitHub 上的解决方案代码。
恭喜您完成 Dice Roller 应用的创建!现在,您可以与好友一起玩这款游戏,乐享下一个游戏之夜!
此文章的解决方案代码位于下方所示的项目和模块中。
解决方案代码网址: https://github.com/google-developer-training/android-basics-kotlin-dice-roller-with-images-app-solution
如需获取此文章的代码并在 Android Studio 中打开它,请执行以下操作。
获取代码
在 Android Studio 中打开项目
注意:如果 Android Studio 已经打开,请依次选择 File > New > Import Project 菜单选项。
setImageResource()
更改 ImageView 中显示的图片if / else
表达式或 when
表达式等控制流语句来处理应用中的不同情况,例如,在不同情况下显示不同的图片。ImageView.setImageResource()
注意: 可视需要进行练习。您可以借此机会练习您在此文章中学到的知识。
请进行以下练习:
ImageViews
?这会对 MainActivity.kt
代码产生什么影响?检查您的作品:
创建好的应用应正常运行而不出现错误,并显示两个骰子。
下一篇:【Kotlin 中的类和继承】