Android Kotlin 基础知识 01.2:基本应用结构

此 Codelab 是“Android Kotlin 基础知识”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘此课程的价值。“Android Kotlin 基础知识”Codelab 着陆页列出了所有课程 Codelab。

简介

到目前为止,您已完成所有设置,Android Studio 也为您创建了许多代码。在修改所有这些代码之前,请务必了解您刚刚创建的内容以及如何浏览 Android 应用的源文件。

在此 Codelab 中,您将详细了解 Android 应用的主要组件,并向应用添加简单的按钮互动功能。

您应当已掌握的内容

  • 如何安装和打开 Android Studio。
  • 如何创建新的应用项目。
  • 了解如何在模拟器或实体设备上运行应用。

学习内容

  • 如何修改应用的布局文件。
  • 如何创建具有互动行为的应用。
  • 许多新术语。如需了解术语和概念的通俗易懂的说明,请参阅词汇术语库

操作内容

  • 探索 MainActivity Kotlin 文件和 activity 的布局文件。
  • 在 XML 中修改 activity 的布局。
  • 向 activity 的布局添加一个 Button 元素。
  • 将硬编码的字符串提取到字符串资源文件中。
  • 实现点击处理程序方法,以便在用户点按 Button 时在屏幕上显示消息。

在此 Codelab 中,您将创建一个名为 DiceRoller 的新应用项目,并添加带有按钮的基本互动功能。每次点击按钮时,所显示文本的值都会发生变化。此 Codelab 的最终 DiceRoller 应用如下所示:

在上一个 Codelab 中,您了解了应用项目的主要部分,包括 javares 目录。在此任务中,您将重点介绍构成应用的两大最重要的文件:MainActivity Kotlin 文件和 activity_main.xml 布局文件。

第 1 步:检查 MainActivity

MainActivityActivity 的一个示例。Activity 是一个核心 Android 类,用于绘制 Android 应用界面 (UI) 并接收输入事件。当应用启动时,它会启动 AndroidManifest.xml 文件中指定的 activity。

许多编程语言都定义了一个启动程序的主方法。Android 应用没有 main 方法。相反,AndroidManifest.xml 文件表示当用户点按应用的启动器图标时,应启动 MainActivity。为了启动 activity,Android 操作系统会使用清单中的信息来设置应用的环境并构建 MainActivity。然后,MainActivity会依次执行一些设置。

每个 activity 都有一个关联的布局文件。activity 和布局通过一个称为布局扩充的过程连接在一起。当 activity 启动时,XML 布局文件中定义的视图会转换为内存中的 Kotlin 视图对象(或“扩充”为 Kotlin 视图对象)。发生这种情况后,activity 可以将这些对象绘制到屏幕上,还可以动态修改它们。

  1. 在 Android Studio 中,依次选择 File > New > New Project 以创建新项目。使用 Empty activity,然后点击 Next
  2. 调用项目 DiceRoller,并验证项目名称、项目位置的所有其他值。确保“Use AndroidX Artifacts”处于选中状态。点击完成


  3. Project > Android 窗格中,展开 java > com.example.android.diceroller。双击 MainActivity。代码编辑器显示了 MainActivity 中的代码。


  4. 在软件包名称和 import 语句下方是 MainActivity 的类声明。MainActivity 类扩展 AppCompatActivity
class MainActivity : AppCompatActivity() { ...
  1. 请注意 onCreate() 方法。activity 不使用构造函数来初始化对象。相反,系统会在 activity 设置过程中调用一系列预定义的方法(称为“生命周期方法”)。其中一个生命周期方法是 onCreate(),您始终会在自己的应用中替换该方法。您将在后面的 Codelab 中详细了解生命周期方法。

    onCreate() 中,您可以指定与 activity 关联的布局,并扩充该布局。setContentView() 方法会同时执行这两项操作。
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
}

setContentView() 方法使用 R.layout.activity_main 引用布局,而 R.layout.activity_main 实际上是一个整数引用。R 类是在您构建应用时生成的。R 类包含应用的所有资源,包括 res 目录的内容。

在本例中,R.layout.activity_main 指的是生成的 R 类、layout 文件夹和 activity_main.xml 布局文件。(资源不包含文件扩展名。)您将使用 R 类中的类似引用来引用应用的许多资源(包括图片、字符串和布局文件中的元素)。

第 2 步:检查并探索应用布局文件

应用中的所有 activity 在应用的 res/layout 目录中都有关联的布局文件。布局文件是一个 XML 文件,用于表达 activity 的实际外观。布局文件通过定义视图以及定义视图在屏幕上的显示位置来实现此目的。

