原文:
zh.annas-archive.org/md5/507BA3297D2037C2888F887A989A734A
译者:飞龙
前言
您是否正在尝试开始 Android 编程的职业生涯,但还没有找到合适的方法?您是否有一个很棒的应用程序想法,但不知道如何将其变成现实?或者您只是对学习 Android 必须已经了解 Kotlin 感到沮丧。如果是这样,那么这本书就是为您准备的。
面向初学者的 Kotlin Android 编程将成为您创建 Android 应用程序的指南。我们将向您介绍在 Android 环境中编程的所有基本概念,从 Kotlin 的基础知识到使用 Android API。所有示例都是在官方的 Android 开发环境 Android Studio 中创建的,这有助于加速应用程序开发过程。在这个速成课程之后,我们将深入研究 Android 编程,您将学会如何通过片段创建具有专业标准 UI 的应用程序,并使用 SQLite 存储用户数据。此外,您还将了解如何使您的应用程序支持多语言,如何用手指在屏幕上绘图,以及如何处理图形、声音和动画。
通过本书,您将准备好开始在 Android 和 Kotlin 中构建自己的定制应用程序。
这本书是为谁准备的
如果您完全不了解 Kotlin、Android 或编程,并且想制作 Android 应用程序,那么这本书就是为您准备的。这本书也可以作为那些已经有一些基本使用 Kotlin 在 Android 上经验的人的复习,以提高他们的知识,并通过早期项目快速进步。
本书涵盖内容
第一章 开始使用 Android 和 Kotlin,欢迎您来到令人兴奋的 Android 和 Kotlin 世界。在这一章中,我们将立即开始开发 Android 应用程序。我们将探讨 Android 的优势,Android 和 Kotlin 是什么,它们如何工作和相互补充,以及作为未来开发人员对我们意味着什么。接下来,我们将设置所需的软件,以便我们可以构建和部署一个简单的第一个应用程序。
第二章 Kotlin、XML 和 UI 设计师,讨论了在这个阶段,我们已经拥有了一个可用的 Android 开发环境,并且已经构建和部署了我们的第一个应用程序。然而,显然,由 Android Studio 自动生成的代码不会成为下一个在 Google Play 上畅销的应用程序。我们需要探索这些自动生成的代码,以便我们可以开始了解 Android,然后学习如何在这个有用的模板上构建。
第三章 探索 Android Studio 和项目结构,带领我们创建和运行另外两个 Android 项目。这些练习的目的是更深入地探索 Android Studio 和 Android 项目的结构。当我们为部署构建我们的应用程序时,代码和资源文件需要被打包到 APK 文件中 - 就像它们现在的样子。因此,所有布局文件和其他资源,我们很快就会看到,需要以正确的结构存在。幸运的是,当我们从模板创建项目时,Android Studio 会为我们处理这些。然而,我们仍然需要知道如何找到和修改这些文件,如何添加我们自己的文件,有时删除 Android Studio 创建的文件,以及资源文件如何相互关联 - 有时与彼此关联,有时与 Kotlin 代码关联(即自动生成的 Kotlin 代码以及我们自己的代码)。除了了解我们项目的组成,确保我们充分利用模拟器也是有益的。
第四章《使用布局和 Material Design 入门》建立在我们已经看到的基础上;也就是说,Android Studio UI 设计师和更多 Kotlin 的实际操作。在这个实践性的章节中,我们将构建三个更多的布局 - 仍然相当简单,但比我们到目前为止所做的更上一层楼。在我们开始实际操作之前,我们将快速介绍material design的概念。我们将看看另一种称为LinearLayout
的布局类型,并通过它来创建可用的 UI。我们将进一步使用ConstraintLayout
来理解约束并设计更复杂和精确的 UI 设计。最后,我们将使用TableLayout
来将数据布置在一个易读的表格中。我们还将编写一些 Kotlin 代码,以在一个应用/项目中在不同的布局之间切换。这是第一个将多个主题链接在一起的重要应用程序。这个应用程序被称为探索布局。
第五章《使用 CardView 和 ScrollView 创建美丽的布局》是在我们花一些时间专注于 Kotlin 和面向对象编程之前的最后一个布局章节。我们将对我们已经看到的一些不同属性进行正式学习,并且还将介绍两个更酷的布局,ScrollView
和CardView
。最后,我们将在平板模拟器上运行CardView
项目。
第六章《Android 生命周期》将使我们熟悉 Android 应用程序的生命周期。一开始,计算机程序有生命周期这个概念可能听起来很奇怪,但很快就会变得合理。生命周期是所有 Android 应用程序与 Android 操作系统交互的方式。就像人类的生命周期使他们能够与周围的世界互动一样,我们别无选择,只能与 Android 生命周期互动,并且必须准备处理许多不可预测的事件,如果我们希望我们的应用程序能够生存下来。我们将探索应用程序经历的生命周期阶段,从创建到销毁,以及这如何帮助我们知道根据我们想要实现的目标在何处放置我们的 Kotlin 代码。
第七章《Kotlin 变量、运算符和表达式》,以及接下来的一章,解释了 Kotlin 的核心基础。实际上,我们将探索编程的主要原则。在这一章中,我们将专注于数据本身的创建和理解,在下一章中,我们将探讨如何操作和响应数据。
第八章《Kotlin 决策和循环》从变量中移开,我们现在了解如何使用表达式更改它们所持有的值,但是当一个变量的值取决于一个变量时,我们如何采取行动呢?我们当然可以将新消息的数量添加到先前未读消息的数量中,但是例如,当用户已经阅读了所有消息时,我们如何触发应用程序中的操作呢?第一个问题是我们需要一种测试变量值的方法,然后在值落在一系列值范围内或等于特定值时做出响应。编程中常见的另一个问题是,我们需要根据变量的值执行代码的某些部分一定次数(多次,或者有时根本不执行)。为了解决第一个问题,我们将学习如何在 Kotlin 中使用if
、else
和when
做出决策。为了解决后者,我们将学习 Kotlin 中的循环,包括while
、do
-while
、for
、continue
和break
。此外,我们将了解在 Kotlin 中,决策也是产生值的表达式。
第九章,“Kotlin 函数”,解释了函数是我们应用程序的构建块。我们编写执行特定任务的函数,然后在需要执行该特定任务时调用它们。由于我们在应用程序中需要执行的任务将是相当多样化的,我们的函数需要满足这一点并且非常灵活。Kotlin 函数非常灵活,比其他与 Android 相关的语言的函数更灵活。因此,我们需要花费整整一章的时间来学习它们。函数与面向对象编程密切相关,一旦我们理解了函数的基础知识,我们就能够很好地掌握更广泛的面向对象编程的学习。
第十章,“面向对象编程”,解释了在 Kotlin 中,类对几乎所有事情都是基本的,事实上,几乎所有事情都是一个类。我们已经谈到了重用其他人的代码,特别是 Android API,但在本章中,我们将真正掌握这是如何工作的,并学习面向对象编程(OOP)以及如何使用它。
第十一章,“Kotlin 中的继承”,展示了继承的实际应用。事实上,我们已经看到了,但现在我们将更仔细地研究它,讨论其好处,并编写我们从中继承的类。在整个章节中,我将向您展示几个继承的实际例子,在本章结束时,我们将改进上一章的海战模拟,并展示如何通过使用继承来节省大量的输入和未来的调试工作。
第十二章,“将我们的 Kotlin 连接到 UI 和空值”,在本章结束时,完全揭示了我们的 Kotlin 代码和我们的 XML 布局之间的缺失链接,使我们能够像以前一样向我们的布局添加各种小部件和 UI 功能,但这一次我们将能够通过我们的代码来控制它们。在本章中,我们将控制一些简单的 UI 元素,如Button
和TextView
,在下一章中,我们将进一步操作一系列 UI 元素。为了使我们能够理解发生了什么,我们需要更多地了解应用程序中的内存,特别是其中的两个领域 - 堆栈和堆。
第十三章,“将 Android 小部件带到生活中”,讨论了由于我们现在对 Android 应用程序的布局和编码以及我们新获得的面向对象编程(OOP)的洞察力,以及如何从我们的 Kotlin 代码中操纵 UI 有了很好的概述,我们现在可以尝试使用 Android Studio 调色板中的更多小部件。有时,OOP 是一件棘手的事情,这一章介绍了一些对初学者来说可能尴尬的主题。然而,通过逐渐学习这些新概念并反复练习,它们将随着时间的推移成为我们的朋友。在本章中,我们将通过回到 Android Studio 调色板并查看半打我们要么根本没有看到过要么尚未完全使用过的小部件来进行多样化。一旦我们这样做了,我们将把它们都放入布局中,并练习用我们的 Kotlin 代码操纵它们。
第十四章,“Android 对话框窗口”,解释了如何向用户呈现弹出式对话框窗口。然后,我们可以将我们所知道的一切放入我们的第一个多章节应用程序“Note to self”的第一阶段。然后,我们将在本章和接下来的四章(直到第十八章,“本地化”)中学习有关 Android 和 Kotlin 的新功能,然后利用我们新获得的知识来增强“Note to self”应用程序。
第十五章,“处理数据和生成随机数”,显示我们正在取得良好的进展。我们对 Android UI 选项和 Kotlin 的基础知识有了一个全面的了解。在前几章中,我们开始将这两个领域结合起来,并使用 Kotlin 代码操作 UI,包括一些新的小部件。然而,在构建 Note to self 应用程序时,我们遇到了一些知识上的空白。在本章中,我们将填补这些空白中的第一个,然后在下一章中,我们将使用这些新信息来继续应用程序。我们目前没有办法管理大量相关数据。除了声明,初始化和管理数十,数百甚至数千个属性或实例之外,我们如何让我们的应用程序用户拥有多个笔记?我们还将快速了解随机数。
第十六章,“适配器和回收器”,首先带我们了解适配器和列表的理论。然后,我们将看看如何在 Kotlin 代码中使用RecyclerAdapter
实例,并将RecyclerView
小部件添加到布局中,该小部件充当我们的 UI 的列表,然后通过 Android API 的明显魔力将它们绑定在一起,以便RecyclerView
实例显示RecyclerAdapter
实例的内容,并允许用户滚动查看充满Note
实例的ArrayList
实例的内容。您可能已经猜到,我们将使用这种技术来显示 Note to self 应用程序中的笔记列表。
第十七章,“数据持久性和共享”,介绍了将数据保存到 Android 设备的永久存储的几种不同方法。此外,我们将首次向我们的应用程序添加第二个Activity
实例。在我们的应用程序中实现一个单独的“屏幕”,例如“设置”屏幕时,将新的Activity
实例添加到其中通常是有意义的。我们可以费力地隐藏原始 UI,然后在同一个Activity
中显示新的 UI,就像我们在第四章,“使用布局和 Material Design 入门”中所做的那样,但这很快会导致令人困惑和容易出错的代码。因此,我们将看到如何添加另一个Activity
实例并在它们之间引导用户。
第十八章,“本地化”,快速简单,但我们将学习的内容可以使您的应用程序可供数百万潜在用户使用。我们将看到如何添加其他语言,以及为什么通过字符串资源以正确的方式添加文本在添加多种语言时对我们有益。
第十九章,“动画和插值”,探讨了如何使用Animation
类使我们的 UI 不那么静态,更有趣。正如我们所期望的那样,Android API 将允许我们用相对简单的代码做一些相当高级的事情,Animation
类也不例外。
第二十章,“绘图图形”,涉及 Android 的Canvas
类和一些相关类,如Paint
,Color
和Bitmap
。当这些类结合在一起时,在屏幕上绘图具有很大的能力。有时,Android API 提供的默认 UI 并不是我们所需要的。如果我们想制作绘图应用程序,绘制图表,或者可能制作游戏,我们需要控制 Android 设备提供的每个像素。
第二十一章,“线程和启动实时绘图应用程序”,让我们开始我们的下一个应用程序。这个应用程序将是一个儿童风格的绘画应用程序,用户可以用手指在屏幕上绘画。然而,我们创建的绘画应用程序将略有不同。用户绘制的线条将由粒子系统组成,这些粒子系统会爆炸成成千上万的碎片。我们将称这个项目为实时绘图。
第二十二章,“粒子系统和处理屏幕触摸”,在上一章中使用线程实现了我们的实时系统。在本章中,我们将创建实体,它们将存在并在这个实时系统中演变,就好像它们有自己的思想,并形成用户可以创建的绘画外观。我们还将看到用户如何通过学习如何响应与屏幕的交互来实现这些实体。这与在 UI 布局中与小部件交互是不同的。
第二十三章,“Android 音效和 Spinner 小部件”,探讨了 SoundPool 类以及我们根据是否只想播放声音或进一步跟踪我们正在播放的声音而使用它的不同方式。在这一点上,我们可以将我们学到的一切都投入到制作一个很酷的声音演示应用中,这也将向我们介绍一个新的 UI 小部件;Spinner。
第二十四章,“设计模式、多个布局和片段”,展示了我们从一开始就设置 Android Studio 以来所走过的路程。当时,我们一步一步地进行了所有操作,但随着我们的进行,我们试图不仅仅是展示如何将 x 添加到 y,或者将特性 a 添加到应用程序 b,而是让您能够以自己的方式使用所学知识,以实现自己的想法。这一章更多地涉及您未来应用程序的内容,而不是书中迄今为止的任何内容。我们将看一些 Kotlin 和 Android 的方面,您可以将其用作框架或模板,以制作更加令人兴奋和复杂的应用程序,同时保持代码的可管理性。
第二十五章,“具有分页和滑动的高级 UI”,解释了分页是从一页到另一页的移动的行为,在 Android 上,我们通过在屏幕上滑动手指来实现这一点。当前页面会根据手指的移动方向和速度进行过渡。这是一个在应用程序中导航的有用和实用的方式,但也许更重要的是,对用户来说,这是一种极其令人满意的视觉效果。此外,与RecyclerView
一样,我们可以选择性地仅加载当前页面所需的数据,也许是先前和下一页的数据。正如您所期望的那样,Android API 有一些解决方案可以以相当简单的方式实现分页。
第二十六章,“带有导航抽屉和片段的高级 UI”,探讨了(可以说是)最高级的 UI。NavigationView
,或者导航抽屉(因为它滑出内容的方式),可以通过在创建新项目时选择它作为模板来简单创建。我们将这样做,然后我们将检查自动生成的代码,并学习如何与其交互。然后,我们将使用我们对Fragment
类的所有了解来填充每个“抽屉”具有不同行为和视图。然后,在下一章中,我们将学习关于数据库,以为每个Fragment
添加一些新功能。
第二十七章 Android 数据库,解释了如果我们要制作提供给用户重要功能的应用程序,那么几乎肯定我们需要一种管理、存储和过滤大量数据的方法。使用 JSON 可以高效地存储大量数据,但当我们需要有选择地使用这些数据而不仅仅限制在“保存所有”和“加载所有”的选项时,我们需要考虑还有哪些其他选项可用。像往常一样,使用 Android API 中提供的解决方案是有意义的。正如我们所见,JSON
和SharedPreferences
类有其用武之地,但在某个时候,我们需要转而使用真正的数据库来解决现实世界的问题。Android 使用 SQLite 数据库管理系统,正如您所期望的那样,有一个 API 可以尽可能地简化它。
第二十八章 告别前的快速交谈,包含了一些想法和指针,您可能在匆忙离开并制作自己的应用程序之前想要看看。
充分利用本书
要成功阅读本书,您不需要任何经验。如果您对自己选择的操作系统(Windows、Mac 或 Linux)有信心,您可以学习使用 Kotlin 编程语言制作 Android 应用程序。学习开发专业质量的应用程序是任何人都可以开始并坚持下去的旅程。
如果您具有以前的编程(Kotlin、Java 或任何其他语言)、Android 或其他开发经验,那么您将在前几章中取得更快的进展。
下载示例代码文件
您可以从您在www.packtpub.com
的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support
并注册,以便直接通过电子邮件接收文件。
您可以按照以下步骤下载代码文件:
-
在
www.packtpub.com
上登录或注册。 -
选择SUPPORT选项卡。
-
单击Code Downloads & Errata。
-
在搜索框中输入书名,并按照屏幕上的说明操作。
下载文件后,请确保使用最新版本的解压缩或提取文件夹:
-
WinRAR / 7-Zip for Windows
-
Zipeg / iZip / UnRarX for Mac
-
7-Zip / PeaZip for Linux
该书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Android-Programming-with-Kotlin-for-Beginners
。如果代码有更新,将在现有的 GitHub 存储库上进行更新。
我们还有来自我们丰富书籍和视频目录的其他代码包,可在github.com/PacktPublishing/
上找到。去看看吧!
下载彩色图像
我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在这里下载:www.packtpub.com/sites/default/files/downloads/9781789615401_ColorImages.pdf
。
使用的约定
本书中使用了许多文本约定。
CodeInText
:指示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。例如:“将下载的WebStorm-10*.dmg
磁盘映像文件挂载为系统中的另一个磁盘。”
代码块设置如下:
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
当我们希望引起您对代码块的特定部分的注意时,相关的行或项目将以粗体显示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
粗体:表示一个新术语,一个重要的词,或者你在屏幕上看到的词,例如在菜单或对话框中,也会在文本中出现。例如:“如果没有,请点击Logcat选项卡”
注意
警告或重要说明会出现在这样。
提示
提示和技巧会出现在这样。
第一章:开始使用安卓和 Kotlin
欢迎来到令人兴奋的安卓和 Kotlin 世界!在这第一章中,我们将立即开始开发安卓应用程序,不会浪费任何时间。
我们将看看安卓有什么好处,安卓和 Kotlin 是什么,它们如何协同工作和互补,以及对我们作为未来开发者意味着什么。接下来,我们将设置所需的软件,以便我们可以构建和部署一个简单的第一个应用程序。
在本章中,我们将涵盖以下主题:
-
学习 Kotlin 和安卓如何协同工作
-
设置我们的开发环境,安卓 Studio
-
学习什么是安卓应用程序
-
学习 Kotlin
-
构建我们的第一个安卓应用程序
-
部署安卓模拟器
-
在安卓模拟器和真实设备上运行我们的应用程序
这是一个很多内容要学习的,所以让我们开始吧。
为什么使用 Kotlin 和安卓?
当安卓于 2008 年首次出现时,与苹果 iPhone/iPad 上更时尚的 iOS 相比,它显得有些沉闷。但是,通过一系列与实用、价格敏感的消费者以及时尚意识和技术精通的消费者产生共鸣的手机产品,安卓用户数量迅速增加。
对于许多人来说,包括我自己在内,为安卓开发是最有回报的业余爱好和商业。
快速地将一个想法的原型组合起来,完善它,然后决定运行并将其连接成一个完整的应用程序,这是一个令人兴奋和有回报的过程。任何编程都可以很有趣 - 我一生都在编程 - 但为安卓创造东西却是非常有回报的。
确切地定义为什么会这样很难。也许是因为这个平台是免费和开放的。你可以在不需要大型控制公司的许可下分发你的应用程序 - 没有人能阻止你。同时,你也可以在亚马逊应用商店和谷歌 Play 等成熟的、由公司控制的大众市场上分发应用。
然而,更有可能的是,为什么为安卓开发会给人如此好的感觉是因为设备本身的性质。它们是非常个人化的。你可以开发与人们生活互动的应用程序,教育、娱乐、讲故事等等,它就在他们的口袋里准备好了 - 在家里、工作场所或度假中。
你当然可以为桌面构建更大的东西,但知道成千上万(甚至数百万)的人将你的作品装在口袋里并与朋友分享,这不仅仅是一种兴奋。
开发应用程序不再被认为是怪异、书呆子或隐居。事实上,为安卓开发被认为是非常有技巧的,最成功的开发者受到极大的钦佩,甚至崇敬。
如果所有这些空洞的、精神上的东西对你毫无意义,那也没关系;为安卓开发可以让你谋生,甚至让你致富。随着设备拥有量的持续增长,CPU 和 GPU 性能的不断提升,以及安卓操作系统本身的不断演进,对专业应用程序开发者的需求只会增长。
简而言之,最优秀的安卓开发者 - 更重要的是,拥有最佳创意和最大决心的安卓开发者 - 比以往任何时候都更受欢迎。没有人知道这些未来的安卓应用程序开发者是谁,他们甚至可能还没有写下他们的第一行代码。
那么,为什么不是每个人都是安卓开发者呢?显然,并不是每个人都会像我一样对创造能够帮助改善人们生活的软件充满热情,但我猜测,因为你正在阅读这篇文章,你可能会。
初学者的第一个绊脚石
不幸的是,对于那些和我一样对此充满热情的人来说,进步的道路上存在一种玻璃墙,这让许多有抱负的安卓开发者感到沮丧。
Android 要求有志成为开发者的人选择三种编程语言来制作应用程序。每一本 Android 书籍,即使是针对所谓的初学者,也都假设读者至少具有中级水平的 Kotlin、C++或 Java,大多数需要高级水平。因此,良好到优秀的编程知识被视为学习 Android 的先决条件。
不幸的是,在完全不同的环境中学习这些语言有时可能会有点乏味,而你学到的大部分知识也不能直接转移到 Android 的世界中。你可以理解为什么初学者对 Android 往往感到厌倦。
这本书不需要这样。在这本书中,我精心安排了你在厚重的 Kotlin 初学者专著中学到的所有 Kotlin 主题,并将它们重新制作成了三个多章节的应用程序和十多个快速的迷你应用程序,从一个简单的备忘录应用程序开始,逐渐发展到一个酷炫的绘图应用程序和一个数据库应用程序。
如果你想成为一名专业的 Android 开发者,或者只是想在学习 Kotlin 和 Android 时更有乐趣,这本书会帮助你。
Kotlin 和 Android 是如何协同工作的
Android 软件开发工具包(SDK)主要是用 Java 编写的,因为 Kotlin 是新生力量;但是当我们告诉 Android Studio 将我们的 Kotlin 代码转换成可工作的应用程序时,它会与 SDK 中的 Java 合并在一起,形成一种中间形式,然后转换成一种称为 DEX 代码的格式,这是 Android 设备用来转换成运行应用程序的。对于我们开发者来说,这是无缝的,但了解这一点(也许是相当有趣的)是值得的。
无论你是用 Kotlin 还是 Java 编写应用程序,最终的 DEX 代码都是一样的。然而,使用 Kotlin 有一些显著的优势。
Kotlin 是以俄罗斯圣彼得堡附近的一个岛屿命名的。Kotlin 与苹果的 Swift 语言非常相似,因此现在学习 Kotlin 将为学习 iPhone/iPad 开发奠定良好的基础。
Kotlin 是最简洁的语言,因此最不容易出错,这对初学者来说非常好。Kotlin 也是最有趣的语言,主要是因为简洁性意味着你可以更快地得到结果,而且代码更少。谷歌认为 Kotlin 是官方(一流)的 Android 语言。Kotlin 还有一些其他优点,使其更不容易出错,也不太可能出现导致崩溃的错误。随着我们的学习,我们将发现这些优点的细节。
许多最先进、创新和流行的应用程序都是使用 Kotlin 编写的。其中一些例子包括 Kindle、Evernote、Twitter、Expedia、Pinterest 和 Netflix。
在我们开始 Android 之旅之前,我们需要了解 Android 和 Kotlin 是如何协同工作的。在我们用 Java 或 Kotlin 为 Android 编写程序之后,我们点击一个按钮,我们的代码就会被转换成另一种形式,这种形式是 Android 可以理解的。这种形式被称为达尔维克可执行代码,或DEX代码,转换过程被称为编译。当应用程序安装在设备上时,DEX 代码会被操作系统再次转换成优化的可执行状态。
注意
我们将在本章后面设置开发环境后立即看到这个过程。
Android 是一个复杂的系统,但你不需要深入了解它就能开始制作令人惊叹的应用程序。
提示
只有在长时间的使用和互动之后才能完全理解。
要开始,我们只需要了解基础知识。Android 运行在一个经过特殊适配的 Linux 操作系统上。因此,用户在 Android 上看到的只是在另一个操作系统上运行的应用程序。
Android 是一个系统中的系统。典型的 Android 用户看不到 Linux 操作系统,很可能甚至不知道它的存在。
其中一个目的是隐藏 Android 运行的硬件和软件的复杂性和多样性,但同时暴露出所有有用的功能。这些功能的暴露以两种方式进行:
-
首先,操作系统本身必须完全访问硬件,它已经做到了。
-
其次,这种访问必须对程序员友好且易于使用,这是因为 Android 应用程序编程接口(API)。
让我们继续深入了解 Android API。
Android API
Android API 是使得做出非凡事情变得容易的代码。一个简单的类比可以用一台机器来画出,也许是一辆汽车。当你踩油门时,引擎盖下会发生一大堆事情。我们不需要理解燃烧或燃油泵,因为一些聪明的工程师为我们做了一个接口;在这种情况下,是一个机械接口——油门踏板。
例如,下面这行代码在书的这个阶段可能看起来有点吓人,但它是一个很好的例子,说明了 Android API 如何帮助我们:
locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
一旦你学会了这一行代码搜索太空中可用的卫星,与它们在地球轨道上通信,然后获取你在地球表面的精确纬度和经度,你就能轻松地窥见 Android API 的强大和深度。
这段代码在书的这个阶段看起来有点具有挑战性,甚至令人费解,但试想以其他方式与卫星交流!
Android API 已经编写了大量代码供我们随意使用。
我们必须问的问题,也是这本书试图回答的问题是:我们如何使用所有这些代码来做酷炫的事情?或者,为了符合之前的类比,我们如何找到和操作 Android API 的踏板、方向盘,以及最重要的是天窗?
这个问题的答案就是 Kotlin 编程语言,以及 Kotlin 被设计来帮助程序员处理复杂性,避免错误,并快速取得进展。让我们深入了解 Kotlin 和面向对象编程(OOP)。
Kotlin 是面向对象的
Kotlin 是一种面向对象的语言。这意味着它使用可重用的编程对象的概念。如果这听起来像技术术语,另一个类比将有所帮助。Kotlin 使我们和其他人(比如 Android API 开发团队)能够编写可以基于现实世界事物构建的代码,这是重要的部分——它可以重复使用。
因此,使用汽车的类比,我们可以问以下问题:如果一个制造商一天制造多辆汽车,他们是否为每辆汽车重新设计每个零件?
当然,答案是否定的。他们会找到高技能的工程师来开发完全正确的组件,经过多年的磨练、改进和提高。然后,这些相同的组件会一次又一次地被重复使用,偶尔也会被改进。
如果你对我的类比挑剔的话,你可以指出每个汽车的组件仍然必须使用真实的工程师或机器人从原材料中构建。这是真的。
软件工程师编写代码时所做的是为一个对象建立一个蓝图。然后我们使用代码从他们的蓝图中创建一个对象,一旦我们有了那个对象,我们可以配置它,使用它,将它与其他对象组合,以及其他更多的操作。
此外,我们可以自己设计蓝图并从中制作对象。然后编译器将我们定制的创作转化为 DEX 代码。嘿,变魔术!我们有了一个 Android 应用。
在 Kotlin 中,蓝图被称为类。当一个类被转化为一个真正工作的“东西”时,我们称它为该类的对象或实例。
注意
简而言之,对象。
我们可以继续进行类比。就我们目前而言,Kotlin(以及大多数现代编程语言)是一种允许我们编写一次代码,然后可以重复使用的语言。
这非常有用,因为它节省了我们的时间,并且允许我们使用其他人的代码来执行我们可能没有时间或知识来编写的任务。大多数情况下,我们甚至不需要看到这段代码,甚至不需要知道它是如何工作的!
最后一个类比。我们只需要知道如何使用那段代码,就像我们需要学会开车一样。
因此,Android 总部的一些聪明的软件工程师编写了一个非常复杂的程序,可以与卫星通信,因此,通过一行代码,我们就可以在地球表面获得我们的位置。不错。
大部分 Android API 是用另一种语言(Java)编写的,但这对我们来说并不重要,因为我们在使用更简洁的 Kotlin 时可以完全访问到这些功能(用 Java 编码)。Android Studio 和 Kotlin 编译器会在幕后处理复杂性。
软件工程师已经考虑了如何使这段代码对所有想要开发使用用户位置进行酷炫操作的 Android 程序员有用。他们将做的一件事是使功能变得简单,比如将设备的位置获取到世界上变成一个简单的一行任务。
因此,我们之前看到的一行代码会启动许多我们看不到、也不需要看到的代码。这是一个使用别人的代码来使我们的代码变得无限简单的例子。
提示
如果你不必看到所有的代码让你感到失望,那么我理解你的感受。当我们学习某些东西时,有些人希望了解每一个细节。如果你是这样的人,那么请放心,学习 Android API 内部工作的最佳起点是按照 API 程序员的意图使用它。此外,在整本书中,我将定期指出更多的学习机会,让你了解 Android API 的内部工作。此外,我们将编写自己可重用的类,有点像我们自己的 API,只是我们的类将专注于我们希望我们的应用程序执行的任务。
欢迎来到OOP的世界。我将在每一章中不断提到 OOP,并且在第十章中会有一个重大揭示,面向对象编程。
再跟我说一遍,Android 到底是什么?
在 Android 上完成任务,我们编写了自己的代码,这也使用了 Android API 的代码。然后,这些代码被编译成 DEX 代码,其余的由 Android 设备处理,而 Android 设备又运行在一个称为 Linux 的底层操作系统上,该操作系统处理着不同的 Android 设备的复杂和极其多样的硬件。
Android 设备的制造商和各个硬件组件的制造商显然也知道这一点,并且他们编写了称为驱动程序的高级软件,以确保他们的硬件(例如 CPU、GPU、GPS 接收器、存储芯片和硬件接口)可以在底层的 Linux 操作系统上运行。
DEX 代码(以及其他一些资源)被放在一个称为Android 应用程序包(APK)的文件包中,这就是设备运行我们的应用所需的内容。
提示
不需要记住我们的代码与硬件交互时经历的步骤的细节。只需要理解我们的代码经历了一些自动化过程,成为了我们将发布到 Google Play 商店的应用程序。
接下来的问题是:所有这些编码和编译成 DEX 代码,以及 APK 打包,究竟是在哪里进行的?让我们看看我们将要使用的开发环境。
Android Studio
开发环境是一个术语,指的是在一个地方拥有你开发所需的一切,并且已经准备就绪。
开发 Android 需要一整套工具,当然还需要 Android API。这整套要求统称为 SDK。幸运的是,下载和安装一个单一应用程序将把这些东西捆绑在一起。这个应用程序就是 Android Studio。
Android Studio 是一个集成开发环境(IDE),将处理编译代码和与 Android API 链接的所有复杂性。安装完 Android Studio 后,我们可以在这个单一应用程序内完成所有需要做的事情,并将我们讨论过的许多复杂性放到脑后。
提示
随着时间的推移,这些复杂性将变得很自然。不需要立即掌握它们以继续进展。
所以,最好还是用上 Android Studio。
设置 Android Studio
设置 Android Studio 相当简单,尽管有点费时。拿些点心,按照以下步骤开始:
-
访问developer.android.com/studio/index.html。点击大的下载 Android Studio按钮继续:
-
勾选复选框接受条款和条件,然后点击下载 Android Studio for Windows按钮:
-
下载完成后,运行刚刚下载的文件。文件名以
android-studio-ide…
开头,文件名的结尾会根据当前阅读时的版本而有所不同。 -
点击**下一步 >**按钮继续:
-
保持默认选项,如下截图所示,然后点击**下一步 >**按钮:
-
接下来,我们需要选择安装 Android Studio 的位置,如下截图所示:
安装向导建议500MB 的可用空间,但你可能已经注意到前一个屏幕建议了 2.1GB。然而,在安装过程的后期还有更多的要求。此外,如果你的 Android Studio 部分和项目文件都在同一硬盘上,那会更容易一些。
出于这些原因,我建议至少有 4GB 的可用空间。如果需要切换驱动器以适应这一点,那么使用**浏览…**按钮浏览到硬盘上合适的位置。
提示
记下你选择的位置。
-
当你准备好了,点击**下一步 >**按钮。
-
在接下来的窗口中,选择Android Studio将出现在开始菜单中的文件夹。你可以保持默认设置,如下:
-
点击安装。这一步可能需要一些时间,特别是在旧机器上或者如果你的网络连接较慢。当这个阶段完成时,你会看到以下屏幕:
-
点击下一步 >。
-
Android Studio 现在已安装 - 有点。勾选启动 Android Studio复选框,然后点击完成按钮:
-
你会看到欢迎屏幕,如下截图所示:
-
点击下一步按钮。
-
选择标准安装类型,如下截图所示:
-
点击下一步按钮。
-
选择你喜欢的配色方案。我选择了IntelliJ,如下截图所示:
-
点击下一步。
-
现在你会看到验证设置屏幕:
-
点击完成按钮。Android Studio 现在将开始一些更多的下载,可能需要一些时间。
-
当 Android Studio 准备好时,您将有运行它的选项。在这一点上,点击完成按钮。Android Studio 很可能已经准备好了。如果您要直接进行下一节,可以将其保持打开,或者在下一节的指示下关闭它,然后重新打开它。
最后一步 - 目前为止
使用您喜欢的文件管理软件,也许是 Windows 资源管理器,创建一个名为AndroidProjects
的文件夹。将其放在您安装 Android Studio 的相同驱动器的根目录下。因此,如果您在C:/Program Files/Android
安装了 Android Studio,那么请在C:/AndroidProjects
创建您的新文件夹。
或者,如果您在D:/Program Files/Android
安装了 Android Studio,那么请在D:/AndroidProjects
创建您的新文件夹。
提示
请注意,下一节的截图显示了D:
驱动器上的AndroidProjects
文件夹。这是因为我的C:
驱动器有点满了。两者都可以。我在一个有足够空间的 C:驱动器上的借来的 PC 上进行了安装教程的屏幕截图,因为那是 Android Studio 的默认位置。将其保持在与 Android 安装相同的驱动器上更整洁,可能会避免未来的问题,所以如果可以的话,请这样做。
请注意Android
和Projects
之间没有空格,并且两个单词的第一个字母都是大写的。大写是为了清晰起见,而省略空格是 Android Studio 所要求的。
Android Studio 和我们需要的支持工具已经安装并准备就绪。我们现在离构建我们的第一个应用程序非常接近了。
现在,让我们稍微了解一下 Android 应用程序的组成。
什么构成了 Android 应用程序?
我们已经知道,我们将编写 Kotlin 代码,该代码将使用其他人的代码,并编译为 DEX 代码,该代码将在我们的用户 Android 设备上使用。除此之外,我们还将添加和编辑其他文件,这些文件将包含在最终的 APK 中。这些文件被称为Android 资源。
Android 资源
正如本章前面提到的,我们的应用程序将包括资源,例如图像、声音和用户界面布局,这些资源将保存在与 Kotlin 代码分开的文件中。我们将在本书的过程中慢慢介绍它们。
它们还将包括我们应用程序的文本内容的文件。约定俗成的是通过单独的文件引用应用程序中的文本,因为这样可以很容易地进行更改,并且可以创建适用于多种不同语言和地理区域的应用程序。
此外,尽管我们可以选择使用可视化设计师来实现应用程序的用户界面(UI)布局,但 Android 实际上是从基于文本的文件中读取它们的。
Android(或任何计算机)无法像人类那样阅读和识别文本。因此,我们必须以高度组织和预定义的方式呈现我们的资源。为此,我们将使用可扩展标记语言(XML)。XML 是一个庞大的主题;幸运的是,它的整个目的是既适合人类阅读又适合机器阅读。我们不需要学习这种语言;我们只需要注意(然后遵守)一些规则。此外,大多数时候,当我们与 XML 交互时,我们将通过 Android Studio 提供的一个整洁的可视化编辑器来进行。我们可以通过文件扩展名为.xml
来判断我们是否在处理 XML 资源。
您不需要记住这一点,因为我们将不断地在本书中回到这个概念。
Android 代码的结构
除了这些资源之外,值得注意的是 Android 的代码结构。我们可以利用数百万行代码。显然,这些代码需要以便于查找和引用的方式进行组织。它们被组织成特定于 Android 的包。
包
每当我们创建一个新的 Android 应用程序时,我们都会选择一个唯一的名称,称为包。我们很快就会看到我们是如何做到这一点的,在标题为我们的第一个 Android 应用的部分中。包通常被分成子包,以便它们可以与其他类似的包一起分组。我们可以简单地将它们想象成文件夹和子文件夹,这几乎就是它们的实际情况。
我们可以将 Android API 提供给我们的所有包想象成来自代码库的代码。我们将使用的一些常见的 Android 包包括以下内容:
-
android.graphics
-
android.database
-
android.view.animation
正如你所看到的,它们被安排和命名,以使其中的内容尽可能明显。
提示
如果你想了解 Android API 的深度和广度,可以查看developer.android.com/reference/packages
上的 Android 包索引。
类
早些时候,我们了解到我们可以将可重用的代码蓝图转换为对象,这些蓝图被称为类。类包含在这些包中。在我们的第一个应用程序中,我们将看到如何轻松地导入其他人的包,以及从这些包中导入特定的类供我们的项目使用。一个类通常包含在与类同名的文件中。
函数
在 Kotlin 中,我们进一步将我们的类分成执行不同操作的部分,我们称这些面向操作的部分为函数。正是类的函数将用于访问 Android 代码中数百万行提供的功能。
我们不需要阅读代码。我们只需要知道哪个类有我们需要的东西,它在哪个包中,以及类内的哪个函数能给我们准确的结果。
我们可以以相同的方式来思考我们将自己编写的代码的结构,尽管通常每个应用程序只有一个包。
当然,由于 Kotlin 的面向对象的特性,我们只会使用 API 中的部分内容。还要注意,每个类都有自己独特的数据。通常,如果你想要访问类中的数据,你需要拥有该类的对象。
提示
你不需要记住这些,因为我们将在本书中不断回到这个概念。
本章结束时,我们将导入多个包,以及其中的一些类。到第二章结束时,我们甚至将编写我们自己的函数。
我们的第一个 Android 应用
现在我们可以开始我们的第一个应用程序了。在编程中,传统上新学生的第一个应用程序会使用他们正在使用的语言/操作系统向世界问好。我们将快速构建一个可以做到这一点的应用程序,并且在第二章中,Kotlin, XML 和 UI 设计师,我们将超越这一点,并添加一些在用户点击时响应的按钮。
注意
本章结束时的完整代码在Chapter01
文件夹的下载包中供您参考。但是,您不能简单地复制和粘贴这些代码。您仍然需要按照本章(以及所有项目的开始)中解释的项目创建阶段进行操作,因为 Android Studio 在幕后做了大量工作。一旦您熟悉了这些步骤,并理解了哪些代码是由您,程序员,输入的,哪些代码/文件是由 Android Studio 生成的,那么您就可以通过从我在下载包中提供的文件中复制和粘贴来节省时间和输入。
按照以下步骤启动项目:
- 以与运行其他应用程序相同的方式运行 Android Studio。例如,在 Windows 10 上,启动图标会出现在开始菜单中。
提示
如果提示从…导入 Studio 设置:选择不导入设置。
-
您将看到 Android Studio 的欢迎屏幕,如下截图所示。找到开始新的 Android Studio 项目选项,然后单击它:
-
之后,Android Studio 将弹出选择您的项目窗口,如下所示:
-
我们将使用基本活动选项,就像在上一张截图中选择的那样。Android Studio 将自动生成一些代码和一些资源来启动我们的项目。我们将在下一章详细讨论代码和资源。选择基本活动,然后点击下一步。
-
接下来的屏幕是配置您的项目屏幕,在这里我们将执行以下步骤以及一些其他操作:
-
命名新项目
-
提供公司域作为包名,以区分我们的项目和其他任何项目,以防我们决定将其发布到 Play 商店
-
选择计算机上项目文件的位置
-
选择我们首选的编程语言
-
我们的项目名称将是
Hello World
,文件的位置将是我们在设置 Android Studio部分中创建的AndroidProjects
文件夹。 -
包名可以是几乎任何您喜欢的东西。如果您有网站,可以使用
com.yourdomain.helloworld
的格式。如果没有,可以使用com.gamecodeschool.helloworld
,或者您随意编造的东西。这只有在您决定发布时才重要。 -
为了清楚起见,如果您无法清楚地看到以下截图中的细节,这里是我使用的值。请记住,您的值可能会根据您选择的公司域和项目保存位置而有所不同:
选项 | 输入的值 |
---|---|
名称: | Hello World |
包名: | com.gamecodeschool.helloworld |
语言: | Kotlin |
保存位置: | D:\AndroidProjects\HelloWorld |
最低 API 级别: | 保持默认值 |
此项目将支持即时应用程序: | 保持默认值 |
使用 AndroidX 构件: | 选择此选项 |
注意
请注意,应用程序名称中的"Hello"和"World"之间有一个空格,但项目位置没有,如果有空格则无法工作。
关于最低 API 级别设置,我们已经知道 Android SDK 是我们将用于开发应用程序的代码包集合。像任何好的 SDK 一样,Android SDK 定期更新,每次进行重大更新时,版本号都会增加。简单来说,版本号越高,您可以使用的功能就越新;版本号越低,我们的应用程序就能在更多设备上运行。目前,默认的**API 15, Android 4.0.3 (IceCreamSandwich)**版本将为我们提供许多出色的功能,并且几乎与当前使用的 Android 设备兼容。如果在阅读时,Android Studio 建议使用更新的 API,则请使用该 API。
如果您在未来的某个时候阅读本书,那么最低 API选项可能会默认为不同的内容,但本书中的代码仍将有效。
在您输入所有信息后,下面的截图显示了配置您的项目屏幕:
注意
您可以使用几种不同的语言编写 Android 应用程序,包括 C++和 Java。与使用 Kotlin 相比,每种语言都有各种优缺点。学习 Kotlin 将是对其他语言的很好介绍,而且 Kotlin 也是 Android 的最新(也可以说是最好的)官方语言。
- 单击完成按钮,Android Studio 将为我们准备新项目。这可能需要几秒钟或几分钟,具体取决于您的计算机性能。
在这个阶段,您可能已经准备好继续了,但是,根据安装过程的不同,您可能需要点击一些额外的按钮。
提示
这就是为什么我提到我们只是可能完成了安装和设置。
在 Android Studio 的底部窗口中查看是否有以下消息:
注意
请注意,如果您在 Android Studio 底部没有看到类似以下截图中显示的水平窗口,您可以跳过这两个额外的步骤。
可能的额外步骤 1
如果需要,点击安装缺少的平台并同步项目,接受许可协议,然后点击下一步,接着点击完成。
可能的额外步骤 2
您可能会收到另一条类似的消息:
如果出现上述消息,请点击安装构建工具…,然后点击完成。
提示
您可以通过单击 Android Studio 底部的消息选项卡来整理屏幕,并关闭底部的水平窗口,但这并不是强制性的。
到目前为止,部署应用程序
在我们探索任何代码并学习我们的第一部分 Kotlin 之前,您可能会惊讶地发现我们已经可以运行我们的项目。这将是一个相当简陋的屏幕,但由于我们将尽可能频繁地运行应用程序来检查我们的进度,让我们现在看看如何做到这一点。您有三个选项:
-
在 PC 上的模拟器上运行应用程序(Android Studio 的一部分)处于调试模式
-
在 USB 调试模式下在真实的 Android 设备上运行应用程序
-
将应用程序导出为可以上传到 Play 商店的完整 Android 项目
第一个选项(调试模式)是最容易设置的,因为我们在设置 Android Studio 的过程中已经完成了。如果您有一台功能强大的 PC,您几乎看不到模拟器和真实设备之间的区别。然而,屏幕触摸是由鼠标点击模拟的,并且在一些后期的应用程序中无法进行用户体验的有效测试,比如绘图应用程序。此外,您可能更喜欢在真实设备上测试您的创作 - 我知道我是这样的。
第二个选项,使用真实设备,有一些额外的步骤,但一旦设置好,就和第一个选项一样好,屏幕触摸是真实的。
最后一个选项大约需要五分钟(至少)来准备,然后您需要手动将创建的包放到真实设备上并安装它(每次更改代码时)。
最好的方法可能是使用模拟器快速测试和调试代码的小增量,然后定期在真实设备上使用 USB 调试模式,以确保一切仍然如预期。只有偶尔你会想要导出一个实际可部署的包。
提示
如果您的 PC 特别慢或 Android 设备特别老旧,您可以只使用一个选项或另一个选项来运行本书中的项目。请注意,慢的 Android 手机可能会正常运行,但非常慢的 PC 可能无法处理后期应用程序的模拟器运行,并且您将受益于在手机/平板电脑上运行它们。
出于这些原因,我现在将介绍如何使用模拟器和 USB 调试在真实设备上运行应用程序。
在 Android 模拟器上运行和调试应用程序
按照以下简单步骤在默认的 Android 模拟器上运行应用程序:
-
在 Android Studio 的主菜单栏中,选择工具 | AVD 管理器。AVD 代表 Android 虚拟设备(模拟器)。您将看到以下窗口:
-
注意列表中是否有一个模拟器。在我的情况下,它是Pixel 2 XL API 28。如果您在将来的某个时候阅读本书,它将是默认安装的不同模拟器。这并不重要。单击以下截图中显示的绿色播放图标(右侧),然后等待模拟器启动:
-
现在,你可以点击 Android Studio 快速启动栏上的播放图标,如下截图所示,然后在提示时选择Pixel 2 XL API 28(或者你的模拟器叫什么)应用程序将在模拟器上启动:
你完成了。这是目前在模拟器中的应用程序外观。请记住,你可能(很可能)有一个不同的模拟器,这没关系:
显然,在我们搬到硅谷寻找财务支持之前,我们还有很多工作要做,但这是一个良好的开始。
我们需要经常测试和调试我们的应用程序,以便在开发过程中检查任何错误、崩溃或其他意外情况。
注意
我们将在下一章中看到如何从我们的应用程序中获取错误和其他调试反馈。
确保它在你想要定位的每种设备类型/尺寸上看起来好看并且运行正确是很重要的。显然,我们并不拥有成千上万种 Android 设备中的每一种。这就是模拟器的用武之地。
然而,模拟器有时会有点慢和繁琐,尽管最近已经有了很大的改进。如果我们想要真正感受到用户体验,那么我们无法击败部署到真实设备。因此,在开发我们的应用程序时,我们既想使用真实设备,又想使用模拟器。
提示
如果你计划不久后再次使用模拟器,请保持其运行,以避免等待它再次启动。
如果你想在平板电脑上尝试你的应用程序,你需要一个不同的模拟器。
注意
创建新的模拟器
为不同的 Android 设备创建模拟器很简单。从主菜单中选择工具 | AVD 管理器。在AVD 管理器窗口中,左键单击创建新的虚拟设备。现在左键单击你想要创建的设备类型 - 电视,手机,Wear OS或平板电脑。现在只需左键单击下一步,按照说明创建你的新 AVD。下次运行你的应用程序时,新的 AVD 将出现为运行应用程序的选项。
现在我们可以看看如何将我们的应用程序放到真实设备上。
在真实设备上运行应用程序
首先要做的事情是访问你的设备制造商的网站,获取并安装你的设备和操作系统所需的任何驱动程序。
提示
大多数新设备不需要驱动程序,所以你可能首先想尝试以下步骤。
接下来的几个步骤将为 Android 设备设置调试。请注意,不同的制造商对菜单选项的结构略有不同。但是对于大多数设备来说,启用调试的以下顺序可能非常接近,如果不是完全相同:
-
点击你手机/平板上的设置菜单选项或设置应用程序。
-
这一步对不同版本的 Android 略有不同。开发者选项菜单被隐藏起来,以免给普通用户带来麻烦。你必须执行一个稍微奇怪的任务来解锁菜单选项。点击关于设备或关于手机选项。找到构建号选项,重复点击它,直到你收到一条消息,告诉你你现在是开发者了!
提示
一些制造商有不同的、晦涩的方法来完成这一步。如果这一步不起作用,请搜索你的设备和“解锁开发者选项”。
-
返回设置菜单。
-
点击开发者选项。
-
点击USB 调试的复选框。
-
将你的 Android 设备连接到计算机的 USB 端口。
-
从 Android Studio 工具栏中点击播放图标,如下截图所示:
-
在提示时,点击确定在你选择的设备上运行应用程序。
现在我们准备好学习一些 Kotlin,并将我们自己的 Kotlin 代码添加到 Hello World 项目中。
常见问题
问:那么,Android 实际上并不是一个操作系统;它只是一个虚拟机,所有 Android 手机和平板电脑实际上都是 Linux 机器吗?
答:不,Android 设备的所有不同子系统,包括 Linux、库和驱动程序,构成了 Android 操作系统。
总结
到目前为止,我们已经建立了一个 Android 开发环境,并在模拟器和真实设备上创建和部署了一个应用程序。如果您仍然有未解答的问题(您可能比本章开始时还有更多问题),不要担心,因为随着我们深入了解 Android 和 Kotlin 的世界,事情会变得更清晰。
随着章节的进展,您将建立起对所有内容如何相互关联的全面理解,然后成功只是一个练习和更深入了解 Android API 的问题。
在下一章中,我们将使用可视化设计师和原始 XML 代码来编辑 UI,同时编写我们的第一个 Kotlin 函数,并且我们将使用 Android API 为我们提供的一些函数。
第二章:Kotlin,XML 和 UI 设计师
在这个阶段,我们已经有了一个可用的 Android 开发环境,并且已经构建并部署了我们的第一个应用程序。然而,显然,由 Android Studio 自动生成的代码不会成为下一个畅销的 Google Play 应用程序。我们需要探索这些自动生成的代码,以便开始了解 Android,然后学习如何构建这个有用的模板。为了达到这个目的,我们将在本章中进行以下操作:
-
了解如何从我们的应用程序中获得技术反馈。
-
检查我们第一个应用程序的 Kotlin 代码和用户界面(UI)XML 代码。
-
第一次尝试使用 Android UI 设计师。
-
编写我们的第一个 Kotlin 代码。
-
学习一些核心的 Kotlin 基础知识以及它们与 Android 的关系。
首先,让我们看看如何从我们的应用程序中获得反馈。
检查日志输出
在上一章中,我们提到我们的应用程序在模拟器或真实设备上以调试模式运行;这样我们就可以监视它,并在出现问题时获得反馈。那么,所有这些反馈在哪里呢?
您可能已经注意到 Android Studio 窗口底部有很多滚动文本。如果没有,请点击 logcat 选项卡,如下面截图中标有 1 的区域所示:
提示
请注意,模拟器必须在运行状态,或者真实设备必须以调试模式连接,才能看到下面的窗口。此外,如果由于某种原因重新启动了 Android Studio,并且尚未执行应用程序,则 logcat 窗口将为空。请参考第一章,在模拟器或真实设备上运行应用程序:
如果需要查看更多内容,您可以像大多数其他 Windows 应用程序一样,拖动窗口使其更高。
这个窗口称为 logcat,有时也被称为控制台。这是我们的应用程序告诉我们用户看不到的情况。如果应用程序崩溃或出现错误,原因或线索将出现在这里。如果我们需要输出调试信息,我们也可以在这里进行。
提示
如果你不明白为什么你的应用程序崩溃了,将 logcat 中的一部分文本复制粘贴到 Google 中通常会揭示原因。
过滤 logcat 输出
您可能已经注意到,logcat 的大部分内容,如果不是全部,几乎是难以理解的。没关系;现在,我们只对将在红色中突出显示的错误和我们将在下面学习的调试信息感兴趣。为了在 logcat 窗口中看到更少的无关文本,我们可以打开一些过滤器以使事情更清晰。
在上一张截图中,我还突出显示了另外两个区域,标记为 2 和 3。区域 2 是控制第一个过滤器的下拉列表。现在左键单击它,并将其从 Verbose 更改为 Info。我们已经大大减少了文本输出。当我们对应用程序进行了一些更改并重新部署后,我们将看到这是有用的。在我们探索构成我们项目的代码和资产之后,我们将这样做。另外,请确保标记为 3 的区域显示为“仅显示所选应用程序”。如果不是,请左键单击它,并将其更改为“仅显示所选应用程序”。
现在我们可以看一下 Android Studio 自动生成的内容,然后开始修改和添加代码,以个性化我们从项目创建阶段得到的内容。
探索项目的 Kotlin 代码和主要布局的 XML 代码
我们将查看包含定义简单 UI 布局的代码的资源文件,以及包含我们 Kotlin 代码的文件。在这个阶段,我们不会试图理解所有内容,因为在理解之前我们需要学习更多的基础知识。然而,我们将看到这两个文件的基本内容和结构,以便将它们的内容与我们已经了解的 Android 资源和 Kotlin 知识相协调。
检查 MainActivity.kt 文件
首先让我们看一下 Kotlin 代码。你可以通过左键单击MainActivity.kt
标签来查看这段代码,如下面的截图所示:
由于我们不会详细讨论代码的细节,带注释的截图比以文本形式重现实际代码更有用。在阅读本节时,请经常参考以下截图:
首先要注意的是,我在代码中添加了一些空行,以便排版和呈现更清晰的图像。
在 Android Studio 中折叠(隐藏)代码
现在,看一下 Android Studio 窗口的左侧(不是前面的截图),观察编辑器左侧的所有**+和-**按钮,可以折叠和展开代码的部分:
我已经折叠了一些代码部分,留下了其他部分可见。因此,你屏幕上看到的与前面的截图略有不同。在 Android Studio 中,尝试一段时间使用左侧的**+和–按钮,练习隐藏和显示代码的部分。你可能能够让你的屏幕看起来像前面的截图,但这并不是继续的要求。像这样隐藏代码的技术术语称为折叠**。
包声明
第1部分称为包声明,正如你所看到的,它是我们在创建项目时选择的包名称,前面加上package
这个词。每个 Kotlin 文件的顶部都会有一个包声明。
导入类
第2部分是六行代码,都以import
开头。在import
之后,我们可以看到各种用点分隔的单词。每行的最后一个单词是该行导入到我们项目中的类的名称,而每行中较早的单词都是包和子包,其中包含这些类。
例如,下一行导入了androidx.appcompat.app
包和子包中的AppCompatActivity
类:
import androidx.appcompat.app.AppCompatActivity
这意味着在我们的项目中,我们将可以访问这些类。实际上,正是这些类被自动生成的代码用来制作我们在上一章中看到的简单应用程序。
在本章中我们不会讨论所有这些类。重要的是我们可以导入这些类,这使我们能够立即获得更多功能。请注意,我们可以随时从任何包中添加额外的类,并且在不久的将来我们将这样做以改进我们的应用程序。
类声明
我们的代码的第3部分称为类声明。以下是完整的那一行;我已经突出显示了其中的一部分,如下所示:
class MainActivity : AppCompatActivity() {
类声明是类的开始。请注意,突出显示的部分MainActivity
是在创建项目时自动生成的名称,也与MainActivity.kt
文件名相同。这正如我们之前讨论过的 Kotlin 类一样。
冒号(:
)表示我们的名为MainActivity
的类将是AppCompatActivity
类型。这表明,虽然这个文件中的代码行数不多,但我们也使用了更多的代码,这些代码来自AppCompatActivity
类。所有这些以及更多内容将在第十章面向对象编程中变得清晰。
最后,对于第3部分,看一下行末的左花括号:{
。现在看看我们代码截图的第4部分。这个右花括号(}
)表示类的结束。在左右花括号之间的所有内容,{...},
都是类的一部分。
类内的函数
现在看看代码的第5部分。这是完整的代码行,其中突出显示了我们当前讨论的关键部分:
override fun onCreate(savedInstanceState: Bundle?) {
这是一个函数签名。高亮显示的部分onCreate
是函数名称。Kotlin 的fun
关键字清楚地表明这是函数的开始。我们通过使用函数的名称来执行其代码。当我们这样做时,我们说我们正在调用一个函数。
尽管我们现在不会关心函数名两侧的代码部分,但你可能已经注意到Bundle
,这是我们在代码的第2部分中导入的类之一。如果我们删除相关的import
行,Android Studio 将不知道Bundle
类是什么,它将无法使用,并且会以红色下划线突出显示为错误。
然后我们的代码将无法编译和运行。请注意,前面代码行的最后一件事是一个左花括号({
)。这表示onCreate
函数中包含的代码的开始。现在跳到我们代码的第6部分,您将看到一个右花括号(}
)。您可能已经猜到这是函数的结束。在onCreate
函数的左右花括号之间的所有代码都是在调用函数时执行的代码。
我们现在不需要深入了解这段代码的作用,但是总体上,它通过引用一些由 Android Studio 在创建项目时自动生成的资源文件来设置应用的外观和布局。我在前面的截图中用标号9标出了这些资源文件的轮廓。
部分7和8也是我折叠起来以使截图和讨论更简单的函数。它们的名称分别是onCreateOptionsMenu
和onOptionsItemSelected
。
我们对我们的 Kotlin 代码了解足够多,可以取得一些进展。我们将在本章后面再次看到这段代码并进行更改。
到目前为止的 Kotlin 代码摘要
确实,在前面的代码中包含了一些复杂的语法。然而,我们正在建立对这段代码的足够了解,以便我们可以在其中工作,并开始快速学习 Kotlin 和 Android,而不必先阅读数百页的 Kotlin 理论。到本书结束时,所有的代码都会让人明白。但为了现在快速进展,我们只需要接受一些细节将在稍后一段时间内仍然是个谜。
检查主布局文件
现在我们将只看其中一个.xml
文件。在本书的整个过程中,我们将遇到几个不同的布局文件,但让我们从最重要的一个开始,它决定了我们应用的外观。
单击我们一直在讨论的MainActivity.kt
标签旁边的content_main.xml
标签。
在右侧的主窗口中,您将看到我们应用的设计视图,如下截图所示:
我们在设计应用程序时所做的大部分工作将在这个设计视图中完成。然而,了解背后发生了什么是很重要的。
设计视图是content_main.xml
文件中包含的 XML 代码的图形表示。单击Text标签(如前面截图底部附近所示)以查看构成布局的 XML 代码。我已经注释了 XML 文本的截图,以便我们可以接下来讨论它:
首先要注意的是,这个文件并不代表整个布局。但它确实代表了大部分的表面积和中心的Hello World消息。此外,在左侧,我们可以看到现在熟悉的**+和-**图标,以便我们可以折叠和展开代码的各个部分。
UI 布局元素
如果我们首先看一下标记为1的代码部分,我们会看到的第一件事是…ConstraintLayout...
。ConstraintLayout
元素是用于包装 UI 的 UI 元素。
注意
有更多技术和具体的方式来指代我们用户界面设计的不同“元素”。随着我们的进展,我们将介绍诸如小部件、视图和视图组等术语。
当我们在 Android 中的 UI 中添加一个新元素时,我们总是以左尖括号(<
)开头,后面跟着元素的名称。
紧随这行看起来相当长而繁琐的代码之后的代码定义了这个元素将具有的属性。这可能包括数十种不同的东西,取决于它是什么类型的 UI 元素。在这里,除了其他一些 XML 之外,我们可以看到诸如layout_width
、layout_height
和showIn
之类的东西。所有这些属性定义了ConstraintLayout
元素将如何出现在用户的屏幕上。ConstraintLayout
元素的属性在第一个右尖括号(>
)处结束,标记为1b。
如果我们看一下 XML 截图的底部,我们会看到标记为2的代码。这段代码</…ConstraintLayout>
标志着ConstraintLayout
元素的结束。在元素的属性的右尖括号(>
)和定义其结束的</…ConstraintLayout>
代码之间的任何内容都被视为元素的子元素。因此,我们可以看到我们的ConstraintLayout
元素有(或包含)一个子元素。现在让我们来看看这个子元素。
UI 文本元素
利用我们刚刚学到的知识,我们可以推断出在截图的位置3开始的 UI 元素称为TextView
元素。就像它的父元素一样,它以左尖括号(<
)和它的名称开始:<TextView...
。如果我们进一步查看我们的TextView
元素,我们会发现它有几个属性。它有一个text
属性,设置为"Hello world!"
。当然,这就是我们的应用向用户显示的确切文本。它还有layout_width
和layout_height
属性,都设置为"wrap_content"
。这告诉TextView
元素,它可以占用所需的内容空间,但不会超过。正如我们将在本书中看到的,对于这个和其他 UI 元素,还有许多其他属性可用。
请注意我们的 XML 截图上4位置的代码是/>
。这标志着TextView
元素的结束。这与ConstraintLayout
元素的结束方式略有不同。当 XML 中的元素没有子元素时,我们可以像这样结束它:/>
。当元素有子元素并且其结束位置在其属性定义的代码之后时,通过重复其名称来结束元素会更清晰,像这样:</…ConstraintLayout>
。
注意
您可能想知道为什么TextView
元素的元素名称简短而简洁(只是TextView
),而ConstraintView
元素的完整名称前面有明显复杂的杂乱(androidx.constraintlayout.widget.ConstraintLayout
)。这个ConstraintLayout
元素是布局的特殊版本,用于确保我们的应用与较旧版本的 Android 兼容。正如我们将在一分钟内看到的,当我们向应用添加按钮时,大多数元素都有简单而简洁的名称。
我们将在下一节中编辑这段代码,并了解更多关于属性的知识,同时探索另一种类型的 UI 元素——即Button
元素。
将按钮添加到主布局文件
在这里,我们将在屏幕上添加一对按钮,然后探索一种快速的方法让它们做一些事情。我们将以两种不同的方式添加按钮;首先,使用可视化设计师,其次,通过直接添加和编辑 XML 代码。
通过可视化设计师添加按钮
要开始添加我们的第一个按钮,请点击我们刚刚讨论过的 XML 代码下方的Design选项卡,切换回设计视图。按钮在以下截图中被突出显示:
注意,在布局的左侧,我们有一个名为Palette的窗口:
调色板窗口分为两部分。左侧列表显示了 UI 元素的类别,并允许您选择类别,而右侧显示了当前选定类别中所有可用的 UI 元素。
确保选择了Common类别,如前面的截图所示。现在,左键单击并按住Button小部件,然后将其拖放到布局的顶部和中心附近。
它不需要完全一样;然而,练习做对是很好的。所以,如果你对按钮的位置不满意,那么你可以左键单击它在布局上进行选择,然后在键盘上按下Delete键将其删除。现在你可以重复上一步,直到你有一个你满意的整齐放置的按钮,就像下图所示:
此时,我们可以在模拟器上或真实设备上运行应用程序,按钮将会出现。如果我们点击它,甚至会有一个简单的动画来表示按钮被按下和释放。如果你愿意,现在可以尝试一下。
为了使应用程序更有趣,我们将使用Attributes窗口编辑按钮的属性。
编辑按钮的属性
确保按钮被左键单击选择。现在找到编辑窗口右侧的Attributes窗口,如下所示:
在前面的截图中,您可以看到我们可以访问一些按钮的属性,尽管不是全部。要显示更多属性,请单击查看所有属性链接(如前面的截图所示)。
现在你可以看到按钮的全部细节,我们可以开始编辑它。看起来似乎很惊讶,看到一个按钮这样一个明显简单的东西有如此多的属性。这是 Android API 为 UI 操作提供的多功能性和强大性的体现。看下面的截图,显示了我们最近添加的按钮的完整属性列表:
此外,注意即使前面的截图并没有显示所有内容,你可以使用Attributes窗口右侧的滚动条来显示更多属性。
正如你所看到的,我们可以在 UI 设计师中编辑的不同属性有很多。在第十二章中,将我们的 Kotlin 连接到 UI 和空值性,我们还将使用我们的 Kotlin 代码编辑和操作这些属性。现在,我们只编辑一个属性。滚动Attributes窗口,直到找到onClick属性,然后左键单击它进行编辑,如下图所示:
提示
属性按字母顺序排列,onClick大约在冗长列表的三分之二处。
在onClick属性的编辑框中键入topClick
,然后在键盘上按Enter。确保使用相同的大小写,包括略微反直觉的小写t
和大写C
。
当完成时,Attributes窗口将如下截图所示:
我们在这里做的是在我们的代码中命名了我们想要在用户点击该按钮时调用(或执行)的 Kotlin 函数。名称是任意的,但是,由于这个按钮位于屏幕顶部,名称似乎有意义且易于记忆。我们使用的奇怪大小写是一种约定,将帮助我们保持代码清晰易读。随着我们的代码变得越来越长和复杂,我们将看到这种做法的好处。
当然,topClick
函数目前还不存在。Android Studio 非常有帮助,但有一些事情我们需要自己做。在添加第二个按钮到我们的 UI 后,我们将使用 Kotlin 代码编写这个函数。此时,您可以运行应用程序,它仍然可以工作,但如果单击按钮,应用程序将崩溃,并显示错误消息,因为该函数不存在。
检查新按钮的 XML 代码
在为该项目添加最终按钮之前,单击编辑器下方的Text选项卡,切换回查看构成我们 UI 的 XML 代码:
请注意,在我们之前检查的 XML 代码中有一个新的代码块。以下是新代码块的屏幕截图:
请注意以下细节,这些细节应该与我们对 XML 和 Android UI 元素的了解相对应:
-
新代码以
<Button
开头,以/>
结束。 -
新代码具有一系列属性,定义了按钮,包括
layoutWidth
和layoutHeight
。 -
代码包括我们添加的带有值“topClick”的
onClick
属性。 -
onClick
属性的topClick
值被红色下划线标出,显示错误,因为函数目前还不存在。 -
按钮代码的开始和结束被包含在
ConstraintLayout
元素中。
注意
dp
是一个测量/距离的单位,将在第五章中更深入地讨论,使用 CardView 和 ScrollView 创建美丽的布局。
将鼠标悬停在下划线的topClick
值上,以显示问题的详细信息,如下面的屏幕截图所示:
我们可以确认问题是,Android Studio 期望我们的代码中实现一个名为topClick
的函数。在添加第二个按钮后,我们将这样做。
通过编辑 XML 代码添加按钮
为了多样化,并证明我们可以,我们现在将只使用 XML 代码添加另一个按钮,而不使用 UI 设计师。大多数时候,我们会使用 UI 设计师,但这个快速练习应该巩固 UI 设计师和底层 XML 代码之间的关系。
我们将通过复制和粘贴现有按钮的代码来实现这一点。然后,我们将对粘贴的代码进行一些小的编辑。
在以<Button
开头的按钮代码之前单击左键。请注意,代码的开头和结尾现在有轻微的高亮显示:
这已经确定了我们要复制的代码部分。现在,左键单击并拖动以选择所有按钮代码,包括高亮显示的开头和结尾,如下一个屏幕截图所示:
按下Ctrl + C组合键复制突出显示的文本。将光标放在现有按钮代码下方,然后按几次Enter键,留下一些额外的空行。
按下Ctrl + V组合键粘贴按钮代码。此时,我们有两个按钮;但是,有一些问题:
我们在代表我们的按钮的两个代码块中都有额外的错误。id
属性(在两个代码块中)被红色下划线标出。这个错误的原因是两个按钮都有相同的id
属性。id
属性应该将 UI 元素与所有其他 UI 元素区分开来,所以它们不能相同。让我们试着解决这个问题。
给按钮唯一的 id 属性
我们可以通过将第二个按钮称为button2
来解决问题,但更有意义的是改变它们两个。编辑第一个按钮的代码,给它一个id
属性为buttonTop
。为此,找到第一个按钮中的以下代码行:
android:id="@+id/button"
然后,将代码行更改为以下内容:
android:id="@+id/buttonTop"
提示
注意button
中的小写b
和Top
中的大写T
。
现在找到第二个按钮中的以下代码行:
android:id="@+id/button"
然后,将代码行更改为以下内容:
android:id="@+id/buttonBottom"
id
属性行上的错误已经消失。此时,你可能会认为我们可以继续解决我们的缺失功能问题。
然而,如果你运行应用程序并快速浏览一下,你会发现我们似乎只有一个按钮。不仅如此,按钮的位置也不是我们期望的:
这样做的原因是我们没有明确地定位它们,所以它们默认位于屏幕的左上角。我们在设计选项卡上看到的位置只是设计时的位置。所以,现在让我们来改变它。
在布局中定位这两个按钮
我们只能看到一个按钮的原因是,两个按钮都在同一个位置。第二个按钮正好覆盖在第一个按钮上。因此,即使在设计选项卡中(随时可以查看),按钮仍然重叠在一起,尽管它们位于屏幕中间:
注意
你可能会想知道为什么 UI 布局工具被设计成这种看似反直觉的方式;原因是灵活性。正如你将在接下来的两章中看到的,不仅可以在设计时以不同的方式定位 UI 元素,而且还有一系列不同的布局方案供应用程序设计者(也就是你)选择,以适应他们的计划。这种灵活性在学习 Android 时会导致一些尴尬,但一旦你克服了这种尴尬,就会获得很强大的设计能力。但不要担心,我们会一步一步地进行,直到你掌握了这个技巧。
我们将让 Android Studio 自动为我们解决问题,首先添加到我们的代码,然后使用 UI 设计工具。首先,让我们正确设置设计时布局。在第二个按钮的代码中,找到以下代码行:
tools:layout_editor_absoluteY="30dp" />
现在将其编辑为与以下代码行相同:
tools:layout_editor_absoluteY="100dp" />
提示
根据你放置第一个按钮的确切位置,Android Studio 中的值可能与刚刚讨论的值不同。如果第二个按钮比第一个按钮高大约 70dp,那么你可以继续进行这个练习。
这个微小的改变会使第二个按钮向下移动一点,但只在设计时有效。如果你在设计选项卡中查看,按钮将整齐地放置在第一个按钮的下方,但如果你在模拟器上运行应用程序,它们仍然都在屏幕的左上角,并且彼此重叠。
切换到设计选项卡,找到如下截图所示的推断约束按钮:
点击推断约束按钮。Android Studio 将编辑 XML 代码。让我们简要看一下幕后发生了什么。从代表按钮的代码部分的末尾,删除了以下代码行:
tools:layout_editor_absoluteX="147dp"
tools:layout_editor_absoluteY="30dp" />
这两行代码是水平(…absoluteX
)和垂直(…absoluteY
)定位按钮的原因。
Android Studio 还向第一个按钮添加了四行代码,向第二个按钮添加了三行代码。以下是在第一个按钮附近添加的代码:
android:layout_marginTop="30dp"
提示
dp
的确切值可能会有所不同,具体取决于您放置按钮的位置。
这段代码导致按钮在顶部有一个30
的边距。但是在什么顶部呢?看看以下添加到第一个按钮末尾的三行代码:
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
注意layout_constraintEnd_toEndOf
,layout_constraintStart_toStartOf
和layout_constraintTop_toTopOf
的新属性。分配给这些属性的值是"parent"
。这导致第一个按钮相对于父 UI 元素进行定位。父元素是包含所有其他元素的布局;在这种情况下,父元素是ConstraintLayout
元素。
现在看看添加到第二个(底部)按钮的三行代码。
在代码的开头附近,我们看到以下内容:
android:layout_marginTop="22dp"
提示
再次强调,dp
的确切值可能会有所不同,具体取决于您放置按钮的位置。
在第二个按钮的代码末尾,我们看到以下两行额外的代码:
app:layout_constraintStart_toStartOf="@+id/buttonTop"
app:layout_constraintTop_toBottomOf="@+id/buttonTop" />
这意味着第二个按钮相对于buttonTop
的位置有一个22
的边距。
现在运行应用程序,您将看到我们有两个不同的按钮。一个具有buttonTop
的id
属性,它在具有buttonBottom
的id
属性的另一个按钮上方:
显然,布局比我目前提到的要复杂得多,但您已经初步了解了 Android Studio 提供的选项,用于设计我们应用程序的 UI。我们将更仔细地研究ConstraintLayout
,并在第四章开始布局和材料设计中探索更多的布局选项。
我们想对我们的 XML 代码进行一些修改。
使按钮调用不同的函数
切换回Text标签,并在第二个(buttonBottom
)按钮中找到下一行代码:
android:onClick="topClick"
接下来,编辑代码如下:
android:onClick="bottomClick"
现在我们有两个按钮,一个在另一个上面。顶部按钮的id
属性为buttonTop
,onClick
属性的值为topClick
。另一个按钮的id
属性为buttonBottom
,onClick
属性的值为bottomClick
。
这些最后的 XML 代码更改现在意味着我们需要在 Kotlin 代码中提供两个函数(topClick
和bottomClick
)。
提示
从技术上讲,当点击两个按钮时调用相同的函数是可以的-这不是语法错误。然而,大多数按钮确实有不同的目的,因此如果我们的按钮执行不同的操作,这个练习将更有意义。
我们很快就会做到,但在我们这样做之前,让我们先了解一下 Kotlin 注释,并查看一些我们可以编写的 Kotlin 代码,以向用户发送消息,并为调试目的向自己发送消息。
在我们的 Kotlin 代码中留下注释
在编程中,写代码注释是一个聪明的主意,并且在你的代码中大量使用它们。这是为了提醒我们在编写代码时的想法。要做到这一点,你只需添加双斜杠,然后输入你的注释,如下所示:
// This is a comment and it could be useful
此外,我们可以使用注释来注释掉一行代码。假设我们有一行代码暂时想要禁用;我们可以通过添加两个斜杠来实现,如下所示:
// The code below used to send a message
// Log.i("info","our message here")
// But now it doesn't do anything
// And I am getting ahead of where I should be
提示
使用注释注释掉代码应该只是一个临时措施。一旦找到正确的代码使用,注释掉的代码应该被删除,以保持代码文件的清洁和有组织性。
让我们来看看在 Android 中发送消息的两种不同方式,然后我们可以编写一些函数,当我们的 UI 按钮被按下时发送消息。
向用户和开发者编码消息
在本章的介绍和上一章中,我们谈到了一些关于使用其他人的代码的事情,特别是通过安卓 API 的类和它们的函数。我们看到我们可以用相当少量的代码做一些相当复杂的事情(比如与卫星通信)。
为了让我们开始编码,我们将使用安卓 API 中的两个不同类,这些类允许我们输出消息。第一个类Log
允许我们将消息输出到 logcat 窗口。第二个类Toast
不是一种美味的早餐款待,而是会为我们的应用程序用户产生一个类似吐司的弹出消息。
这是我们需要编写的代码,以便将消息发送到 logcat:
Log.i("info","our message here")
为什么这样做有效将在第十章面向对象编程中变得更清晰,但现在,我们只需要知道我们放在引号之间的任何内容都将输出到 logcat 窗口。我们很快就会看到在哪里放置这种类型的代码。
这是我们需要编写的代码,以便向用户屏幕发送消息:
Toast.makeText(this, "our message",
Toast.LENGTH_SHORT).show()
这是一行非常复杂的代码,它的工作原理将在第九章Kotlin 函数中变得更清晰。这里重要的是,我们放在引号之间的任何内容都将出现在一个弹出消息中供我们的用户查看。
让我们把一些代码(就像我们刚刚看到的)真正放入我们的应用程序中。
编写我们的第一个 Kotlin 代码
因此,我们现在知道了将输出到 logcat 或用户屏幕的代码。但是,我们应该把代码放在哪里呢?要回答这个问题,我们需要理解MainActivity.kt
中的onCreate
函数在应用程序准备展示给用户时执行。因此,如果我们将我们的代码放在这个函数的末尾,它将在用户看到它时运行;听起来不错。
提示
我们知道要执行函数中的代码,我们需要调用它。我们已经将我们的按钮连接起来调用一些函数,比如topClick
和bottomClick
。很快,我们将编写这些函数。但是谁或什么在调用onCreate
呢?这个谜团的答案是,安卓本身在用户点击应用图标运行应用时调用onCreate
。在第六章安卓生命周期中,我们将深入探讨,清楚地了解代码何时执行。你现在不需要完全理解这一点;我只是想给你一个概述。
让我们快速尝试一下;切换到安卓工作室中的MainActivity.kt
选项卡。
我们知道onCreate
函数是在应用程序真正启动之前调用的。让我们将一些代码复制粘贴到我们的 Hello World 应用程序的onCreate
函数中,看看当我们运行它时会发生什么。
向onCreate
函数添加消息代码
找到onCreate
函数的闭合大括号(}
),并在下面的代码块中添加突出显示的代码。在代码中,我没有显示onCreate
函数的完整内容,而是使用…
表示未显示的一些行代码。重要的是将新代码(完整显示)放在最后,但在闭合大括号(}
)之前:
override fun onCreate(savedInstanceState: Bundle?) {
…
…
…
// Your code goes here
Toast.makeText(this, "Can you see me?",
Toast.LENGTH_SHORT).show()
Log.i("info", "Done creating the app")
}
注意,在安卓工作室中,Toast
和Log
的实例被标记为红色。它们是错误。我们知道Toast
和Log
是类,而类是代码的容器。
问题是,安卓工作室在我们告诉它之前并不知道它们。我们必须为每个类添加一个import
指令。幸运的是,这是半自动的。
在Toast
上单击左键;现在按住Alt键,然后点击Enter键。您需要执行此步骤两次,一次是为Toast
,一次是为Log
。安卓工作室将import
指令添加到我们其他导入的代码顶部,并且错误消失了。
提示
Alt + Enter只是许多有用的键盘快捷键之一。以下是 Android Studio 的键盘快捷键参考。更具体地说,这是基于的 IntelliJ Idea IDE 的键盘快捷键。收藏这个网页;在本书的过程中它将是无价的:www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard.pdf
。
滚动到MainActivity.kt
的顶部,查看添加的import
指令。以下是为您方便起见:
import android.util.Log
import android.widget.Toast
以通常的方式运行应用程序,并查看logcat窗口中的输出。
检查输出
以下是 logcat 窗口中输出的屏幕截图:
查看 logcat,您会看到我们的消息 - 完成创建应用程序 - 已输出,尽管它混杂在我们目前不感兴趣的其他系统消息中。如果您在应用程序首次启动时观看模拟器,您还将看到用户将看到的整洁弹出消息:
您可能想知道为什么消息在那个时间输出。简单的答案是onCreate
函数在应用程序开始响应用户之前被调用。在 Android 开发人员中,将代码放在此函数中以准备好应用程序并为用户输入做好准备是常见做法。
现在,我们将进一步迈出一步,编写我们自己的函数,这些函数由我们的 UI 按钮调用。我们将在其中放置类似的Log
和Toast
消息。
编写我们自己的 Kotlin 函数
让我们直接开始编写我们的第一个 Kotlin 函数,并在其中添加一些Log
和Toast
消息。
提示
如果您还没有这样做,现在是获取包含所有代码文件的下载包的好时机。您可以查看每个章节的完成代码。例如,本章的完成代码可以在Chapter02
文件夹中找到。我进一步将Chapter02
文件夹细分为kotlin
和res
文件夹(用于 Kotlin 和资源文件)。在有多个项目的章节中,我将进一步划分文件夹以包含项目名称。您应该在文本编辑器中查看这些文件。我的最爱是 Notepad++,可以从notepad-plus-plus.org/download/
免费下载。在文本编辑器中查看代码比直接从书中阅读更容易,尤其是平装版本,尤其是代码行很长的情况下。文本编辑器还是将代码部分选择复制并粘贴到 Android Studio 中的绝佳方式。您可以在 Android Studio 中打开代码,但这样您就有可能将我的代码与 Android Studio 的自动生成代码混淆。
识别MainActivity
类的闭合大括号(}
)。
提示
请注意,您要寻找的是整个类的结尾,而不是onCreate
函数的结尾,就像前一节一样。花点时间来识别新代码以及它在现有代码中的位置。
在那个大括号内,输入以下突出显示的代码:
override fun onCreate(savedInstanceState: Bundle?) {
…
…
…
…
}
…
…
…
fun topClick(v: View) {
Toast.makeText(this, "Top button clicked",
Toast.LENGTH_SHORT).show()
Log.i("info", "The user clicked the top button")
}
fun bottomClick(v: View) {
Toast.makeText(this, "Bottom button clicked",
Toast.LENGTH_SHORT).show()
Log.i("info", "The user clicked the bottom button")
}
} // This is the end of the class
注意,两个View
实例是红色的,表示错误。只需使用Alt + Enter键组合导入View
类并删除错误。
以通常的方式将应用程序部署到真实设备或模拟器,并开始点击按钮,以便我们观察输出。
检查输出
最后,我们的应用程序有所作为!我们可以看到我们在按钮onClick
属性中定义的函数名称确实在按钮被点击时被调用;适当的消息被添加到logcat窗口;并且适当的Toast
消息显示给用户。
诚然,我们仍然不理解为什么Toast
和Log
真正起作用,也不完全理解我们函数语法中的(v: View)
部分,或者自动生成的代码的其余部分。随着我们的进展,这将变得清晰起来。如前所述,在第十章 面向对象编程中,我们将深入探讨类的世界,在第九章 Kotlin 函数中,我们将掌握与函数相关的其余语法。
查看 logcat 输出;您可以看到onCreate
函数之前记录了一个日志条目,以及我们自己编写的两个函数,每次点击按钮时都会记录。在下面的屏幕截图中,您可以看到我点击了每个按钮三次:
由于您现在已经熟悉了logcat窗口的位置,因此在未来,我将以修剪后的文本形式呈现 logcat 输出,因为这样更容易阅读:
The user clicked the top button
The user clicked the top button
The user clicked the top button
The user clicked the bottom button
The user clicked the bottom button
The user clicked the bottom button
在下面的屏幕截图中,您可以看到顶部按钮已被点击,并且topClick
函数被调用,触发了弹出的Toast
消息:
在本书中,我们将定期输出到 logcat,以便我们可以看到我们应用程序 UI 背后发生了什么。Toast
消息更多是用于通知用户发生了某事。这可能是下载完成,新邮件到达,或者其他需要他们注意的事件。
常见问题
Q.1)你能提醒我函数是什么吗?
A)函数是我们的代码的容器,可以从代码的其他部分执行(调用)它们。
Q.2)像第一个问题一样,我觉得这一章很难。我需要重新阅读吗?
A)不,如果您成功构建了应用程序,您已经取得了足够的进步来处理下一章的所有内容。我们知识中的所有空白将逐渐填补,并随着书籍的进展而被美好的领悟时刻所取代。
总结
在这个练习中,我们取得了很多成就。的确,XML 代码仍然普遍难以理解。没关系,因为在接下来的两章中,我们将真正掌握可视化设计师,并更多地了解 XML 代码,尽管最终我们的目标是尽可能少地使用 XML 代码。
我们已经看到,当我们将按钮拖放到设计中时,XML 代码会为我们生成。此外,如果我们在属性窗口中更改属性,那么 XML 代码也会被编辑。此外,我们可以直接输入(或者在我们的情况下,复制和粘贴)XML 代码来创建新的按钮或编辑现有按钮。
我们已经看到并编写了我们的第一个 Kotlin 代码,包括帮助我们记录代码的注释,并且我们甚至添加了自己的函数来输出调试消息到 logcat 和弹出Toast
消息给用户。
在下一章中,我们将全面介绍 Android Studio,以了解不同的操作在哪里完成。此外,我们将了解项目的资产(如文件和文件夹)的结构以及如何管理它们。这将为我们更深入地研究 UI 设计做好准备,第四章 开始布局和 Material Design 和第五章 CardView 和 ScrollView 创建美丽的布局,在这两章中,我们将为我们的应用程序构建一些重要的真实布局。
第三章:探索 Android Studio 和项目结构
在本章中,我们将创建并运行另外两个 Android 项目。这些练习的目的是更深入地探索 Android Studio 和 Android 项目的结构。
当我们构建应用程序准备部署时,代码和资源文件需要像在 APK 文件中那样打包起来。因此,所有布局文件和其他资源(我们很快将要看到的)都需要处于正确的结构中。
幸运的是,当我们从模板创建项目时,Android Studio 会为我们处理这些。然而,我们仍然需要知道如何找到和修改这些文件,如何添加我们自己的文件,有时还需要删除 Android Studio 创建的文件,以及资源文件如何相互关联 - 有时是彼此之间,有时是与 Kotlin 代码(即自动生成的 Kotlin 代码以及我们自己的代码)之间。
除了了解我们项目的组成,确保我们充分利用模拟器也将是有益的。
提示
模拟器在您希望确保您的应用程序在您未拥有的硬件上运行时特别有用。此外,了解一些最新功能(正如我们将在本书中了解到的)通常需要最新的手机,模拟器是一种经济有效的方式,可以让您在不购买最新手机的情况下跟随所有迷你应用程序。
在本章中,我们将执行以下操作:
-
探索空活动项目模板的文件和文件夹结构。
-
查看空活动和基本活动模板之间的区别。
-
了解如何充分利用模拟器。
本章将使我们能够在下一章中构建和部署多个不同的布局。
Android Studio 快速导览
要开始,请查看 Android Studio 的这个带注释的图解。我们将重新熟悉我们已经看过的部分,并了解我们尚未讨论的部分:
正式指出并命名 Android Studio用户界面(UI)的各个部分将是有益的,这样我就可以按名称引用它们,而不是一直描述它们的位置并展示截图。因此,让我们从1开始逐个介绍它们:
- 这是项目窗口,也是本章的主要焦点。它使我们能够探索项目的文件夹、代码和资源,并且也被称为项目资源管理器窗口。在这里双击一个文件以打开文件并在图中的区域3添加一个新标签。这里的文件和文件夹结构与最终打包到完成的 APK 文件中的结构非常相似。
提示
正如我们将看到的,虽然 Android 项目的文件夹结构保持不变,但文件、文件名和文件内容差异很大。因此,在本章中,我们将探索两个项目,然后随着我们在本书中的进展,再看更多的项目。
-
这是编辑器窗口。正如我们已经看到的,编辑器窗口会根据我们正在编辑的内容而呈现出不同的形式。如果我们正在编辑 Kotlin,那么我们可以看到我们的代码被整齐地格式化并准备好进行编辑;如果我们正在设计 UI,则它会为我们提供可视化编辑视图或文本/XML 代码视图。您还可以在此窗口中查看和编辑图形和其他文件。
-
这些标签允许我们在项目中不同的文件之间切换。编辑器窗口将显示我们在这里选择的文件。我们可以通过在项目窗口中双击文件来在此部分添加另一个标签。
-
这使我们能够在当前正在编辑的文件的设计和文本(代码)视图之间切换。
-
这个窗口根据图表第六部分中选择的选项而有所不同。通常,在本书中,我们会在构建窗口和Logcat窗口之间切换,以查看我们的项目是否已经编译和启动,以及调试输出和应用程序的任何错误或崩溃报告。
-
这个 UI 区域用于在第五部分描述的不同显示之间进行切换。
注意
在 Android Studio 中有更多的选项卡,但在本书的上下文中我们不需要它们。
现在我们知道如何明确地引用 UI 的各个部分,让我们把注意力转向项目/项目资源管理器窗口。
项目资源管理器和项目解剖
当我们创建一个新的 Android 项目时,我们通常会使用项目模板,就像我们在第一章中所做的那样,开始使用 Android 和 Kotlin。我们使用的模板决定了 Android Studio 将生成的文件的精确选择和内容。虽然所有项目之间存在很大的相似之处值得注意,但了解差异也有帮助。让我们构建两个不同的模板项目,并检查文件、它们的内容以及通过代码(XML 和 Kotlin)如何链接在一起。
空活动项目
最简单的项目类型是自动生成 UI 的空活动项目。在这里,UI 是空的,但可以添加内容。也可以生成一个完全没有 UI 的项目。当我们创建一个项目时,即使 UI 为空,Android Studio 也会自动生成 Kotlin 代码来显示 UI。因此,当我们添加内容时,它已经准备好显示。
让我们创建一个空活动项目。这几乎与我们在第一章中所做的过程相同,开始使用 Android 和 Kotlin,但有一个我会指出的细微差别:
-
在 Android Studio 中,选择文件 | 新建 | 新项目…。
-
在选择您的项目屏幕上,选择空活动模板,然后点击下一步。
-
将名称字段更改为
空活动应用
。 -
选择与上一个项目相同的包名称和保存位置。
-
确保选择Kotlin作为语言。
-
勾选使用 AndroidX 构件复选框,就像我们之前做的那样。
-
其余设置可以保留为默认设置,所以只需点击下一步。
Android Studio 将生成所有代码和其他项目资源。现在我们可以看到已生成的内容,并将其与项目资源管理器窗口中的预期内容进行比较。
如果模拟器尚未运行,请通过选择工具 | AVD 管理器来启动它,然后在Android 虚拟设备窗口中启动您的模拟器。通过在快速启动栏中点击播放按钮在模拟器上运行应用程序:
看看这个应用程序,注意它与第一个项目有些不同。它是空的;顶部没有菜单,底部没有浮动按钮。但是,它仍然有**Hello World!**文本:
注意
不要担心参考第一个项目;我们很快就会再建立一个类似的项目。
现在我们有了一个全新的空活动应用项目,让我们探索 Android Studio 为我们生成的文件和文件夹。
探索空活动项目
现在,是时候深入了解我们应用程序的文件和文件夹了。这将节省我们很多时间和困惑,以后在书中。请注意,无需记住所有这些文件的位置,甚至更不需要理解文件中的代码。事实上,XML 代码的部分内容在书的最后仍然是个谜,但这不会阻止您设计、编码和发布令人惊叹的应用程序。
创建项目后,请查看项目资源管理器窗口:
注意前面截图中指示的两个箭头。你可能已经猜到,这些箭头允许我们展开app
和Gradle Scripts
文件夹。
注意
在本书的背景下,我们不需要探索Gradle Scripts
文件夹。Gradle 是 Android Studio 的重要组成部分,但其作用是隐藏用户不需要了解的复杂过程,例如添加资源文件,编译和构建项目。因此,我们不需要深入研究这一点。然而,如果您决定将 Android 提升到下一个水平,那么深入了解 Gradle 及其与 Android Studio 的关系是值得投资时间的。
我们将更详细地探索app
文件夹。单击app
文件夹旁边的箭头以展开其内容,我们将开始探索。第一级内容显示在以下截图中:
我们已经揭示了另外三个文件夹:manifests
、java
和res
。让我们从顶部开始仔细查看这三个文件夹。
注意
我们将把我们的 Kotlin 代码放在java
文件夹中。此外,自从 Android Studio 3.3 版本发布以来,还有一个名为generatedjava
的文件夹,但我们不需要探索它。
manifests 文件夹
manifests
文件夹里面只有一个文件。展开manifests
文件夹,双击AndroidManifest.xml
文件。注意文件已在编辑窗口中打开,并添加了一个选项卡,以便我们可以轻松地在此文件和其他文件之间切换。以下截图显示了新添加的选项卡,以及manifests
文件夹中AndroidManifest.xml
文件中包含的 XML 代码:
我们不需要理解文件中的所有内容,但值得指出的是,我们将偶尔在这里进行修改,例如,当我们需要请求用户许可以访问其设备的某些功能时,例如消息应用或图像文件夹。当我们想要为游戏等全屏应用进行沉浸式体验时,我们也会编辑此文件。
注意文件的结构与我们在上一章中看到的布局文件的结构非常相似。例如,有明确定义的部分,以<section name
开头,以</section name>
结尾。这样的真实示例包括<application
和</application>
,以及<activity
和</activity>
。
事实上,除了第一行之外,整个文件内容都包含在<manifest
和</manifest>
中。
就像我们将计算的括号输入计算器一样,这些开放和关闭部分必须匹配,否则文件将在我们的项目中引起错误。Android Studio 会在行前缩进(即放置制表符),以使结构中的各个部分及其深度更清晰。
这段代码的一些特定部分值得注意,所以我将指出其中的一些行。
以下行告诉 Android,我们希望在他们的应用抽屉/主屏幕中向用户显示的图标,并且用户可以使用它来启动应用,它包含在mipmap
文件夹中,名为ic_launcher
:
android:icon="@mipmap/ic_launcher"
随着我们继续探索,我们将自己验证这一点。
下一行有两个值得讨论的方面。首先,它表示我们给我们的应用的名称;其次,这个名称作为一个字符串包含在app_name
标签中:
android:label="@string/app_name"
提示
在编程中,包括 Kotlin 和 XML,字符串可以是任何字母数字值。我们将在整本书中学习更多关于字符串的知识,从第七章开始。
因此,我们可以猜测app_name
标签的字母数字值是Empty Activity App
,因为这是我们创建应用程序时的名称。
这可能听起来很不寻常,但我们很快就会看到这个文件以及它的标签。在以后的项目中,我们还会为其添加更多的标签和值。我们也会明白为什么我们以这种看似复杂的方式向我们的应用程序添加文本的原因。
我们可以讨论AndroidManifest.xml
文件中的每一行,但我们不需要这样做。让我们看看另外两行,因为它们是相关的。下一行指示了我们的 Activity 的名称,这是在创建项目时自动生成的。我已经突出显示了 Activity 名称,以使其更加突出:
<activity android:name=".MainActivity">
出现在<activity
和</activity>
标签内的下一行表示它是activity
文件的属性。这告诉我们,这个 Activity 是在应用程序启动时应该运行的 Activity;它是LAUNCHER
。
<category android:name="android.intent.category.LAUNCHER" />
这意味着我们的应用程序可以有多个 Activity。很多时候,如果你的应用程序有多个屏幕,比如主屏幕或设置屏幕,这些屏幕是由多个 Activity 类的实例构建的。
注意
在 XML 中,比如AndroidManifest
文件,activity
是小写的;但在 Kotlin 中,Activity
类的A
是大写的。这只是一种约定,不需要担心。
正如你刚刚看到的,XML 中的activity
具有一个name
属性,其值指向 Kotlin Activity
的一个实例。
现在让我们深入java
文件夹。
java 文件夹
在这里,我们将找到所有的 Kotlin 代码。起初,这只包括一个文件,但随着我们的项目进一步发展,我们会添加更多文件。展开java
文件夹,你会发现另外三个文件夹,如下截图所示:
对于本书,我们只需要这三个文件夹中的一个;也就是顶层文件夹。这些文件夹的名称由包名(在创建应用程序时选择)和应用程序名称组成,以小写形式呈现,没有空格(这也是在创建应用程序时选择的)。
提示
有多个同名文件夹的原因是由于自动化测试,这超出了本书的范围。因此,你可以安全地忽略以(androidTest)
和(test)
结尾的文件夹。
我们在本书中感兴趣的唯一文件夹是顶层文件夹,对于我的屏幕上的这个应用程序来说,它是com.gamecodeschool.emptyactivityapp
。根据你选择的包名和我们当前正在工作的应用程序的名称,文件夹的名称会发生变化,但我们始终需要访问并添加或编辑其内容的是顶层文件夹。
现在展开com.gamecodeschool.emptyactivityapp
(或者你的应用程序名称)文件夹以查看其内容。在下面的截图中,你可以看到该文件夹只有一个文件:
这是MainActivity.kt
文件,尽管项目窗口中没有显示文件扩展名,但在编辑器窗口上方的标签中是有的。事实上,本书中java/packagename.appname
文件夹中的所有文件都将有.kt
扩展名。
如果你双击MainActivity.kt
文件,它会在编辑器窗口中打开,尽管我们也可以直接点击编辑器窗口上方的MainActivity.kt
标签。随着我们向项目中添加更多的 Kotlin 文件,知道它们的存放位置将会很有用。
检查MainActivity.kt
文件,你会发现它是第一个项目中我们使用的 Kotlin 文件的简化版本。它是一样的,只是在onCreate
函数中有更少的函数和更少的代码。函数缺失是因为 UI 更简单,不需要它们;因此,Android Studio 没有生成它们。
参考一下以下截图中MainActivity.kt
文件的内容:
文件仍然有onCreate
函数,在应用程序运行时运行,但代码更少,onCreate
是唯一的函数。在onCreate
函数的最后一行代码上看一下,我们将在继续探索res
文件夹之前讨论这行代码:
setContentView(R.layout.activity_main)
代码调用了一个名为setContentView
的函数,并将一些数据传递给setContentView
,以便setContentView
函数中的代码可以使用。传递给setContentView
的数据是R.layout.activity.main
。
目前,我只是提一下setContentView
函数是由 Android API 提供的,它是准备并向用户显示 UI 的函数。那么,R.layout.activity_main
到底是什么?
让我们通过探索res
文件夹来找出答案。
res 文件夹
res
文件夹是所有资源的存放地。左键单击展开res
文件夹,我们将检查里面的内容。这是res
文件夹内顶层文件夹的截图:
让我们从列表的顶部开始;也就是说,从drawable
文件夹开始。
res/drawable 文件夹
名称有点透露了一些,但drawable
文件夹中不仅仅包含图形。随着我们在本书中的进展,我们确实会向这个文件夹中添加图形;但是,目前它只包含两个文件。
这些文件是ic_launcher_foreground
和ic_launcher_background
。我们不会检查这些文件,因为我们永远不需要修改它们,但我会快速提一下它们是什么。
如果你打开这些文件,你会发现它们非常长且技术性很强。它们包括坐标、颜色等列表。它们被称为图形蒙版。
它们被 Android 用来适应或蒙版其他图形;在这种情况下,是应用程序的启动器图标。这些文件是给 Android 的指令,告诉它如何调整应用程序的启动器图标。
这个系统是为了让不同的设备制造商可以创建适合自己 Android 设备的蒙版。这些蒙版默认位于drawable
文件夹中(ic_launcher_foreground
和ic_launcher_background
),是默认的自适应蒙版,可以为启动器图标添加视觉上令人愉悦的阴影和深度。
提示
如果自适应图标的概念对你有吸引力,那么你可以参考 Android 开发者网站上关于自适应图标的完整且非常直观的解释。
现在我们已经对drawable
有了足够的了解,让我们继续学习layout
。
res/layout 文件夹
展开layout
文件夹,你会看到我们在上一章中编辑过的熟悉的布局文件。这次内容更少,因为我们生成了一个空活动项目。它并不完全为空,因为它仍然包含一个ConstraintLayout
布局,包裹着一个显示Hello World!
的TextView
小部件。
确保查看内容-你会发现它看起来如你所料,但这里感兴趣的不是内容。仔细看一下文件的名称(不包括 XML 文件扩展名):activity_main
。
现在回想一下MainActivity.kt
文件中的 Kotlin 代码。以下是设置 UI 的代码行;我已经突出显示了代码的一部分:
setContentView(R.layout.activity_main);
R.layout.activity_main
代码确实是对res
/layout
文件夹中的activity_main
文件的引用。这是我们的 Kotlin 代码和 XML 布局/设计之间的连接。
在第一个项目中有一个不同之处;在第一个项目的layout
文件夹中,有一个额外的文件。在本章后面,我们将使用相同的模板(基本活动)构建另一个项目,以理解为什么。
在这之前,让我们探索最后两个文件夹及其所有子文件夹,从列表中的下一个mipmap
开始。
res/mipmap 文件夹
mipmap
文件夹很简单 - 相当简单。展开文件夹,查看其内容,如下截图所示:
在这里,你可以看到两个子文件夹;它们是ic_launcher
和ic_launcher_round
。ic_launcher
的内容包括我们在设备的应用抽屉/主屏幕中看到的常规启动器图标的图形,而ic_launcher_round
则包含使用圆形图标的设备的图形,而不是方形图标。双击每个文件夹中的一个.png
文件,我在这个截图中将它们并排放置,以帮助我们的讨论:
你可能也想知道为什么每个文件夹中都有五个ic_launcher….png
文件。原因是为不同尺寸和分辨率的屏幕提供合适比例的图标是一个良好的做法。使用hdpi
、mdpi
、xhdpi
、xxhdpi
和xxxhdpi
资格的图像允许不同的 Android 设备选择最适合用户的图标。
注意
dpi
代表每英寸点数,h
、m
、xh
、xxh
和xxxh
前缀代表高、中、超高、超超高等。这些被称为限定符,随着你在本书中的学习,你会发现 Android 有很多限定符,这些限定符帮助我们构建适合用户选择的各种不同设备的应用程序。
mipmap
文件夹中的最后一个谜团是每个子文件夹中还有一个 XML 文件。打开其中一个,你会看到它们引用了我们在drawable
文件夹中看到的ic_launcher_foreground
和ic_launcher_background
文件。这告诉 Android 设备从哪里获取自适应图标的详细信息。这些文件不是必需的,但它们使图标看起来更好,并增加了外观的灵活性。
我们还有一个文件夹及其所有文件要探索,然后我们将最终理解 Android 应用程序的结构。
res/values 文件夹
打开res
/values
文件夹,可以看到三个文件,我们将依次简要讨论。所有这些文件相互关联,并引用了我们已经看过的其他文件。
为了完整起见,这里是res
/values
文件夹中三个文件的截图:
理解的关键不在于记住连接,当然也不在于试图记住或理解文件中的代码,而是要欣赏到目前为止我们所看到的所有文件和代码之间相互关联的本质。
让我们逐个查看文件的内容。
colors.xml 文件
接下来,看一下colors.xml
文件的内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
请注意,起始和结束标签采用了我们从 XML 文件中期望的通常模式。作为资源的子元素,有三对<color> … </color>
标签。
每个color
标签中都包含一个name
属性和一些由数字和字母组成的奇怪代码。name
属性是颜色的名称。我们将在接下来的另一个文件中看到,这个文件中的各种名称是从另一个文件中引用的。
代码是定义实际颜色的内容。因此,当引用名称时,屏幕上显示的是相关代码定义的颜色。
注意
该代码被称为十六进制代码,因为在代码的每个位置上,可以使用值0
到9
和字母a
到f
,共 16 个可能的值。如果您想了解更多关于十六进制颜色
的信息,请访问www.color-hex.com/color-wheel/
。如果您对十六进制(基数 16)、二进制(基数 2)和其他数字基数感到好奇,请查看这篇文章,该文章解释了它们,并讨论了为什么人类通常使用十进制:betterexplained.com/articles/numbers-and-bases/
。
我们将在稍后看到这些名称是如何被引用的。
strings.xml 文件
大多数现代应用程序都是为尽可能广泛的受众而制作的。此外,如果应用程序规模或复杂度较大,则软件公司中的角色通常被划分为许多不同的团队。例如,为 Android 应用程序编写 Kotlin 代码的人很可能与设计 UI 布局的人几乎没有关系。
通过将应用程序的内容与应用程序的编程分开,可以更容易地随时进行更改,并且还可以为多种不同的语言创建内容,而无需为每种语言修改 Kotlin 代码。
看一下strings.xml
文件的以下内容:
<resources>
<string name="app_name">Empty Activity App</string>
</resources>
您可以看到,在现在熟悉的<resources>…</resources>
标签内,有一个<string>…</string>
标签。在string
标签内,有一个名为name
的属性,其值为app_name
,然后是Empty Activity App
的进一步值。
让我们再看一下我们之前在清单文件夹部分探讨过的AndroidManifest.xml
文件中的一行。所讨论的行显示在以下代码中,但如果您想要查看完整的上下文中的行,请参考 Android Studio 中的文件本身:
android:label="@string/app_name"
android:label
属性被赋予了@string/app_name
的值。在 Android 中,@string
指的是strings.xml
文件中的所有字符串。在这个特定的应用程序中,具有app_name
标签的string
属性具有Empty Activity App
的值。
因此,先前在AndroidManifest.xml
文件中显示的代码行在应用程序运行时对屏幕产生以下影响:
虽然这个系统起初可能看起来很复杂,但在实践中,它将设计和内容与编码分离开来,这样做非常高效。如果设计人员想要更改应用程序的名称,他们只需编辑strings.xml
文件。无需与 Kotlin 程序员互动,而且,如果应用程序中的所有文本都以字符串资源的形式提供,那么在项目进行过程中所有这些文本都可以轻松地进行更改和调整。
Android 通过允许开发人员为每种语言和区域设置使用不同的文件来存储字符串资源,进一步提高了灵活性。这意味着开发人员可以使用完全相同的 Kotlin 代码来满足整个星球上的快乐用户。Kotlin 程序员只需引用字符串资源的name
属性,而不是将文本本身硬编码,然后其他部门可以设计文本内容并处理诸如翻译之类的任务。我们将在第十八章本地化中使应用程序支持多种语言。
注意
有可能直接在 Kotlin 代码中硬编码实际文本,而不是使用字符串资源,大多数情况下,我们会这样做,以便轻松演示一些 Kotlin 代码,而不必陷入编辑或添加到strings.xml
文件中。
我们已经了解了关于strings.xml
的足够信息,可以继续探索空项目模板的最终文件。
styles.xml 文件
在这里,您可以看到这个项目模板的互连拼图的各个部分最终汇聚在一起。研究styles.xml
文件中的代码,然后我们可以讨论它:
<resources>
<!-- Base application theme. -->
<style name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
这是另一个资源文件,但它是在引用我们之前看到的colors.xml
文件。请注意,这里有一个style
标签,它包含多个item
标签;每个item
标签都有一个名称,比如colorPrimary
,colorPrimaryDark
或colorAccent
。然后,每个名称都被赋予一个值,比如@color/colorPrimary
。
你可能想知道发生了什么;@color
指的是colors.xml
文件,colorPrimary
,colorPrimaryDark
和colorAccent
指的是在该文件中用十六进制值定义的实际颜色。但为什么要创建颜色并给它们命名,然后在另一个文件中定义item
实例并将这些颜色分配给item
实例呢?为什么不直接将十六进制颜色值分配给每个item
呢?
看一下代码块的顶部,了解这种看似不必要的复杂性背后的原因。我再次展示了相关的代码行,这样我们可以更容易地讨论它们:
<style name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
正在进行的是已经定义了项目,并且这些项目包含在style
元素中。正如你所看到的,样式被称为AppTheme
。此外,该样式有一个名为Theme.AppCompat.Light.DarkActionBar
的父级。
该系统允许设计师选择一系列颜色,然后在colors.xml
文件中定义它们。然后他们可以进一步构建使用这些颜色的不同组合的样式 - 通常每个应用程序会有多个样式。样式还可以与主题(parent = "..."
)相关联。这个父主题可以是完全由应用设计师的样式和颜色设计的,也可以是 Android 的默认主题之一,比如Theme.AppCompat.Light.DarkActionBar
。
UI 设计师可以简单地在AndroidManifest.xml
文件中引用样式,就像这一行:
android:theme="@style/AppTheme"
UI 设计师可以愉快地调整颜色和它们的使用方式(项目),而不会干扰 Kotlin 代码。这也允许为世界不同地区创建不同的样式,而不需要对实际布局文件(在本例中为activity_main.xml
)进行任何更改。
例如,在西方文化中,绿色可以代表自然和正确性等主题;在许多中东国家,绿色代表生育,是与伊斯兰教相关的颜色。虽然你可能会在这两个地区都使用绿色,但你的应用将被认为是非常不同的。
如果你在印度尼西亚推出你的应用,你会发现绿色在许多(尽管不是所有)印尼人中是受到文化鄙视的。接下来,如果你在中国推出,你会发现绿色可能会带有与不忠的配偶有关的负面含义。这是典型程序员永远不会学会应对的困难。而且,幸运的是,由于我们可以在 Android Studio 中分配责任的方式,他们不需要学会。
因此,颜色、样式和主题是非常专业的主题。虽然我们不会深入探讨比绿色更深入的内容,但希望你能看到一个分离了编程、布局、颜色和文本内容责任的系统的好处。
提示
我认为在这一点上值得一提的是,图片也可以根据不同的区域划分,以便在同一个应用程序中,不同地区的用户看到不同的图片。而且,如果你在想,是的,这将意味着为每个区域提供不同的分辨率(比如hdpi
和xhdpi
等)。
值得一提的是,完全有可能制作出一款受到成千上万甚至数百万用户喜爱的应用,而不需要为每个地区单独定制。然而,即使我们不打算雇佣设计师、翻译人员和文化专家,我们仍然必须在这个旨在使他们能够工作的系统中工作,这就是为什么我们要深入探讨。
在这个阶段,我们已经很好地掌握了 Android 项目中的内容以及它们之间的联系。现在让我们构建另一个应用程序,以查看不同应用程序模板对 Android Studio 生成的基础文件的影响。
基本活动项目
下一个最简单的项目类型是自动生成 UI 的基本活动项目。这是我们在第一章中创建的相同类型的项目,开始使用 Android 和 Kotlin。现在可以打开该项目,但建议生成一个新项目,以便我们可以在没有任何修改和添加干扰讨论的情况下进行检查。
让我们创建一个基本活动项目,如下所示:
-
在 Android Studio 中,选择文件 | 新建 | 新项目…。
-
在选择您的项目屏幕上,选择基本活动模板,然后点击下一步。
-
将名称字段更改为
基本活动应用
。 -
选择与上一个项目相同的包名称,并将位置保存为之前的项目。
-
确保选择Kotlin作为语言。
-
像之前一样,勾选使用 AndroidX 构件复选框。
-
其余设置可以保持默认,所以只需点击下一步。
现在我们可以深入研究文件。我们不会像我们在空活动项目中那样详细地查看所有内容;我们只会查看差异和额外的部分。
探索基本活动项目
让我们首先比较 Kotlin 代码。查看代码编辑器中的MainActivity.kt
选项卡。它们都包含一个名为MainActivity
的类。不同之处在于函数的数量和onCreate
函数的内容。
如前所述,基本活动项目比空活动项目更复杂。
提示
您可以打开尽可能多的 Android Studio 实例。如果要并排比较项目,请选择文件 | 打开,然后选择项目,然后在提示时选择新建窗口,以打开项目而不关闭已经打开的任何项目。
第一个不同之处在于onCreate
函数中有一些额外的代码。
MainActivity.kt 文件
我在第二章中非常简要地提到了存在于 Kotlin 代码和 XML 代码中的相互关系。让我们浏览资源文件,并指出这段 Kotlin 代码指向的 XML 文件。
这是onCreate
函数中相关的 Kotlin 代码;我稍微重新格式化了它,以便在书中更易读:
setSupportActionBar(toolbar)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action",
Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
完全理解这段代码需要更多的章节,但只需指出这段代码使用资源文件的地方只需要一会儿,然后我们就会更加了解构成我们项目的组件。
与空活动项目相比,该代码引用了两个更多的资源。第一个是工具栏
,第二个是浮动操作按钮
,两者都引用了我们将在下一步中看到的 XML 文件。
如果您在项目窗口中打开res
/layout
文件夹,您会发现它们看起来与空活动项目中的情况有些不同:
现在有两个自动生成的文件。我们将探索content_main.xml
文件,并很快理解为什么需要它。
activity_main.xml 文件
现在,打开activity_main.xml
文件,您会看到一些元素代表工具栏
和浮动操作按钮
。通过引用这些元素,Kotlin 代码正在设置工具栏和浮动操作按钮以供使用。正如我们所期望的那样,XML 代码描述了它们的外观。
这是工具栏的 XML 代码:
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
请注意,它引用了Toolbar
、颜色和样式,以及其他一些内容。它是以android:id…
开头的那一行,它声明了一个类型为Toolbar
的小部件及其@+id/toolbar
值,这使得它可以通过 Kotlin 代码中的toolbar
实例名称访问。
为了清晰起见,这是实际工作中应用中的工具栏:
这是浮动操作按钮的 XML 代码。我稍微重新格式化了代码的第一行为两行:
<com.google.android.material.floatingactionbutton.
FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
请注意,它具有fab
的id
属性。通过这个id
属性,我们可以在我们的 Kotlin 代码中访问浮动操作按钮。
现在,我们的 Kotlin 代码中的fab
可以直接控制浮动操作按钮及其所有属性。在第十三章让 Android 小部件活起来中,我们将详细学习如何做到这一点。
这是实际应用中的浮动操作按钮:
很明显,我还没有详细解释代码;在这个阶段没有意义。相反,要注意这些相互关联,如下所示:
-
XML 文件可以引用其他 XML 文件。
-
Kotlin 可以引用 XML 文件(以及,正如我们将很快看到的,其他 Kotlin 文件)。
-
在 Kotlin 中,我们可以通过其
id
属性控制 XML 文件中的特定部分。
我们已经从这个文件中看到足够了;让我们继续并深入了解剩下的文件。
MainActivity.kt 中的额外函数
那么,这些函数是做什么的,它们何时被调用,以及谁确切地调用它们呢?
下一个不同之处是这个额外的函数,如下所示:
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to
// the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
这段代码准备(膨胀)了在menu_main.xml
文件中定义的菜单。和onCreate
一样,这个函数也被重写,并且直接由操作系统调用。
然后还有另一个函数,如下所示:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
这个函数也被重写,并且直接由操作系统调用。它处理用户选择菜单中的项目(或选项)时发生的情况。目前,它只处理一个选项,即设置选项,目前不执行任何操作。
前面的代码确定了是否单击了设置菜单选项;如果是,那么return
代码执行,控制返回到被用户单击设置菜单选项中断之前执行的应用程序的任何部分。我们将在第八章Kotlin 决策和循环中更多地了解 Kotlin 的when
关键字。
我们现在几乎知道足够了;不要担心记住所有这些连接。我们将回到每个连接,深入调查,并巩固我们对每个连接的理解。
那么,为什么我们需要res
/layout
文件夹中的第二个文件呢?
content_main.xml 文件
MainActivity.kt
文件在R.layout.activity_main
上调用了setContentView
。然后,activity_main
有这行代码被突出显示:
…
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
<com.google.android.material.floatingactionbutton
.FloatingActionButton
…
代码的高亮行确实include
了content_main
文件。因此,在应用栏添加到布局后,执行分支到content_main
,在那里,所有的 XML 代码都转换为 UI;然后,执行返回到activity_main
,并且浮动操作栏添加到布局中。在第五章使用 CardView 和 ScrollView 创建美丽的布局中,我们将使用include
,当我们构建一些整洁的滚动CardView
布局并将定义CardView
的代码与CardView
的实际内容分开时。
探索 Android 模拟器
随着我们的进展,熟悉如何使用 Android 模拟器确实有所帮助。如果您还没有使用过最新版本的 Android,甚至执行简单任务(如查看所有应用程序)的方式可能与您当前的设备工作方式不同。此外,我们还想知道如何使用所有模拟器附带的额外控件。
模拟器控制面板
您可能注意到了当您运行模拟器时,旁边会出现一个迷你控制面板。让我们看一下一些最有用的控件。看一下这个模拟器控制面板的截图。我已经做了标注以帮助讨论:
我只会提到一些更明显的控件,并在必要时深入一些:
-
这些是窗口控件。它们最小化或关闭模拟器窗口。
-
从上到下,第一个按钮用于关闭模拟器,模拟关闭实际设备的电源。接下来的两个图标分别是调高和调低音量。
-
这两个按钮允许您将模拟器向左和向右旋转。这意味着您可以测试您的应用在所有方向上的外观,以及应用在运行时如何处理方向变化。这两个按钮下面的图标分别是截图和放大。这是模拟器在水平旋转后的样子:
-
这些图标模拟返回按钮、主页按钮和查看运行中应用程序按钮。尝试一下这些按钮-我们有时需要使用它们,包括在第六章中,Android 生命周期。
-
按照标注图像中标有5的按钮,启动高级设置菜单,您可以与传感器、GPS、电池和指纹识别器等进行交互。如果您感兴趣,可以尝试一些这些设置:
让我们玩一下模拟器本身。
使用模拟器作为真实设备
模拟器可以模拟真实手机的每个功能,因此可以单独撰写一本关于它的书。如果您想编写用户喜爱的应用程序,那么了解各种 Android 设备是值得花时间去做的。我只想在这里指出一些最基本的功能,因为没有这些基本交互,将很难跟上本书的内容。此外,如果您有一部旧的 Android 设备,那么一些基本的操作(如访问应用抽屉)已经发生了变化,您可能会感到有些困惑。
访问应用抽屉
将鼠标光标放在主屏幕底部并向上拖动,以访问应用抽屉(包括所有应用程序);以下截图显示了这个动作进行到一半的情况:
现在您可以运行模拟器上安装的任何应用。请注意,当您通过 Android Studio 运行您的应用程序之一时,它将保留在模拟器上安装,并且可以从应用抽屉中运行。但是,您在 Android Studio 中对应用程序所做的每一次更改都需要您再次运行或安装应用程序,方法是单击 Android Studio 快速启动栏上的播放按钮,就像我们一直在做的那样。
查看活动应用程序和在应用程序之间切换
要查看活动应用程序,您可以使用模拟器控制面板,也就是截图上标有数字4的方块。要使用手机屏幕访问相同的选项(就像您在真实设备上所做的那样),向上滑动,就像访问应用抽屉一样,但只需滑动屏幕长度的四分之一,如下截图所示:
现在您可以通过最近的应用程序向左或向右滑动,向上滑动应用程序以关闭它,或者点击返回按钮返回到您在查看此选项之前所做的事情。请尝试一下,因为我们在本书中经常会使用这些基本功能。
摘要
请记住,本章的目标是熟悉 Android 系统和 Android 项目的结构。Android 项目是 Kotlin 和大量资源文件的复杂交织。资源文件可以包含 XML 来描述我们的布局、文本内容、样式和颜色,以及图像。资源可以针对世界各地的不同语言和地区进行生产。我们将在整本书中看到并使用的其他资源类型包括主题和音效。
记住不同资源文件和 Kotlin 文件相互关联的不同方式并不重要。重要的是意识到它们是相互关联的,并且能够检查各种类型的文件,并意识到它们何时依赖于另一个文件中的代码。每当我们从 Kotlin 代码创建连接到 XML 代码时,我都会再次指出连接的细节。
我们不需要额外学习 XML,而是会在接下来的 25 章中对其有一些了解。Kotlin 将是本书的重点,但我们的 Kotlin 代码将经常涉及 XML 代码,因此理解并看到一些相互关联的示例将使您更快地取得进展。
我们还探索了模拟器,以便在测试我们的应用程序时充分利用它。
在下一章中,我们将使用三种不同的 Android 布局方案构建三个自定义布局。我们还将编写一些 Kotlin 代码,以便我们可以通过点击按钮在它们之间进行切换。
第四章:开始使用布局和材料设计
我们已经看到了安卓工作室的 UI 设计师,以及 Kotlin 的一些实际应用。在这个动手实践的章节中,我们将构建三个更多的布局-仍然相当简单,但比我们迄今为止所做的更进一步。
在我们开始动手之前,我们将快速介绍材料设计的概念。
我们将看看另一种布局类型,称为LinearLayout
,并通过使用它来创建可用的 UI 来详细介绍它。我们将进一步使用ConstraintLayout
,既了解约束,又设计更复杂和精确的 UI 设计。最后,我们将介绍TableLayout
,以便在易于阅读的表格中布置数据。
我们还将编写一些 Kotlin 代码,以在一个应用程序/项目中在不同的布局之间进行切换。这是第一个将多个主题整合到一个整洁包裹中的重要应用程序。该应用程序名为“探索布局”。
在本章中,我们将涵盖以下主题:
-
材料设计
-
构建
LinearLayout
并学习何时最好使用此类型 -
构建另一个稍微更高级的
ConstraintLayout
,并了解更多关于使用约束的信息 -
构建
TableLayout
并填充数据以显示 -
将所有内容链接在一个名为“探索布局”的单个应用程序中
首先是材料设计。
材料设计
你可能听说过材料设计,但它究竟是什么?材料设计的目标很简单,就是实现美观的用户界面。然而,它也是为了使这些用户界面在安卓设备上保持一致。材料设计并不是一个新的想法。它直接采用了纸和笔设计中使用的设计原则,比如具有视觉上令人愉悦的装饰,如阴影和深度。
材料设计使用材料层的概念,您可以将其视为照片编辑应用程序中的图层。一套原则、规则和指南实现了一致性。必须强调材料设计完全是可选的,但也必须强调材料设计是有效的,如果您不遵循它,用户很可能不喜欢您的设计。毕竟,用户已经习惯了某种类型的 UI,而该 UI 很可能是使用材料设计原则创建的。
因此,材料设计是一个值得努力的合理标准,但在学习材料设计的细节时,我们不应该让它阻碍我们学习如何开始使用安卓。
本书将专注于完成任务,同时偶尔指出材料设计如何影响我们的做法,并指向更深入了解材料设计的进一步资源。
探索安卓 UI 设计
我们将看到在安卓 UI 设计中,我们学到的很多东西都是依赖上下文的。给定小部件的 x 属性如何影响其外观可能取决于小部件的 y 属性,甚至取决于另一个小部件的属性。这并不容易直接学习。最好期望通过实践逐渐取得更好和更快的结果。
例如,如果您通过将小部件拖放到设计中来玩转设计师,生成的 XML 代码将根据您使用的布局类型而有很大不同。随着我们在本章中的进行,我们将看到这一点。
这是因为不同的布局类型使用不同的方法来决定其子元素的位置。例如,我们将在下一节中探索的LinearLayout
与我们项目中默认添加的ConstraintLayout
的工作方式完全不同,第一章中已经介绍了开始使用安卓和 Kotlin。
这些信息可能起初看起来像是一个问题,甚至是一个坏主意,当然可能有点尴尬。然而,我们将开始学习的是,这种清晰的布局选项的丰富性及其各自的怪癖是一件好事,因为它们为我们提供了几乎无限的设计潜力。您几乎可以想象不可能实现的布局很少。
然而,正如所暗示的,这种几乎无限的潜力伴随着一些复杂性。开始掌握这一点的最佳方法是构建几种类型的工作示例。在本章中,我们将看到三种 - LinearLayout
,ConstraintLayout
和TableLayout
。我们将看到如何使用可视化设计师的独特功能使事情变得更容易,并且我们还将对自动生成的 XML 进行一些关注,以使我们的理解更全面。
布局
我们已经看到了ConstraintLayout
,但还有更多。布局是将其他 UI 元素/小部件组合在一起的构建块。布局本身可以包含其他布局。
让我们看一些在 Android 中常用的布局,因为了解不同的布局及其优缺点将使我们更加了解可以实现什么,因此将扩展我们对可能性的认识。
我们已经看到,一旦我们设计了一个布局,我们就可以在 Kotlin 代码中使用setContentView
函数将其付诸实践。
让我们使用不同的布局类型构建三种设计,然后将setContentView
付诸实践并在它们之间切换。
创建“探索布局”项目
在 Android 中最困难的事情之一不仅是找出如何做某事,而是在其他事物中找出如何做某事。这就是为什么在本书中,除了向您展示如何做一些很酷的东西之外,我们还将把许多主题链接到跨越多个主题和章节的应用程序中。探索布局项目是这种类型的第一个应用程序。我们将学习如何构建多种类型的布局,同时将它们全部链接在一个方便的应用程序中:
- 在 Android Studio 中创建一个新项目。如果您已经打开了一个项目,请选择文件 | 新建项目。在提示时,选择在同一窗口中打开,因为我们不需要参考我们之前的项目。
提示
如果您在 Android Studio 的启动屏幕上,只需点击开始新的 Android Studio 项目选项即可创建一个新项目。
-
选择空活动项目模板,因为我们将从头开始构建大部分 UI。点击下一步按钮。
-
为项目命名为“探索布局”。
-
其余所有设置与我们之前使用的三个项目相同。
-
点击完成按钮。
查看MainActivity.kt
文件。以下是整个代码,不包括import…
语句:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
找到setContentView
的调用并删除整行。该行在上一个代码中显示为高亮显示。
这正是我们想要的,因为现在我们可以构建自己的布局,探索底层的 XML,并编写自己的 Kotlin 代码来显示这些布局。如果您现在运行该应用程序,您将只获得一个带有标题的空白屏幕;甚至没有“Hello World!”消息。
我们将要探索的第一种布局类型是LinearLayout
。
使用 LinearLayout 构建菜单
LinearLayout
可能是 Android 提供的最简单的布局。顾名思义,其中的所有 UI 项都是线性布局的。您只有两个选择 - 垂直和水平。通过添加以下代码行(或通过属性窗口进行编辑),您可以配置LinearLayout
以垂直布局:
android:orientation="vertical"
然后(您可能已经猜到了)将"vertical"
更改为"horizontal"
以水平布局。
在我们可以对LinearLayout
执行任何操作之前,我们需要将其添加到布局文件中。而且,由于我们在此项目中构建了三个布局,因此我们还需要一个新的布局文件。
向项目添加 LinearLayout
在项目窗口中,展开res
文件夹。现在右键单击layout
文件夹,然后选择New。注意到有一个Layout resource file选项,如下截图所示:
选择Layout resource file,然后会看到New Resource File对话框窗口:
在File name字段中输入main_menu
。名称是任意的,但这个布局将成为我们的“主”菜单,用于选择其他布局,所以这个名称似乎合适。
请注意,它已经选择了LinearLayout作为Root element选项。
单击OK按钮,Android Studio 将在名为main_menu
的 XML 文件中生成一个新的LinearLayout
,并将其放置在layout
文件夹中,准备好构建我们的新主菜单 UI。Android Studio 还将打开带有左侧调色板和右侧属性窗口的 UI 设计器。
准备工作区
通过拖动和调整窗口边界的大小(就像大多数窗口化应用程序一样),调整窗口的大小,使调色板、设计和属性尽可能清晰,但不要超出必要的范围。这个小截图显示了我选择的大致窗口比例,以使设计我们的 UI 和探索 XML 尽可能清晰。截图中的细节并不重要:
请注意,我已经尽可能地缩小了项目、调色板和属性窗口,但没有遮挡任何内容。我还关闭了屏幕底部的构建/logcat 窗口,结果是我有一个很清晰的画布来构建 UI。
检查生成的 XML
单击Text选项卡,我们将查看当前阶段形成我们设计的 XML 代码的当前状态。以下是代码,以便我们进一步讨论:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
我们有通常的起始和结束标签,正如我们可以预测的那样,它们是<LinearLayout
和</LinearLayout>
。目前还没有子元素,但有三个属性。我们知道它们是LinearLayout
的属性,而不是子元素,因为它们出现在第一个闭合>
之前。为了清晰起见,前面的代码中突出显示了定义这个LinearLayout
的三个属性。
第一个属性是android:orientation
,或者更简洁地说,我们将只是提到没有android:
部分的属性。orientation
属性的值是vertical
。这意味着,当我们开始向这个布局添加项目时,它将垂直地从上到下排列它们。我们可以将值从vertical
更改为horizontal
,它将从左到右布局。
接下来的两个属性是layout_width
和layout_height
。这些属性确定了LinearLayout
的大小。给定给这两个属性的值都是match_parent
。布局的父级是整个可用空间。因此,通过水平和垂直匹配父级,布局将填充整个可用空间。
向 UI 添加一个 TextView
切换回Design选项卡,我们将向 UI 添加一些元素。
首先,在调色板中找到TextView。这可以在Common和Text类别中找到。左键单击并将TextView拖放到 UI 上,注意它整齐地位于LinearLayout
的顶部。
查看Text选项卡上的 XML,并确认它是LinearLayout
的子元素,并且缩进了一个制表符以清晰地表示这一点。以下是TextView
的代码,没有周围的LinearLayout
代码:
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
请注意,它有四个属性:id
,以防我们需要从另一个 UI 元素或我们的 Kotlin 代码中引用它;layout_width
设置为match_parent
,这意味着TextView
横跨整个LinearLayout
的宽度;layout_height
属性设置为wrap_content
,这意味着TextView
的高度恰好足够容纳其中的文本;最后,目前,它具有一个text
元素,用于确定它将显示的实际文本,目前仅设置为TextView
。
切换回设计选项卡,我们将进行一些更改。
我们希望这段文本成为此屏幕的标题文本,即主菜单屏幕。在属性窗口中,单击搜索图标,输入text
到搜索框中,并将text属性更改为Menu
,如下截图所示:
提示
您可以通过搜索或滚动选项来查找任何属性。找到要编辑的属性后,左键单击选择它,然后按键盘上的Enter键使其可编辑。
接下来,使用您喜欢的搜索技术找到textSize
属性,并将textSize
设置为50sp
。输入新值后,文本大小将增加。
sp
代表可伸缩像素。这意味着当用户在其 Android 设备上更改字体大小设置时,字体将动态重新调整大小。
现在,搜索gravity属性,并通过单击以下截图中指示的小箭头展开选项:
将gravity设置为center_horizontal,如下截图所示:
gravity
属性指的是TextView
本身的重力,我们的更改会使TextView
内的实际文本移动到中心。
提示
请注意,gravity
与layout_gravity
是不同的。layout_gravity
属性设置了布局内的重力:在这种情况下,是父LinearLayout
。我们将在项目的后续部分使用layout_gravity
。
此时,我们已更改了TextView
的文本,增加了其大小,并使其水平居中。UI 设计师现在应该如下图所示:
快速浏览Text选项卡,查看 XML 代码,会发现以下代码:
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Menu"
android:textSize="50sp" />
您可以看到新的属性如下:gravity
设置为center_horizontal
;文本更改为Menu
;textSize
设置为50sp
。
如果运行应用程序,可能看不到预期的效果。这是因为我们在 Kotlin 代码中没有调用setContentView
来加载 UI。您仍然会看到空白的 UI。我们将在 UI 有了更多进展后解决这个问题。
将多行 TextView 添加到 UI
切换回Design选项卡,在调色板的Text类别中找到Multiline Text,并将其拖放到刚刚添加的TextView
下方的设计中。
使用您喜欢的搜索技术,将text设置为选择布局类型以查看示例。每个按钮的 onClick 属性将调用一个函数,该函数执行 setContentView 以加载新布局
。
您的布局现在将如下截图所示:
您的 XML 将在TextView
之后的LinearLayout
中更新为另一个子元素,代码如下:
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textMultiLine"
android:text="Select a layout type to view an example.
The onClick attribute of each button will call a function
which executes setContentView to load the new layout" />
您可以查看 UI 项的详细信息,结果发现多行文本的调色板上的描述并不明显,究竟是什么。查看 XML 后,我们发现有一个inputType
属性,表示用户可以编辑此文本。还有另一个我们以前没有见过的属性,那就是ems
。ems
属性控制每行可以输入多少个字符,而10
的值是 Android Studio 自动选择的。然而,另一个属性layout_width="match_parent"
覆盖了这个值,因为它使元素扩展以适应其父元素;换句话说,覆盖整个屏幕的宽度。
当您运行应用程序(在下一节中),您将看到文本确实是可编辑的-尽管对于这个演示应用程序的目的来说,它没有实际用途。
用 Kotlin 代码连接 UI(第一部分)
为了实现一个交互式的应用程序,我们将做以下三件事:
-
我们将从
onCreate
函数中调用setContentView
来显示我们运行应用程序时的 UI 进度。 -
我们将编写另外两个我们自己的函数,每个函数将在不同的布局上调用
setContentView
(我们还没有设计)。 -
然后,在本章后面,当我们设计另外两个 UI 布局时,我们将能够在点击按钮时加载它们。
因为我们将构建一个ConstraintLayout
和一个TableLayout
,所以我们将分别调用我们的新函数loadConstraintLayout
和loadTableLayout
。
现在让我们这样做,然后我们将看到如何添加一些按钮,调用这些函数以及一些整齐格式的文本。
在onCreate
函数中,添加以下突出显示的代码:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_menu)
}
该代码使用setContentView
函数来加载我们当前正在工作的 UI。现在您可以运行应用程序,看到以下结果:
在MainActivity
类的onCreate
函数之后,添加这两个新函数:
fun loadConstraintLayout(v: View) {
setContentView(R.layout.activity_main)
}
fun loadTableLayout(v: View) {
setContentView(R.layout.my_table_layout)
}
第一个函数有一个错误,第二个函数有两个错误。第一个我们可以通过添加一个import
语句来修复,以便 Android Studio 意识到View
类。左键单击View
单词选择错误。按住Alt键,然后点击Enter键。您将看到以下弹出窗口:
选择导入类。错误现在已经消失。如果您滚动到代码的顶部,您将看到刚才执行的快捷方式添加了一行新代码。以下是新代码:
import android.view.View
Android Studio 不再将View
类视为错误。
然而,第二个函数仍然有一个错误。问题在于该函数调用setContentView
函数来加载一个新的 UI(R.layout.my_table_layout
)。由于此 UI 布局尚不存在,因此会产生错误。您可以注释掉此调用以消除错误,直到我们在本章后面创建文件并设计 UI 布局。添加双斜杠(//
),如下面的代码中所突出显示的那样:
fun loadTableLayout(v: View) {
//setContentView(R.layout.my_table_layout)
}
现在我们可以添加一些按钮,我们可以点击这些按钮来调用我们的新函数,并加载我们即将构建的新布局。但是,添加一对带有一些文本的按钮太容易了-我们以前已经做过了。我们想要做的是将一些文本与按钮对齐,使其位于文本的右侧。问题在于我们的LinearLayout
的orientation
属性设置为vertical
,正如我们所见,我们添加到布局中的所有新部分都将垂直排列。
在布局中添加布局
将一些元素以不同方向布局的解决方案是在布局中嵌套布局。以下是如何做到的。
从调色板的布局类别中,将LinearLayout(水平)拖放到设计中,将其放置在多行文本的下方。注意,有一个蓝色边框占据了多行文本下方的所有空间:
这表明我们的新**LinearLayout(水平)**正在填充空间。请记住这个蓝色边框区域,因为我们将在 UI 上放置下一个项目。
现在,返回到调色板的Text类别,并将一个TextView拖放到我们刚刚添加的新LinearLayout中。请注意,TextView
紧密地位于新LinearLayout
的左上角:
起初,这似乎与我们 UI 中一开始的垂直LinearLayout
发生的情况没有什么不同。但是当我们添加 UI 的下一个部分时,看看会发生什么。
注意
用于指代在布局中添加布局的术语是嵌套。应用于 UI 上的任何项目(例如按钮和文本)的 Android 术语是视图,而包含视图的任何内容都是视图组。由于视图和视图组这两个术语在某些情境下并不总是清晰表达其含义,我通常会具体指称 UI 的部分(如TextView
、Button
和LinearLayout
)或更广泛地(UI 元素、项目或小部件)。
从Button类别中,将一个Button拖放到先前TextView
的右侧。请注意,按钮位于文本的右侧,如下图所示:
接下来,通过单击其空白部分选择LinearLayout
(水平的)。找到layout_height
属性并将其设置为wrap_content
。注意,LinearLayout
现在只占用所需的空间:
在添加 UI 的下一部分之前,让我们配置TextView
和Button
的text
属性。将Button
的text
属性更改为LOAD
。将我们的新TextView
的文本属性更改为Load ConstraintLayout
。
提示
你自己解决了如何实现之前的指令吗?是的?太棒了!现在你已经熟悉了编辑 Android 视图属性。没有?左键单击要编辑的项目(在本例中为TextView
),使用搜索图标搜索或滚动查找要在属性窗口中编辑的属性(在本例中为text
属性),选择属性,然后按Enter进行编辑。现在我可以更简洁地说明如何构建未来的 UI 项目,这将使你成为 Android 忍者的旅程更快。
现在我们可以重复自己,在刚刚完成的另一个**LinearLayout(水平)**中添加另一个TextView
和Button
属性。要这样做,请按顺序执行以下步骤:
-
在前一个下方再添加一个LinearLayout(水平)
-
在新的
LinearLayout
中添加一个TextView -
将
TextView
的text
属性更改为Load TableLayout
-
在
TextView
的右侧添加一个Button
-
将
Button
的text
属性更改为LOAD
-
通过将
layout_height
属性更改为wrap_content
来调整LinearLayout
的大小
现在我们有两个整齐(水平)对齐的文本和按钮。
只是为了好玩,也为了更多地探索调色板,找到调色板的小部件类别,并将一个RatingBar拖放到最终LinearLayout
的下方。现在,你的 UI 应该看起来与下一个截图非常相似:
注意
在前两个截图中,我还没有更改两个Button
元素的text
属性。其他所有内容应该与你的一样。
让我们为布局添加一些视觉上的修饰。
使布局看起来漂亮
在本节中,我们将探讨一些控制 UI 细节的更多属性。您可能已经注意到 UI 在某些地方看起来有点挤,而在其他地方看起来不对称。随着我们在书中的进展,我们将不断增加我们的技能来改善我们的布局,但这些简短的步骤将介绍并处理一些基础知识:
-
选择
多行文本
,然后展开Padding
属性。将all
选项设置为15sp
。这样在文本周围留出了整洁的空间。 -
为了在
多行文本
下方留出一个漂亮的空间,找到并展开Layout_Margin
属性,将bottom
设置为100sp
。 -
在与按钮对齐/相关的两个
TextView
小部件上,将textSize
属性设置为20sp
,layout_gravity
设置为center_vertical
,layout_width
设置为match_parent
,layout_weight
设置为.7
。 -
在两个按钮上,将权重设置为
.3
。注意现在两个按钮的宽度都是.3
,文本占据LinearLayout
的.7
,整体外观更加美观。 -
在
RatingBar
上,找到Layout_Margin
属性,然后将left
和right
设置为15sp
。 -
仍然使用
RatingBar
和Layout_Margin
属性,将top
更改为75sp
。
现在,您可以运行应用程序,看到我们的第一个完整布局的全部荣耀。
请注意,您可以玩RatingBar
,尽管在关闭应用程序时评分不会保留。
提示
作为读者挑战,找到一个或两个属性,可以进一步改善LoadConstraintLayout
和LoadTableLayout
文本的外观。它们看起来离屏幕边缘有点近。参考第五章开头的快速摘要部分,使用 CardView 和 ScrollView 创建美丽的布局。
不幸的是,按钮目前还没有功能。让我们解决这个问题。
使用 Kotlin 代码连接 UI(第二部分)
选择Load ConstraintLayout
文本旁边的按钮。找到onClick
属性,将其设置为loadConstraintLayout
。
选择Load TableLayout
文本旁边的按钮。找到onClick
属性,将其设置为loadTableLayout
。
现在,按钮将调用函数,但loadTableLayout
函数内的代码已被注释掉以避免错误。随时运行应用程序,看看您是否可以通过单击loadConstraintLayout
按钮切换到ConstraintLayout
。但它只有一个Hello World消息。
现在我们可以继续构建这个ConstraintLayout
。
使用 ConstraintLayout 构建精确的 UI
打开创建项目时自动生成的ConstraintLayout
。它可能已经在编辑器顶部的选项卡中。如果没有,它将在res
/layout
文件夹中。它的名称是activity_main.xml
。
检查Text选项卡中的 XML,并注意它是空的,除了一个说Hello World
的TextView
。切换回Design选项卡,左键单击TextView
以选择它,然后按Delete键将其删除。
现在我们可以构建一个简单而复杂的 UI。当您想要非常精确地定位 UI 的部分和/或相对于其他部分时,ConstraintLayout
非常有用。
添加日历视图
首先,在调色板的Widgets类别中找到CalenderView
。将CalenderView
拖放到靠近顶部且水平居中的位置。当您拖动CalenderView
时,注意它会跳到某些位置。
还要注意视图对齐时的微妙视觉提示。我在以下截图中突出显示了水平中心的视觉提示:
当它水平居中时,释放它,就像截图中一样。现在,我们将调整它的大小。
在 ConstraintLayout 中调整视图大小
左键单击并按住一个角落的方块,当你放开CalenderView
时会显示出来,向内拖动以减小CalenderView
的大小:
将大小减小约一半,将CalenderView
留在屏幕顶部,水平居中。在调整大小后,你可能需要重新调整一下位置,就像下面的图示一样:
你不需要把CalenderView
放在和我完全一样的位置。练习的目的是熟悉指示你放置位置的视觉线索,而不是创建一个和我的布局一模一样的副本。
使用组件树窗口
现在看一下组件树窗口 - 就在可视化设计师的左边和调色板下面。组件树是一种可视化 XML 布局的方式,但没有所有的细节。
在下面的截图中,我们可以看到CalenderView
向右缩进到ConstraintLayout
的右侧,因此是一个子元素。在我们构建的下一个 UI 中,我们将看到我们有时需要利用组件树来构建 UI。
目前,我只是想让你观察一下我们的CalenderView
旁边有一个警告标志。我在下面的截图中已经用颜色标出来了:
错误提示说此视图没有约束。它只有设计时的位置,因此在运行时会跳转到(0,0),除非你添加约束。还记得我们在第二章中首次将按钮添加到屏幕上时,它们只是简单地消失在左上角吗?
提示
现在运行应用程序,如果你想提醒自己这个问题,点击加载 ConstraintLayout按钮。
现在,我们可以通过点击推断约束按钮来修复这个问题,就像我们在第二章中使用的那样,Kotlin、XML 和 UI 设计师。这里再次提醒一下:
但学会手动添加约束是值得的,因为它为我们提供了更多的选项和灵活性。随着你的布局变得更加复杂,总会有一两个项目不按照你的意愿行事,手动修复几乎总是必要的。
手动添加约束
确保CalenderView
被选中,并观察顶部、底部、左侧和右侧的四个小圆圈:
这些是约束手柄。我们可以点击并拖动它们,将它们锚定到 UI 的其他部分或屏幕的边缘。通过将CalenderView
与屏幕的四个边缘锚定,我们可以在应用程序运行时将其锁定到位置。
依次点击并拖动顶部手柄到设计的顶部,右侧手柄到设计的右侧,底部手柄到设计的底部,左侧手柄到设计的左侧。
观察到CalenderView
现在被约束在中心。左键单击并拖动CalenderView
回到屏幕的上部某个位置,就像下面的图示一样。使用视觉线索(也显示在下面的截图中)确保CalenderView
水平居中:
在这个阶段,你可以运行应用程序,CalenderView
将会被定位到前面截图中显示的位置。
让我们向 UI 添加几个项目,并看看如何约束它们。
添加和约束更多的 UI 元素
从调色板的小部件类别中拖动一个ImageView
,并将其放置在CalenderView
的下方和左侧。当你放置ImageView
时,会弹出一个窗口提示你选择一个图像。选择项目 | ic_launcher,然后点击确定。
将ImageView
的左侧和底部约束到 UI 的左侧和底部。现在,您应该处于以下位置:
ImageView
在左下角被约束。现在,抓住ImageView
上的顶部约束手柄,并将其拖动到CalenderView
的底部约束手柄。现在的情况是这样的:
ImageView
只在一个侧面水平约束,因此被固定/约束在左侧。它还在垂直方向上被约束,并且在CalenderView
和 UI 的底部之间是均匀约束的。
接下来,在ImageView
的右侧添加一个TextView
。将TextView
的右侧约束到 UI 的右侧,并将TextView
的左侧约束到ImageView
的右侧。将TextView
的顶部约束到ImageView
的顶部,并将TextView
的底部约束到 UI 的底部。现在,您将得到类似以下图表的东西:
注意,Component Tree窗口中关于未约束项的所有警告都消失了。
注意
有关硬编码字符串的警告,因为我们直接向布局添加文本而不是strings.xml
文件,并且有关缺少contentDescription属性的警告。contentDescription属性应该用于添加文本描述,以便视觉障碍用户可以在应用中获得图像的口头描述。为了快速推进ConstraintLayout
,我们将忽略这两个警告。我们将在第十八章本地化中正确添加字符串资源,并且您可以在 Android 开发者网站的 Android Studio 上阅读有关辅助功能的信息,网址为developer.android.com/studio/intro/accessibility
。
您可以移动三个 UI 元素并将它们整齐地排列,就像您想要的那样。请注意,当您移动ImageView
时,TextView
也会随之移动,因为TextView
被约束到ImageView
。但也请注意,您可以独立移动TextView
,并且无论您放置在哪里,这都代表了它相对于ImageView
的新约束位置。无论项被约束到什么,其位置始终相对于该项。而且,正如我们所看到的,水平和垂直约束是彼此独立的。我将我的位置放置如下图所示:
提示
ConstraintLayout
是最新的布局类型,虽然它比其他布局更复杂,但它是最强大的,也是在用户设备上运行最好的。值得花更多时间查看有关ConstraintLayout
的更多教程。特别是在 YouTube 上查看,因为视频是学习调整ConstraintLayout
的好方法。我们将在整本书中回到ConstraintLayout
,而且您不需要知道比我们已经涵盖的更多内容才能继续前进。
使文本可点击
我们几乎完成了我们的ConstraintLayout
。我们只想要将一个链接返回到主菜单屏幕。这是一个很好的机会来演示TextView
(以及大多数其他 UI 项)也是可点击的。实际上,可点击的文本在现代 Android 应用程序中可能比传统的按钮更常见。
将TextView
的text
属性更改为返回菜单
。现在,找到onClick
属性并输入loadMenuLayout
。
现在,在MainActivity.kt
文件中添加以下函数,就在loadTableLayout
函数之后,如下所示:
fun loadTableLayout(v: View) {
//setContentView(R.layout.my_table_layout)
}
fun loadMenuLayout(v: View) {
setContentView(R.layout.main_menu)
}
现在,每当用户点击“返回菜单”文本时,loadMenuLayout
函数将被调用,setContentView
函数将加载main_menu.xml
中的布局。
你可以运行应用程序,在主菜单(LinearLayout
)和CalenderView
小部件(ConstraintLayout
)之间来回切换。
让我们为本章构建最终的布局。
使用 TableLayout 布局数据
在项目窗口中,展开res
文件夹。现在,右键单击layout
文件夹,然后选择新建。注意,有一个布局资源文件的选项。
选择布局资源文件,你会看到新建资源文件对话框窗口。
在文件名字段中输入my_table_layout
。这与我们在loadTableLayout
函数中调用setContentView
时使用的名称相同。
注意它已经选择了LinearLayout作为根元素选项。删除LinearLayout
,并在其位置键入TableLayout
。
点击确定按钮,Android Studio 将在名为my_table_layout
的 XML 文件中生成一个新的TableLayout
,并将其放在layout
文件夹中,准备为我们构建基于表格的新 UI。Android Studio 还将打开 UI 设计师(如果尚未打开),左侧是调色板,右侧是属性窗口。
现在,取消注释loadTableLayout
函数:
fun loadTableLayout(v: View) {
setContentView(R.layout.my_table_layout)
}
现在,当你运行应用程序时,你可以切换到基于TableLayout
的屏幕,尽管目前它是空白的。
向 TableLayout 添加 TableRow
从布局类别中将一个TableRow
元素拖放到 UI 设计中。注意,这个新的TableRow
的外观几乎是看不见的,以至于不值得在书中插入图表。UI 顶部只有一条蓝线。这是因为TableRow
已经将自己围绕其内容折叠起来,而目前内容是空的。
我们可以将选择的 UI 元素拖放到这条蓝线上,但这也有点别扭,甚至有点违反直觉。此外,一旦我们在一起有多个TableRow
元素,情况就会变得更加困难。解决方案在于组件树窗口,我们在构建ConstraintLayout
时简要介绍过。
当视觉设计师无法完成时使用组件树
查看组件树,注意你可以看到TableRow
作为TableLayout
的子级。我们可以直接将 UI 拖放到组件树中的TableRow
上。在组件树中将三个TextView
对象拖放到TableRow
上,这样就会得到以下布局。我已经用 photoshop 修改了以下截图,以展示组件树和常规 UI 设计师在同一图表中:
现在添加另外两个TableRow
对象(从布局类别)。你可以通过组件树窗口或 UI 设计师添加它们。
提示
你需要将它们放在窗口的最左边,否则新的TableRow
将成为前一个TableRow
的子级。这将使整个表格有点混乱。如果你意外地将TableRow
添加为前一个TableRow
的子级,你可以选择它,然后点击删除键,使用Ctrl + Z 键组合来撤消,或者将位置错误的TableRow
拖到左边(在组件树中)使其成为表格的子级 - 这是应该的。
现在,为每个新的TableRow
项目添加三个TextView
对象。最简单的方法是通过组件树窗口添加它们。检查你的布局,确保它与以下截图中的一样:
让表格看起来更像是一个真正的数据表,通过改变一些属性。
在TableLayout
上,将layout_width
和layout_height
属性设置为wrap_content
。这样就可以去掉多余的单元格。
通过编辑textColor
属性将所有外部(沿顶部和左侧)的TextView
对象的颜色更改为黑色。您可以通过选择第一个TextView
,搜索其color
属性,然后在color
属性值字段中输入black
来实现这一点。然后,您将能够从下拉列表中选择@android:color/black
。对每个外部TextView
元素都要这样做。
编辑每个TextView
的padding
并将all
属性更改为10sp
。
组织表格列
此时似乎我们已经完成了,但是我们需要更好地组织数据。我们的表格,像许多表格一样,将在左上角有一个空白单元格来分隔列和行标题。为了实现这一点,我们需要对所有单元格进行编号。为此,我们需要编辑layout_column
属性。
提示
单元格编号从左边开始编号为零。
首先删除左上角的TextView
。注意右侧的TextView
已经移动到左上角位置。
接下来,在新的左上角TextView
中,编辑layout_column
属性为1
(这将把它分配给第二个单元格,因为第一个是0
,我们想要留下第一个为空),然后,对于下一个单元格,编辑layout_column
属性为2
。
对于接下来的两行单元格,将它们的layout_column
属性从左到右从0
更改为2
。
如果您想要在编辑后了解此行的确切代码,请参阅以下片段,并记得在Chapter04
的/LayoutExploration
文件夹中查看整个文件的上下文:
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_column="1"
android:padding="10sp"
android:text="India"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_column="2"
android:padding="10sp"
android:text="England"
android:textColor="@android:color/black" />
</TableRow>
尝试完成这个练习,但是如果可能的话,请使用属性窗口。
链接回主菜单
最后,对于这个布局,我们将添加一个按钮,链接回主菜单。通过组件树添加另一个TableRow
。将按钮拖放到新的TableRow
上。编辑其layout_column
属性为1
,使其位于行的中间。编辑其text
属性为Menu
,并编辑其onClick
属性以匹配我们已经存在的loadMenuLayout
函数。
现在,您可以运行应用程序并在不同的布局之间来回切换。
如果您愿意,您可以通过编辑TextView
小部件的所有text
属性来向表格添加一些有意义的标题和数据,就像我在下面的截图中所做的那样,显示了在模拟器中运行的TableLayout
:
最后,思考一下一个呈现数据表的应用程序。很可能数据将动态地添加到表中,而不是由开发人员在设计时添加,而更可能是由用户或来自网络数据库的数据。在第十六章适配器和回收器中,我们将看到如何使用适配器动态地向不同类型的布局添加数据,而在第二十七章Android 数据库中,我们还将看到如何在我们的应用程序中创建和使用数据库。
摘要
我们在几十页中涵盖了许多主题。我们不仅构建了三种不同类型的布局,包括具有嵌套布局的LinearLayout
,手动配置约束的ConstraintLayout
,以及TableLayout
(尽管使用的是假数据),而且我们还通过可点击的按钮和文本将所有布局连接在一起,触发我们的 Kotlin 代码在所有这些不同的布局之间切换。
在下一章中,我们将继续讨论布局的主题。我们将回顾我们所见过的许多属性,并通过将多个CardView
布局整合到平滑滚动的ScrollView
布局中,构建迄今为止最美观的布局。