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中。
检查您的作品:
创建好的应用应正常运行而不出现错误,并在应用中显示二个骰子。