视图是指扩展了 View 类的文本、图片和按钮等内容。视图类型有很多,包括 TextViewButtonImageViewCheckBox

在此任务中,您将检查并修改应用布局文件。

  1. Project > Android 窗格中,展开 res > layout,然后双击 activity_main.xml。系统随即会打开布局设计编辑器。Android Studio 包含此编辑器,可让您以可视化方式构建应用的布局并预览布局设计。在后续的 Codelab 中,您将详细了解设计编辑器。
  2. 如需以 XML 格式查看布局文件,请点击窗口底部的 Text 标签。


  3. 删除布局编辑器中的所有现有 XML 代码。如果您使用的是 Android Studio 设计编辑器,那么新项目随附的默认布局是一个不错的起点。在本课程中,您将使用底层 XML 从头开始构建新布局。
  4. 将此代码复制并粘贴到布局中:
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout   
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

</LinearLayout>

现在,我们来检查一下代码:

  1. 布局的顶层或根元素是 <LinearLayout> 元素。LinearLayout 视图是一个 ViewGroup。视图组是包含其他视图的容器,有助于指定视图在屏幕上的位置。

    您添加到布局中的所有视图和视图组都以视图层次结构的形式进行组织,其中最顶层的 XML 元素是该层次结构的根。根视图可以包含其他视图和视图组,而所包含的视图组可以包含其他视图和视图组。当应用运行 XML 布局文件中的视图层次结构时,布局扩充后,该层次结构会变成对象层次结构。在这种情况下,根视图组是一个线性布局,用于以线性方式(垂直或水平)组织其子视图。

    新 Android 项目的默认根元素是 ConstraintLayout,它与设计编辑器配合使用效果很好。对于此应用,您将使用比约束布局更简单的 LinearLayout 视图组。在下一课中,您将详细了解视图组和 ConstraintLayout。
  2. LinearLayout 标记内,注意 android:layout_width 属性。此 LinearLayout 的宽度设置为 match parent,这使其宽度与其父级相同。由于这是根视图,因此布局会展开到屏幕的完整宽度。
  3. 请注意,android:layout_height 属性设置为 wrap_content。此属性可使 LinearLayout 的高度与其包含的所有视图(目前只有 TextView)的总高度一致。
  4. 检查 <TextView> 元素。此 TextView 用于显示文本,是 DiceRoller 应用中唯一的视觉元素。android:text 属性包含要显示的实际字符串,在本例中为字符串 "Hello World!"
  5. 请注意 <TextView> 元素中的 android:layout_widthandroid:layout_height 属性,它们均设置为 wrap_content。文本视图的内容就是文本本身,因此该视图只会占用文本所需的空间。

如果用户无法掷骰子并查看掷出的点数,那么掷骰子应用就没什么用。首先,向布局添加一个用于掷骰子的按钮,并添加用于显示用户掷出的骰子值的文本。

第 1 步:向布局添加按钮

  1. 在文本视图下方的布局中添加一个 Button 元素,方法是输入 <Button,然后按回车键。系统会显示一个以 /> 结尾并包含 layout_widthlayout_height 属性的 Button 块。
<Button
   android:layout_width=""
   android:layout_height="" />
  1. layout_widthlayout_height 属性均设置为 "wrap_content"。使用这些值时,按钮的宽度和高度与其中包含的文本标签相同。
  2. 为按钮添加 android:text 属性,并将其值设为“Roll”。Button 元素现在如下所示:
<Button
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="Roll" />


对于 Button 视图,text 属性是按钮的标签。在布局编辑器中,该属性会以黄色突出显示,表示提示或警告。在本例中,黄色突出显示是因为字符串 "Roll" 硬编码在按钮标签中,但该字符串应为资源。您将在下一部分中了解字符串资源。

第 2 步:提取字符串资源

最好将应用的所有字符串都放在一个单独的文件中,而不是将字符串硬编码到布局或代码文件中。该文件名为 strings.xml,位于应用的资源中,即 res/values/ 目录中。

将字符串放在单独的文件中可让您更轻松地管理它们,尤其是在您多次使用这些字符串的情况下。此外,字符串资源对于翻译和本地化应用来说是必需的,因为您需要为每种语言创建一个字符串资源文件。

