1. 准备工作
在此 Codelab 中,您将创建一个 Dice Roller Android 应用,用户可在该应用中点击 Button
掷骰子。掷骰子的结果将显示在屏幕上的 TextView
中。
您将使用 Android Studio 中的布局编辑器构建应用布局,然后编写 Kotlin 代码处理点击 Button
时发生的情况。
前提条件
- 了解如何在 Android Studio 中创建和运行“Hello, World!”应用。
- 熟悉如何在应用中使用
TextViews
。 - 了解如何在布局编辑器中修改
TextView
的属性。 - 了解如何将文本提取到字符串资源中,从而更轻松地翻译应用和重复使用字符串。
- 了解 Kotlin 编程基础知识
学习内容
- 如何向 Android 应用添加
Button
。 - 如何在应用中添加点按
Button
时的行为。 - 如何打开和修改应用的
Activity
代码。 - 如何显示
Toast
消息。 - 如何在应用运行时更新
TextView
的内容。
构建内容
- 一款 Dice Roller Android 应用,该应用具有可用于掷骰子的
Button
且可根据掷骰结果更新屏幕上的文字。
所需工具
- 一台安装了 Android Studio 的计算机。
以下是完成此 Codelab 后,您构建的应用的样子。
2. 设置应用
创建 Empty Activity 项目
- 如果您已在 Android Studio 中打开现有项目,请前往 File > New > New Project...,打开 Create New Project 屏幕。
- 在 Create New Project 中,使用 Empty Activity 模板创建一个新的 Kotlin 项目。
- 将应用命名为“Dice Roller”,将最低 API 级别设为 19 (KitKat)。
- 运行新应用,界面应如下所示。
3. 为应用创建布局
打开布局编辑器
- 在 Project 窗口中,双击
activity_main.xml
(app > res > layout > activity_main.xml) 将其打开。您应该会看到布局编辑器,其中在应用的中心仅显示“Hello World”TextView
。
接下来,您将为您的应用添加一个 Button
。Button
是 Android 中的界面 (UI) 元素,用户可以通过点按该元素执行相应操作。
在此任务中,您将在“Hello World”TextView
下方添加 Button
。TextView
和 Button
位于同一 ConstraintLayout
内,ConstraintLayout 是一种 ViewGroup
。
当 ViewGroup
中存在 Views
时,Views
将被视作父级 ViewGroup
的子级。在您的应用中,TextView
和 Button
将被视作父级 ConstraintLayout
的子级。
将 Button
添加为应用中现有 ConstraintLayout
的子级。
向布局添加 Button
- 将
Button
从 Palette 拖动到 Design 视图中,并将其置于“Hello World”TextView
下方。
- 在 Palette 下方的 Component Tree 中,验证
Button
和TextView
是否列在ConstraintLayout
项下(作为ConstraintLayout
的子级)。 - 请注意
Button
未受约束的错误。由于Button
位于ConstraintLayout
内,因此您必须设置垂直约束条件和水平约束条件来确定 Button 的位置。
确定 Button 的位置
在此步骤中,您将添加一个从 Button
的顶部到 TextView
的底部的垂直约束条件。这会将 Button
的位置确定在 TextView
的下方。
- 在 Design 视图的
Button
的顶部边缘,按住带蓝色边框的白色圆圈。拖动指针,箭头将跟随指针移动。当指针到达“Hello World”TextView
的下边缘时释放。这样就建立了布局约束条件,Button
会滑动到TextView
正下方。
- 请查看布局编辑器右侧的 Attributes。
- 在 Constraint Widget 中,您会看到针对
TextView
的底部设置的一个新的布局约束条件,即 Top → BottomOf textView (0dp)。(0dp) 表示边距为 0。您还会遇到缺少水平约束条件的错误。
- 添加一个从
Button
的左侧到父级ConstraintLayout
的左侧的水平约束条件。 - 然后对右侧执行同样的操作,将
Button
的右边缘与ConstraintLayout
的右边缘连接起来。结果应如下所示:
- 保持选中
Button
,Constraint Widget 应如下所示。请注意,添加了以下两个额外约束条件:Start → StartOf parent (0dp) 和 End → EndOf parent (0dp)。这意味着Button
在其父级(即ConstraintLayout
)内水平居中。
- 运行应用。应用界面应如以下屏幕截图所示。您可以点击
Button
,但还不能执行任何操作。让我们继续构建吧!
更改 Button 文字
您将在布局编辑器中进行一些界面更改。
请不要使用 Button
标签显示“Button”,而是将其更改为指示按钮将要执行的操作:“Roll”。
- 在布局编辑器中,选中
Button
后,前往 Attributes,将 text 更改为 Roll,然后按Enter
(Mac 上为Return
)键。
- 在 Component Tree 中,
Button
旁边会显示一个橙色的警告三角形。如果将指针悬停在三角形上,系统会显示一条消息。Android Studio 在您的应用代码中检测到硬编码字符串(“Roll”),并建议改用字符串资源。
使用硬编码字符串意味着应用将难以翻译成其他语言,并且很难在应用的不同部分重复使用字符串。幸运的是,Android Studio 会自动为您修复。
- 在 Component Tree 中,点击橙色三角形。
系统会打开完整的警告消息。
- 在消息的底部,点击 Suggested Fix 下的 Fix 按钮。(您可能需要向下滚动)。
- Extract Resource 对话框随即打开。如需提取字符串,您需要使用文字“Roll”,并在
strings.xml
中创建一个名为roll
的字符串资源 (app > res > values > strings.xml)。默认值正确无误,请点击 OK。
- 请注意,在 Attributes 中,
Button
的 text 属性现在会显示@string/roll
,表示您刚刚创建的资源。
在 Design 视图中,Button
仍会显示 Roll。
设置 TextView 的样式
“Hello World!”文字很小,而且消息与您的应用无关。在此步骤中,您需要将文字非常小的消息“Hello, World!”替换为显示掷骰结果值的数值,并调大字体,以便用户查看。
- 在 Design Editor 中,选择
TextView
,使其属性出现在 Attributes 窗口中。 - 将
TextView
的 textSize 更改为 36sp,使其足够大,易于用户查看。您可能需要滚动才能找到 textSize。
- 清除
TextView
的 text 属性。在用户掷骰子之前,您不需要在TextView
中显示任何内容。
但是,在为应用编辑布局和代码时,查看 TextView
中的一些文字会很有帮助。为此,您可以向 TextView
添加文字,这些文字仅在布局预览中显示,在应用运行时不会显示。
- 在 Component Tree 中选择
TextView
。 - 在 Common Attributes 下,找到 text 属性,在其下方会显示另一个带有工具图标的 text 属性。text 属性是应用运行时向用户显示的内容。带有工具图标的 text 属性是专为开发者提供的“tools text”属性。
- 在
TextView
中将 tools text 设置为“1”(以假设您掷骰子的结果为 1)。“1”只会在 Android Studio 的 Design Editor 中显示,在实际设备或模拟器上运行应用时不会显示。
请注意,由于该文字只能由应用开发者查看,因此您无需为其创建字符串资源。
- 在预览中查看您的应用。系统会显示“1”。
- 运行您的应用。以下是应用在模拟器上运行时的界面。系统未显示“1”。该行为正确无误。
太棒了,您已完成布局更改!
这样,您就创建好了一款带 Button 的应用,但在您点按该 Button 时,应用不会执行任何操作。如需改变该情况,您需要编写相关 Kotlin 代码,以在点按 Button 时掷骰子并更新屏幕。
如需进行此更改,您需要更详细地了解 Android 应用的构建方式。
4. Activity 简介
Activity
提供窗口供应用在其中绘制界面。通常,Activity
会占用正在运行的应用的整个屏幕。每个应用都有一个或多个 Activity。顶级 Activity 或第一个 Activity 通常称为 MainActivity
,由项目模板提供。例如,当用户滚动浏览设备上的应用列表并点按“Dice Roller”应用图标时,Android 系统会启动该应用的 MainActivity
。
在 MainActivity
代码中,您需要提供 Activity
布局以及用户应如何与其交互的详细信息。
- 在 Birthday Card 应用中,有一个
Activity
用于显示生日消息和图片。 - 在 Dice Roller 应用中,有一个
Activity
用于显示您刚构建的TextView
和Button
布局。
对于更复杂的应用,可能会设置多个屏幕和多个 Activity
。每个 Activity
都有特定用途。
例如,在“Photo Gallery”应用中,您可以使用一个 Activity
来显示照片网格,一个 Activity
来查看单张照片,以及另一个 Activity
来修改单张照片。
打开 MainActivity.kt 文件
您将添加代码以响应 MainActivity
中的按钮点按。如需正确执行此操作,您需要详细了解应用中已有的 MainActivity
代码。
- 导航到
MainActivity.kt
文件并打开文件 (app > java > com.example.diceroller > MainActivity.kt)。您应该会看到以下内容:如果看到import...
,请点击...
以展开导入。
package com.example.diceroller
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
您无需理解上述代码的每个单词,但需要大致了解其功能。您对 Android 代码的使用越多,您对代码就会越熟悉,也就对其越了解。
- 查看
MainActivity
类的 Kotlin 代码,由关键字class
和名称标识。
class MainActivity : AppCompatActivity() {
...
}
- 请注意,您的
MainActivity
中没有main()
函数。
之前,您已了解每个 Kotlin 程序都必须具有 main()
函数。Android 应用的工作方式有所不同。当您第一次打开应用时,Android 系统会调用 MainActivity
的 onCreate()
方法,而不会调用 main()
函数。
- 查找
onCreate()
方法,该方法类似于以下代码。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
您将在后面的 Codelab 中了解 override
(目前不用操心)。onCreate()
方法的其余部分使用导入中的代码设置 MainActivity
,并通过 setContentView()
设置起始布局。
- 请注意以
import
开头的行。
Android 提供了许多类的框架,可帮助您更轻松地编写 Android 应用,但需要准确了解您所指的具体类。您可以使用 import
语句指定要在代码中使用的框架中的类。例如,Button
类在 android.widget.Button
中定义。
启用自动导入
使用更多类时,记住添加 import
语句将会变得很繁琐。幸运的是,当您使用其他程序提供的类时,Android Studio 可以帮助您选择正确的导入。在此步骤中,您将对 Android Studio 进行配置,使其尽可能自动添加导入,并自动从您的代码中移除未使用的导入。
在 macOS 中,依次前往 File > New Project Settings > Preferences for New Projects...,打开设置展开 Other Settings > Auto Import。在 Java 和 Kotlin 部分中,确保选中 Add unambiguous imports on the fly 和 Optimize imports on the fly (for current project)。请注意,每个部分中有两个复选框。按 OK,保存更改并关闭设置。
在 Windows 中,依次前往 File > Settings > Editor > General > Auto Import,打开设置。在 Java 和 Kotlin 部分中,确保选中 Add unambiguous imports on the fly 和 Optimize imports on the fly (for current project)。请注意,每个部分中有两个复选框。按 OK,保存更改并关闭设置。
明确导入设置会指示 Android Studio 自动添加 import 语句,只要它可以确定要使用的语句即可。优化导入设置会指示 Android Studio 移除您的代码未使用的任何导入。
5. 将 Button 设为可交互
您现在对 MainActivity
有了更多了解,接下来您将修改此应用,以便通过点击 Button
在屏幕上执行某些操作。
点击 Button 时显示消息
在此步骤中,您将指定点击该 Button 时,屏幕底部会出现一条简短的消息。
- 在调用
setContentView()
后,将以下代码添加到onCreate()
方法中。findViewById()
方法在布局中找到Button
。R.id.button
是Button
的资源 ID,该 ID 是 Button 的唯一标识符。
val rollButton: Button = findViewById(R.id.button)
代码将对 Button
对象的引用保存在名为 rollButton
的变量中,而不是 Button
对象本身。
onCreate()
方法现在应如下所示:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
}
- 验证 Android Studio 是否自动为
Button
添加了import
语句。请注意,现在有 3 个 import 语句。
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
接下来,您需要将代码与 Button
相关联,以便在点按 Button
时执行代码。点击监听器是一些代码,用于在发生点按或点击时执行相应操作。此处的点击监听器用于监听用户点击 Button
的操作。
- 使用
rollButton
对象,并调用setOnClickListener()
方法为其设置点击监听器。实际上,在方法名称后面您将使用大括号,而不在方法名称后面使用圆括号。这是用于声明 Lambda 的特殊语法,在未来的 Codelab 中可了解详情。
当前您需要注意的是,在大括号内,给出了在点按按钮时应执行的操作的说明。您可以让应用显示消息框,其中显示有关下一步的简短消息。
rollButton.setOnClickListener {
}
在输入过程中,Android Studio 可能会显示多个建议。在本例中,请选择 setOnClickListener {...} 选项。
在大括号中,您可以说明点按按钮时应执行的操作。现在,您可以让应用显示 Toast
,这是一条向用户显示的简短消息。
- 通过调用
Toast.makeText()
创建包含文字"Dice Rolled!"
的Toast
。
val toast = Toast.makeText(this, "Dice Rolled!", Toast.LENGTH_SHORT)
- 然后,通过调用
show()
方法告知Toast
自行显示。
toast.show()
更新后的 MainActivity
类如下所示;package
和 import
语句仍位于文件顶部:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener {
val toast = Toast.makeText(this, "Dice Rolled!", Toast.LENGTH_SHORT)
toast.show()
}
}
}
您可以将点击监听器中的两行合并为一行,而不使用变量。该模式一种常见模式,您在其他代码中也可以找到。
Toast.makeText(this, "Dice Rolled!", Toast.LENGTH_SHORT).show()
- 运行该应用,然后点击 Roll 按钮。消息框消息将在屏幕底部弹出,不久后就会消失。
太棒了!点击按钮后,消息就会弹出!这是您第一次为 Android 编写 Kotlin 代码!
点击 Button 时更新 TextView
您需要编写代码以在用户点击 Roll 按钮时更新屏幕上的 TextView
,而不是显示临时 Toast
消息。
- 返回到
activity_main.xml
(app > res > layout >activity_main.xml) - 点击
TextView
。 - 请注意,id 为 textView。
- 打开
MainActivity.kt
(app > java > com.example.diceroller > MainActivity.kt) - 删除用于创建和显示
Toast
的代码行。
rollButton.setOnClickListener {
}
- 在其中创建一个名为
resultTextView
的新变量来存储TextView
。 - 使用
findViewById()
以在布局中使用 textView 的 ID 查找textView
,并存储对其的引用。
val resultTextView: TextView = findViewById(R.id.textView)
- 将
resultTextView
上的文字设置为带引号的“6”。
resultTextView.text = "6"
这与在 Attributes 中设置 text 类似,但它现已在代码中,因此文字必须位于双引号中。明确进行此设置意味着,TextView
当前始终显示 6。您将添加代码,以在下一个任务中掷骰子并显示不同的值。
MainActivity
类的内容应如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener {
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = "6"
}
}
}
- 运行应用。点击按钮。它应将
TextView
更新为“6”。
6. 添加掷骰子逻辑
唯一缺少的操作实际是掷骰子。您可以重复使用上一个 Codelab 中的 Dice
类,该类负责执行掷骰子的逻辑。
添加 Dice 类
- 在
MainActivity
类中最后一个大括号后,使用roll()
方法创建Dice
类。
class Dice(val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
- 请注意,Android Studio 可能会为
numSides
添加灰色波浪线下划线。(可能需要一段时间才会显示)。 - 将指针悬停在
numSides
上,系统会显示一个弹出式窗口,显示 Property ‘numSides' could be private。
将 numSides
标记为 private
将仅允许在 Dice
类中访问 numSides。由于使用 numSides
的唯一代码位于 Dice
类中,可以将该参数设置为 private
用于 Dice
类。在下一单元中,您将了解到 private
与 public
变量。
- 继续,然后点击 Make ‘numSides' ‘private',根据 Android Studio 提出建议。
创建 rollDice() 方法
现在,您已经在应用中添加了 Dice
类,接下来将更新 MainActivity
以使用该类。为了更好地组织您的代码,请将有关掷骰子的所有逻辑整合在一个函数中。
- 通过调用
rollDice()
替换用于将文字设置为“6”的点击监听器中的代码。
rollButton.setOnClickListener {
rollDice()
}
- 由于尚未定义
rollDice()
,Android Studio 会标记错误并以红色字体显示rollDice()
。 - 如果将指针悬停在
rollDice()
上方,Android Studio 会显示问题和一些可能的解决方案。
- 点击 More actions...,系统将显示一个菜单。Android Studio 助您完成更多工作!
- 选择 Create function ‘rollDice'。Android Studio 会为
MainActivity
内的函数创建空定义。
private fun rollDice() {
TODO("Not yet implemented")
}
创建新的 Dice 对象实例
在此步骤中,您将使用 rollDice()
方法创建并掷骰子,然后在 TextView
中显示结果。
- 在
rollDice()
中,删除TODO()
调用。 - 添加代码以创建包含 6 个面的骰子。
val dice = Dice(6)
- 通过调用
roll()
方法掷骰子,并将结果保存在名为diceRoll
的变量中。
val diceRoll = dice.roll()
- 通过调用
findViewById()
查找TextView
。
val resultTextView: TextView = findViewById(R.id.textView)
变量 diceRoll
是一个数字,但 TextView
则使用文字。您可以在 diceRoll
上使用 toString()
方法将其转换为字符串。
- 将
diceRoll
转换为字符串,并使用该字符串更新resultTextView
的文本。
resultTextView.text = diceRoll.toString()
rollDice()
方法如下所示:
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = diceRoll.toString()
}
- 运行您的应用。除 6 外,骰子结果应更改为其他值!由于它是一个 1 到 6 的随机数,值 6 有时也会出现。
太棒了,您真棒!
7. 采用规范的编码做法
当您在此处调整完各部分并使您的应用正常运行后,您的代码看起来有些混乱,这十分正常。但是,在您离开代码之前,您应该执行一些简单的清理任务。这样,应用将保持良好的状态,更易于在日后继续维护。
这些是专业的 Android 开发者在编写代码时的习惯。
Android 风格指南
与团队合作时,团队成员最好采用类似的方式编写代码,以便保持代码之间的一致性。正因如此,Android 提供了一套有关如何编写 Android 代码的风格指南,其中包括命名惯例、格式以及其他要遵循的良好做法。编写 Android 代码时遵循以下准则:面向 Android 开发者的 Kotlin 样式指南。
您可以通过以下几种方式遵循风格指南。
清理代码
压缩代码
您可以将代码压缩成几行较短的代码行,从而使代码更加简洁。例如,以下代码用于在 Button
上设置点击监听器。
rollButton.setOnClickListener {
rollDice()
}
因为点击监听器的说明只有 1 行,所以您可以将 rollDice()
方法调用和大括号压缩成一行。其外观如下。使用一行而不是三行!
rollButton.setOnClickListener { rollDice() }
重新格式化代码
现在,您将重新设置代码格式,以确保其遵循建议的 Android 代码格式规范。
- 在
MainActivity.kt
类中,在 Windows 中使用键盘快捷键Control+A
(或 Mac 上的Command+A
)选择文件中的所有文本。或者,您也可以前往 Android Studio 中的菜单 (Edit > Select All) 完成相应操作。 - 选择文件中的所有文本后,前往 Android Studio 菜单的 Code > Reformat Code,或使用键盘快捷键
Ctrl+Alt+L
(在 Mac 上,则使用Command+Option+L
)。
这样将更新代码的格式,包括空格、缩进等。您可能看不到任何变化,这样最好。然后,您的代码应该就正确格式化了!
添加代码注释
在代码中添加一些注释,以描述您编写的代码会执行什么操作。随着代码复杂程度的增加,您还必须记住编写代码以执行相关功能的原因。如果您之后返回该代码进行更改,代码功能可能依旧十分清晰,但您可能不记得你当初这样编写的原因。
通常为每个类(MainActivity
和 Dice
是您的应用中仅有的类)和您编写的每种方法添加注释。在注释的开头和末尾使用 /**
和 */
符号,以告知系统这不是代码。系统会在执行您的代码时忽略这些行。
关于类的注释的示例:
/**
* This activity allows the user to roll a dice and view the result
* on the screen.
*/
class MainActivity : AppCompatActivity() {
关于方法的注释的示例:
/**
* Roll the dice and update the screen with the result.
*/
private fun rollDice() {
在方法中,如果您希望帮助读者了解代码,您可以随意添加注释。请注意,您可以在注释的开头使用 //
符号。一行中 //
符号后面的所有内容都会被视为注释。
方法中 2 条注释的示例:
private fun rollDice() {
// Create new Dice object with 6 sides and roll it
val dice = Dice(6)
val diceRoll = dice.roll()
// Update the screen with the dice roll
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = diceRoll.toString()
}
- 继续,并花点时间为您的代码添加注释。
- 在更改注释和格式设置后,最好再次运行应用以确保它可以按预期运行。
查看解决方案代码,了解为代码添加注释的方式。
8. 解决方案代码
此 Codelab 的解决方案代码位于下方所示的项目和模块中。
如需获取此 Codelab 的代码并在 Android Studio 中打开它,请执行以下操作。
获取代码
- 点击提供的网址。此时,项目的 GitHub 页面会在浏览器中打开。
- 检查并确认分支名称与 Codelab 中指定的分支名称匹配。例如,在以下屏幕截图中,分支名称为 main。
- 在项目的 GitHub 页面上,点击 Code 按钮,这时会出现一个弹出式窗口。
- 在弹出式窗口中,点击 Download ZIP 按钮,将项目保存到计算机上。等待下载完成。
- 在计算机上找到该文件(很可能在 Downloads 文件夹中)。
- 双击 ZIP 文件进行解压缩。系统将创建一个包含项目文件的新文件夹。
在 Android Studio 中打开项目
- 启动 Android Studio。
- 在 Welcome to Android Studio 窗口中,点击 Open。
注意:如果 Android Studio 已经打开,则改为依次选择 File > Open 菜单选项。
- 在文件浏览器中,前往解压缩的项目文件夹所在的位置(很可能在 Downloads 文件夹中)。
- 双击该项目文件夹。
- 等待 Android Studio 打开项目。
- 点击 Run 按钮 以构建并运行应用。请确保该应用按预期构建。
9. 总结
- 使用布局编辑器在 Android 应用中添加
Button
。 - 修改
MainActivity.kt
类,以便向应用添加交互行为。 - 弹出
Toast
消息作为临时解决方案,可验证您的操作是否正确。 - 使用
setOnClickListener()
为Button
设置点击监听器,以添加在用户点击Button
时的行为。 - 在应用运行时,您可以通过对布局中的
TextView
、Button
或其他界面元素调用方法来更新屏幕。 - 对您的代码添加注释,帮助阅读代码的其他人了解您的方法。
- 重新格式化代码并清理代码。
10. 了解详情
- 《使用 Kotlin 进行 Android 开发的基础知识》相关词汇
Button
类Toast
类TextView
类- 面向 Android 开发者的 Kotlin 样式指南
11. 自行练习
请进行以下练习:
- 向应用添加另一个骰子。点击 Roll 按钮后将掷 2 个骰子。结果显示在屏幕上两个不同
TextViews
中。
检查您的作品:
创建好的应用应正常运行而不出现错误,并在应用中显示二个骰子。