1. 准备工作
此 Codelab 将介绍如何自行构建一个名为 Lemonade 的新应用,并且会逐步引导您在 Android Studio 完成该项目,包括设置和测试。
此 Codelab 与本课程中的其他 Codelab 不同。与之前 Codelab 的不同点在于,此 Codelab 的目的不是提供关于如何构建应用的分步教程,而是设置一个将由您独立完成的项目,从而为您提供有关如何完成应用及自行检查您的工作成果的说明。
我们在您要下载的应用中提供了测试套件,而不是解决方案代码。您将在 Android Studio 中运行这些测试(此 Codelab 稍后会对此进行演示),并查看您的代码是否通过测试。您可能需要进行多次尝试;即使是专业开发者,也很少有人能在第一次尝试时就通过所有测试!只有在您的代码通过所有测试后,您才可以将此项目视为已完成。
我们理解,您可能只是想对照解决方案代码来检查自己的代码。我们故意不提供解决方案代码,因为我们希望您像一名专业开发者一样进行练习。这可能需要您运用一些平时练得不多的其他技能,例如:
- 使用 Google 搜索引擎搜索应用中出现的您不理解的术语、错误消息和代码段;
- 测试代码,查看错误,然后更改代码并再次测试;
- 复习前面学过的“Android 基础知识”第 1 单元的内容;
- 将您知道可以正常运行的代码(即项目中给出的代码,或您之前在第 1 单元中学过的其他应用的解决方案代码)与您编写的代码进行比较。
一开始您可能会感到有些望而却步,不过如果您能够完成第 1 单元,那就说明您已经为完成此项目做好了准备。对此,我们有十足的信心。慢慢来,不要放弃。您是可以完成此项目的。
前提条件
- 此项目的适用对象为已经完成《使用 Kotlin 进行 Android 开发的基础知识》课程第 1 单元的用户。
构建内容
- 您将使用在第 1 单元中所学的技能,构建一个简单的 Lemonade 应用。
所需条件
- 一台安装了 Android Studio 的计算机。
2. 应用概览
欢迎参与“项目:Lemonade 应用”!
欢迎加入我们的团队,协助我们把制作数字柠檬汁的愿景变为现实。我们的目标是构建一款简单的互动式移动应用,让您使用该应用榨一杯柠檬汁。当然,这里所说的榨汁只是打个比方,您也可以把构建该应用作为一种打发时间的消遣方式!
完成后的 Lemonade 应用由单个画面构成。用户首次启动该应用时,系统会提示他们通过点按柠檬树图片来选择一个柠檬。
用户点按柠檬树后,会看到一个柠檬,点按该柠檬便可以“榨汁”。压榨一定次数(所需的确切压榨次数是随机生成的)后,会转到下一个画面。
用户点按柠檬进行榨汁达到正确的次数后,系统会显示一杯柠檬汁的图片,让用户“饮用”。
用户点按杯子“饮用”柠檬汁后,杯子会变空。用户可以再次点按杯子以返回第一个画面,并从树上选择其他柠檬。
这款应用的构建理念在于简单易用,全程只用到一个 activity。应用的不同状态(无论是用户选择柠檬、将柠檬榨汁、饮用柠檬汁还是最后杯子变空)均使用“状态机”来表示。这听起来像是一个花哨的术语,但其实想表达的意思是:应用的状态(即向用户显示的是哪些文字和图片)由一个包含应用状态(select
、squeeze
等)的变量来确定。系统会更新应用的状态以及所需的任何其他变量,并在完成所有更新后单独对界面进行配置(设置图片和文字)。
我们已为您定义应用状态的所有变量。您的任务是构建应用的布局并实现相应逻辑,以便界面按预期在各状态之间转换。
测试您的代码
对于 Lemonade 应用(以及今后的项目),我们会为您提供一些自动化测试,供您验证您的代码是否一切正常。
到底什么是自动化测试?在软件开发中,您可以将“测试”视为用于验证其他代码能否正常发挥作用的代码。具体方法是,检查输出(例如屏幕上界面元素的内容),看看对于输入而言是否有意义,这称为“测试用例”。Lemonade 应用的起始项目中包含几项测试,您可以运行这些测试,以确保正确实现了相应逻辑。稍后我们将详细介绍这些测试。现在,请下载起始代码并开始构建 Lemonade 应用。
3. 开始
下载项目代码
请注意,文件夹名称为 android-basics-kotlin-lemonade-app
。在 Android Studio 中打开项目时,请选择此文件夹。
如需获取此 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 按钮 以构建并运行应用。请确保该应用按预期构建。
请花点时间熟悉一下入门级项目。要特别注意 MainActivity.kt
文件。
在 MainActivity.kt
中,您会看到几个用于表示应用当前状态的变量。您在后续步骤中会用到这些变量,以让应用具有互动性。虽然此处的代码量看起来非常大,但您无需修改任何未使用 TODO 进行标记的代码。如需具体说明,请参阅后续页面。
此外,您会发现项目中还包含另一个软件包,即 com.example.lemonade (androidTest)。
该软件包中包含一些自动化测试。您将使用这些测试来验证在 MainActivity.kt
中实现的功能是否可以正常运行。我们稍后会再次对其进行详细介绍。现在,您可以开始构建应用了,我们从界面开始。
4. 构建界面
Lemonade 应用仅需要基本的布局;您只需使用两个视图即可实现它的所有功能。
- 一个
TextView
,用于为用户提供说明。 - 一个
ImageView
,用于根据应用的当前状态(例如,将柠檬榨汁)显示图形。
您将在 activity_main.xml
中构建此布局。
您的目标是,运用您掌握的布局编辑器知识,构建一个如下所示的布局,其中这两个视图都处于屏幕中心位置,并且 TextView
位于 ImageView
上方。
5. 让应用具有互动性
构建好布局后,打开 MainActivity.kt
。您将在此处实现应用的所有逻辑。您会看到已有相当多的代码。此外,还有许多标有 // TODO:
的注释(示例如下)。这些是需要您完成的任务。
您需要完成三项基本任务,才能使 Lemonade 应用正常运行。
- 配置
lemonImage
ImageView
,以响应用户输入。 - 实现
clickLemonImage()
,以更新应用的状态。 - 实现
setViewElements()
,以根据应用的当前状态更新界面。
下面我们依次看一下每项任务。
第 1 步:配置 ImageView
点按图片视图应将应用从一种状态转换为另一种状态。在 onCreate()
末尾,请注意有两个监听器需要设置。
setOnClickListener()
应更新应用的状态。可通过clickLemonImage()
方法做到这一点。setOnLongClickListener()
应对用户长按图片的事件做出响应(例如,用户点按图片后没有立即松开手指)。对于长按事件,屏幕底部会显示一个名为“信息条”的 widget,告知用户他们已压榨柠檬的次数。这是通过showSnackbar()
方法实现的。
在下一步中,您将实现用于更改应用状态的逻辑。
第 2 步:实现 clickLemonImage()
完成上一步后,现在每当用户点按图片时,系统都会调用 clickLemonImage()
方法。该方法负责将应用从当前状态转换到下一个状态,并根据需要更新任何变量。有四种可能的状态:SELECT
、SQUEEZE
、DRINK
和 RESTART
;当前状态由 lemonadeState
变量表示。对于每种状态,此方法需要执行不同的操作。
SELECT
:转换为SQUEEZE
状态,通过调用pick()
方法并将squeezeCount
(用户已压榨柠檬的次数)设置为 0 来设置lemonSize
(所需的压榨次数)。SQUEEZE
:以 1 为增量递增squeezeCount
,并以 1 为减量递减lemonSize
。请记住,柠檬需要压榨不定次,应用才能转换其状态。仅当新的lemonSize
等于 0 时,应用才会转换为DRINK
状态。否则,应用应保持SQUEEZE
状态。DRINK
:转换为RESTART
状态,并将lemonSize
设置为 -1。RESTART
:转换回SELECT
状态。
处理完所有更新以及状态之间的转换后,请务必调用 setViewElements()
以基于新的状态更新界面。
第 3 步:实现 setViewElements()
setViewElements()
方法负责根据应用的状态更新界面。应使用下方显示的值更新文字和图片,使其与 lemonadeState
相符。
SELECT
:
- 文字:点击以选择柠檬!
- 图片:
R.drawable.lemon_tree
SQUEEZE
:
- 文字:点击以将柠檬榨汁!
- 图片:
R.drawable.lemon_squeeze
DRINK
:
- 文字:点击以饮用柠檬汁!
- 图片:
R.drawable.lemon_drink
RESTART
:
- 文字:点击以重新开始!
- 图片:
R.drawable.lemon_restart
如何使用字符串资源
在 Android 中,几乎所有内容都是资源。定义您之后可以在应用中访问的资源是 Android 开发的重要环节。
资源可用于定义颜色、图片、布局、菜单和字符串值等任何内容。使用资源的好处是,没有任何内容采用硬编码。所有内容都在资源文件中定义,并且您可以在应用的代码中引用定义好的内容。字符串资源是最简单(也最常见)的资源,可用于支持灵活的本地化文字。
字符串或静态文本可以存储在名为 strings.xml 的单独文件中,该文件位于 res 文件夹的 values 子文件夹中。
对于要在应用中显示的每段文本(例如按钮的标签或 TextView
内的文本),您应先在 res/values/strings.xml
文件中定义相应文本。每个条目是一个键(表示文本的 ID)和一个值(文本本身)。例如,如果您希望按钮显示“提交”,请将以下字符串资源添加到 res/values/strings.xml
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello!</string>
<string name="submit_label">Submit</string>
</resources>
如需直接在代码中访问该资源,只需使用 getResources.getString()
或 getString()
方法基于资源 ID 访问值 R.string.submit_label
:
val submitText = getResources().getString(R.string.submit_label)
如要将字符串资源中的文本直接设置为 TextView,可以对 TextView 对象调用 setText()
,并传入资源 ID。
val infoTextView: TextView = findViewById(R.id.info_textview)
infoTextView.setText(R.string.info_text)
字符串资源还可以包含用于设置文本格式的特殊字符。例如,您可能有一个字符串资源,允许您将另一段文本插入字符串中。
<string name="ingredient_tablespoon">%1$d tbsp of %2$s</string>
在代码中,您可以通过传入参数来访问字符串资源并设置其格式。
getResources().getString(R.string.ingredient_tablespoon, 2, "cocoa powder")
声明字符串资源时,每个参数都会按照其显示顺序进行编号(1
、2
等),并用字母来标识类型(十进制数为 d
,字符串为 s
等)。如果参数的类型正确,则可以传递给对 getString()
的调用。
2 tbsp of cocoa powder
如需了解详情,请参阅有关字符串资源的文档。
6. 运行您的应用
构建应用的界面并实现主 activity 后,您就可以实际查看工作成果了。使用 Run > Run ‘app' 菜单运行应用,模拟器将启动。
应用现在可全面互动,您应该能够通过点按图片视图转换状态。
当屏幕上显示柠檬时,您还可以尝试长按(按住)ImageView
,以查看底部显示的信息条,了解已压榨柠檬的总次数。请花些时间多次运行应用并经历所有状态。然后,花一点时间祝贺自己的辛苦工作取得了不错的结果!
7. 测试说明
测试应用
您已完成 Lemonade 应用的实现,但在专业的软件开发中,仅编写代码一般不会是最后一步。除了应用代码,专业的优质应用还包括测试代码,用于验证应用代码能否按预期运行,以及确定对应用代码所做的更改不会引入任何新的 bug,此过程称为“自动化测试”。虽然介绍如何进行自动化测试不在此项目的涵盖范围内,但 Lemonade 应用捆绑了一些测试,可帮助您验证是否正确实现了项目。您可以将这作为一种形式的自我评估,看看您是否已满足所有项目要求,以及是否需要对应用进行任何更改。
到底什么是“测试”?测试只是您的 Android Studio 项目中包含的一段代码,它会运行应用的一部分代码,可能“通过”,也可能“失败”,具体取决于应用代码的行为是否符合预期。
在哪里可以找到并运行应用的测试?您可以在测试“目标”中找到 Lemonade 应用的测试。目标仅仅是一个软件开发术语,表示一组绑定在一起的类。例如,Lemonade 应用位于名为“app”的目标中,而测试位于名为“LemonadeTests”的目标中。虽然 LemonadeTests 目标可以访问应用目标中的代码,但它们是完全独立的,应用的代码不包含任何测试代码。
在“Android”视图中查看文件时,测试目标将采用与应用相同的软件包名称,但带有 (androidTest)。
提到测试代码,有几个关键术语需要了解。
- 测试套件 - 包含您的所有“测试用例”的目标。
- 测试用例 - 包含用于相关功能的各个测试的类(Lemonade 应用只有一个测试用例,但较大的应用通常具有更多测试用例)。
- 测试 - 用于测试特定内容的函数。
一个测试用例可以有多个测试,项目的测试套件可以有多个测试用例。
运行测试
如需运行测试,您可以执行以下任一操作。
对于单个测试用例,打开测试用例类,并点击类声明左侧的绿色箭头。然后,从菜单中选择“Run”选项。这样会运行测试用例中的所有测试。
很多时候您可能只需要运行单个测试,例如,其他测试都通过了,只有一个测试失败。您可以像运行整个测试用例一样运行单个测试。点击绿色箭头并选择 Run 选项。
如果您有多个测试用例,也可以运行整个测试套件。就像运行应用一样,您可以在 Run 菜单中找到此选项。
请注意,Android Studio 默认运行您运行的最后一个目标(应用目标、测试目标等),因此如果菜单仍然显示 Run > Run ‘app',您可以通过选择 Run > Run 运行测试目标。
然后从弹出式菜单中选择测试目标。
运行测试的结果显示在 Run 标签页中。在左侧的窗格中,您会看到失败测试的列表(如有)。如果测试失败,其函数名称旁边会显示红色感叹号。如果测试通过,则测试名称旁边会显示绿色对勾标记。
如果测试失败,文本输出会提供相关信息,帮助您修复导致测试失败的问题。
例如,在上述错误消息中,测试检查 TextView
是否有使用特定字符串资源。但是,测试失败了。“Expected”和“Got”后面的文本不一致,也就是说,测试预期的值与从正在运行的应用中获取的值不一致。在此示例中,TextView
中使用的字符串不是测试所预期的实际上的 squeeze_count
。
8. 可选:分享您的应用!
在享用多杯柠檬汁后,截取您喜欢的屏幕的截图,并在 Twitter 上分享,以展示您学到的知识。分享时请添加 @AndroidDev 标签,以及 #AndroidBasics 标签。