Android Studio 会通过提示和警告来帮助您记住将字符串放入资源文件中。

  1. <Button> 代码的 android:text 属性中,点击一次“Roll”字符串。
  2. Alt+Enter(在 macOS 上为 Option+Enter),然后从弹出式菜单中选择 Extract string resource
  3. 输入 roll_label 作为资源名称
  4. 点击确定。在 res/values/string.xml 文件中创建字符串资源,并将 Button 元素中的字符串替换为对该资源的引用:
    android:text="@string/roll_label"
  5. Project > Android 窗格中,依次展开 res > values,然后双击 strings.xml 以在 strings.xml 文件中查看字符串资源:
<resources>
   <string name="app_name">DiceRoller</string>
   <string name="roll_label">Roll</string>
</resources>

第 3 步:设置视图的样式和位置

您的布局现在包含一个 TextView 视图和一个 Button 视图。在此任务中,您将排列视图组中的视图,使其看起来更具吸引力。

  1. 点击 Design 标签页可查看布局的预览效果。目前,这两个视图并排显示在屏幕顶部。


  2. 点击 Text 标签页,返回到 XML 编辑器。为 LinearLayout 标记添加 android:orientation 属性,并将其值设为 "vertical"<LinearLayout> 元素现在应如下所示:
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   tools:context=".MainActivity">

LinearLayout 视图组会将其包含的视图按顺序排列在一行中(水平排列)或一叠中(垂直排列)。默认值为水平。由于您希望 TextView 堆叠在 Button 之上,因此您将方向设置为垂直。现在,设计如下所示,按钮位于文字下方:

  1. TextViewButton 添加 android:layout_gravity 属性,并将其值设为 "center_horizontal"。这会使两个视图沿水平轴的中心对齐。TextView 和 Button 元素现在应如下所示:
<TextView   
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center_horizontal"
   android:text="Hello World!" />

<Button
   android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="@string/roll_label" />
  1. 向线性布局添加 android:layout_gravity 属性,并为其赋予值 "center_vertical"。您的 LinearLayout 元素现在应如下所示:
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   android:layout_gravity="center_vertical"
   tools:context=".MainActivity">
  1. 如需增大文本视图中的文字大小,请向 <TextView> 元素添加 android:textSize 属性,并将该属性的值设为 "30sp"sp 缩写代表可缩放像素,这是一种用于调整文字大小的度量单位,与设备的显示质量无关。TextView 元素现在应如下所示:
<TextView   
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center_horizontal"
   android:textSize="30sp"
   android:text="Hello World!" />
  1. 编译并运行您的应用。


现在,文字和按钮都放置得很好,并且文字视图中的文字更大。该按钮目前没有任何功能,因此点击它不会发生任何事情。您接下来会处理该问题。

第 4 步:在代码中获取对按钮的引用

MainActivity 中的 Kotlin 代码负责定义应用的互动部分,例如点按按钮时会发生什么。如需编写在点击按钮时执行的函数,您需要在 MainActivity 中获取对已扩充布局中 Button 对象的引用。如需获取对按钮的引用,请执行以下操作:

  • 在 XML 文件中为 Button 分配 ID。
  • 在代码中使用 findViewById() 方法可获取对具有特定 ID 的 View 的引用。

获得对 Button 视图的引用后,您可以在应用运行时调用该视图的方法来动态更改它。例如,您可以添加一个点击处理程序,以便在用户点按按钮时执行代码。

  1. 打开 activity_main.xml 布局文件(如果尚未打开),然后点击 Text 标签页。
  2. 向按钮添加 android:id 属性,并为其指定一个名称(在本例中为“@+id/roll_button"”)。现在,您的 <Button> 元素如下所示:
<Button
   android:id="@+id/roll_button"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center_horizontal"
   android:text="@string/roll_label" />

在 XML 布局文件中为视图创建 ID 时,Android Studio 会在生成的 R 类中创建一个具有该 ID 名称的整数常量。因此,如果您将视图命名为 roll_button,Android Studio 会在 R 类中生成并创建一个名为 roll_button 的整数常量。ID 名称的 "@+id" 前缀会告知编译器将该 ID 常量添加到 R 类。XML 文件中的所有视图 ID 都必须带有此前缀。

  1. 打开 MainActivity Kotlin 文件。在 onCreate() 内的 setContentView() 之后,添加以下行:
val rollButton: Button = findViewById(R.id.roll_button)

使用 findViewById() 方法获取您在 XML 类中定义的视图的 View 引用。在这种情况下,您将从 R 类获取 Button 引用和 ID roll_button,并将该引用分配给 rollButton 变量。

  1. 请注意,Android Studio 会以红色突出显示 Button 类并为其添加下划线,以表明它是一个未解析的引用,您需要先导入此类,然后才能使用它。系统可能还会显示一个提示,其中包含完全限定的类名称:


  2. Alt+Enter(在 Mac 上,按 Option+Enter),接受完全限定类名。

第 5 步:添加点击处理程序以显示 Toast

点击处理程序是一种方法,每当用户点击或点按可点击的界面元素(例如按钮)时,系统都会调用该方法。如需创建点击处理程序,您需要:

  • 执行某种操作的方法。
  • setOnClickHandler() 方法,用于将 Button 连接到处理程序方法。

在此任务中,您将创建一个点击处理程序方法来显示 Toast。(消息框是指在屏幕上短暂弹出的消息。)将点击处理程序方法连接到 Button

  1. MainActivity 类中的 onCreate() 之后,创建一个名为 rollDice() 的私有函数。
private fun rollDice() {
  
}
  1. 将以下这行代码添加到 rollDice() 方法,以便在调用 rollDice() 时显示 Toast
Toast.makeText(this, "button clicked", 
   Toast.LENGTH_SHORT).show()

如需创建 Toast,请调用 Toast.makeText() 方法。此方法需要三个方面:

  • 一个 Context 对象。借助 Context 对象,您可以与 Android OS 通信并获取有关其当前状态的信息。您需要在此处添加 Context,以便 Toast 对象可以告知操作系统显示 Toast。由于 AppCompatActivityContext 的子类,因此您只需使用关键字 this 即可获取相应上下文。
  • 要显示的消息,此处为 "button clicked"
  • 显示消息的时长。末尾的 show() 方法会显示 Toast。
  1. onCreate() 中,在对 findViewById() 的调用后,添加以下行以将 rollDice() 分配为 rollButton 对象的点击处理程序:
rollButton.setOnClickListener { rollDice() }

MainActivity 类的完整定义现在应如下所示:

class MainActivity : AppCompatActivity() {

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

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

    private fun rollDice() {
        Toast.makeText(this, "button clicked",
            Toast.LENGTH_SHORT).show()
    }
}
  1. 编译并运行您的应用。每次点按按钮时,都应显示一个 Toast。

在此任务中,您将修改 rollDice() 方法以更改 TextView 中的文本。在第一步中,您将该文本从 "Hello World!" 更改为字符串 "Dice Rolled!"。在第二步中,您显示一个介于 1 到 6 之间的随机数。

第 1 步:显示字符串

  1. 打开 activity_main.xml,然后向 TextView 添加 ID。
android:id="@+id/result_text"
  1. 打开 MainActivity。在 rollDice() 方法中,注释掉用于显示 Toast 的代码行。
  2. 使用 findViewById() 方法按 ID 获取对 TextView 的引用。将引用分配给 resultText 变量。
val resultText: TextView = findViewById(R.id.result_text)
  1. resultText.text 属性分配新字符串以更改显示的文本。您可以忽略将该字符串提取到资源中的提示;这只是一个临时字符串。
resultText.text = "Dice Rolled!"
  1. 编译并运行应用。请注意,现在点按 Roll 按钮会更新 TextView

第 2 步:显示随机数

最后,在此任务中,您将为按钮点击添加随机性,以模拟掷骰子。每次点击或点按该按钮时,您的代码都会从 1 到 6 中随机选择一个数字,并更新 TextView。生成随机数的任务并非 Android 特有的,您可以使用 Random 类来完成此任务。

  1. rollDice() 方法的顶部,使用 Random.nextInt() 方法获取介于 1 到 6 之间的随机数:
val randomInt = Random().nextInt(6) + 1
  1. text 属性设置为随机整数的值(以字符串形式):
resultText.text = randomInt.toString()
  1. 编译并运行应用。每次点按 Roll 按钮时,文本视图中的数字都会发生变化。

Android Studio 项目:DiceRoller

挑战:向应用添加第二个按钮,该按钮标记为“Count Up”,显示在 Roll 按钮的正下方。点按 Count Up 按钮时,该按钮应获取结果文本视图的当前值,将该值加 1,然后更新文本视图。请务必处理以下边缘情况:

  • 如果结果文本视图尚不包含数字(即,如果文本视图仍具有默认的“Hello World”字符串),请将结果文本设置为 1。
  • 如果该数字已为 6,则无需执行任何操作。

编码挑战解决方案代码

Android Studio 项目:DiceRoller-challenge

活动

  • MainActivityAppCompatActivity 的子类,而 AppCompatActivity 又是 Activity 的子类。Activity 是一个核心 Android 类,负责绘制 Android 应用界面和接收输入事件。
  • 所有 activity 都有关联的布局文件,该文件是应用资源中的 XML 文件。布局文件以 activity 命名,例如 activity_main.xml
  • MainActivity 中的 setContentView() 方法将布局与 activity 相关联,并在创建 activity 时扩充该布局。
  • 布局扩充是指将 XML 布局文件中定义的视图转换为(或“扩充为”)内存中的 Kotlin 视图对象的过程。布局膨胀后,Activity 可以将这些对象绘制到屏幕上并动态修改它们。

视图

  • 应用布局中的所有界面元素都是 View 类的子类,称为视图TextViewButton 是视图的示例。
  • View 元素可以分组到 ViewGroup 中。视图组充当其中视图或其他视图组的容器。LinearLayout 是一个以线性方式排列视图的视图组示例。

查看属性

  • android:layout_widthandroid:layout_height 属性用于指示视图的权重和高度。match_parent 值会将视图拉伸到其父级的宽度或高度。wrap_content 值会将视图缩小到适合视图内容的大小。
  • android:text 属性用于指示视图应显示的文本(如果该视图显示文本)。对于按钮,android:text 是按钮标签。
  • LinearLayout 视图组中的 android:orientation 属性用于排列其包含的视图元素。值为 horizontal 时,视图从左到右排列。值为 vertical 时,视图会从上到下排列。
  • android:layout_gravity 属性用于确定视图及其所有子视图的放置位置。
  • android:textSize 属性用于定义文本视图中文字的大小。文字大小以 sp 单位(可缩放像素)指定。通过使用 sp 单位,您可以独立于设备的显示质量来调整文字大小。

字符串

  • 最佳实践是使用字符串资源,而不是在布局中硬编码字符串。
  • 字符串资源包含在 values/res/string.xml 文件中。
  • 如需提取字符串,请使用 Alt+Enter(在 Mac 上,使用 Option+Enter)。从弹出式菜单中选择 Extract string resources

使用视图

  • 如需将 Kotlin 代码连接到布局中定义的视图,您需要在视图扩充后获取对视图对象的引用。在布局中为视图分配一个 ID (android:id),然后使用 findViewById() 方法获取关联的视图对象。
  • 在 XML 布局文件中为视图创建 ID 时,Android Studio 会在生成的 R 类中创建一个具有该 ID 名称的整数常量。然后,您可以在 findViewById() 方法中使用该 R.id 引用。
  • 您可以在 Kotlin 代码中直接通过属性名称设置视图对象的属性。例如,文本视图中的文本由 XML 中的 android:text 属性定义,而在 Kotlin 中则由 text 属性定义。
  • 点击处理程序是一种在用户点击或点按界面元素时调用的方法。如需将点击处理方法附加到视图(例如按钮),请使用 setOnClickListener() 方法。

使用 Toast

消息框是一种视图,可在小型弹出式窗口中向用户显示简单消息。

如需创建 Toast,请对 Toast 类调用 makeText() 工厂方法,并传入三个实参:

如需显示消息框,请调用 show()

Udacity 课程:

Android 开发者文档:

其他:

此部分列出了在由讲师主导的课程中,学生学习此 Codelab 后可能需要完成的家庭作业。讲师自行决定是否执行以下操作:

  • 根据需要布置作业。
  • 告知学生如何提交家庭作业。
  • 给家庭作业评分。

讲师可以酌情采纳这些建议,并且可以自由布置自己认为合适的任何其他家庭作业。

如果您是在自学此 Codelab,可随时通过这些家庭作业来检测您的知识掌握情况。

更改应用

打开 DiceRoller 应用。向该应用添加一个标签为“重置”的按钮,该按钮显示在 Roll 按钮的正下方。让该按钮将结果文本视图重置为 0。

回答以下问题

问题 1

Activity 上的哪种方法可膨胀应用的布局,并使其视图作为对象可用?

  • onCreate()
  • setClickListener()
  • setContentView()
  • show()

问题 2

您会使用哪个视图属性来设置视图的宽度,以便它进行调整来适应内容?

  • android:view_width="wrap"
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:layout_width="match_parent"

提交应用以进行评分

检查以确保应用具有以下内容:

  • 应用布局应包含一个文本视图和两个按钮。
  • 应用的代码应设置两个点击处理程序,每个按钮对应一个。
  • 用于重置文本视图的点击处理程序应将文本属性设置为 0。

开始学习下一课:1.3 图片资源和兼容性

如需本课程中其他 Codelab 的链接,请参阅“Android Kotlin 基础知识”Codelab 着陆页