精通-Android-Studio3-全-

精通 Android Studio3(全)

原文:zh.annas-archive.org/md5/9a1caf285755ef105f618b7b4d6fcfa9

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

欢迎阅读《掌握 Android Studio 3》,这是对最新和最复杂的 Android 开发环境的全面指南。无论您是 IDE 的新手还是想要从其他 IDE(如 Eclipse)迁移,本书都使用实际示例来演示 Android Studio 如何促进开发的每个阶段。

本书从工作区本身的介绍开始,然后探索了 IDE 提供的各种 UI 设计工具,包括强大的可视化布局编辑器、自动约束布局工具和动画材料图标。

一旦掌握了 IDE 的设计工具,本书将继续探讨使用 Android Studio 进行代码开发以及其许多有用和创新的编程工具,例如代码完成、模板定制,以及最重要的是提供的 Android Studio 3 的出色测试和分析工具。

Android Studio 不仅是一个用于基本编码的好工具;它还提供了各种插件和扩展,支持诸如 C++和 Kotlin 等语言的本地语言支持。正是这种本地 SDK 的可扩展性使得掌握 Android Studio 3 成为任何移动开发人员的必备技能,本书详细介绍了其中最有用和最受欢迎的内容,使读者能够掌握当今最令人兴奋的开发工具之一。

本书内容

第一章《工作区结构》介绍了整体工作区。它涵盖了主要功能,对于那些全新于 IDE 的人来说将非常有用。

第二章《UI 设计》介绍了 UI 设计和开发的主题,着眼于布局编辑器的自动化和节省时间的功能。

第三章《UI 开发》继续使用 UI 开发工具,探讨了更复杂的布局以及如何使用打包在支持存储库中的代码库轻松实现这些布局。

第四章《设备开发》扩展了之前的工作,探讨了针对物理设备和形态因素的开发,涵盖了屏幕旋转和适用于可穿戴设备的形状感知布局等主题。

第五章《资源和资产》着眼于资源管理,特别是 Android 对材料图标和矢量资产的使用。它演示了 Android Studio 为开发的这一方面提供了很好的节省时间的功能。

第六章《模板和插件》是关于扩展 Android Studio 的两章中的第一章。在这里,我们将看到现成的和免费提供的代码样本,不仅在 IDE 中提供,还通过第三方插件提供。

第七章《语言支持》延续了前一章的主题。在这里,我们将看到如何无缝地包含 C++和 Kotlin 代码。

第八章《测试和分析》探讨了 IDE 提供的强大测试和分析工具,以及如何使用它们来测试和微调我们的工作。

第九章《打包和分发》涵盖了开发周期的最后方面。这涉及仔细研究 Gradle 并涵盖了货币化技术。

本书所需内容

Android Studio SDK 都是开源的,可以从developer.android.com下载。

本书中提到了各种第三方插件,以及相关的下载位置。

本书适合对象

本书适用于任何经验水平的 Android 开发人员,他们希望迁移到或简单掌握 Android Studio 3。

惯例

在本书中,您会发现一些文本样式,用于区分不同类型的信息。以下是一些样式的示例及其含义的解释。文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:"在前面的例子中,我们使用app:srcCompat而不是android:src。"

代码块设置如下:

public class ExampleUnitTest 
    { 
      @Test 
        public void addition_isCorrect() throws Exception { 
               assertEquals(4, 2 + 2); 
   } 
} 

当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示。

buildTypes { 
release { 
         . . .  
         } 
    } 
    productFlavors { 
        flavorDimensions "partial", "full" 

任何命令行输入或输出都以以下方式编写:

gradlew clean 

新术语重要单词以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,会以这样的方式出现在文本中:"除了 MakeBuild 和 Analyze 之外,Build 菜单还有其他有用的条目,例如 Clean Project 项目,它会从构建目录中删除构建工件"

警告或重要说明会以这种方式出现。

技巧和窍门显示如下。

第一章:工作区结构

Android Studio 是一个功能强大且复杂的开发环境,专门用于开发、测试和打包 Android 应用程序。它可以作为一个单一的软件包,与 Android SDK 一起下载,但正如我们将在本书中看到的那样,它实际上是一组工具和组件,其中许多是独立安装和更新的。

Android Studio 并不是开发 Android 应用程序的唯一方式;还有其他 IDE,比如 Eclipse 和 NetBeans,甚至可以仅使用记事本和命令行来开发完整的应用程序,尽管这种方法会非常缓慢和繁琐。

无论您是从其他 IDE 迁移还是只是想充分利用 Android Studio,本书将按照开发应用程序的过程中遇到的顺序,带您了解其最有用的功能,从 UI 开发开始,逐步进行编码和测试,到构建和分发。Android Studio 在这段旅程的每一步都为我们提供了一些有用和智能的工具。

Android Studio 是为了一个目的而构建的,吸引了越来越多的第三方插件,提供了大量有价值的功能,这些功能无法直接通过 IDE 获得。这些包括加快构建时间的插件,通过 Wi-Fi 调试项目等。其中最有用和最受欢迎的将在相关章节中介绍。在整本书中,我们将找到使用这些插件和 Android Studio 内置组件加快繁琐和困难任务的方法。

在本章中,您将涉及以下主题:

  • 探索 Studio 和其他 IDE 之间的差异

  • 进行简要的导览

  • 了解工作区的结构

  • 探索编辑器窗口

  • 创建材料主题

  • 理解工具窗口

  • 探索设备文件系统

  • 使用即时运行来加快构建过程

  • 探索 SDK 管理器

  • 介绍虚拟设备管理器

  • 从其他 IDE 导入项目

Android Studio

如果您已经熟悉 Android Studio,那么您可能希望跳过本介绍章节的一些部分,因为它更多地是针对那些从其他 IDE 迁移的人。然而,您可能之前没有遇到过一些方便的技巧。

尽管可以说是一种更优越的工具,但有一些非常好的理由可以坚持使用另一个 IDE,比如 Eclipse。许多开发人员开发多个平台,这使得 Eclipse 成为一个很好的工具选择。每个开发人员都有截止日期要满足,熟悉陌生软件可能会在一开始大大减慢他们的速度。这本书将加快这种过渡,以便迁移开发人员可以尽可能少地中断地开始利用 Android Studio 提供的附加功能。

Android Studio 的不同之处

Android Studio 与其他 IDE 和开发工具有许多不同之处。其中一些差异相当微妙,比如支持库的安装方式,而其他差异,例如构建过程和 UI 设计,则是完全不同的。

在更仔细地了解 IDE 本身之前,首先了解一些重要的区别是一个好主意。主要的区别列在这里:

  • UI 开发:Studio 和其他 IDE 之间最重要的区别是其布局编辑器,它比任何竞争对手都要优秀,提供文本、设计和蓝图视图,最重要的是,为每个活动或片段提供约束布局工具,易于使用的主题和样式编辑器,以及拖放设计功能。布局编辑器还提供了许多其他地方无法获得的工具,例如全面的预览功能,可以在多种设备上查看布局,以及易于使用的主题和翻译编辑器。

  • 项目结构:尽管底层目录结构保持不变,但 Android Studio 组织每个项目的方式与其前身有很大不同。Studio 使用模块而不是 Eclipse 中的工作区,这样可以更轻松地一起工作而无需切换工作区。

在 Eclipse 中称为工作区的东西在 Studio 中称为项目,在 Eclipse 中称为项目的东西在 Studio 中称为模块。

这种结构上的差异起初可能看起来不寻常,但任何 Eclipse 用户一旦熟悉起来就会看到它可以节省多少时间。

  • 代码补全和重构: Android Studio 智能地在您输入时完成代码的方式使其成为一种愉悦。它经常能够预测您即将输入的内容,通常只需两三次按键就可以输入整行代码。重构也比 Eclipse 和 NetBeans 等替代 IDE 更容易和更广泛。几乎任何东西都可以重命名,从局部变量到整个包。

  • 仿真: Studio 配备了灵活的虚拟设备编辑器,允许开发人员创建设备仿真器来模拟任意数量的真实设备。这些仿真器可以高度定制,无论是在外形因素还是硬件配置方面,虚拟设备都可以从许多制造商那里下载。其他 IDE 的用户可能已经熟悉 Android AVD,尽管他们肯定会喜欢 Design 选项卡中的预览功能。

  • 构建工具: Android Studio 采用了 Gradle 构建系统,它执行与许多 Java 开发人员熟悉的 Apache Ant 系统相同的功能。然而,它提供了更多的灵活性,并允许定制构建,使开发人员能够轻松创建可上传到 TestFlight 的 APK,或者制作应用的演示版本。正是 Gradle 系统使得之前讨论的模块化成为可能。Studio 不是将每个库或第三方 SDK 编译为 JAR 文件,而是使用 Gradle 构建每个库或 SDK。

这些是 Android Studio 与其他 IDE 之间最深远的差异,但还有更多独特的功能。Studio 提供了强大的 JUnit 测试功能,允许云平台支持甚至 Wi-Fi 调试。它也比 Eclipse 快得多,公平地说,Eclipse 必须满足更广泛的开发需求,而不仅仅是一个,而且它可以在性能较低的机器上运行。

Android Studio 还提供了一个令人惊叹的节省时间的工具,即即时运行。这个功能巧妙地只构建了项目中已编辑的部分,这意味着开发人员可以测试代码的小改动,而不必等待每次测试都进行完整的构建。这个功能可以将等待时间从几分钟减少到几乎零。

无论您是新手还是想更多地了解 Android Studio,第一步都是广泛了解其最突出的结构。

工作区结构

Android Studio 的整体结构与其他 IDE 并无不同。有用于编辑文本和屏幕组件的窗口,用于导航项目结构的窗口,以及用于监视和调试的窗口。这个 IDE 非常灵活,可以根据许多特定的需求和偏好进行配置。典型的布局可能是这样的:

典型的工作区布局

尽管这些窗口可以按照我们的意愿进行排列,但一般来说,在上一张截图中,四个窗格可能具有以下功能:

  1. 导航项目、模块或库

  2. 编辑文本和设计布局

  3. 定义组件属性或屏幕预览

  4. 监视和调试

有时打开大量窗格可能会分散注意力;对于这些时候,Studio 有一个无干扰模式,只显示当前编辑器窗口,可以从视图菜单中进入。

我们可以从许多不同的角度看待我们的项目,并有许多组织它们的方式。了解每种方式的最佳方法是依次查看每种方式。

编辑器窗口

在 IDE 中最重要的窗口当然是我们创建和修改所有应用程序代码的窗口。我们不仅使用编辑器来编辑 XML 和 Java,还有其他编辑器用于简化其他资源,如翻译和主题。然而,无论编辑器多么图形化,所有 Android 资源最终都以 XML 文件的形式出现在res目录中。

在大多数情况下,我们可以在不写任何代码的情况下创建大多数 Android 资源。只需点击几下鼠标,就可以使用相应的编辑器创建主题。然而,如果我们要自认为是专家,了解底层代码以及 Studio 存储这些资源的方式和位置是很重要的。以下示例演示了如何使用主题编辑器创建新的 Android 主题:

  1. 启动或打开 Android Studio 项目。

  2. 从工具|Android|主题编辑器中打开主题编辑器。

主题编辑器

  1. 在编辑器右上角的主题下拉菜单中,选择创建新主题,并在新主题对话框中输入名称。

  2. 将主题父级字段保持不变。

  3. 点击 colorPrimary 缩略图。

  4. 从结果色板中选择一个你喜欢的颜色,权重为500

  5. 以相同的方式,为辅助颜色选择权重为700的相同颜色。

  6. 选择一个权重为100的颜色,与主色对比鲜明。

  7. 打开预览或设计编辑器以查看这些更改。

在前面的示例中,我们创建了一个新的主题,该主题将自动应用于整个应用程序。我们本可以简单地编辑默认的AppTheme,但如果以后决定使用多个主题,这种方法将简化问题。IDE 通过向res/values/styles.xml文件添加以下行来立即应用这些更改:

<style name="MyTheme" parent="AppTheme" /> 

实际的颜色更改可以在res/values/colors.xml文件中找到。

主题编辑器相当好地展示了 Studio 编辑器如何在我们只需点击几下鼠标后创建和修改代码。

所有编辑器都可以使用Ctrl + Shift +F12进行最大化。使用相同的键返回到原始布局。

还可以通过从文件菜单中选择设置|编辑器|颜色和字体来更改 IDE 本身的主题,如下图所示:

Studio 主题对话框

Android Studio 只配备了一个备选颜色方案Darcula。这个主题在黑色背景上呈现浅色文本,因此对眼睛来说比默认设置要容易得多,尤其是对于那些长时间的深夜开发。还有其他在线可用的方案,设计自己的方案也很有趣。然而,为了制作印刷材料,我们将在这里坚持使用默认的 IDE 主题。

另一个很好的子编辑器示例是 Translations 编辑器,这也是展示项目结构与其他 IDE 不同的好方法。以下步骤展示了如何实现这一点:

  1. 右键单击res/values/strings.xml文件,从菜单中选择并打开 Translations 编辑器。也可以在设计 XML 编辑器的语言下拉菜单中找到。

  2. 点击编辑器左上角附近的地球图标,并从列表中选择一种语言。

  3. 在顶部窗格中选择要翻译的字符串,并在下方窗格中输入值,如图所示:

翻译编辑器

这是一个非常简单的练习,其目的是演示 Android Studio 如何存储这些资源以及如何显示它们。编辑器已经创建了一个新的strings.xml文件,除了翻译文本的字符串值之外,它在所有方面都与我们的原始文件相同。这个文件将自动被任何将该语言设置为用户默认语言的设备自动引用。

通过项目资源管理器,人们可能会认为在值目录中有一个名为strings.xml的项目目录,并且其中包含两个strings.xml文件。实际上,这样呈现只是为了帮助我们组织资源。检查磁盘上的project文件夹将显示实际上res目录中有两个(或更多)名为valuesvalues-fr的文件夹。这不仅有助于组织我们的工作,还有助于减少应用程序在设备上占用的空间,因为只有需要的资源文件才会安装在最终设备上。

实际的文件夹层次结构可以直接从主工具栏下方的导航栏中确定。

导航栏

主题和翻译是两个最不重要的编辑器,但它们很好地介绍了 Android Studio 如何管理应用程序资源。开发人员的大部分时间都是在使用代码编辑器,当然,这将在整本书中深入介绍。然而,虽然编辑器构成了 IDE 的核心,但还有许多其他有用甚至至关重要的工具可供我们使用,其中最常用的工具可以从工具边缘获得。

工具窗口

我们至少有十几个工具窗口可供使用,如果安装了插件,还有更多。它们可以通过查看|工具窗口菜单、工作区底部状态栏最左侧的工具图标,或按Alt和相应的数字键来打开特定的工具窗口。

工具窗口菜单

工具窗口是高度可配置的,每个窗口都可以设置为停靠、浮动或包含在自己的窗口中。

状态栏上的工具窗口图标可用于隐藏和显示工具窗口标签,围绕工作区的边框。

当使用多个屏幕时,这是特别有用的:

一个停靠的、浮动的和窗口化的工具窗口

在本书的整个过程中,我们将深入介绍所有这些工具。不过,以下是对最常用的工具的简要介绍:

  • 消息Alt + 0。这个工具生成了 Gradle 构建过程的简化版本。更详细的输出可以在 Gradle 控制台中找到。

  • 项目Alt + 1。通常停靠在工作区的左侧,这个工具是我们的主要导航工具。

  • 收藏夹Alt + 2。这是一个非常方便的组织工具,可以快速访问常用的类和组件。要将任何文件添加到收藏夹列表中,只需在项目窗口中右键单击该文件,然后从下拉菜单中选择“添加到收藏夹”。

  • 运行Alt + 3。这是一个强大的诊断工具,在应用程序在设备或模拟器上运行时可用。

  • AndroidAlt + 4。这是 Studio 的主要调试窗口,用于监视运行应用程序的日志输出和截图。

  • 内存监视器Alt + 5。这个非常有用的工具可以在应用程序运行时生成内存使用情况的实时图表。

  • 结构Alt + 6。这个工具提供了关于当前编辑器的详细信息,显示了该特定文件中包含的类、变量和其他组件的分层视图。

最有用的工具窗口之一是设备文件浏览器工具。这使我们能够浏览任何连接设备或模拟器的文件系统。

设备文件浏览器工具。

所有应用程序文件都可以在data/data中找到。

工具窗口非常有用,使我们能够配置集成开发环境以适应我们正在进行的特定任务。能够选择适当的工具是 Android Studio 最有用的功能之一。当然,Android Studio 只不过是一个前端界面,允许我们连接到 Android 背后的真正力量,即 SDK。

Android SDK

从技术上讲,可以将软件开发工具包SDK)描述为不是 Android Studio 的一部分,因为它被其他集成开发环境使用。然而,没有它,集成开发环境将毫无用处,现在是一个很好的时机来快速了解一下它及其管理器。

Android SDK 是一个庞大的 API 集合,包括组织成复杂但逻辑的层次结构的 Java 类和接口,以及其他实用工具,如 USB 驱动程序和硬件加速器。

SDK 及其组件的更新频率远远超过操作系统本身,用户应该对此毫不知情。Android 用户以 Lollipop 或 Honeycomb 为单位;作为开发人员,我们以 SDK 级别来看待 Android 世界。

SDK 由 SDK Manager 控制,可以通过主工具栏或从文件菜单的设置|外观和行为|系统设置|Android SDK 中访问。还有一个独立的 SDK Manager,可以在没有 Android Studio 的情况下运行。这可以在以下目录中找到:\AppData\Local\Android\sdk

Android SDK 独立管理器

SDK 管理器有三个部分:工具、平台和额外。至少,您需要安装最新的 SDK 工具、平台工具和构建工具。您还需要安装最新的平台和任何其他您打算直接定位的平台。您还需要为您希望创建的任何虚拟设备安装系统映像以及 Google USB 驱动程序和 HAXM 硬件加速器。

如果您一直在使用 Eclipse 开发 Android 应用程序,您将熟悉 Android 支持库。在使用 Android Studio 时,应安装支持存储库。

管理各种更新的最简单方法是将它们设置为自动安装,可以在设置对话框(Ctrl + Alt + S)中的外观和行为|系统设置|更新下完成。

SDK 构成了我们开发环境的支柱,但无论我们掌握得多么好,我们仍然需要一种方式来测试我们的创作,在没有大量真实设备的情况下,这取决于使用 Android 设备模拟器创建虚拟设备。

虚拟设备

市场上有如此多的 Android 设备,要在很多真实设备上彻底测试我们的应用几乎是不可能的。正因为如此,系统允许我们使用虚拟设备管理器创建模拟设备。

AVD 管理器允许我们从头开始创建形态因素和硬件配置文件,并提供几个现成的虚拟设备和系统映像,可以从各个制造商的网站上下载。

AVD 配置屏幕

Android 模拟器可能非常慢,即使在非常强大的机器上也是如此,这是可以预料的,因为创建一个完全功能的虚拟设备是一个非常复杂的任务。然而,可以通过设计每个虚拟设备以匹配我们正在开发的应用程序的特定任务来加快速度。例如,如果您的应用程序不使用设备摄像头,则不要在配置中包含它。同样,不要分配比应用程序本身需要的内存多得多。

Android 虚拟设备并不是我们唯一的选择,还有一些少量但不断增长的第三方模拟器。其中许多是专为游戏玩家而不是开发人员设计的;尽管 Genymotion 是一个专门的开发工具,它包含更多功能,通常比原生模拟器更快。它的唯一缺点是只能免费供个人使用,并且只提供手机和平板电脑的系统映像,而不是可穿戴设备或大屏幕设备,如电视。

现实世界的设备自然比任何模拟器反应更快,当测试基本功能时,使用我们自己的设备会提供更快的结果。这种方法非常适合测试应用程序的基本功能,但几乎没有提供关于我们的应用程序在 Android 设备上可能具有的各种屏幕尺寸、形状和密度的反馈。

使用真实设备是测试应用程序逻辑的快速方法,但为特定型号甚至通用大小和形状开发应用程序将不可避免地需要创建虚拟设备。幸运的是,Android Studio 配备了一个加速构建过程:Instant Run。

Instant Run

在较早的 Android Studio 版本中,每次在任何设备上运行项目时,都必须执行完整的构建。即使我们只对代码进行了微小的更改,我们仍然必须等待整个应用程序重新构建和重新安装。这可能非常耗时,特别是在性能较差的机器上。这种缓慢通常导致不得不一次性测试多个修改,导致比理想情况更复杂的调试过程。

Instant Run 尝试仅构建自上次构建以来已更改的那些类或活动,并且只要清单文件没有被编辑,应用程序甚至不会被重新安装,有些情况下,启动活动甚至不会被重新启动。

由于 Instant Run 是一项最近的创新,遗憾的是它并不适用于所有版本的 Android,并且要充分利用它,您需要将最低 SDK 级别设置为 API 21 或更高级别,尽管它的一些元素将与 API 级别 15 及更高级别一起工作。在 Android Studio 中,此级别是从build.gradle(Module:app)文件中设置的,如下所示:

android { 
    compileSdkVersion 25 
    buildToolsVersion "25.0.1" 
    defaultConfig { 
        applicationId "com.mew.kyle.chapterone" 
        minSdkVersion 21 
        targetSdkVersion 25 
        versionCode 1 
        versionName "1.0" 
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 
    } 

我们往往会尽可能使我们的应用程序向后兼容,开发一个只能在 API 级别 21 或更高级别上运行的应用程序将严重限制我们能够触及的用户数量。然而,Instant Run 为我们节省的时间使得值得测试和调试 API 21 或更高级别的应用程序,然后稍后重新组装以匹配我们希望目标的版本。

在决定要针对哪些 Android 版本时,一个有用的仪表板显示了平台和屏幕的最新使用数据。它可以在developer.android.com/about/dashboards/index.html找到。

从另一个 IDE 迁移到 Android Studio 不需要是一个困难的过渡,一旦完成将会非常有价值。但是,您可能有在其他 IDE 中开发的项目,希望继续使用 Studio 进行开发。幸运的是,这是一个简单的任务,如下一节所示。

将项目导入到 Android Studio

Eclipse 毫无疑问是最好的开发工具之一,15 年来,我们中的许多人对它非常熟悉。在开发各种平台时,Eclipse 是一个很棒的工具,但在开发 Android 应用程序时无法与 Android Studio 竞争。

如果您从 Eclipse 迁移,您很可能有一些项目希望导入到 Studio 中。以下步骤演示了如何完成此操作:

  1. 首先确保您的 Eclipse ADT 根目录包含srcres目录以及AndroidManifest.xml文件。

  2. 记下您使用过的 Eclipse 第三方插件,因为您需要在 Studio 中安装相应的插件。

  3. 打开 Android Studio 并从欢迎屏幕或文件|新建|导入项目中选择导入项目。

  4. 选择包含清单的文件夹并准备一个目标文件夹,然后按照提示完成导入。

导入过程会完整复制项目,原始项目不受影响,这意味着如果您愿意,仍然可以在 Eclipse 中进行工作。不幸的是,无法导入第三方插件,但 Studio 有大量不断增长的插件可用,很可能您能找到相应的插件。这些可以从文件|设置|插件中浏览。

如果您在同一个工作空间中有几个 Eclipse 项目,那么您应该将一个项目导入为项目,其余的导入为模块。

当我们进行项目配置时,我们将再次查看这个过程,但除此之外,从现在开始,我们将假设所有项目都是在 Android Studio 中开始的。

总结

本章对于那些不熟悉 Android Studio 的读者来说,作为一个简短但完整的介绍。我们探讨了工作空间的结构以及我们可以使用的各种编辑器。这次探索使我们创建了一个 Material Design 主题,使用工具窗口执行各种有用的任务,并应用了“即时运行”来加快原本耗时的构建过程。

本章以快速查看虚拟设备以及如何从其他 IDE 导入项目结束。有了这个介绍,接下来的章节将深入探讨布局编辑器本身,我们将看到如何设计适用于最广泛形态的应用界面。

第二章:UI 设计

Android Studio 中最突出的一个特性,包括 Gradle 构建系统在内,就是强大的用户界面(UI)开发工具。该 IDE 提供了多种设计视图,允许我们在 UI 开发中结合拖放构建和硬编码。Android Studio 还配备了全面的预览系统,可以让我们在实际设备上运行项目之前在任何设备上测试我们的设计。除了这些功能,Android Studio 还包括有用的支持库,如用于创建材料设计布局的设计库和用于简化复杂比例设计的百分比支持库。

这一章是四章中的第一章,涵盖了 UI 开发。在这一章中,我们将更仔细地研究 Studio 的布局编辑器和工具。我们将使用最有用的布局/ViewGroup 类构建工作界面,并设计和管理屏幕旋转。本章还将探讨 Studio 的预览系统以及 XML 布局资源的存储和应用。最后,本章将回顾主题、材料设计和设计支持库。

在本章中,您将学习如何:

  • 探索布局编辑器

  • 应用线性和相对布局

  • 安装约束库

  • 创建ConstraintLayout

  • 应用约束

  • 使用图形约束编辑器

  • 添加约束指南

  • 对齐TextView基线

  • 应用偏差

  • 使用自动连接

  • 为虚拟设备构建硬件配置文件

  • 创建虚拟 SD 卡

布局编辑器

如果有一个理由使用 Android Studio,那就是布局编辑器及其相关工具和预览系统。一旦打开一个项目,差异就显而易见。布局和蓝图视图之间的差异也在下图中显示:

设计和蓝图布局视图

蓝图模式是 Android Studio 2.0 的新功能,它展示了我们 UI 的简化轮廓视图。在编辑复杂布局的间距和比例时,这是特别有用的,而不会受到内容的干扰。默认情况下,IDE 会并排显示设计和蓝图视图,但编辑器的工具栏允许我们只查看一个视图,在大多数情况下,我们会选择最适合当前任务的模式。

B键可用于在设计、蓝图和组合视图之间切换,作为工具栏图标的替代方法。

完全可以使用这些图形视图为项目生成所需的每个布局,而不需要了解底层代码。不过,这并不是一个非常专业的方法,了解底层 XML 的知识对于良好的测试和调试至关重要,而且如果我们知道自己在做什么,通常调整代码比拖放对象更快。

负责前一个布局的 XML 如下:

<LinearLayout  

    android:id="@+id/layout_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

    <TextView 
        android:id="@+id/text_view_top" 
        android:layout_width="match_parent" 
        android:layout_height="0dp" 
        android:layout_weight="1" /> 

    <TextView 
        android:id="@+id/text_view_center" 
        android:layout_width="match_parent" 
        android:layout_height="0dp" 
        android:layout_weight="3" /> 

    <TextView 
        android:id="@+id/text_view_bottom" 
        android:layout_width="match_parent" 
        android:layout_height="0dp" 
        android:layout_weight="2" /> 

</LinearLayout> 

希望您对前面代码中使用的术语很熟悉。layout_weight的使用经常与线性布局一起使用,用于分配比例,在开发具有略有不同纵横比的屏幕时节省了大量时间。

直到最近,我们创建更复杂 UI 的唯一选择是线性和相对布局。这两种布局都不是理想的选择,要么是不必要的昂贵,要么是琐碎的。Android Studio 2 引入了约束布局,为这些问题提供了一个优雅的解决方案。为了更好地理解其价值,首先看一下旧的类是有意义的,这些类在许多简单的设计中仍然有用。

线性和相对布局类

线性布局相对较轻,对于基于单行或单列的布局非常有用。然而,更复杂的布局需要在彼此内部嵌套布局,这很快就会变得资源密集。看一下下面的布局:

嵌套线性布局

前面的布局只使用了线性布局,可以从以下组件树中看到:

组件树

尽管这种布局完全可行且易于理解,但它的效率不如可能。即使是一个额外的布局嵌套层也会对性能产生影响。在约束布局出现之前,这个问题是通过相对布局解决的。

如其名称所示,相对布局允许我们将屏幕组件放置在彼此之间的关系中,使用诸如layout_toStartOflayout_below之类的标记。这使我们能够扁平化视图层次结构,并且前面的布局可以仅使用一个单独的相对根视图组来重新创建。以下代码演示了如何在不嵌套任何新布局的情况下生成前一个布局中的图像行:

<ImageView 
    android:id="@+id/image_header_1" 
    android:layout_width="128dp" 
    android:layout_height="128dp" 
    android:layout_alignParentStart="true" 
    android:layout_below="@+id/text_title" 
    app:srcCompat="@drawable/pizza_01" /> 

<ImageView 
    android:id="@+id/image_header_2" 
    android:layout_width="128dp" 
    android:layout_height="128dp" 
    android:layout_below="@+id/text_title" 
    android:layout_toEndOf="@+id/image_header_1" 
    app:srcCompat="@drawable/pizza_02" /> 

<ImageView 
    android:id="@+id/image_header_3" 
    android:layout_width="128dp" 
    android:layout_height="128dp" 
    android:layout_alignParentEnd="true" 
    android:layout_below="@+id/text_title" 
    app:srcCompat="@drawable/pizza_03" /> 

<ImageView 
    android:id="@+id/image_header_4" 
    android:layout_width="128dp" 
    android:layout_height="128dp" 
    android:layout_alignParentStart="true" 
    android:layout_below="@+id/text_title" 
    app:srcCompat="@drawable/pizza_04" /> 

即使您是 Android Studio 的新手,也假定您熟悉线性布局和相对布局。您可能不太可能遇到约束布局,它是专门为 Studio 开发的,以弥补这些旧方法的缺点。

在前面的示例中,我们使用了app:srcCompat而不是android:src。这在这里并不是严格要求的,但如果我们希望对图像应用任何着色并希望将应用程序分发给较旧的 Android 版本,这个选择将使这成为可能。

约束布局

约束布局类似于相对布局,它允许我们生成复杂的布局,而无需创建占用内存的视图组层次结构。Android Studio 使得创建这样的布局变得更加容易,因为它提供了一个可视化编辑器,使我们不仅可以拖放屏幕组件,还可以拖放它们的连接。能够如此轻松地尝试布局结构为我们提供了一个很好的沙盒环境,用于开发新的布局。

以下练习将带您完成安装约束库的过程,以便您可以开始自己进行实验。

  1. 从 Android Studio 3.0 开始,默认情况下会下载ConstraintLayout,但如果要更新早期项目,则需要打开 SDK 管理器。约束布局和约束求解器都可以在 SDK 工具选项卡下找到,如下所示:

约束布局 API

  1. 勾选显示包详细信息框,并记下版本号,因为这很快将需要。

  2. 接下来,将ConstraintLayout库添加到我们的依赖项中。最简单的方法是选择您的模块,然后选择项目结构对话框的依赖项选项卡,该对话框可以从文件菜单中访问。

  3. 单击+按钮,然后选择 1 Library dependency 并从列表中选择约束库。

  4. 最后,从工具栏、构建菜单或Ctrl + Alt + Y同步您的项目。

这是添加模块依赖项的最简单方法,但作为开发人员了解底层发生的事情总是很好。在这种情况下,我们可以通过打开模块级build.gradle文件并将以下突出显示的文本添加到dependencies节点来手动添加库:

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    androidTestCompile('com.android.support.test.espresso:espresso-
                        core:2.2.2', { 
        exclude group: 'com.android.support', module: 'support-annotations' 
    }) 
    compile 'com.android.support:appcompat-v7:25.1.0' 
    compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4' 
    testCompile 'junit:junit:4.12' 

那些使用相对布局开发的人将熟悉诸如layout_toRightOflayout_toTopOf之类的命令。这些属性仍然可以应用于ConstraintLayout,但还有更多。特别是,ConstraintLayout允许我们基于单个边来定位视图,例如layout_constraintTop_toBottomOf,它将我们的视图的顶部与指定视图的底部对齐。

有关这些属性的有用文档可以在以下网址找到:developer.android.com/reference/android/widget/RelativeLayout.LayoutParams.html

创建约束布局

有两种方法可以创建 ConstraintLayout。第一种是将现有布局转换为 ConstraintLayout,可以通过右键单击组件树或图形编辑器中的布局,然后选择转换选项来完成。然后会出现以下对话框:

转换为 ConstraintLayout 对话框

通常最好同时检查这两个选项,但值得注意的是,这些转换并不总是会产生期望的结果,通常视图尺寸需要进行一些微调才能忠实地复制原始布局。

当它起作用时,以前的方法提供了一个快速的解决方案,但是如果我们要掌握这个主题,我们需要知道如何从头开始创建约束布局。这一点特别重要,因为一旦我们熟悉了约束布局的工作方式,我们将会发现这是设计界面最简单、最灵活的方式。

ConstraintLayout 与布局编辑器完美结合,可以设计任何布局而无需编写任何 XML。然而,我们将密切关注图形和文本两个方面,以便更深入地了解这项技术。

您可以从项目资源管理器的上下文菜单中的 res/layout 目录中创建一个新的 ConstraintLayout,作为一个具有以下根元素的新布局资源文件:

添加新的 ConstraintLayout

这将生成以下 XML:

<?xml version="1.0" encoding="utf-8"?> 
<android.support.constraint.ConstraintLayout 

    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

</android.support.constraint.ConstraintLayout> 

与其他布局类型一样,约束层提供了在其中定位和对齐视图和小部件的机制。这主要通过可以在图形上定位以调整大小和对齐视图的手柄来完成。

应用约束

了解其工作原理的最佳方法是尝试一下,这几个简单的步骤将进行演示。按照前面描述的方式创建 ConstraintLayout,并从调色板拖放一个或两个视图或小部件到蓝图屏幕上,类似于以下图示:

约束手柄

每个视图的角落和边上都有约束手柄。角落上的手柄用于简单地调整视图的大小,而边上的手柄用于创建约束。这些位置视图是相对于其父级或彼此的,与相对布局并没有太大不同。

由于这主要是一种图形形式的编辑,最好通过操作来进行演示。将视图的左侧锚点拖向布局的左侧,并按照提示释放鼠标按钮以创建父约束。这是一个包含其他内容的布局,将成为父约束。

当您尝试使用约束时,您会注意到边距会自动粘附到创意设计指南推荐的值。

如果现在打开文本编辑器,您将看到约束如下所示:

app:layout_constraintLeft_toLeftOf="parent" 

您还会注意到从代码中生成了一个错误。这是因为每个视图都需要垂直和水平约束。可以通过以下方式实现:

app:layout_constraintTop_toTopOf="parent" 

也可以使用相同的拖放技术在子视图之间创建约束,或者:

app:layout_constraintTop_toBottomOf="@+id/image_view" 

在视图的四个边上设置约束将使其居中在其容器中。

约束可用于对齐兄弟视图以及连接两个相邻的边,生成以下代码:

app:layout_constraintLeft_toLeftOf="@+id/image_view" 

可以通过在任一编辑模式下单击其起始手柄来简单地删除约束。

这种拖放方法并不是 Android Studio 独有的,但是 Android Studio 提供了一个可编辑的示意图视图,通过属性工具来实现。

图形属性工具

当选择 ConstraintLayout 视图时,属性窗口中会弹出一个视图的图解表示:

属性示意图。

这个工具允许通过单击来编辑大小和位置属性,并且可以立即以简单的示意图形式理解输出。学习起来只需要几秒钟,可以大大加快界面设计的速度,特别是在尝试不同的布局时。

在代表我们视图的中央正方形内,有四条线,单击它们会循环显示以下三种状态:

  • 实线:视图是精确的宽度,例如240dp

  • 模糊线:视图可以是任何大小(取决于偏差),match_parent

  • 有向线:视图匹配其自身内容,wrap_content

通常,我们不希望将视图约束到其容器的边缘。例如,我们可能希望将布局分成两个或多个部分,并在其中组织视图。指南允许我们将屏幕分成几个部分,并且可以像父边缘一样使用。看下面的例子:

约束指南

像这样的指南最容易从设计编辑器顶部的约束工具栏中添加。指南被添加为 XML 组件,看起来像这样:

<android.support.constraint.Guideline 
    android:id="@+id/gl_vertical" 
    android:layout_width="wrap_content" 
    android:layout_height="311dp" 
    android:orientation="vertical" 
    app:layout_constraintGuide_begin="175dp" /> 

现在我们可以使用这些指南来根据整个布局或我们创建的四个窗格之一来居中元素,而无需嵌套任何布局。在下面的屏幕截图中,我们有一个居中的标题和侧边栏,另一个视图包含在一个单独的窗格中,当然我们可以对这些部分应用偏差:

应用约束指南

如果这个系统还没有提供足够的优势,那就还有更多。首先,当对齐文本以及一种称为偏差的更强大的定位技术时,它被证明非常有用,它执行与权重属性类似的功能,但在设计多个屏幕时更好。我们首先来看一下文本对齐约束。

基线对齐

使用它们的基线将文本对齐到多个视图可能有些麻烦,特别是当文本大小不同时。幸运的是,约束布局提供了一种简单而轻松的方法来实现这一点。

任何受约束的视图或设计用于包含文本的小部件,都会在其中心处有一条横杠。将鼠标悬停在此处片刻,直到它闪烁,然后将其拖动到您希望将其文本与之对齐的视图,如下所示:

基线对齐。

您可能已经熟悉相对布局类使用的重力属性来控制位置。

基线约束只能连接到其他基线。

约束布局引入了一种新的方法,允许我们控制视图两侧的相对距离。

使用偏差控制位置

在这里,偏差最好理解为百分比值,但与其根据中心或角落的位置,它是它两侧空间的百分比。因此,如果向上的偏差为 33%,则下方的边距将是下方边距的两倍。

设置偏差甚至比理解它更容易,因为一旦在视图的任何对立面上设置了约束,属性编辑器中将出现一个关联的滑块:

使用 GUI 应用偏差

快速浏览生成的代码,显示了该属性的格式如下:

app:layout_constraintHorizontal_bias="0.33" 

使用偏差来定位屏幕元素的价值部分在于其简单的方法,但其真正价值在于开发多个屏幕时。有这么多型号可用,它们似乎都有稍微不同的比例。这可能使得在所有这些屏幕上看起来很棒的设计布局非常耗时,即使是 720 x 1280 和 768 x 1280 这样相似的形状在使用相同的布局进行测试时也可能产生不良结果。使用偏差属性在很大程度上解决了这些问题,我们将在稍后看到更多内容,当我们看到布局预览和百分比库时。

编辑器的设计和文本模式可以使用Alt +左或右进行切换。

好像所有这些都没有使设计布局变得足够简单,约束布局还有另外两个非常方便的功能,几乎可以自动化 UI 设计:自动连接和推断。

约束工具栏

尽管我们总是希望花费大量时间完善我们的最终设计,但开发周期的大部分时间将用于实验和尝试新想法。我们希望尽快测试这些单独的设计,这就是自动连接和推断的用武之地。这些功能可以通过约束工具栏访问,其中包含其他有用的工具,值得详细了解。

约束工具栏

从左到右,工具栏分解如下。

  • 显示约束:显示所有约束,而不仅仅是所选组件的约束。

  • 自动连接:启用此功能后,新视图和小部件的约束将根据它们放置的位置自动设置。

  • 清除所有约束:顾名思义,一键解决方案。这可能会导致一些意想不到的结果,因此应该小心使用。

  • 推断约束:设计布局后应用此功能。它将自动应用约束,类似于自动连接,但它会一次性对所有视图应用约束。

推断过程

  • 默认边距:设置整个布局的边距。

  • Pack:提供一系列分布模式,帮助均匀扩展或收缩所选项目使用的区域。

  • 对齐:此下拉菜单提供了最常用的组对齐选项。

  • 指南:允许快速插入指南。

自动连接和推断都提供了智能和快速的构建约束布局的方法,虽然它们是测试想法的绝佳工具,但它们远非完美。这些自动化经常会包括不必要的约束,需要删除。此外,如果您在使用这些技术之后检查 XML,您会注意到一些值是硬编码的,您会知道这不是最佳实践。

希望您在本节中已经看到,Android Studio 和 ConstraintLayout 确实是为彼此而生的。这并不是说它应该在所有情况下取代线性和相对布局。在简单列表方面,线性布局仍然是最有效的。对于只有两个或三个子元素的布局,相对布局通常也更便宜。

ConstraintLayout类还有更多内容,比如分布链接和运行时约束修改,我们将在整本书中经常回到这个主题,但现在我们将看看 Android Studio 的另一个独特而强大的工具,设备预览和仿真。

多屏幕预览

Android 开发人员面临的最有趣的挑战之一是使用它的设备数量令人困惑。从手表到宽屏电视,各种设备都在使用。我们很少希望开发一个单一的应用程序在这样的范围内运行,但即使为所有手机开发布局也是一项艰巨的任务。

幸运的是,SDK 允许我们将屏幕形状、大小和密度等功能分类到更广泛的组中,从而帮助这一过程。Android Studio 还添加了另一个强大的 UI 开发工具,即复杂的预览系统。这可以用于预览许多流行的设备配置,同时也允许我们创建自定义配置。

在前面的部分中,我们看了 ConstraintLayout 工具栏,但正如您可能已经注意到的那样,还有一个更通用的设计编辑器工具栏:

设计编辑器工具栏

这些工具中的大多数都是不言自明的,您可能已经使用过其中的许多。然而,其中有一两个值得更仔细地研究,特别是如果您是 Android Studio 的新手。

迄今为止,我们可以使用的最有用的设计工具之一是编辑器中的设备工具,在前面的图中显示为 Nexus 4。这使我们能够预览我们的布局,就像它们在任意数量的设备上显示一样,而无需编译项目。下拉菜单提供了一系列通用和真实世界的配置文件,我们可能创建的任何 AVD,以及添加我们自己的设备定义的选项。现在我们将看看这个选项。

硬件配置文件

从编辑器中的设备下拉菜单中选择“添加设备定义...”将打开 AVD 管理器。要创建新的硬件配置文件,请单击“创建虚拟设备...”按钮。选择硬件对话框允许我们安装和编辑前面下拉菜单中列出的所有设备配置文件,以及创建或导入定义的选项。

AVD 管理器的独立版本可以从user\AppData\Local\Android\sdk\运行。这对于性能较低的机器非常有用,因为 AVD 可以在没有 Studio 运行的情况下启动。

通常更容易采用现有的定义并根据我们的需求进行调整,但为了更深入地了解操作,我们将通过单击“选择硬件”对话框中的“新硬件配置文件”按钮,从头开始创建一个。这将带您进入“配置硬件配置文件”对话框,在那里您可以选择硬件仿真器,如摄像头和传感器,以及定义内部和外部存储选项。

硬件配置

一旦您完成配置文件并点击“完成”,您将返回到硬件选择屏幕,在那里您的配置文件现在已被添加到列表中。然而,在继续之前,我们应该快速看一下如何模拟存储硬件。

虚拟存储

每个配置文件都包含一个 SD 卡磁盘映像来模拟外部存储,显然这是一个有用的功能。然而,如果我们能够移除这些卡并与其他设备共享,那将更加有用。幸运的是,Android Studio 有一些非常方便的命令行工具,我们将在本书中遇到。这里我们感兴趣的命令是mksdcard

mksdcard可执行文件位于sdk/tools/中,创建虚拟 SD 卡的格式为:

mksdcard <label> <size> <file name> 

例如:

mksdcard -l sharedSdCard 1024M sharedSdCard.img 

在大量虚拟设备上测试应用程序时,能够共享外部存储器可以节省大量时间,当然,这样的映像可以存储在实际的 SD 卡上,这不仅使它们更加便携,还可以减轻硬盘的负担。

我们的配置文件现在已准备好与系统映像结合,形成 AVD,但首先我们将导出它,以便更好地了解它是如何组合的。这将保存为 XML 文件,并且可以通过右键单击硬件选择屏幕的主表中的配置文件来实现。这不仅提供了洞察力,也是跨网络共享设备的便捷方式,而且编辑本身也非常快速简单。

配置本身可能会相当长,因此以下是一个示例节点,以提供一个想法:

<d:screen> 
    <d:screen-size>xlarge</d:screen-size> 
    <d:diagonal-length>9.94</d:diagonal-length> 
    <d:pixel-density>xhdpi</d:pixel-density> 
    <d:screen-ratio>notlong</d:screen-ratio> 
    <d:dimensions> 
        <d:x-dimension>2560</d:x-dimension> 
        <d:y-dimension>1800</d:y-dimension> 
    </d:dimensions> 
    <d:xdpi>314.84</d:xdpi> 
    <d:ydpi>314.84</d:ydpi> 
    <d:touch> 
        <d:multitouch>jazz-hands</d:multitouch> 
        <d:mechanism>finger</d:mechanism> 
        <d:screen-type>capacitive</d:screen-type> 
    </d:touch> 
</d:screen> 

在这里定义屏幕的方式,为我们提供了一个有用的窗口,可以了解在开发多个设备时需要考虑的功能和定义。

要查看我们的配置文件的实际效果,我们需要将其连接到系统映像并在模拟器上运行。这是通过选择配置文件并点击“下一步”来完成的。

要彻底测试应用程序,通常最好为要发布应用程序的每个 API 级别、屏幕密度和硬件配置创建一个 AVD。

选择图像后,您将有机会调整硬件配置文件,然后创建 AVD:

Android AVD

模拟最新的移动设备是一项令人印象深刻的任务,即使对于最坚固的计算机来说,即使使用 HAXM 硬件加速,速度仍然可能令人沮丧地慢,尽管即时运行的添加大大加快了这个过程。除了 Genymotion 之外,几乎没有其他选择,Genymotion 提供了更快的虚拟设备和一些在本机模拟器上无法使用的功能。这些功能包括拖放安装、实时窗口调整大小、工作网络连接和一键模拟位置设置。唯一的缺点是 Android Wear、TV 或 Auto 没有系统映像,并且仅供个人免费使用。

本节展示了我们如何在许多形态因素上预览我们的布局,以及如何构建一个虚拟设备以匹配任何目标设备的精确规格,但这只是故事的一部分。在下一章中,我们将看到如何为所有目标设备创建布局文件。

总结

在本章中,我们介绍了界面开发的基础知识,这在很大程度上是使用和理解各种布局类型的问题。本章的大部分内容都致力于约束布局,因为这是最新和最灵活的视图组之一,并且在 Android Studio 中配备了直观的可视化工具。

本章最后介绍了如何将完成的布局在模拟器上查看,并使用自定义的硬件配置文件。

在接下来的章节中,我们将更深入地研究这些布局,并看到协调布局是如何用来协调多个子组件一起工作的,而我们几乎不需要编写任何代码。

第三章:UI 开发

在上一章中,我们看到安卓工作室为快速简单地设计布局提供了许多宝贵的工具。然而,我们只关注了静态 UI 的设计。当然,这是一个必不可少的第一步,但我们的界面可以,也应该是动态的。根据材料设计指南,用户交互应该通过运动和颜色直观地展示,比如点击按钮时产生的涟漪动画。

要了解如何做到这一点,我们需要看一个实际的例子,并开始构建一个简单但功能的应用程序。但首先,我们将研究一两种应用我们想要的外观和感觉的方式,并且安卓用户期望将其应用到我们的设计中。这个过程在很大程度上得到了支持库的帮助,特别是 AppCompat 和 Design 库。

我们将从查看安卓工作室如何通过基于材料的视觉主题编辑器和设计支持库来实现材料设计开始本章。

在本章中,您将学习以下内容:

  • 生成材料样式和主题

  • 使用 XML 字体

  • 创建 XML 字体系列

  • 使用基本代码完成

  • 应用协调布局

  • 协调设计组件

  • 创建一个可折叠的应用栏

  • 部署原始资源

  • 使用百分比支持库

我们在上一章中看到,当使用设计编辑器调整约束布局的屏幕元素的大小和移动时,我们的视图往往会粘在一组特定的尺寸上。这些尺寸是根据 Material 设计指南选择的。如果您不知道,Material 是一种设计语言,由谷歌规定,基于传统的设计和动画技术,旨在通过移动和位置来清理用户界面的过程。

材料设计

虽然 Material 设计并不是必不可少的,如果您正在开发全屏应用程序,比如游戏,它通常有自己的设计规则,可以完全忽略它,但它仍然是一种优雅的设计范式,并且被用户群广泛认可和理解。

实施材料的一个非常好的理由是,许多其特性,如卡片视图和滑动抽屉,都可以通过相关的支持库非常容易地应用。

我们需要做的第一个设计决定之一是,我们想要将什么颜色方案或主题应用到我们的应用程序中。关于我们主题的色调和对比度,有一两个材料指南。幸运的是,安卓工作室的主题编辑器确实非常简单地生成符合材料的主题。

安卓样式

图形属性,如背景颜色、文本大小和高程,都可以在任何 UI 组件上单独设置。将属性组合到一起成为一个样式通常是有意义的。安卓将这些样式存储在 values 目录中的styles.xml文件中的 XML 中。一个例子如下:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <style name="TextStyle" parent="TextAppearance.AppCompat"> 
        <item name="android:textColor">#8000</item> 
        <item name="android:textSize">48sp</item> 
    </style> 
</resources> 

这样的样式可以简单地应用到视图和小部件上,而无需指定每个属性,如下所示:

<TextView 
    . . .  
    android:textAppearance="@style/TextStyle" 
    . . . /> 

完全可以通过定义所有属性来从头开始创建任何样式,但更实际的做法是从现有样式中继承并仅修改我们希望更改的属性。这是通过设置parent属性来完成的,可以在前面的例子中看到。

我们也可以继承自我们自己的样式,而无需设置父属性,例如:

<style name="TextStyle.Face"> 
    <item name="android:typeface">monospace</item> 
</style> 

之前,我们创建了一个新的资源文件,但我们也可以将一个新的<style>节点添加到现有的styles.xml文件中。

如果您是 Android Studio 的新手,您会注意到代码完成下拉框在您输入时出现,如下面的屏幕截图所示:

代码完成

这是一个非常有价值的工具,我们将在稍后更详细地看一下。现在,知道代码完成存在三个级别是有用的,如此简要地概述:

  • 基本Ctrl + 空格; 显示下一个单词的可能性。

  • 智能Ctrl + Shift + 空格; 上下文敏感建议。

  • 语句Ctrl + Shift + Enter; 完成整个语句。

连续两次调用基本和智能代码完成将扩大建议的范围。

创建和应用这样的样式是微调应用程序外观的好方法,而无需进行大量额外的编码,但有时我们也希望将外观和感觉应用于整个应用程序,为此,我们使用主题。

材料主题

在为应用程序创建整体主题时,我们有两个相反的目标。一方面,我们希望我们的应用程序脱颖而出,并且容易被识别;另一方面,我们希望它符合用户对平台的期望,并且希望他们发现控件熟悉且简单易用。主题编辑器在个性和一致性之间取得了很好的折衷。

在最简单的情况下,材料主题采用两种或三种颜色,并在整个应用程序中应用这些颜色,以使其具有一致的感觉,这可能是使用主题的主要好处。作为重点选择的颜色将用于着色复选框和突出显示文本,并且通常选择以突出显示并吸引注意。另一方面,主要颜色将应用于工具栏,并且与早期版本的 Android 不同,还将应用于状态栏和导航栏。例如:

<color name="colorPrimary">#ffc107</color>
<color name="colorPrimaryDark">#ffa000</color>
<color name="colorAccent">#80d8ff</color>

这使我们能够在应用程序运行时控制整个屏幕的颜色方案,避免与任何本机控件发生丑陋的冲突。

选择这些颜色的一般经验法则是选择主要值的两种色调和一个对比但互补的颜色作为重点。谷歌对要使用哪些色调和颜色更加精确,没有硬性规定来决定哪些颜色与其他颜色搭配得好。然而,有一些有用的指南可以帮助我们,但首先我们将看一下谷歌的材料调色板。

主题编辑器

谷歌规定在 Android 应用程序和 Material 设计网页中使用 20 种不同的色调。每种色调有十种阴影,如下例所示:

材料调色板

完整的调色板以及可下载的样本可以在以下网址找到:material.io/guidelines/style/color.html#color-color-palette

材料指南建议我们使用 500 和 700 的阴影作为我们的主要和深色主要颜色,以及 100 作为重点。幸运的是,我们不必过分关注这些数字,因为有工具可以帮助我们。

这些工具中最有用的是主题编辑器。这是另一个图形编辑器,可以从主菜单中的工具 | Android | 主题编辑器中访问。

一旦打开主题编辑器,您将看到它分为两个部分。右侧是颜色属性列表,左侧是显示这些选择对各种界面组件的影响的面板,为我们提供了方便的预览和快速直观地尝试各种组合的机会。

正如您所看到的,不仅有两种主要颜色和一个重点。实际上有 12 种,涵盖文本和背景颜色,以及深色和浅色主题的替代方案。这些默认设置为styles.xml文件中声明的父主题的颜色。

要快速设置自定义材料主题,请按照以下步骤进行:

  1. 开始一个新的 Studio 项目或打开一个您希望应用主题的项目。

  2. 从工具 | Android 菜单中打开主题编辑器。

  3. 选择 colorPrimary 字段左侧的实色块。

  1. 在资源对话框的右下角选择一个纯色块,然后单击“确定”。

  2. 打开 colorPrimaryDark 对话框,然后在右侧的颜色选择选项下选择唯一的建议块。它将是相同的色调,但是 700 的阴影。

  3. 选择重点属性,然后从建议的颜色中选择一个。

这些选择可以立即在编辑器左侧的预览窗格中看到,也可以从布局编辑器中看到。

正如你所看到的,这些颜色并不是直接声明的,而是引用了values/colors.xml文件中指定的值。

编辑器不仅帮助通过建议可接受的颜色来创建材料主题,还可以帮助我们选择自己选择的颜色。在“选择资源”窗口的颜色表中的任何位置单击都会提示选择最接近的材料颜色。

在选择重点颜色时,有几种思路。根据色彩理论,可以使用色轮创建与任何颜色和谐互补色的几种方法,例如以下色轮:

RYB 色轮显示和谐互补色

计算和谐颜色的最简单方法是取色轮上与我们相对的颜色(称为直接互补色)。然而,具有艺术视野的人认为这有些显而易见,缺乏微妙之处,并更喜欢所谓的分裂互补色。这意味着从那些与直接互补色紧密相邻的颜色中进行选择,如前所示。

当选择重点颜色时,主题编辑器在颜色选择器下方建议几种分裂互补色。然而,它也建议类似的和谐色。这些颜色与原色接近,虽然看起来很好,但不适合作为重点颜色的选择,因为对比度小,用户可能会错过重要的提示。

有一个非常令人愉悦的 JetBrains 插件可用,可以将材料主题应用于 IDE 本身。它可以在以下网址找到:plugins.jetbrains.com/androidstudio/plugin/8006-material-theme-ui

正如我们刚才看到的,主题编辑器在生成材料主题时非常有帮助。还有越来越多的在线工具可以通过几次点击生成完整的 XML 材料主题。MaterialUps 可以在以下网址找到:www.materialpalette.com

这将生成以下colors.xml文件:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <color name="primary">#673AB7</color> 
    <color name="primary_dark">#512DA8</color> 
    <color name="primary_light">#D1C4E9</color> 
    <color name="accent">#FFEB3B</color> 
    <color name="primary_text">#212121</color> 
    <color name="secondary_text">#757575</color> 
    <color name="icons">#FFFFFF</color> 
    <color name="divider">#BDBDBD</color> 
</resources> 

乍一看,这看起来像是选择主题属性的快速方法,但是如果查看文本颜色,你会发现它们是灰色的阴影。根据材料设计指南,这是不正确的,应该使用 alpha 通道使用透明度创建阴影。当文本放在纯色背景上时,这没有什么区别,但当放在图像上时,灰度文本可能更难阅读,特别是浅色阴影,如此所示:

灰度与透明度

Android 主题允许我们以颜色的形式定义应用程序的外观,但通常我们希望做的不仅仅是自定义文本的颜色,能够以与其他资源类似的方式包含字体是最近非常有用的补充。

XML 字体

从 API 级别 26 开始,可以将字体作为 XML 资源包含在res目录中。这一特性简化了在应用程序中使用非默认字体的任务,并使该过程与其他资源管理保持一致。

添加 XML 字体非常简单,如下练习所示:

  1. 右键单击res目录,然后选择“新建|Android 资源目录”。

  2. 从资源类型下拉菜单中选择字体,然后单击“确定”。

  3. 右键单击新创建的字体文件夹,然后选择在资源管理器中显示。

  4. 重命名您的字体文件,使其只包含可允许的字符。例如,times_new_roman.ttf而不是TimesNewRoman.ttf

  5. 将所选字体放入字体目录中。

  6. 现在可以直接从编辑器中预览这些。

XML 字体。

在布局中使用这些字体甚至比将它们添加为资源更简单。只需使用fontFamily属性,如下所示:

<TextView
         . . .
        android:fontFamily="@font/just_another_hand"
         . . . />

在处理字体时,通常希望以各种方式强调单词,比如使用更粗的字体或使文本变斜体。与其为每个版本依赖不同的字体,更方便的做法是能够引用字体组或字体系列。只需右键单击您的font文件夹,然后选择新建|字体资源文件。这将创建一个空的字体系列文件,然后可以按照以下方式填写:

<?xml version="1.0" encoding="utf-8"?>
<font-family >
    <font
        android:fontStyle="bold"
        android:fontWeight="400"
        android:font="@font/some_font_bold" />

    <font
        android:fontStyle="italic"
        android:fontWeight="400"
        android:font="@font/some_font_italic" />
</font-family>

当然,设计语言远不止于选择正确的颜色和字体。关于间距和比例有惯例,通常还有一些特别设计的屏幕组件。在材料的情况下,这些组件采用小部件和布局的形式,例如 FAB 和滑动抽屉。这些不是作为原生 SDK 的一部分提供的,而是包含在设计支持库中。

设计库

如前所述,设计支持库提供了在材料应用程序中常见的小部件和视图。

正如您所知,设计库和其他支持库一样,需要在模块级build.gradle文件中作为 gradle 依赖项包含,如下所示:

dependencies { 
    compile fileTree(include: ['*.jar'], dir: 'libs') 
    androidTestCompile('com.android.support.test.espresso:espresso-
      core:2.2.2', { 
          exclude group: 'com.android.support', module: 'support-
                annotations' 
    }) 
    compile 'com.android.support:appcompat-v7:25.1.1' 
    testCompile 'junit:junit:4.12' 
    compile 'com.android.support:design:25.1.1' 
} 

虽然了解事情是如何做的总是有用的,但实际上,有一个很好的快捷方式可以将支持库添加为项目依赖项。从文件菜单中打开项目结构对话框,然后选择您的模块和依赖项选项卡。

项目结构对话框

您可以通过单击右上角的添加图标并从下拉菜单中选择库依赖项来选择您想要的库。

可以使用Ctrl + Alt + Shift + S键来召唤项目结构对话框。

使用这种方法还有另外两个优点。首先,IDE 将自动重建项目,其次,它将始终导入最新的修订版本。

许多开发人员通过使用加号来预防未来的修订,如下所示:compile 'com.android.support:design:25.1.+'。这样可以应用未来的次要修订。但是,这并不总是保证有效,并且可能会导致崩溃,因此最好手动保持版本最新,即使这意味着发布更多更新。

除了导入设计库之外,如果您计划开发材料应用程序,您很可能还需要CardViewRecyclerView库。

熟悉 IDE 的最佳方法是通过实际示例进行操作。在这里,我们将制作一个简单的天气应用程序。它不会很复杂,但它将带领我们完成应用程序开发的每个阶段,并且将遵循材料设计准则。

协调布局

设计库提供了三个布局类。有一个用于设计表格活动,一个用于工具栏,但最重要的布局是CoordinatorLayout,它充当材料感知容器,自动执行许多材料技巧,例如当用户滚动到列表顶部时扩展标题,或者在弹出的小吃栏出现时确保 FAB 滑出。

协调布局应放置在活动的根布局中,并且通常看起来像以下行:

<android.support.design.widget.CoordinatorLayout  

    android:id="@+id/coordinator_layout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true"> 

    . . . 

</android.support.design.widget.CoordinatorLayout> 

属性fitsSystemWindows特别有用,因为它将状态栏设置为部分透明。这样可以使我们的设计主导本机控件,而不会完全隐藏它们,同时避免与系统颜色发生冲突。

在状态栏后面绘制

还可以使用colorPrimaryDark来分配状态栏的颜色,将fitsSystemWindows与我们自己选择的颜色结合起来。

导航栏的颜色也可以使用navigationBarColor属性进行更改,但这并不建议,因为具有软导航控件的设备正在变得越来越少。

CoordinatorLayoutFrameLayout非常相似,但有一个重要的例外。协调布局可以使用CoordinatorLayout.Behavior类直接控制其子项。最好的方法是通过一个例子来看看它是如何工作的。

Snackbar 和浮动操作按钮

Snackbar 和浮动操作按钮FABs)是最具代表性的 Material 小部件之一。尽管它并不完全取代 toast 小部件,但 Snackbar 提供了一种更复杂的活动通知形式,允许控件和媒体而不仅仅是文本,而这是 toast 的情况。FABs 执行与传统按钮相同的功能,但使用它们的位置来指示它们的功能。

如果没有协调布局来控制行为,Snackbar从屏幕底部升起会遮挡其后的任何视图或小部件。如果小部件能够优雅地滑出去,这将更可取,这是你在设计良好的 Material 应用中经常看到的情况。以下练习解释了如何实现这一点:

  1. 在 Android Studio 中开始一个新项目。

  2. 在这里用CoordinatorLayout替换主活动的根布局:

<android.support.design.widget.CoordinatorLayout  

    android:id="@+id/coordinator_layout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true"> 
  1. 添加以下按钮:
<Button 
    android:id="@+id/button" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_gravity="top|start" 
    android:layout_marginStart= 
            "@dimen/activity_horizontal_margin" 
    android:layout_marginTop= 
            "@dimen/activity_vertical_margin" 
    android:text="Download" />
  1. 接着是Snackbar
<android.support.design.widget.FloatingActionButton 
    android:id="@+id/fab" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_gravity="bottom|end" 
    android:layout_marginBottom= 
            "@dimen/activity_vertical_margin" 
    android:layout_marginEnd= 
            "@dimen/activity_horizontal_margin" 
    app:srcCompat="@android:drawable/stat_sys_download" /> 
  1. 打开主活动的 Java 文件,并扩展类声明以实现点击监听器,如下所示:
public class MainActivity 
    extends AppCompatActivity 
    implements View.OnClickListener 
  1. 这将生成一个错误,然后会出现一个红色的灯泡(称为快速修复)。

快速修复

  1. 选择实现方法以添加OnClickListener

  2. 在类中添加以下字段:

private Button button; 
private CoordinatorLayout coordinatorLayout; 
  1. onCreate()方法中为这些组件创建引用:
coordinatorLayout = (CoordinatorLayout)  
    findViewById(R.id.coordinator_layout); 
button = (Button) 
    findViewById(R.id.button); 
  1. 将按钮与监听器关联,如下所示:
button.setOnClickListener(this); 
  1. 然后按照以下方式完成监听器方法:
@Override 
public void onClick(View v) { 
    Snackbar.make(coordinatorLayout, 
            "Download complete", 
            Snackbar.LENGTH_LONG).show(); 
    } 
} 

现在可以在模拟器或真实设备上测试这段代码。单击按钮将临时显示Snackbar,并将 FAB 滑开以便显示。

Snackbar在之前的演示中的行为与 toast 完全相同,但SnackbarViewGroup而不是像 toast 那样的视图;作为布局,它可以充当容器。要查看如何实现这一点,请用以下方法替换之前的监听器方法:

@Override 
public void onClick(View v) { 
    Snackbar.make(coordinatorLayout, 
            "Download complete", 
            Snackbar.LENGTH_LONG) 
            .setAction("Open", new View.OnClickListener() { 

                @Override 
                public void onClick(View v) { 
                    // Perform action here 
                } 

            }).show(); 
    } 
} 

FAB 如何在Snackbar的遮挡下自动移开是由父协调布局自动处理的,对于所有设计库小部件和 ViewGroups 都是如此。我们很快将看到,当包含原生视图时,如文本视图和图像,我们必须定义自己的行为。我们也可以自定义设计组件的行为,但首先我们将看一下其他设计库组件。

可折叠的应用栏

另一个广为人知的 Material 设计特性是可折叠的工具栏。通常包含相关的图片和标题。当用户滚动到内容顶部时,这种类型的工具栏将填充屏幕的大部分空间,当用户希望查看更多内容并向下滚动时,它会巧妙地躲开。这个组件有一个有用的目的,它提供了一个很好的品牌机会,让我们的应用在视觉上脱颖而出,但它不会占用宝贵的屏幕空间。

一个可折叠的应用栏

查看它的构造方式最好的方法是查看其背后的 XML 代码。按照以下步骤重新创建它:

  1. 在 Android Studio 中开始一个新的项目。我们将创建以下布局:

项目组件树

  1. 首先打开styles.xml文件。

  2. 确保父主题不包含操作栏,如下所示:

<style name="AppTheme" 
    parent="Theme.AppCompat.Light.NoActionBar"> 
  1. 如果要使用半透明状态栏,请添加以下行:
<item name="android:windowTranslucentStatus">true</item> 
  1. 与以前一样,创建一个以CoordinatorLayout为根的布局文件。

  2. 接下来,嵌套以下AppBarLayout

<android.support.design.widget.AppBarLayout 
    android:id="@+id/app_bar" 
    android:layout_width="match_parent" 
    android:layout_height="300dp" 
    android:fitsSystemWindows="true" 
    android:theme="@style/ThemeOverlay 
        .AppCompat 
        .Dark 
        .ActionBar"> 
  1. 在其中,添加CollapsingToolbarLayout
<android.support.design.widget.CollapsingToolbarLayout 
    android:id="@+id/collapsing_toolbar" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    app:contentScrim="?attr/colorPrimary" 
    app:expandedTitleMarginEnd="64dp" 
    app:expandedTitleMarginStart="48dp" 
    app:layout_scrollFlags="scroll|exitUntilCollapsed" 
    app:> 
  1. 在工具栏中,添加这两个小部件:
<ImageView 
    android:id="@+id/image_toolbar" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    android:scaleType="centerCrop" 
    app:layout_collapseMode="parallax" 
    app:srcCompat="@drawable/some_image" /> 

<android.support.v7.widget.Toolbar 
    android:id="@+id/toolbar" 
    android:layout_width="match_parent" 
    android:layout_height="?attr/actionBarSize" 
    app:layout_collapseMode="pin" 
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> 
  1. AppBarLayout下面,放置NestedScrollViewTextView
<android.support.v4.widget.NestedScrollView 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    app:layout_behavior= 
        "@string/appbar_scrolling_view_behavior"> 

    <TextView 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:padding="@dimen/activity_horizontal_margin" 
        android:text="@string/some_string" 
        android:textSize="16sp" /> 

</android.support.v4.widget.NestedScrollView> 
  1. 最后添加一个 FAB:
<android.support.design.widget.FloatingActionButton 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_margin="@dimen/activity_horizontal_margin" 
    app:layout_anchor="@id/app_bar" 
    app:layout_anchorGravity="bottom|end" 
    app:srcCompat="@android:drawable/ic_menu_edit" /> 

如果现在在设备或模拟器上测试这个,你会看到工具栏会自动折叠和展开,无需任何编程,这就是设计库的美妙之处。要在没有它的情况下编写这种行为将是一个漫长而常常困难的过程。

前面的大部分 XML 都是不言自明的,但有一两个点值得一看。

原始文本资源

为了演示滚动行为,前面文本视图中使用了一个较长的字符串。这个字符串放在了strings.xml文件中,虽然这样做完全没有问题,但并不是管理长文本的优雅方式。这种文本最好作为可以在运行时读取的文本文件资源来处理。

以下步骤演示了如何做到这一点:

  1. 准备一个纯文本文件。

  2. 通过右键单击项目资源管理器中的res文件夹并选择New | Directory来创建一个名为raw的目录。

  3. 将文本文件添加到此目录。

可以从资源管理器上下文菜单快速打开项目目录。

  1. 打开包含要填充文本视图的 java 活动,并添加此函数:
private StringBuilder loadText(Context context) throws IOException { 
    final Resources resources = this.getResources(); 
    InputStream stream = resources 
        .openRawResource(R.raw.weather); 
    BufferedReader reader =  
        new BufferedReader( 
        new InputStreamReader(stream)); 
    StringBuilder stringBuilder = new StringBuilder(); 
    String text; 

    while ((text = reader.readLine()) != null) { 
        stringBuilder.append(text); 
    } 

    reader.close(); 
    return stringBuilder; 
} 
  1. 最后,将此代码添加到onCreate()方法中:
TextView textView = (TextView) 
    findViewById(R.id.text_view); 

StringBuilder builder = null; 

try { 
    builder = loadText(this); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 

textView.setText(builder); 

在前面的演示中,另一个要点是在扩展工具栏的高度上使用了硬编码值,即android:layout_height="300dp"。这在被测试的模型上运行得很好,但要在所有屏幕类型上实现相同的效果可能需要创建大量的替代布局。一个更简单的解决方案是只重新创建dimens文件夹,例如,可以简单地复制和粘贴dimens-hdpi,然后只编辑适当的值。甚至可以创建一个单独的文件来包含这个值。另一种解决这个问题的方法是使用专为这种情况设计的支持库。

百分比库

百分比支持库只提供了两个布局类PercentRelativeLayoutPercentFrameLayout。需要将其添加到 gradle 构建文件中作为依赖项,如下所示:

compile 'com.android.support:percent:25.1.1' 

为了重新创建上一节中的布局,我们需要将AppBarLayout放在PercentRelativeLayout中。然后我们可以使用百分比值来设置我们应用栏的最大高度,如下所示:

<android.support.percent.PercentRelativeLayout 

  android:layout_width="match_parent" 
  android:layout_height="match_parent"> 

    <android.support.design.widget.AppBarLayout 
        android:id="@+id/app_bar" 
        android:layout_width="match_parent" 
        android:layout_height="30%" 
        android:fitsSystemWindows="true" 
        android:theme="@style/ThemeOverlay 
            .AppCompat 
            .Dark 
            .ActionBar"> 

        . . .  

    </android.support.design.widget.AppBarLayout> 

</android.support.percent.PercentRelativeLayout>    

这种方法节省了我们不得不创建大量替代布局来在众多设备上复制相同效果的麻烦,尽管总是需要生成多个。

实现这种统一性的另一种有效方法是创建我们的图像可绘制对象,使其在 dp 中具有所需的确切高度,并在 XML 中将布局高度设置为wrap_content。然后我们只需要为每个所需的指定资源目录创建一个图像,这是我们很可能会做的事情。

总之,前面的工具使得设计材料界面变得简单直观,并提供了减少为用户提供的令人困惑的设备准备布局所需的时间的方法。

总结

在本章中,我们在上一章的基础上探讨了如何使用协调布局及其相关库来轻松构建更复杂的布局,这些库可以为我们做很多工作,比如自动折叠工具栏和防止小部件重叠。

我们通过探讨另一个宝贵的设计库——百分比库来结束了本章,它可以在开发针对非常不同的屏幕尺寸和形状时解决大量的设计问题。

下一章将在本章的基础上扩展,探讨更多用于界面开发的动态元素,如屏幕旋转、为可穿戴设备开发和读取传感器。

第四章:设备开发

Android Studio 提供了一些非常强大的布局工具,使我们能够快速轻松地尝试和开发用户界面。然而,任何 Android 开发人员面临的最大挑战可能是他们的应用程序可能在多种形态因素上运行的令人困惑的数量。

我们在之前的章节中看到了一些类,例如约束布局和百分比库等,可以帮助我们设计统一和一致的布局。然而,这些技术只提供了一般解决方案,我们都会遇到一些似乎并没有真正考虑我们设备的应用程序。通过一点知识和努力,这些设计缺陷可以很容易地避免。

在本章中,您将学习:

  • 创建替代布局文件

  • 提取字符串资源

  • 管理屏幕旋转

  • 配置资源

  • 创建可穿戴 UI

  • 构建形状感知布局

  • 读取传感器数据

  • 使用虚拟传感器

  • 应用 Studio 的模板

  • 创建调试过滤器

  • 监视设备

在研究如何开发我们的 UI,使其在所有用户设备上都能看起来很棒之前,我们需要探索我们将遇到的最重要的布局情况:在纵向和横向模式之间旋转屏幕。

屏幕方向

大量为手机和平板设计的 Android 应用程序都设计为在横向和纵向模式下都能工作,并且通常会自动在这两种模式之间切换。许多活动,比如视频,在横向模式下观看效果最佳,而列表通常在纵向模式下更容易扫描;然而,有一些活动,甚至整个应用程序,其方向是固定的。

有一些布局无论以哪种方式查看都很好,但这并不经常发生;大多数情况下,我们都希望为每种方向设计一个布局。Android Studio 通过为我们节省从头开始开发替代布局的任务,简化并加快了这个过程。

以这里的简单布局为例:

纵向布局

可以通过在设计编辑器顶部的布局变体工具中单击一次来创建横向变体,如下所示:

布局变体工具。

如果您重新创建此练习或创建自己的等效练习,您很快会发现,这样的布局在旋转时看起来并不好,您将不得不重新调整视图以最适合这个纵横比。如果您尝试使用约束布局,您将发现它的一些弱点,而且最终的布局可能会非常混乱。

您如何重新创建这些布局取决于您自己的艺术和设计技能,但值得注意的是 Android Studio 存储和呈现这些文件的方式,这可能有点令人困惑,特别是如果您正在从管理方式不同的 Eclipse 迁移。

如果您在项目资源管理器中打开刚刚创建的项目,在 Android 下,您将在activity_main.xml (land)中找到横向变体,显然在activity_main.xml目录中。Studio 以这种方式呈现它,因为将所有布局放在一个地方很方便,但这并不是它们的存储方式。将项目资源管理器切换到项目视图将显示实际的文件结构,如下所示:

项目结构。

这种结构也可以从 IDE 顶部的导航栏中确定。

如果您创建类似这样的布局变体,将视图移动到更令人愉悦的配置,并为两个版本赋予相同的 ID,当用户旋转其设备时,这些将自动在它们的两个状态之间进行动画。我们将在后面看到如何构建我们自己的自定义动画,但往往默认动画是最好的选择,因为它们有助于促进统一的用户体验。

如果你重新创建了上面的示例,你可能已经注意到 IDE 执行的一个非常巧妙的技巧,以加快提供文本资源的过程。

你可能已经知道,使用硬编码字符串是强烈不推荐的。像许多编程范式一样,Android 开发旨在使数据和代码分开创建和处理。硬编码字符串也几乎不可能进行翻译。

我们之前看到快速修复功能如何让我们自动实现方法。在这里,我们可以使用它来创建字符串资源,甚至无需打开strings.xml文件。

只需在布局文件中输入硬编码的字符串,然后按照快速修复提示将其提取为字符串资源。

字符串资源提取。

布局编辑器提供了两个现成的变体,横向和超大,但我们可以创建适合任何形态因素的自定义变体。

现在我们已经开始添加一些动态元素,比如屏幕旋转,布局编辑器已经不够用了,我们需要在设备或仿真器上运行我们的应用程序。

虚拟设备

很长一段时间以来,Android 虚拟设备(AVD)一直以有 bug 和运行极慢而闻名。硬件加速的引入带来了很大的改变,但仍建议使用一台性能强大的计算机,特别是如果你想同时运行多个 AVD,这种情况经常发生。

Android 仿真的最大变化不是硬件加速,而是替代仿真器的出现。正如我们将很快看到的,其中一些提供了与本机仿真器不同的优势,但 AVD 不应被忽视。尽管存在缺点,Android 仿真器是唯一可以在所有 Android 版本上运行的仿真器,包括最新的仅供开发者使用的版本。不仅如此,Android 仿真器是最可定制的,任何可能的硬件或软件配置都可以通过一点努力重新创建。

在开发过程的早期阶段,能够快速测试我们的想法非常重要,使用一两个真实设备可能是最好的选择;然而,迟早我们需要确保我们的布局在所有可能的设备上看起来很棒。

布局和图像资格

这里有两个问题需要考虑:屏幕密度和纵横比。如果你之前做过任何 Android 开发,你会了解 DPI 和屏幕大小分组。这些指定的文件夹提供了方便的快捷方式,以适应各种可用的形态因素,但我们都会遇到布局在我们设备上不太适用的应用。这是完全可以避免的,尽管我们需要付出一些努力来对抗它,但这将避免那些可能损害收入流的差评。

很容易产生一个能在尽可能多的形态因素上运行的应用程序的诱惑,而 Android Studio 偶尔会鼓励你这样思考。实际上,我们必须考虑设备的使用时间和地点。如果我们在等公交车,那么我们可能想要一个可以轻松开关并且可以快速完成任务的游戏。尽管也有例外,但这些不是人们选择在大屏幕上长时间玩耍的游戏。选择正确的平台是至关重要的,尽管这可能听起来违反直觉,但通常排除一个平台比仅仅假设它可能赚取更多收入更明智。

考虑到这一点,我们将考虑一个仅设计用于手机和平板电脑的应用程序;然而,除了查看屏幕大小和密度等熟悉的功能之外,我们还将看到如何为许多其他配置问题提供定制资源。

最常用的资源指定是屏幕大小和密度。Android 提供了以下四种大小指定。

  • layout-small:从两到四英寸,320 x 420dp 或更大

  • layout-normal:从三到五英寸,320 x 480dp 或更大

  • layout-large:从四到七英寸,480 x 640dp 或更大

  • layout-xlarge:从七到十英寸,720 x 960dp 或更大

如果您正在为 Android 3.0(API 级别 11)或更低版本开发,这个范围较低的设备通常会被错误地分类。唯一的解决方案是为单独的设备进行配置,或者根本不开发这些设备。

一般来说,我们需要为上述每种尺寸制作一个布局。

使用密度无关像素dpdip)意味着我们不需要为每个密度设置设计新的布局,但我们必须为每个密度类提供单独的可绘制资源,如下所示。

  • drawable-ldpi 〜120dpi

  • drawable-mdpi 〜160dpi

  • drawable-hdpi 〜240dpi

  • drawable-xhdpi 〜320dpi

  • drawable-xxhdpi 〜480dpi

  • drawable-xxxhdpi 〜640dpi

上述列表中的 dpi 值告诉我们我们的资源需要的像素相对大小。例如,drawable-xhdpi目录中的位图需要是drawable-mdpi文件夹中相应位图大小的两倍。

实际上不可能在每台设备上创建完全相同的输出,这甚至不可取。人们购买高端设备是因为他们想要令人惊叹的图像和精细的细节,我们应该努力提供这种质量水平。另一方面,许多人购买小型和价格较低的设备是出于便利和预算的原因,我们应该在设计中反映这些选择。与其试图在所有设备上完全复制相同的体验,我们应该考虑人们选择设备的原因以及他们想要从中获得什么。

以下简短的练习演示了这些差异如何在不同的屏幕配置中表现出来。这将让读者有机会看到如何最好地利用用户选择的设备,利用他们自己的艺术和设计才能。

  1. 选择任何高分辨率图像,最好是一张照片。

  2. 使用您选择的任何工具,创建一个宽度和高度为原始尺寸一半的副本。

  3. 打开一个新的 Android Studio 项目。

  4. 从项目资源管理器中,在 res 目录内创建两个名为drawable-mdpidrawable-hdpi的新文件夹。

  5. 将准备好的图像放入这些文件夹中。

  6. 构建一个带有图像视图和一些文本的简单布局。

  7. 创建两个虚拟设备,一个密度为mdpi,一个为hdpi

  8. 最后,在每个设备上运行应用程序以观察差异。

密度为 mdpi 和 hdpi 的设备。

这实际上并不是我们可以使用的唯一密度限定符。为电视设计的应用程序通常使用tvdpi限定符。这个值介于mdpihdpi之间。还有nodpi限定符,当我们需要精确的像素映射时使用,以及anydpi,当所有艺术作品都是矢量可绘制时使用。

还有很多其他限定符,完整列表可以在以下网址找到:

developer.android.com/guide/topics/resources/providing-resources.html

现在值得看一下一些更有用的限定符。

比例和平台

像之前讨论过的那样的概括限定符非常有用,适用于大多数情况,并且节省了我们大量的时间。然而,有时我们需要更精确的关于我们的应用程序运行设备的信息。

我们想要了解的最重要的功能之一是屏幕尺寸。我们已经遇到了诸如小型、普通和大型之类的限定符,但我们也可以配置更精确的尺寸。其中最简单的是可用宽度和可用高度。例如,res/layout/w720dp中的布局只有在可用宽度至少为 720dp 时才会被填充,而res/layout/h1024dp中的布局则在屏幕高度等于或大于 1024dp 时被填充。

另一个非常方便的功能是配置平台版本号的资源。这是基于 API 级别的。因此,当在 Android Jelly Bean 设备上运行时,可以使用v16的限定符来使用资源。

能够为如此广泛的硬件选择和准备资源意味着我们可以为那些能够显示它们的设备提供丰富的资源,对于容量较小的设备则提供更简单的资源。无论我们是为预算手机还是高端平板开发,我们仍然需要一种测试应用程序的方法。我们已经看到了 AVDs 的灵活性,但很值得快速看一下其他一些选择。

替代模拟器

最好的替代模拟器之一可能是 Genymotion。不幸的是,这个模拟器不是免费的,也不像原生 AVDs 那样及时更新,但它速度快,支持拖放文件安装和移动网络功能。它可以在以下网址找到:

www.genymotion.com

另一个快速且易于使用的模拟器是 Manymo。这是一个基于浏览器的模拟器,其主要目的是测试 Web 应用程序,但对于移动应用程序也非常有效。它也不是免费的,但它有各种各样的预制形态因子。它可以在以下网址找到:

www.manymo.com

在这方面还有一个类似的工具是 Appetize,它位于:

appetize.io

这样的模拟器越来越多,但上面提到的那些可能是从开发的角度来看最功能齐全的。以下列表将读者引向其他一些模拟器:

有一种情况下,这些替代方案都不合适,我们被迫使用 AVD 管理器,那就是当我们想要为可穿戴设备(如智能手表)开发时,这是我们接下来要看的内容。

Android Wear

可穿戴设备最近变得非常流行,Android Wear 已完全整合到 Android SDK 中。设置 Wear 项目比其他项目稍微复杂一些,因为可穿戴设备实际上是作为应用程序的伴侣设备,应用程序本身是从移动设备上运行的。

尽管有这种轻微的复杂性,为可穿戴设备开发可能会非常有趣,至少因为它们经常为我们提供访问一些很酷的传感器,比如心率监测器。

连接到可穿戴 AVD

也许您有可穿戴设备,但在以下练习中我们将使用模拟器。这是因为这些设备有两种类型:方形和圆形。

当要将这些模拟器之一与手机或平板配对时,可以使用真实设备或另一个模拟器,但最好使用真实设备,因为这会对计算机造成较小的压力。这两种方法略有不同。以下练习假设您正在将可穿戴模拟器与真实设备配对,并解释了如何在最后与模拟移动设备配对。

  1. 在做任何其他事情之前,打开 SDK 管理器并检查是否已下载了 Android Wear 系统镜像:

  1. 打开 AVD 管理器并创建两个 AVD,一个是圆形的,一个是方形的。

  2. 在手机上从 Play 商店安装 Android Wear 应用程序,并将其连接到计算机。

  3. 找到并打开包含adb.exe文件的目录。这可以在\AppData\Local\Android\Sdk\platform-tools\中找到。

  4. 发出以下命令:

adb -d forward tcp:5601 tcp:5601
  1. 在手机上启动伴侣应用程序,并按照屏幕上的说明配对设备。

每次重新连接手机时,您都需要执行端口转发命令。

如果要将可穿戴设备与虚拟手机配对,该过程非常类似,唯一的区别是伴侣应用程序的安装方式。按照以下步骤来实现这一点:

  1. 启动或创建一个目标为 Google APIs 的 AVD。

  2. 下载com.google.android.wearable.app-2.apk。有很多在线地方可以找到这个文件,比如 www.file-upload.net/download。

  3. 将文件放入 platform-tools 文件夹中,并使用以下命令进行安装:

adb install com.google.android.wearable.app-2.apk 
  1. 启动可穿戴 AVD 并在命令提示符(或者如果您在 Mac 上,则在终端)中输入adb devices来检查两个设备是否可见。

  2. 输入adb telnet localhost 5554,其中5554是手机模拟器。

  3. 最后输入adb redir add tcp:5601:5601。您现在可以像之前的练习一样在模拟手机上使用穿戴应用程序配对设备。

尽管它是自动为我们添加的,但重要的是要理解 Android Wear 应用程序需要一个支持库。这可以通过检查build.gradle文件中的模块级别来看到。

 compile 'com.google.android.gms:play-services-wearable:10.2.0' 

现在我们的设备已经配对,我们可以开始实际开发和设计我们的可穿戴应用程序了。

可穿戴布局

在 Android Wear UI 开发中最有趣的挑战之一是这些智能手表的两种不同形状。我们可以以两种方式来解决这个问题。

其中一种类似于我们之前管理事物的方式,并涉及为每种形态因素设计布局,而另一种技术使用一种产生适用于任何形状的布局的方法。

除了这些技术之外,可穿戴支持库还配备了一些非常方便的小部件,适用于曲面和圆形布局以及列表。

Android Studio 最有用和有教育意义的功能之一是在项目首次设置时提供的项目模板。这些模板有很好的选择,它们为大多数项目提供了良好的起点,特别是穿戴应用程序。

穿戴模板

从这种方式开始一个项目可能是有帮助和启发性的,甚至空白的活动模板设置了 XML 和 Java 文件,创建了一个非常可信的起点。

如果您从空白的穿戴活动开始一个项目,您会注意到,我们之前只有一个模块(默认称为 app),现在有两个模块,一个称为 mobile,取代了 app,另一个名为 wear。这两个模块的结构与我们之前遇到的相同,包含清单、资源目录和 Java 活动。

WatchViewStub 类

空白的穿戴活动模板应用了我们之前讨论的管理不同设备形状的第一种技术。这采用了WatchViewStub类的形式,可以在wear/src/main/res/layout文件夹中找到。

<?xml version="1.0" encoding="utf-8"?> 
<android.support.wearable.view.WatchViewStub  

    android:id="@+id/watch_view_stub" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    app:rectLayout="@layout/rect_activity_main" 
    app:roundLayout="@layout/round_activity_main" 
    tools:context="com.mew.kyle.wearable.MainActivity" 
    tools:deviceIds="wear" /> 

如前面的示例所示,主要活动将系统引导到两种形状的布局之一,模板也提供了这两种布局。

正如您所看到的,这不是我们之前选择正确布局的方式,这是因为WatchViewStub的操作方式不同,并且需要一个专门的监听器,一旦WatchViewStub检测到手表的类型,它就会填充我们的布局。这段代码也是模板在主活动 Java 文件中提供的:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    final WatchViewStub stub = (WatchViewStub) 
            findViewById(R.id.watch_view_stub); 

    stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() { 

        @Override 
        public void onLayoutInflated(WatchViewStub stub) { 
            mTextView = (TextView) stub.findViewById(R.id.text); 
        } 

    }); 
} 

诱人的是认为WatchViewStub是我们设计可穿戴布局所需的全部。它允许我们独立设计两个表盘,这正是我们想要做的。然而,可穿戴布局通常非常简单,复杂的设计被强烈不鼓励。因此,对于一个简单的设计,几乎只有一张图片和一个按钮,拥有一个shape-aware类,根据设备的形状分发其内容,只是一种方便。这就是BoxInsetLayout类的工作原理。

形状感知布局

BoxInsetLayout类是 Wear UI 库的一部分,允许我们设计一个布局,可以优化自身适应方形和圆形表盘。它通过在任何圆形框架内充气最大可能的正方形来实现这一点。这是一个简单的解决方案,但BoxInsetLayout还非常好地确保我们选择的任何背景图像始终填充所有可用空间。正如我们将在一会儿看到的,如果您将组件水平放置在屏幕上,BoxInsetLayout类会自动分发它们以实现最佳适配。

在使用 Android Studio 开发时,您将想要做的第一件事情之一是利用布局编辑器提供的强大预览系统。这提供了每种可穿戴设备的预览,以及您可能创建的任何 AVD。这在测试布局时节省了大量时间,因为我们可以直接从 IDE 中查看,而无需启动 AVD。

预览工具可以从View | Tool Windows菜单中访问;或者,如果布局文本编辑器打开,可以在右侧边距中找到,默认情况下。

WatchViewStubs不同,BoxInsetLayout类不是由任何模板提供的,必须手动编码。按照以下简短步骤,使用BoxInsetLayout类构建动态的 Wear UI。

  1. 将以下BoxInsetLayout创建为 wear 模块中主 XML 活动的根容器:
<android.support.wearable.view.BoxInsetLayout  

    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@drawable/snow" 
    android:padding="15dp"> 

</android.support.wearable.view.BoxInsetLayout> 
  1. 将这个FrameLayout放在BoxInsetLayout类中:
<FrameLayout 
    android:id="@+id/wearable_layout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:padding="5dp" 
    app:layout_box="all"> 

</FrameLayout> 
  1. FrameLayout中包括这些小部件(或您自己选择的小部件):
<TextView 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:gravity="center" 
    android:text="@string/weather_warning" 
    android:textAppearance= 
        "@style/TextAppearance.WearDiag.Title" 
    tools:textColor="@color/primary_text_light" /> 

<ImageView 
    android:layout_width="60dp" 
    android:layout_height="60dp" 
    android:layout_gravity="bottom|start" 
    android:contentDescription= 
        "@string/generic_cancel" 
    android:src="img/ic_full_cancel" /> 

<ImageView 
    android:layout_width="60dp" 
    android:layout_height="60dp" 
    android:layout_gravity="bottom|end" 
    android:contentDescription= 
        "@string/buttons_rect_right_bottom" 
    android:src="img/ic_full_sad" />
  1. 最后,在圆形和方形模拟器上运行演示:

BoxInsetLayout

BoxInsetLayout类非常易于使用。它不仅节省我们的时间,还可以保持应用的内存占用量,因为即使是最简单的布局也有一定的成本。在圆形视图中,它可能看起来有些浪费空间,但是 Wear UI 应该是简洁和简化的,空白空间不是应该避免的东西;一个设计良好的可穿戴 UI 应该能够被用户快速理解。

Android Wear 最常用的功能之一是心率监测器,因为我们正在处理可穿戴设备,现在是时候看看如何访问传感器数据了。

访问传感器

佩戴在手腕上的设备非常适合健身应用,许多型号中都包含心率监测器,使它们非常适合这样的任务。SDK 管理所有传感器的方式几乎相同,因此了解一个传感器的工作方式也适用于其他传感器。

以下练习演示了如何在可穿戴设备上读取心率传感器的数据:

  1. 打开一个带有移动和可穿戴模块的 Android Wear 项目。

  2. 创建您选择的布局,确保包括一个TextView来显示输出。

  3. 在可穿戴模块的Manifest文件中添加以下权限:

<uses-permission 
    android:name="android.permission.BODY_SENSORS" /> 
  1. 在可穿戴模块的MainActivity.java文件中添加以下字段:
private TextView textView; 
private SensorManager sensorManager; 
private Sensor sensor; 
  1. Activity实现传感器事件监听器,如下所示:
public class MainActivity extends Activity implements SensorEventListener { 
  1. 实现所需的方法。

  2. 编辑onCreate()方法如下:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    textView = (TextView) findViewById(R.id.text_view); 

    sensorManager = ((SensorManager) getSystemService(SENSOR_SERVICE)); 
    sensor = sensorManager 
        .getDefaultSensor(Sensor 
        .TYPE_HEART_RATE); 
} 
  1. onResume()方法中注册监听器,当活动启动或重新启动时:
@Override 
protected void onResume() { 
    super.onResume(); 

sensorManager.registerListener(this, this.sensor, 3); 
}
  1. 然后添加onPause()方法,以确保在不需要时关闭监听器:
@Override 
protected void onPause() { 
    super.onPause() 

sensorManager.unregisterListener(this); 
} 
  1. 最后,编辑onSensorChanged()回调,如下所示:
@Override 
public void onSensorChanged(SensorEvent event) { 
   textView.setText("" + (int) event.values[0] + "bpm"); 
} 

如前所述,所有传感器都可以以相同的方式访问,尽管它们输出的值根据其目的而异。关于这一点的完整文档可以在以下网址找到:

developer.android.com/reference/android/hardware/Sensor.html

现在,当然,读者会认为这个练习没有实际传感器的实际设备是没有意义的。幸运的是,在模拟器中弥补这种硬件缺乏的方法不止一种。

传感器仿真

如果您有一段时间没有使用 Android 模拟器,或者是第一次使用它们,您可能会错过每个 AVD 的扩展控件。这些可以从模拟器工具栏的底部访问。

这些扩展控件提供了许多有用的功能,比如轻松设置模拟位置和替代输入方法的能力。我们感兴趣的是虚拟传感器。这些允许我们直接模拟各种传感器和输入值:

虚拟传感器

在模拟设备上运行传感器还有其他几种方法。其中大多数依赖于连接真实设备并使用它们的硬件。这些 SDK 控制器传感器可以从 Play 商店下载。GitHub 上也有一些很棒的传感器模拟器,我个人最喜欢的是:

github.com/openintents/sensorsimulator

既然我们开始开发不仅仅是静态布局,我们可以开始利用一些 Studio 更强大的监控工具。

设备监控

通常,只需在设备或模拟器上运行应用程序就足以告诉我们我们设计的东西是否有效,以及我们是否需要更改任何内容。但是,了解应用程序行为的实时监控情况总是很好的,而在这方面,Android Studio 有一些很棒的工具。

我们将在下一个模块中详细介绍调试,但现在玩一下Android Debug BridgeADB)和 Android Studio 的设备监视器工具是选择 IDE 而不是其他替代品的最重要的好处之一。

这一部分还提供了一个很好的机会来更仔细地查看项目模板,这是 Android Studio 的另一个很棒的功能。

项目模板

Android Studio 提供了许多有用的项目模板。这些模板设计用于一系列典型的项目类型,例如全屏应用程序或 Google 地图项目。模板是部分完成的项目,包括代码、布局和资源,可以作为我们自己创作的起点。材料设计的不断出现使得导航抽屉活动模板成为最常用的模板之一,也是我们将用来检查设备监视器工具的模板。

导航抽屉活动模板在几个方面都很有趣和有用。首先,请注意,有四个布局文件,包括我们熟悉的activity_main.xml文件。检查这段代码,您会注意到以下节点:

<include 
    layout="@layout/app_bar_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

这个节点的目的很简单,app_bar_main.xml文件包含了我们在本书前面介绍过的协调布局和其他视图。使用<include>标签并不是必需的,但如果我们想在另一个活动中重用该代码,它是非常有用的,当然,它会产生更清晰的代码。

这个模板中另一个值得关注的地方是在 drawable 目录中使用矢量图形。我们将在下一章中详细讨论这些,但现在知道它们提供了一个很好的方法来解决为每个屏幕密度组提供单独图像的问题,因为它们可以缩放到任何屏幕。

在我们看一下如何监视应用程序行为之前,快速查看一下主活动 Java 代码。这很好地展示了各种功能的编码方式。这些示例功能可能不符合我们的要求,但可以很容易地替换和编辑以适应我们的目的,并且可以从这个起点构建整个应用程序。

监控和分析

所有开发人员都希望能够在运行时监视应用程序的能力。观察用户操作对硬件组件(如内存和处理器)的实时影响是识别可能的瓶颈和其他问题的绝佳方式。Android Studio 拥有一套复杂的分析工具,将在下一个模块中进行全面讨论。然而,Android Profiler 对 UI 开发以及编码都很有用,因此在这里简要地看一下是值得的。

Android Profiler 可以从“查看|工具窗口”菜单、工具栏或按Alt + 6打开。它默认出现在 IDE 底部,但可以使用设置图标进行自定义以适应个人偏好:

Android Profiler

通过运行配置对话框可以使用高级分析选项;这将在下一个模块中介绍。目前还有另一个简单的调试/监控工具,对 UI 设计和开发非常有用。

分析器提供的视觉反馈提供了大量有用的信息,但这些信息是瞬息万变的,尽管高级分析允许我们记录非常详细的检查,但通常我们只需要确认特定事件发生了,或者某些事件发生的顺序。

对于这一点,我们可以使用另一个工具窗口 logcat,当我们只需要获取关于我们的应用程序如何以及在做什么的基本文本反馈时,我们可以为此创建一个 logcat 筛选器。

执行以下步骤来完成这个过程:

  1. 通过“查看|工具窗口”菜单或边距打开 logcat 工具窗口。

  2. 从右侧的筛选下拉菜单中选择“编辑筛选配置”。

  3. 按照以下方式完成对话框:

创建 logcat 筛选器

  1. 将以下字段添加到您的main活动中:
private static final String DEBUG_TAG = "tag"; 
  1. 包括以下导入:
import android.util.Log;
  1. 最后,在以下代码中添加突出显示的行:
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 
fab.setOnClickListener(new View.OnClickListener() { 

    @Override 
    public void onClick(View view) { 
        Snackbar.make(view, "Replace with your own action", 
            Snackbar.LENGTH_LONG) 
                .setAction("Action", null) 
                .show(); 
        Log.d(DEBUG_TAG, "FAB clicked"); 
    } 

}); 
  1. 在打开 logcat 并点击 FAB 运行应用程序时,将产生以下输出。
...com.mew.kyle.devicemonitoringdemo D/tag: FAB clicked 

尽管这个例子很简单,但这种技术的强大是显而易见的,这种调试形式是检查简单 UI 行为、程序流程和活动生命周期状态的最快、最简单的方式。

摘要

本章涵盖了很多内容;我们已经在之前的章节中介绍了 Android 布局的工作,并开始探索如何将这些布局从静态图形转变为更动态的结构。我们已经看到 Android 提供了使开发适应不同屏幕比其他 IDE 更容易的类和库,以及模拟器可以用来生成所有可能的形态,包括最新的平台。

在我们转向编码之前,这个模块中只剩下一个关于布局和设计的章节;在其中,我们将介绍如何管理我们可用的众多资源以及 Android Studio 如何协助我们进行管理。

第五章:资产和资源

到目前为止,在本书中,我们已经涵盖了布局、设计以及支持它们的库和工具。然后我们继续探讨为不同的屏幕尺寸、形状和密度以及其他形态因素进行开发。这是 UI 开发模块中的最后一章,我们将看看 Android Studio 如何管理各种资产和资源,如图标和其他可绘制图形。

当涉及将可绘制图标包含在我们的项目中时,Android Studio 非常包容,特别是在涉及矢量图形时,这对于 Android 开发人员非常宝贵,因为它们可以很好地适应不同的屏幕尺寸和密度,这是通过一个非常有价值的工具——矢量资产工作室来实现的。除此之外,还有一个资产工作室用于生成和配置位图图像。

矢量可绘制图标广泛用于应用程序图标和组件,如菜单、选项卡和通知区域,并且在对图标进行动画和从一个图标转换为另一个图标时也非常灵活,在小屏幕上非常有用的节省空间的功能。

在本章中,您将学会以下内容:

  • 使用资源工作室创建图标

  • 构建自适应图标

  • 创建材料启动器图标

  • 使用材料图标插件

  • 创建矢量资产

  • 导入矢量资产

  • 图标动画

  • 使用插件查看动态布局

  • 从图像中提取显著颜色

资产工作室

几乎没有任何应用程序不使用某种形式的图标,即使这些只是启动器和操作图标,正确的选择和设计也决定了成功的 UI 和令人困惑的 UI 之间的区别。

尽管这并非必需,但 Google 非常希望我们使用材料设计图标。这是为了在整个平台上创建统一的用户体验,以抵消 iOS 提供更一致感觉的认知。这并不奇怪,因为 iOS 是一个对开发人员施加了很多限制的封闭系统。另一方面,Google 更愿意为开发人员提供更多的创造自由。过去,这导致苹果设备获得了比 Android 更为流畅的声誉,为了抵消这一点,Google 推出了材料设计指南,这些指南远远超出了最初的预期,现在可以在许多其他平台上找到,包括 iOS。

正如预期的那样,Android Studio 提供了工具来帮助我们整合这些设计特性和可绘制图标。这就是资源工作室的形式。这有助于创建和配置各种图标,从色彩鲜艳的详细启动器图标到完全定制和可伸缩的矢量图形操作和通知图标。随着 API 级别 26,Android 引入了自适应图标,可以在不同设备上显示为不同形状并执行简单的动画。

资源工作室具有两个单独的界面:一个用于一般图像,一个用于矢量图形。我们将在下一节中看到第一个。

图像资产工作室

在为不同的屏幕配置创建图像时,我们经常必须创建相同图像的几个版本,这通常不是一件大事。另一方面,当涉及到图标时,我们可能有几个单独的图标和数十个版本,使得调整大小和缩放它们变得繁琐。幸运的是,Android Studio 提供了一个简洁的解决方案,即图像资产工作室。

设备制造商可能更关心在其型号之间创建一致的外观和感觉。当涉及到启动器图标在其主屏幕上的显示方式时,这尤为明显。理想的情况是,开发人员可以设计一个单一的图标,然后制造商可以将其放入统一的形状中,例如方形或圆形,具体取决于其在设备上的位置和制造商自己的设计理念。

图像资产工作室通过创建一个使用我们原始图像和一个纯色背景层的双层图标来实现这一点,可以对其应用蒙版以创建所需的整体形状,通常是以下三个图像之一:

自适应图标

可以通过从项目的可绘制上下文菜单中选择“新建|图像资产”来打开图像资产工作室:

资产工作室

创建能够在最广泛的设备和 API 级别上运行的图标有几个阶段,这些阶段由向导中的以下三个选项卡表示:前景层、背景层和传统。每个选项卡中都包含一些有价值的功能,将在下一节中概述。

分层图标

前景层是我们应用图像的地方。这可以是我们自己的艺术品,如果我们正在创建操作图标,也可以是剪贴画/文本。向导会自动生成每种可能用途的图标,包括 Play 商店图标,这涉及创建一个全新的资产。“显示安全区域”功能无疑是预览功能中最有用的功能,因为它显示了一个边界圆圈,如果我们的图标要在所有设备和平台上正确显示,我们的资产不应该超出这个区域。调整大小:控件允许我们快速确保我们的图标没有超出这个区域。

选择修剪:作为缩放选项将在创建完成的图标之前删除任何多余的像素,这意味着顶层的多余透明像素将被删除,通常会显著减小文件大小。

自适应图标的背景层需要足够大,以允许对其进行修剪,以创建前面图像中显示的形状和大小。默认的ic_launcher_background.xml生成描述网格的矢量图形。这在定位和调整我们的艺术品时非常有帮助,但不适用于已完成的应用程序。Google 建议您使用没有边框或外部阴影的纯色背景,尽管 Material 指南允许一些内部阴影,但最简单的解决方案是使用颜色而不是图像作为背景层。这还允许我们从我们的主题中选择一个突出的颜色,进一步推广我们的品牌。

资产背景选择

前面的图像使用了剪贴画选择的图标,这很好地展示了在设计我们自己的图标时指南的目的。

只有在编辑前景层时才能选择源图像,无论您正在使用哪个选项卡。

传统选项卡使我们能够确保我们的图标仍然可以在运行 API 级别 25 及更低版本的设备上运行,并为运行这些较早版本的设备提供所需的所有设计功能,例如适合许多这些设备的细长矩形图标。

编辑传统图标

许多开发人员也是优秀的艺术家,他们将非常乐意从头开始设计启动器图标。对于这些读者来说,重要的是要知道,自 API 级别 26 开始,启动器图标的指定尺寸已经发生了变化。尽管图标是为48 x 48 px的网格设计的,但现在必须是108 x 108 px,中心的72 x 72 px代表了必须始终可见的部分。但是,无法保证未来的制造商会如何遵循这些指南,因此建议尽可能多地针对所有设备进行测试。

这里给出的指南不仅有助于确保我们的图像不会被不必要地裁剪,还有助于满足许多制造商现在包含的脉冲和摇摆动画。这些动画通常用于指示尝试的用户交互的成功或失败。

当然,创建自适应图标并不是严格必要的,一旦掌握了基础知识,我们当然可以直接设计和包含我们自己的 XML。这可以在清单文件中使用android:roundIcon标识符来完成,如下所示:

<application
 . . . 
     android:roundIcon="@mipmap/ic_launcher_round"
 . . . >
</application>

自适应图标可以使用adaptive-icon属性添加到任何 XML 布局中,如下所示:

<adaptive-icon>
     <background android:drawable="@color/ic_some_background"/>
     <foreground android:drawable="@mipmap/ic_some_foreground"/>
</adaptive-icon>

尽管包含的动作图标集很全面,但尽可能多的选择总是好的,可以在material.io/icons/找到一个更大更不断更新的集合。

材料图标

图像资产工具非常适合生成我们在选项卡、操作栏等上使用的小型应用内图标,但在创建启动器图标时受到限制,启动器图标应该是明亮、多彩的,并且在材料方面是 3D 的。因此,启动器图标值得拥有自己的小节。

启动器图标工具

一般来说,启动器图标是使用外部编辑器创建的,正如我们将看到的,有 Studio 插件可以帮助我们创建时尚的 Android 图标。其中一个最好的工具是 Asset Studio 本身的在线、替代和增强版本。它是由谷歌设计师 Roman Nurik 创建的,可以在 GitHub 上找到romannurik.github.io/AndroidAssetStudio

这个在线版本提供了半打不同的图标生成器,包括原生版本中没有的功能,以及一个整洁的图标动画。这里感兴趣的是启动器图标生成器,因为它允许我们设置 IDE 中没有提供的材料特性,如高程、阴影和评分。

这个编辑器最好的地方之一是它显示了材料设计图标的关键线。

启动器图标关键线

谷歌称之为产品图标的设计超出了本书的范围,但谷歌在这个问题上有一些非常有趣的指南,可以在material.io/guidelines/style/icons找到。

然而,当您配置启动器图标时,您将需要某种外部图形编辑器。有一些工具可以帮助我们将 Android Studio 与这些编辑器集成。

Android Material Design 图标生成器是来自 JetBrains 的一个很棒的插件,它正是其标题所暗示的。它不需要下载,因为它可以在插件存储库中找到。如果您想在另一个 IDE 中使用它,可以从以下 URL 下载:

github.com/konifar/android-material-design-icon-generator-plugin

如果您是 Android Studio 插件的新手,请执行以下简单步骤:

  1. 从文件|设置中打开设置对话框....

  2. 打开插件对话框,然后单击浏览存储库....

  3. 在搜索框中键入材料,然后选择并安装插件。

插件存储库

  1. 重新启动 Android Studio。

现在可以从大多数 New...子菜单或Ctrl + Alt + M打开插件。图标生成器很简单,但提供了所有重要的功能,比如能够创建位图和矢量图像以及所有密度分组的选择,以及颜色和大小选择器。

Android Material Design 图标生成器插件

图标生成器还有一个方便的链接到不断增长的 GitHub 材料设计图标存储库。

Sympli 是一个复杂但昂贵的设计工具,可以与您选择的图形编辑器和 Android Studio 一起使用。它可以自动生成图标和其他资产,并设计用于团队使用。它可以在sympli.io找到。

虽然不是 Studio 插件本身,但 GitHub 上有一个方便的 Python 脚本,GIMP 用户可以在github.com/ncornette/gimp-android-xdpi找到。

只需下载脚本并将其保存在 GIMP 的plug-ins文件夹中,命名为gimpfu_android_xdpi.py。然后可以从图像的滤镜菜单中访问它。

自动生成图标

如前面的屏幕截图所示,此插件提供了将单个图像转换为一组图标时需要做出的所有主要选择。

使用这些工具创建和配置图标非常有用,也节省时间,但有很多时候我们根本不会使用位图作为我们的图标,而是使用矢量图形,它只需要一个图像来适配所有密度。

矢量图形加载比光栅图像慢,但一旦加载,速度会快一点。非常大的矢量图像加载速度慢,因此应该避免使用。

矢量图形在运行时以正确大小的位图进行缓存。如果要以不同大小显示相同的可绘制对象,请为每个创建一个矢量图形。

对于那些喜欢从头开始创建矢量图像的人来说,有一些非常有用的免费工具。

Method Draw 是一个在线可缩放矢量图形SVG)编辑器,提供了一组简单但非常实用的工具,用于生成简单的矢量图像,比如我们想要用于操作和通知图标的图像。创作可以下载为.svg文件并直接导入到 Studio 中。它可以在editor.method.ac找到。

如果您需要更复杂的工具,Boxy SVG Editor 可以在 Chrome Web Store 上找到,但它可以离线使用,并提供类似于 Inkscape 或 Sketch 的功能。

矢量资源工作室

矢量图形资源工作室执行与光栅图形版本相同的功能,但更有趣。处理预设图标时,甚至可以更简单地使用一个只需要选择材料图标的兄弟。

矢量资源工作室

创建后,这样的资源将以VectorDrawable类的 XML 格式保存:

<vector  
    android:width="24dp" 
    android:height="24dp" 
    android:viewportHeight="24.0" 
    android:viewportWidth="24.0"> 

    <path 
        android:fillColor="#FF000000" 
        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> 

</vector> 

Android 矢量图形与 SVG 格式类似,有点简化,通常与.svg文件相关联。与光栅资源一样,使用现有图标非常容易。只有当我们想要修改这些图标或创建自己的图标时,才变得有趣。

当然,学习 SVG 或者理解VectorDrawablepathData并不是必要的,但了解一点这个过程和我们可以使用的一些工具是有好处的。

矢量图形

矢量工作室允许我们导入 SVG 文件并将其转换为 VectorDrawables。有许多获取矢量图形的方法,许多图形编辑器可以从其他格式转换。还有一些非常好的在线工具可以将其他格式转换为 SVG:

image.online-convert.com/convert-to-svg

JetBrains 插件也可以从以下位置获得:

plugins.jetbrains.com/plugin/8103-svg2vectordrawable

当你编写自己的 SVG 对象时,你可能不会做太多事情,但了解这个过程是有用的,因为这些步骤展示了这些:

  1. 将以下代码保存为.svg文件:
<svg 
    height="210" 
    width="210">

<polygon 
    points="100,10 40,198 190,78 10,78 160,198" 
    style="fill:black;fill-rule:nonzero;"/> 

</svg> 
  1. 打开 Android Studio 项目,然后转到矢量工作室。

  2. 选择本地文件,然后选择前面代码中创建的 SVG 文件。

  3. 单击“下一步”和“完成”以转换为以下VectorDrawable

<vector  
    android:width="24dp" 
    android:height="24dp" 
    android:viewportHeight="210" 
    android:viewportWidth="210"> 

    <path 
        android:fillColor="#000000" 
        android:pathData="M100,10l-60,188l150, 
                -120l-180,0l150,120z" /> 

</vector> 

通常将矢量图标着色为黑色,并使用tint属性进行着色是一个好主意。这样,一个图标可以在不同的主题下重复使用。

SVG<polygon>很容易理解,因为它是定义形状角的简单点列表。另一方面,android:pathData字符串有点更加神秘。最容易解释的方式如下:

  • M是移动

100,10

  • l线到

-60,188

  • l线到

150,-120

  • l线到

-180,0

  • l线到

150,120 z

(结束路径)

前面的格式使用大写字母表示绝对位置,小写字母表示相对位置。我们还可以使用Vv)和Hh)创建垂直和水平线。

不必在路径结束限定符 z 提供的情况下包括最终坐标。此外,如果字符与之前的字符相同,则可以省略一个字符,就像以前的line-to命令一样;考虑以下字符串:

M100,10l-60,188l150,-120l-180,0l150,120z 

前面的字符串可以写成如下形式:

M100,10 l-60,188 150,-120 -180,0z 

请注意,有两组图像尺寸,正如您所期望的那样--viewportWidthviewportHeight;它们指的是原始 SVG 图像的画布大小。

关于矢量数据本身似乎是无关紧要的,因为这是由资源工作室生成的;但是,正如我们将在下面看到的,当涉及到动画图标(以及其他动画矢量图形)时,对矢量可绘制的内部结构的理解是非常有用的。

动画图标

每个使用 Android 设备的人都会熟悉动画图标。也许最著名的例子是当导航抽屉打开和关闭时,汉堡图标如何变换为箭头,反之亦然。矢量图形的使用使得这个过程非常简单。只要这两个图标具有相同数量的点,任何图标都可以转换为任何其他图标。

在移动设备上有效地使用空间是至关重要的,而动画化操作图标不仅看起来不错,而且还节省空间,并且如果应用得当,还会向用户传达意义。

矢量图像可以通过将原始图像上的点映射到目标图像上而轻松地从一个图像转换为另一个图像。这是通过AnimatedVectorDrawable类完成的。

有几种方法可以对这些可绘制进行动画处理。首先,我们可以应用许多预定义的动画,比如旋转和平移。我们还可以使用内置的插值技术来变形从一个可绘制到另一个,而不管点的数量。我们将研究这两种技术。然而,首先,我们将研究如何使用图像路径来控制动画,因为这给了我们最大的控制权。

以下图像表示一个箭头图标从左指向右的动画:

一个动画箭头图标。

以下步骤演示了如何创建这样一个动画矢量可绘制。

  1. 首先将两个箭头的路径存储为字符串,如下所示:
<!-- Spaces added for clarity only --> 
<string name="arrow_right"> 
    M50,10 l40,40 l-40,40 l0,-80z 
</string> 
<string name="arrow_left"> 
    M50,10 l-40,40 l40,40 l0,-80z 
</string> 
  1. 由于两条路径都记录为字符串,我们只需要定义一个矢量可绘制--称之为ic_arrow_left.xml
<vector  
    android:width="24dp" 
    android:height="24dp" 
    android:viewportHeight="100.0" 
    android:viewportWidth="100.0"> 

    <path 
        android:name="path_left" 
        android:fillColor="#000000" 
        android:pathData="@string/arrow_left" /> 

</vector> 
  1. 创建res/animator文件夹和arrow_animation.xml文件,放在其中:
<?xml version="1.0" encoding="utf-8"?> 
<set > 

    <objectAnimator 
        android:duration="5000" 
        android:propertyName="pathData" 
        android:repeatCount="-1" 
        android:repeatMode="reverse" 
        android:valueFrom="@string/arrow_left" 
        android:valueTo="@string/arrow_right" 
        android:valueType="pathType" /> 

</set> 
  1. 我们可以使用这个来创建我们的动画可绘制,ic_arrow_animated.xml
<?xml version="1.0" encoding="utf-8"?> 
<animated-vector  

    android:drawable="@drawable/ic_arrow_left"> 

    <target 
        android:name="path_left" 
        android:animation="@animator/arrow_animation" /> 

</animated-vector> 
  1. 要查看这个动画效果,请使用以下 Java 代码片段:
ImageView imageView = (ImageView) findViewById(R.id.image_arrow); 
Drawable drawable = imageView.getDrawable(); 

if (drawable instanceof Animatable) { 
    ((Animatable) drawable).start(); 
}

通过动画化矢量的路径,我们可以通过重新排列我们的点轻松地创建新的动画。

这个过程的关键是arrow_animation文件中的ObjectAnimator类。这个类比它在这里看起来的要强大得多。在这个例子中,我们选择了要动画化的pathData属性,但我们几乎可以动画化我们选择的任何属性。事实上,任何数值属性,包括颜色,都可以用这种方式进行动画化。

对象动画提供了创造富有想象力的新动画的机会,但只适用于现有属性。但是,如果我们想要动画化我们定义的值或者可能是反映一些特定应用程序数据的变量,该怎么办呢?在这种情况下,我们可以利用 ValueAnimator,从中派生出 ObjectAnimator。

Roman Nurik 的在线资源工作室还有一个功能强大且易于使用的动画图标生成器,可以在以下网址找到:

romannurik.github.io/AndroidIconAnimator

使用路径数据,这种方式提供了一个非常灵活的动画框架,特别是当我们想要将一个图标变形为另一个图标时,因为它改变了它的功能,通常在切换操作中经常看到,比如播放/暂停。然而,这并不是我们唯一的选择,因为有现成的动画可以应用到我们的矢量资产上,以及将图标转换为其他图标的方法,这些图标并不具有相同数量的点。

其他动画

变形路径数据是动画图标(和其他可绘制对象)的最有趣的方式之一,但有时我们只需要一个简单的对称运动,比如旋转和平移。

以下示例演示了如何应用这些动画类型之一:

  1. 选择您喜欢的矢量可绘制对象,并将其pathData保存为字符串。在这里,我们使用ic_first_page_black_24dp图标从资源工作室获取数据。
<string name="first_page"> 
    M18.41,16.59 L13.82,12 l4.59,-4.59 L17,6 l-6,6 6,6 z 
            M6,6 h2 v12 H6 z 
</string> 

ic_first_page_black_24dp 图标

  1. 与以前一样,为此创建一个 XML 资源;在这里,我们将其称为ic_first_page.xml
<vector  
    android:height="24dp" 
    android:width="24dp" 
    android:viewportHeight="24" 
    android:viewportWidth="24" > 

    <group 
        android:name="rotation_group" 
        android:pivotX="12.0" 
        android:pivotY="12.0" > 

        <path 
            android:name="page" 
            android:fillColor="#000000" 
            android:pathData="@string/first_page" /> 

    </group> 

</vector> 
  1. 再次创建一个对象动画器,这次称为rotation.xml,并完成如下:
<?xml version="1.0" encoding="utf-8"?> 
<set > 

    <objectAnimator 
        android:duration="5000" 
        android:propertyName="rotation" 
        android:repeatCount="-1" 
        android:valueFrom="0" 
        android:valueTo="180" /> 

</set> 
  1. 现在,我们可以创建图标的动画版本,就像以前一样,设置一个目标。在这里,文件名为ic_animated_page.xml,看起来像这样:
<?xml version="1.0" encoding="utf-8"?> 
<animated-vector 

    android:drawable="@drawable/ic_first_page"> 

    <target 
        android:name="rotation_group" 
        android:animation="@animator/rotation" /> 

</animated-vector> 
  1. 动画可以通过首先将其添加到我们的布局中,就像我们对任何其他图标所做的那样,并从代码中调用它来调用:
ImageView imagePage = (ImageView) findViewById(R.id.image_page); 
Drawable page_drawable = imagePage.getDrawable(); 

if (page_drawable instanceof Animatable) { 
    ((Animatable) page_drawable).start(); 
} 

除了动画类型之外,这里最大的不同之处在于我们的<path>包含在<group>中。这通常用于当有多个目标时,但在这种情况下,是因为它允许我们使用vectorX/Y为旋转设置一个中心点。它还具有scaleX/YtranslateX/Yrotate的等效设置。

要更改图标的透明度,在<vector>中设置alpha

不得不构建一个项目来测试简单的图形特性,比如这些动画图标,可能非常耗时。Jimu Mirror 是一个布局预览插件,可以显示动画和其他移动组件。它通过设备或模拟器连接,并通过一个复杂的热交换过程,可以在几秒钟内编辑和重新测试布局。Jimu 不是开源的,但价格不是很昂贵,并且可以免费试用。它可以从www.jimumirror.com下载。

本章的重点主要是检查 Android Studio 和相关工具如何促进应用程序图标的生成。这使我们能够总体上了解 Android 可绘制对象,包括位图和矢量图形。我们在本书的前面简要地探讨了其他可绘制对象,现在我们更深入地研究了这个问题,现在是重新审视这些可绘制对象的好时机。

一般的可绘制对象

我们之前看到了如何使用着色将黑色图标转换为与我们的应用程序或当前活动相匹配的颜色。对于其他图像,有时它们占据了屏幕的相当大部分,我们希望应用相反的效果,使我们的图标着色以匹配我们的图形。幸运的是,Android 提供了一个支持库,可以从任何位图中提取突出和主导颜色。

调色板库

将我们自己的主题应用到我们的应用程序可以产生非常时尚的界面,特别是当我们处理我们自己为应用程序创建的文本、图标和图像时。许多应用程序都包含用户自己的图像,在这些情况下,事先无法知道如何选择令人愉悦的设计。调色板支持库为我们提供了这种功能,允许对文本、图标和背景颜色进行精细控制。

以下步骤演示了如何从位图可绘制对象中提取突出的颜色:

  1. 开始一个新的 Android Studio 项目,并从“文件”菜单或Ctrl + Alt + Shift + S打开“项目结构”对话框。

  2. 从应用程序模块中打开依赖选项卡,并从右上角的+图标中添加库依赖项,使用搜索工具查找库。

库依赖项选择器

  1. 这将在您的build.gradle文件中添加以下行:
compile 'com.android.support:palette-v7:25.2.0' 
  1. 创建一个带有大图像视图和至少两个文本视图的布局。将这些文本视图命名为text_view_vibranttext_view_muted

  2. 打开您的主 Java 活动并添加以下字段:

private Palette palette; 
private Bitmap bmp; 
private TextView textViewVibrant; 
private TextView textViewMuted; 
  1. 将前述的TextViews与它们的 XML 对应项关联,如下所示:
textViewVibrant = (TextView) 
        findViewById(R.id.text_view_vibrant); 

textViewMuted = (TextView) 
        findViewById(R.id.text_view_muted); 
  1. 分配在步骤 5 中声明的位图:
 bmp = BitmapFactory.decodeResource(getResources(), 
        R.drawable.color_photo); 
  1. 最后,添加以下条款以从图像中提取突出的鲜艳和柔和的颜色:
// Make sure object exists. 
if (bmp != null && !bmp.isRecycled()) { 
    palette = Palette.from(bmp).generate(); 

    // Select default color (black) for failed scans. 
    int default_color=000000; 

    // Assign colors if found. 
    int vibrant = palette.getVibrantColor(default_color); 
    int muted = palette.getMutedColor(default_color); 

    // Apply colors. 
    textViewVibrant.setBackgroundColor(vibrant); 
    textViewMuted.setBackgroundColor(muted); 
} 

提取的颜色

前面概述的方法是有效但粗糙的。调色板库还有很多功能,我们需要了解很多东西才能充分利用它。

调色板使用default_color是必要的,因为提取这些颜色并不总是可能的,有时会失败。这经常发生在褪色的图像和颜色很少的图像以及定义很少的高度不规则的图像上。有些讽刺的是,当呈现过饱和的图形和颜色很多的非常规则的图案时,扫描也可能失败,因为没有颜色(如果有的话)会支配。

在提取这些调色板时非常重要的一点是,使用大位图可能会严重消耗设备资源,所有与位图的工作在可能的情况下不应在当前线程上执行。前面的示例没有考虑到这一点,但库中有一个监听器类,允许我们异步执行这些任务。

考虑以下示例:

Palette palette = Palette.from(bmp).generate(); 

使用以下监听器,而不是前面的监听器,以在生成位图后做出反应:

Palette.from(bmp).generate(new PaletteAsyncListener() { 

    public void onGenerated(Palette p) { 
        // Retrieve palette here. 

    } 

}); 

在前面的示例中,我们只提取了两种颜色,使用Palette.getVibrantColor()Palette.getMutedColor()。这些通常非常适合我们的目的,但如果不适合,还有每种颜色的浅色和深色版本,可以使用 getter 来访问,比如getDarkVibrantColor()getLightMutedColor()

调色板库的功能比我们在这里介绍的要多,比如能够选择与分析图像匹配的文本颜色,而且它并不是 Android Studio 专属的,因此从其他 IDE 切换过来的读者可能已经熟悉它。

本书中介绍的 Studio 功能展示了 IDE 在开发布局和 UI 时的实用性,但当然,这只是故事的一半。无论我们的布局多么完美,如果没有逻辑支持,它们就毫无用处,这就是 Android Studio 真正开始发挥作用的地方。

摘要

不仅在本章,而且在前面的三章中,我们看到了 Android Studio 如何使得在各种设备和因素上设计和测试我们的图形布局变得简单而直观。Studio 专门为 Android 的特点而设计,也是第一个集成新设计功能的工具,比如约束布局,这彻底改变了视觉活动的设计。

到目前为止,已经涵盖了 IDE 所考虑的所有基本设计问题,并希望向读者介绍了简化和澄清这个常常复杂过程的丰富功能。

在下一章中,我们将开始将这些设计变为现实,看看 Android Studio 如何简化编码、测试和调试应用程序的复杂过程。这些基本过程经常重叠,大多数开发人员会发现自己不得不在微调工作时重新访问每个过程。Android Studio 引导开发人员在这个过程中前后穿梭,使他们能够在进行时跟踪和评估。

Android Studio 已经帮助您将您的想法转化为令人愉悦的布局。下一步是用您的逻辑将这些布局变得生动起来。正如人们可能想象的那样,当涉及到逻辑时,IDE 在设计时一样有帮助。

第六章:模板和插件

作为开发环境,Android Studio 提供了设计和开发任何我们可以想象的 Android 应用程序的设施。在前几章中,我们看到它如何作为一个可视化设计工具,以及一个动态布局编辑器、模拟器和 XML 结构。从现在开始,我们将深入了解 IDE 如何促进、简化和加快编码、测试和微调我们的工作的过程。

大多数读者已经是专业的编码人员,不需要帮助。因此,我们将在接下来的章节中全面探讨 Android Studio 改进这一体验的方式。在本章中,我们将看到 IDE 自带的各种活动模板和 API 示例的代码示例。这些对于探索和学习各种组件的编码方式以及通过提供已经存在的起点来加快编码过程都是有用的。此外,正如我们将看到的,如果捆绑的模板选择不够,Android Studio 允许我们创建自己的模板。

在本章中,您将学习以下内容:

  • 理解内置项目模板

  • 访问结构工具窗口

  • 安装和使用 UML 插件

  • 应用简单的重构

  • 应用代码模板

  • 创建自定义模板

  • 使用项目示例

项目模板

大多数读者在从 IDE 的欢迎屏幕开始新的 Android Studio 项目时已经遇到了项目模板。

项目模板

即使是空活动模板也提供了几乎所有应用程序都必不可少的文件和一些代码,正如您在前面的屏幕截图中所看到的,有越来越多的项目模板集合,旨在适应许多常见的应用程序结构和目的。

导航抽屉模板

在这里不需要检查所有这些模板,但有一两个可能非常有教育意义,并且可以进一步了解 Android Studio 的工作原理。第一个,也许是最有用的,是导航抽屉活动模板,当按原样运行时产生以下输出:

导航抽屉模板

如前面的屏幕截图所示,此模板提供了许多常见且推荐的 UI 组件,包括图标可绘制、菜单和操作按钮。这些模板最有趣的方面是它们创建的代码和使用的文件结构,这两者都是最佳实践的很好示例。

如果我们使用此模板开始一个新项目并开始四处看看,我们将观察到activity_main.xml文件与我们之前看到的不同,因为它使用android.support.v4.widget.DrawerLayout作为其根。在其中,我们发现来自设计库的CoordinatorLayoutNavigationView。与前面的示例一样,协调器布局包含一个工具栏和一个 FAB;然而,这些组件是在一个单独的文件中创建的,并与include标签一起包含,如下所示:

<include 
    layout="@layout/app_bar_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

这种结构可以帮助我们更容易地跟踪代码并使未来的修改更容易。正如可以看到的,这种方法也用于定义导航栏标题和主要内容。

大多数模板生成的 XML 对读者来说都很熟悉,但也有一两个片段可能需要解释。例如,content_main.xml文件包含以下行:

app:layout_behavior="@string/appbar_scrolling_view_behavior" 

引用的字符串不会在strings.xml文件中找到,因为它是系统提供的,并指向AppBarLayout.ScrollingViewBehavior类,确保我们的工具栏是它的一个实例。

tools:showIn="@layout/app_bar_main" 这一行可能也令人困惑。这是 tools 命名空间中的许多有用功能之一,用于在预览编辑器中显示导航抽屉,使开发人员无需每次想查看图形变化时都重新构建项目。

此模板和其他模板生成的 XML 资源只讲述了故事的一半,因为其中许多模板还生成了相当数量的 Java 代码,这同样有趣,甚至比 XML 更有趣。只需快速浏览一下 MainActivity.Java 代码,就可以看到模板如何设置处理基本导航、菜单和按钮点击的方法。这些代码都不难理解,但非常方便,因为所有这些方法都必须在某个时候由我们编写,而注释和占位符使得替换我们自己的资源和添加我们自己的代码变得非常简单。

结构资源管理器

以这种方式浏览代码是很好的,但很少有开发人员有这种奢侈,自然希望以一种快速查看类的结构和内容的方式。Android Studio 通过结构工具窗口提供了一个整洁的、图解的窗口,可以从项目资源管理器栏中选择,或者通过按 Alt + 7 来选择:

结构(*Alt *+ 7)工具窗口

处理具有许多类的项目时,结构工具是一种非常有用的方式来保持概览,并且在处理冗长的类时,选择任何项目将在代码编辑器中突出显示相应的文本。

在结构资源管理器中选择项目后按 F4 将导致代码编辑器跳转到文本中的该位置。

结构窗格顶部的工具栏允许使用一些非常方便的过滤器,以便根据它们的定义类型显示我们的方法(如前图所示),以及其他级别的细节,例如是否显示属性和字段。

尽管能够从这样的角度查看任何类的视角非常有用,但通常情况下,我们希望从更多的角度查看类结构,当然也有允许更深入检查的插件。

类检查插件

有许多方法可以可视化类和类组,使它们更容易遵循或突出显示某些特性或属性。一种经过验证的可视化编程工具是通用建模语言UML)。如果开发人员使用设计模式,这些对于特定用途非常有用。

有几个 UML 类图插件可供我们使用,从基本到复杂不等。如果你只想要一个简单的 UML 工具,那么 JetBrains 插件 simpleUMLCE 可以从以下链接下载:

plugins.jetbrains.com/plugin/4946-simpleumlce

如果您是插件新手,请按照快速步骤安装和使用 simple UML 插件:

  1. 从上面的链接下载插件。

  2. 从 Android Studio 的“文件 | 设置”菜单中打开插件对话框。

  3. 使用“从磁盘安装插件...”按钮来定位并安装插件。

插件对话框

您需要重新启动 IDE 才能访问插件。一旦您这样做了,右键单击项目资源管理器中的包或类,然后从菜单中选择“添加到 simpleUML 图表 | 新图表...”。插件将在左侧边栏添加一个选项卡,打开工具区域并显示以下截图:

simpleUML 图表工具

我们在本章中使用的导航抽屉示例中使用的活动类有点太简单,无法真正展示出以图表方式查看代码的优势,但如果读者将此工具应用于更复杂的类,具有多个字段和依赖关系,其价值很快就会显现出来。

图表可以保存为图像,并且可以从窗口的工具栏中选择多种视角。

大多数 Studio 插件都会在排水沟中添加一个选项卡。这通常放置在左侧排水沟的顶部。这通常会干扰我们自己的工作区首选项。幸运的是,这些选项卡可以通过简单地将它们拖放到首选位置来重新排列。

对于大多数开发人员来说,像这样的简单 UML 工具已经足够了,但如果您喜欢更复杂的工具,那么Code Iris可能是适合您的插件。

Code Iris 不需要下载,因为可以通过浏览插件存储库找到。尽管如此,您仍然需要重新启动 IDE。可以通过单击“浏览存储库...”按钮来访问存储库,该按钮位于设置对话框的插件窗口中,与之前的插件相同。

浏览存储库对话框

快速查看项目网页的描述,可以在plugins.jetbrains.com/plugin/7324-code-iris找到,将显示 Code Iris 可以做的远不止创建类图,并且应该被视为更通用的可视化工具。该工具被描述为基于 UML 的谷歌地图,适用于您的源代码,这使其不仅是个人有用的开发工具,也是团队之间的良好沟通工具,并且更适合绘制整个项目而不是等效工具。

任何第三方插件存储库都可以通过单击“管理存储库...”按钮并将相关 URL 粘贴到生成的对话框中,使其在存储库浏览器中可用。

可以通过打开工具窗口并选择“创建/更新图表”按钮,或者从项目资源管理器中的单个模块、包或类的条目中生成代码可视化。

Code Iris 的强项在于其能够在任何规模上可视化项目,并且可以使用视图滑块快速在这些项目之间切换。与过滤器和称为有机布局的自动排列算法一起使用,我们可以快速生成适当且易于理解的可视化。

Code Iris 可视化

尽管其对生物学的自命不凡和英语使用不佳,有机布局工具实际上非常聪明,有用且外观良好。打开后(使用播放按钮),图表将根据我们的焦点动态排列,这可以通过简单点击感兴趣的类来指定。如果您曾经发现自己不得不处理文档不全的代码,这个工具可以节省您很多时间。

这里介绍的两个插件绝不是我们可用的唯一的检查和可视化工具,只需在互联网上快速搜索,就会发现更多。我们选择的这两个工具之所以被选择,是因为它们代表了周围的工具类型。

我们在这里探讨的导航抽屉模板非常有用,因为它包含了几乎无处不在的 UI 组件。它也非常容易理解。另一个非常方便的项目模板是主/细节流模板。

主/细节流模板

在移动设备上经常会发现主/细节 UI,因为它们通过在屏幕的当前宽度上分别或并排显示列表及其各个项目,非常好地最大化了空间的使用。这导致手机在纵向方向上显示两个单窗格;但是,在横向模式或较大的设备上,如平板电脑上,将会并排显示两个窗格:

双窗格视图

前面的屏幕截图是从未经修改的主/细节流项目模板在横向模式下查看的,以便显示两个窗格。我们之前说过,两窗格视图在手机上的横向模式下是可见的。如果您在许多手机上尝试过这一点,您会发现这并不一定正确。默认情况下,模板仅在最长边为 900dp 或更长的屏幕上显示两个窗格。这可以从res/layout-w900dp目录的存在中推断出来。

要在较小的设备上启用双窗格视图,我们只需要更改此文件夹的名称。当然,这可以直接从我们的文件浏览器中完成,但是 Android Studio 具有强大的重构系统和复杂的预览窗口。虽然在这种情况下并不需要,但它可能最有用的地方是它会搜索对它的引用并将它们重命名。

按下Shift + F6可以直接打开重命名对话框。

当然,如果您尝试从导航栏或项目资源管理器访问layout-w900dp文件夹,您将无法这样做。要做到这一点,切换从 Android 选项卡到资源管理器中的项目选项卡,因为这样可以将项目完全呈现为磁盘上的状态。

快速检查代码将会发现一个名为 DummyContent 的 Java 类。正如您将看到的,有一个 TODO 通知,指出这个类需要在发布之前被移除,尽管当然也可以简单地对其进行重构。这个文件的目的是演示如何定义内容。我们所需要做的就是用我们自己的内容替换模板中的占位符数组。当然,这可以采用我们选择的任何形式,比如视频或 Web 视图。

能够使用现成的代码开始项目非常有用,可以节省大量时间。但是,有许多时候我们可能希望以适合我们目的的自己的结构开始项目,而这可能并不是通过 IDE 可用的。然而,这种情况并不妨碍我们这样做,因为代码模板可以在任何时候使用,而且,正如我们将看到的,甚至可以创建我们自己的模板。

自定义模板

想象一下,你正在开发一个项目,该项目使用我们已经检查过的模板之一,但你也想要一个登录活动。幸运的是,通过 IDE 内部已经启动的项目可以轻松管理。

当我们启动新项目时,呈现给我们的项目模板屏幕可以在任何时候使用。只需从项目资源管理器的上下文敏感菜单中选择 New | Activity | Gallery...,然后选择所需的活动。然后会呈现一个自定义屏幕,与之前看到的类似,但具有声明父级和包的选项,使我们能够使用尽可能多的模板。

如果您访问了活动库,您还会注意到这些活动也可以直接选择,而无需打开库。

同样的菜单还允许我们创建和保存自己的模板。打开源代码文件夹的上下文敏感菜单,选择 New | Edit File Templates...将打开以下对话框:

模板编辑向导

如前面的屏幕截图所示,有许多文件模板可用,以及其他三个选项卡。包含选项卡提供文件头,代码选项卡包含较小的代码单元,其中许多在测试期间非常有用。其他选项卡特别有用,提供了更大的应用程序组件的模板,例如活动、片段、清单、布局和资源。

左上角的+图标允许我们创建自己的模板(但只能从前两个选项卡)。最简单的方法是直接将代码粘贴到提供的窗口中。命名并保存后,它将出现在“文件”|“新建”菜单或项目资源管理器目录中。

仅仅快速查看一些内置模板就会立即发现${VARIABLE}形式的占位符变量的使用。正是这些占位符使得定制模板成为一个有用和灵活的工具。

了解这些变量如何工作的最简单方法是使用现有模板之一,看看这些占位符是如何实现的;这在以下练习中有详细说明:

  1. 打开文件模板向导,如前面所述。

  2. 从其他选项卡中,复制Activity.java条目中的代码。

  3. 使用+按钮创建新模板,命名它,并将代码粘贴到提供的空间中。

  4. 根据您的需求编辑代码,确保包含自定义变量,就像以下代码中所示:

package ${PACKAGE_NAME}; 

import android.app.Activity; 
import android.os.Bundle; 

#parse("File Header.java") 
public class ${NAME} extends Activity { 

public String ${USER_NAME} 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
    } 
} 
  1. 点击“确定”保存模板,它将出现在“新建”菜单中。

  2. 现在,每当您创建此模板的实例时,Android Studio 将提示您输入模板占位符中定义的任何变量:

从模板生成类

所有开发者都依赖可以反复使用的代码,尽管其他集成开发环境提供了代码模板,但内置结构的数量和创建的便利性使得这是 Android Studio 最大的节省时间的功能。

第三方模板

在继续之前,我们需要快速看一下另一种访问现成模板的方式:第三方模板。网上有很多这样的模板,快速搜索就会发现。很多最好的模板不幸的是并不免费,尽管很多提供免费试用。

诸如 Softstribe 和 Envato 之类的网站为大量应用程序类型提供高度开发的模板,例如广播流、餐厅预订和城市指南。这些模板大多是完全开发的应用程序,几乎只需要配置和定制。这种方法可能不适合经验丰富的开发者,但如果速度是您的首要任务,并且您有预算支持,这些服务提供了项目完成的强大快捷方式。

模板不是 Android Studio 提供现成代码时的唯一节省时间的功能,您无疑已经注意到 IDE 中提供的许多示例项目。

项目示例

尽管可以从示例浏览器中访问示例,但更常见的是在项目开始时打开其中一个示例。这可以从欢迎屏幕下的“导入 Android 代码示例”中完成。有数百个这样的示例(还可以在网上找到更多),这些示例在示例浏览器中被很好地分组到类别中。

与模板一样,示例可以作为更大项目的起点,但它们本身也是教育的一部分,因为它们是由非常有经验的开发者编写的,并且是最佳实践的绝佳示例:

示例浏览器

从前面的示例浏览器中可以看到,示例不仅可以从浏览器下载,还可以在 GitHub 中查看。读者会知道,GitHub 是一个非常有用的代码存储库,对各种开发人员都非常有用,它包含了示例浏览器中找到的所有示例;还有成千上万的其他 Android 项目、代码库和插件。从示例的角度来看,这个资源是一个很好的时间节省器,因为在决定是否下载和构建之前,查看代码要快得多。

有这么多示例,它们都同样有用和有教育意义,这使得很难选择任何一个来在这里进行检查,尽管 Camera2 示例可能有助于探索,因为这是许多开发人员可能以前没有检查过的 API。这归结为以下两个因素:

  • 很多时候,相机功能可以通过简单地从我们自己的应用程序中调用原生应用程序(或用户已安装的应用程序)来访问。

  • Camera2 API 与运行在 API 级别 20 及以下的设备不兼容。与许多 API 不同,没有方便的支持库可以使 Camera2 向后兼容。

尽管存在这些缺点,如果您计划开发一个专注于捕获图像和视频的应用程序,那么您将需要自己构建所有的功能。这就是这些示例可能非常有用的地方。Camera2Basic 示例可能是首选查看的最佳示例。

示例只包含三个类和一个简单的布局结构,适用于横向和纵向方向。这些类包括一个基本的启动活动,一个扩展的 TextureView,根据运行设备的不同调整要捕获的区域的大小,以及一个大部分工作的 Fragment。所有这些类都有很好的注释,基本上是自解释的,只需要快速查看代码就可以理解它的工作原理。像其他示例一样,Camera2Basic 可以在没有任何进一步修改的情况下构建和运行:

Camera2Basic 示例

存储库中的所有示例都同样有用,取决于您选择的项目,它们都同样写得很好并且具有教育意义。

GitHub 最有用的特性之一是那里有大量的第三方库可用,当我们更仔细地查看 Gradle 构建过程时,我们将看到这些库如何可以作为依赖项从 Android Studio 中包含。

这些并不是 GitHub 上唯一有用的工具,GitHub 也绝不是这些工具的唯一来源。随着我们从 UI 开发转向编码,现在是时候再次看一些可用于 Android Studio 的第三方插件了。

第三方插件

有大量不断增长的第三方插件可用,这使得选择一个公平的样本变得困难。以下部分涵盖了一小部分插件,主要是因为它们的通用用途。

ADB Wi-Fi

这是一个 JetBrains 插件,因此可以在设置对话框的插件屏幕上使用“浏览存储库...”按钮找到:

插件对话框

这个插件简单地允许我们使用共享的 Wi-Fi 连接来调试我们的应用程序。尽管它很简单,但这个应用程序不仅仅是一个节省电缆的便利,因为它允许对许多设备的传感器进行实时调试,这些传感器如果被绑定到主机机器上将受到很大限制。健身和增强现实应用程序可以通过这种方式更轻松地进行测试。

ADB Wi-Fi 非常简单设置和使用,特别是如果您的主机和设备已经共享了 Wi-Fi 连接,并且设备之前已经用于调试。

大多数插件比这个更复杂,许多使用了一些非常复杂的技术,比如人工智能。

Codota

最近人们对人工智能的进步大做文章,虽然许多这样的说法夸大和自负,但仍然有一些引人注目的例子,表明真正有用的人工智能是可用的,Codota 就是其中之一。

许多开发人员可能已经熟悉 Codota 作为在线搜索工具或浏览器扩展程序。尽管这些工具很聪明和有用,但 Codota 真正发挥作用的地方是作为 IDE 插件。它使用一个名为 CodeBrain 的智能和不断发展的 AI 系统。这种代码是基于一种称为 Example-Centric Programming 的新兴编程形式。

Codota 的 CodeBrain

从技术上讲,Codota 插件并不是真正的插件,因为它必须单独安装并在与 IDE 不连接的窗口中运行。这是因为它与 Java IDE 一起工作,而不是与个别环境连接。这有几个优势,其中之一是插件是一个 Java 插件,而不是特定于 Android,这意味着它将在任何版本的 Studio 上运行,并且(不像一些插件)不需要等待更新。

Android Studio Codota 插件作为一个独立的应用程序运行,这在某种程度上是一个优势。一旦打开,它就非常聪明(根据其算法哲学,变得越来越聪明)。该软件声称提供智能编码助手,很多时候确实如此,我挑战任何人不被其才智所折服。它有一些缺陷,但往往能够从各种在线来源(包括 stackoverflow 和 GitHub)中找到几乎总是有用的示例。

一旦下载并打开,只需打开 IDE 并点击感兴趣的代码,Codota 很可能会提供一些非常好的答案来回答您可能有的任何问题。想象一下有一个助手,虽然不是很聪明,但非常有知识,并且可以在几秒钟内扫描所有相关的在线代码。这就是 Codota,无论您将其用作浏览器扩展程序、搜索工具还是 IDE 插件,它都是最好的编码助手之一。

总结

在本章中,我们已经研究了两种辅助代码开发过程的方法:现成的代码和助手插件和附加组件。模板通常是快速启动项目的好方法,类检查插件使我们能够轻松理解更大的模板,而无需仔细研究大量代码。

本章中我们看到的其他插件提供了一些不同的方法,使应用程序开发任务更加轻松和有趣。当然,市面上有很多很棒的工具,编码也在不断变得不那么单调,更有创造力。

本章重点介绍了 Java 编程,但正如任何开发人员所知道的那样,这绝不是唯一可用的语言。Android Studio 支持 C++和 Kotlin(后者甚至可以与 Java 代码一起使用)。

在下一章中,我们将探讨如何支持其他语言,以及看看 Android Things,尽管不是另一种语言,但确实需要许多传统开发人员可能不熟悉的技能。幸运的是,Android Studio 提供了使为单板计算机开发与开发其他 Android 应用程序非常相似的工具。

第七章:语言支持

要被认为是真正必不可少的 IDE,它必须做的不仅仅是提供基础功能。特别是,它必须对来自各种背景、使用各种语言和哲学的开发者都是可访问的。例如,许多开发者更喜欢采用面向对象的方法,而其他人更喜欢更基于功能的哲学,许多潜在项目更容易适应这两种范式中的一种。

Android Studio 3 为 C++和 Kotlin 提供了完整的语言支持,使开发者有机会根据手头项目的需求专注于速度或可编程性。

除了提供这种语言支持外,Android Studio 还促进了为各种形态的设备开发应用程序。读者可能已经熟悉 Android Wear 和 Android Auto,最近 IDE 还包括了对 Android Things 的支持。

在本章中,我们将看看这些语言支持系统以及构成物联网IoT)的令人兴奋的新形态。

在本章中,您将学习以下内容:

  • 包括 Kotlin 语言支持

  • 将 Kotlin 与 Java 集成

  • 应用 Kotlin 扩展

  • 设置本地组件

  • 在项目中包括 C/C++代码

  • 创建 Android Things 项目

Kotlin 支持

自移动应用诞生以来,软件开发经历了不止一次革命,而 Android 框架也不陌生于这些变化。许多开发者更喜欢 Java,因为它相对容易使用,但总会有一些时候,我们想要 C++的原始速度,而 Java 早在移动设备出现几十年前就存在了。如果有一种高级语言,比如 Java,它是专门为移动开发而设计的,那不是很好吗。

幸运的是,俄罗斯的 JetBrains 团队创建了 Kotlin,它可以与 Java 一起工作,甚至在 Java 虚拟机上运行,以创建一个更适合 Android 开发者需求的语言。它还与 Java 完全可互操作,因此您可以在同一个项目中使用 Java 和 Kotlin 文件,一切仍然可以编译。您还可以继续使用 Kotlin 与所有现有的 Java 框架和库。

Kotlin 作为插件已经提供给开发者一段时间了,但自从 Android Studio 3.0 推出以来,Kotlin 现在已完全集成到 IDE 中,并且已被 Google 正式支持为开发语言。IDE 中包括了用 Kotlin 编写的工作样本和向导模板。

在项目设置向导中包括 Kotlin 支持

学习一门新的编程语言很少会有多少乐趣,Kotlin 也不例外。使用它的好处在于,我们不必一下子从一种语言跳到另一种语言,可以逐渐引入 Kotlin,随时选择使用。

我们中的许多人已经使用 Java 很长时间了,并且没有真正改变的理由。毕竟,Java 运行得很好,多年的经验可以带来一些非常快速的工作实践。此外,互联网上充斥着高质量的开源代码库,使得研究和学习新技能对 Java 程序员非常有吸引力。

在 Android 开发中,学习和使用 Kotlin 绝对不是必须的,但值得一看的是,为什么有这么多开发者认为它代表了 Android 应用开发的未来。

Kotlin 的优势

除了谷歌自己的认可之外,还有许多理由让开发者考虑 Kotlin。其中一个原因可能是终结空指针异常,编译器通过不允许将 null 值分配给任何对象引用来实现这一点。语言的其他令人兴奋的特性包括更青睐组合而非继承、智能转换和创建数据类。

看到这些创新提供了多大的优势,最好的方法是看一下这些。

正如前一节中的屏幕截图所示,Kotlin 可以直接从模板向项目中包含,但是我们也可以以与添加任何其他类或文件相同的方式,从已经打开的项目中包含 Kotlin 类。

添加一个新的 Kotlin 文件/类

以这种方式包含一个 Kotlin 类将提示 IDE 自动配置 Kotlin 与 Gradle。它通过修改顶级build.gradle文件来实现:

buildscript { 
    ext.kotlin_version = '1.1.3-2' 

    repositories { 
        google() 
        jcenter() 
    } 
    dependencies { 
        classpath 'com.android.tools.build:gradle:3.0.0-alpha9' 
        classpath "org.jetbrains.kotlin:kotlin-gradle-
                      plugin:$kotlin_version" 
    } 
} 

allprojects { 
    repositories { 
        google() 
        jcenter() 
    } 
} 

task clean(type: Delete) { 
    delete rootProject.buildDir 
} 

在 Android 应用中使用 Kotlin 几乎不会产生额外开销;而且,一旦编译完成,Kotlin 代码的运行速度不会比其 Java 等价物慢,也不会占用更多的内存。

像这样包含一个新的 Kotlin 类或文件非常有用,但是如何从模板创建一个 Kotlin 活动或片段呢?作为 Java 开发人员,我们习惯于使用简单的配置对话框来设置这些。幸运的是,Kotlin 活动也没有什么不同,配置活动对话框允许我们适当地选择源语言。

选择源语言

与以前一样,值得一看的是最终代码,看看与传统的 Java 活动/片段模板相比,它有多么简洁和可读。

class ItemDetailFragment : Fragment() { 

    private var mItem: DummyContent.DummyItem? = null 

    public override fun onCreate(savedInstanceState: Bundle?) { 
        super.onCreate(savedInstanceState) 

        if (getArguments().containsKey(ARG_ITEM_ID)) { 
            mItem = DummyContent.ITEM_MAP.
                     get(getArguments().getString(ARG_ITEM_ID)) 

            val activity = this.getActivity() 
            val appBarLayout = activity.findViewById<View>
                   (R.id.toolbar_layout) as CollapsingToolbarLayout 
            if (appBarLayout != null) { 
                appBarLayout!!.setTitle(mItem!!.content) 
            } 
        } 
    } 

    public override fun onCreateView(inflater: LayoutInflater?, 
         container: ViewGroup?, savedInstanceState: Bundle?): View? { 
        val rootView = inflater!!.inflate(R.layout.item_detail, 
                               container, false) 

        if (mItem != null) { 
            (rootView.findViewById(R.id.item_detail) as 
                         TextView).setText(mItem!!.details) 
        } 

        return rootView 
    } 

    companion object { 
        val ARG_ITEM_ID = "item_id" 
    } 
} 

通过使用 Kotlin 扩展来删除对findViewById()的调用,可以使这段代码变得更加简洁,如下一节所述。

虽然这种混合语言的方式对于调整和更新现有应用非常有用,但是当应用于整个项目时,Kotlin 才能充分发挥其作用。也许它最吸引人的特点是简洁性,通过从头开始创建两个项目并比较它们的代码,这一点很容易看出。以下是导航抽屉模板代码中的onCreate()列表:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 

    FloatingActionButton fab = (FloatingActionButton) 
                 findViewById(R.id.fab); 
    fab.setOnClickListener(new View.OnClickListener() { 
        @Override 
        public void onClick(View view) { 
            Snackbar.make(view, "Replace with your own action", 
                   Snackbar.LENGTH_LONG) 
                    .setAction("Action", null).show(); 
        } 
    }); 

    DrawerLayout drawer = (DrawerLayout) 
              findViewById(R.id.drawer_layout); 
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( 
            this, drawer, toolbar, R.string.navigation_drawer_open, 
                  R.string.navigation_drawer_close); 
                  drawer.addDrawerListener(toggle); 
                  toggle.syncState(); 

    NavigationView navigationView = (NavigationView) 
              findViewById(R.id.nav_view); 
    navigationView.setNavigationItemSelectedListener(this); 
} 

以下是它的 Kotlin 等价物:

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

    fab.setOnClickListener { view -> 
        Snackbar.make(view, "Replace with your own action", 
                  Snackbar.LENGTH_LONG) 
                .setAction("Action", null).show() 
    } 

    val toggle = ActionBarDrawerToggle( 
            this, drawer_layout, toolbar, 
            R.string.navigation_drawer_open, 
            R.string.navigation_drawer_close) 
    drawer_layout.addDrawerListener(toggle) 
    toggle.syncState() 

    nav_view.setNavigationItemSelectedListener(this) 
} 

这种语法的增加简洁性是所有开发人员都会欢迎的,而这绝不是使用 Kotlin 的唯一优势。

扩展 Kotlin

正如人们对任何强大的编程范式所期望的那样,可以通过插件来扩展它,以进一步增加其实用性。

每个 Android 开发人员都会对自己多少次输入findViewById()失望。他们也会意识到这种静态类型可能有多容易出错。

在项目设置期间启用 Kotlin 支持时,默认情况下会包含 Kotlin 扩展,如模块级build.gradle文件中所示:

apply plugin: 'com.android.application' 

apply plugin: 'kotlin-android' 

apply plugin: 'kotlin-android-extensions' 

使用扩展还需要将其导入到适当的类中,通常是活动或片段。读者很可能已经通过系统设置设置了自动导入。然后所需的就是以通常的方式使用 XML 创建视图,如下所示:

 <TextView 
        android:id="@+id/text_view" 
       . . . /> 

现在,设置该小部件的值所需的只是以下内容:

text_view.setText("Some text") 

无论您是否设置了自动包含导入,了解这些导入的格式是很有用的。考虑以下示例:

import kotlinx.android.synthetic.main.some_layout.* 

还可以导入特定视图的引用,而不是整个布局文件,如下所示:

import kotlinx.android.synthetic.main.some_layout.text_view

  1. 在这种情况下,some_layout.xml将是包含我们的text_view的文件。

  2. 如果您习惯于在活动 XML 中使用<include>引用内容 XML,那么您将需要两个类似于以下的导入:

import kotlinx.android.synthetic.main.some_activity.*
import kotlinx.android.synthetic.main.some_content.*

我们不仅可以在这里设置文本;任何我们喜欢的函数都可以以同样的方式调用,而无需通过搜索其 ID 来引用视图。

请注意,在 Kotlin 中,分号作为语句分隔符是完全可选的。

希望到目前为止,读者已经被说服了在 Kotlin 中编码的优势,但在我们继续之前,Kotlin 的一个功能隐藏在主Code菜单的底部。这是将 Java 文件转换为 Kotlin 文件。这正是它所说的,甚至可以找到并解决大多数转换问题,使其成为一个很好的节省时间的工具,也是学习两种语言之间差异的一种有趣方式。

使用Ctrl + Alt + Shift + K可以自动将 Java 转换为 Kotlin。

尽管 Kotlin 可能是 Android Studio 中最新的添加之一,但它并不是我们选择替代语言的唯一选择,C++提供的速度和低级内存访问已经成为许多开发人员的首选。在接下来的部分中,我们将看到这种强大的语言如何被 IDE 轻松支持。

C/C++支持

到目前为止,我们已经看到所有编程语言都有其利弊。C 和 C++可能需要更多的纪律来掌握,但这往往会被语言提供的低级控制所弥补。

在使用 Android Studio 时,需要一组略有不同的开发工具。这包括本地开发工具包NDK)和Java 本机接口JNI),以及其他调试和构建方式。与 Android Studio 中的大多数流程一样,设置这些工具非常简单。

NDK

如前一节所述,本地编程需要与我们到目前为止使用的略有不同的一套工具。正如人们所期望的那样,我们需要的一切都可以在 SDK Manager 中找到。

很可能您需要安装以下截图中突出显示的组件;您至少需要 NDK、CMake 和 LLDB:

本地开发组件

  • CMake:这是一个多平台测试和构建工具,与 Gradle 一起工作。有关全面的文档,请访问cmake.org

  • LLDB:这是一个功能强大的开源调试工具,专门设计用于处理多线程应用程序。其详细用法超出了本书的范围,但感兴趣的用户可以访问lldb.llvm.org

安装了正确的软件后,本地编码可以非常顺利地整合到 Android Studio 项目中,与我们的 Java/Kotlin 类和文件一起。与 Kotlin 支持一样,只需在设置过程中勾选适当的框即可。

选择此选项后,您将有机会配置 C++支持,如下所示:

C++支持自定义对话框

如果您使用 CMake,选择标准作为默认的工具链是最佳选择,大多数情况下,异常支持和运行时类型信息支持也值得检查。它们的包含可以通过检查模块级build.gradle文件来最清楚地看到:

DefaultConfig { . . . externalNativeBuild { cmake { cppFlags "-frtti -fexceptions" } } }

通常情况下,要在不用弄脏手的情况下深入了解内部情况的最佳方法之一就是使用现成的 Android 示例。这些示例并不多,但都很好,而且在 GitHub 上有一个不断增长的社区项目,网址是github.com/googlesamples/android-ndk

所有这些示例都显示了添加的代码结构以及位置;与我们之前遇到的项目结构一样,实际的文件结构不会反映在项目文件资源管理器中,该资源管理器根据文件类型而不是位置来组织文件。

明显的添加是main/cpp目录,其中包含源代码,以及 CMake 使用的外部构建文件。

本地代码结构

一旦你选择了一个套件,系统镜像可以在developer.android.com/things/preview/download.html和你的 Things 开发者控制台上找到partner.android.com/things/console/

Android Things

Android Things

实际上,绝对任何设备都可以构成一个 Thing。一个设备甚至不需要有屏幕或任何按钮,只要有 IP 地址并且能够与其他设备通信。甚至可以获得一个带有 IP 地址的牙刷,尽管我对此的好处感到遗憾。

树莓派 3

可以通过一点专业知识和焊接铁来创建自己的开发板,但是像英特尔 Edison 和树莓派这样的低价位开发板,以及来自 Android 的免费系统镜像,使这个过程变得耗时。如果你有一个想法,并且想要快速测试并将其开发成一个成品项目,最好的方法是使用一个经过批准的开发套件,比如树莓派 3,如下图所示:

开发套件

一如既往,Android Studio 旨在尽可能地帮助我们,当然,有一个支持库、系统镜像和一个不断增长的工作样本集合来帮助我们。不幸的是,没有简单地模拟 Android Things 的方法,尽管一些功能可以在一些移动 AVD 上模拟,但仍需要一定形式的物理开发套件。

C++并不是每个人的菜,深入讨论超出了本书的范围。从 Android Studio 的角度来看,想要更充分利用 NDK 的人会发现,CMake 与 Gradle 的无缝集成使得调用本地库进行测试和构建应用程序变得非常方便。

对于用户、制造商和开发人员来说,Android 操作系统的美妙之一是它可以运行在各种设备上。起初,这种现象出现在我们的手表、电视机和汽车上。最近,物联网的发展导致需要在许多电子设备上使用复杂的操作系统。这促使谷歌开发了 Android Things。

物联网已经通过引入智能家用电器(如水壶和洗衣机)对消费者产生了影响。除此之外,许多市政当局使用这项技术来管理交通和公用事业的使用。

Android Things 和其他形式的 Android 开发之间最显著的区别可能是硬件。人们很容易认为需要嵌入式电路方面的专业知识,尽管在这个领域有一点知识是有用的,但绝对不是必需的,因为谷歌与英特尔、NXP 和树莓派等 SoC 制造商合作,生产开发套件,让我们能够快速制作和测试原型。

有关可用于 Things 的单板计算机的信息可以在developer.android.com/things/hardware/developer-kits.html找到。每个板还有外围套件可用,并且可以在同一页上找到。

从开发者的角度来看,物联网非常令人兴奋,SDK 中的 API 的加入打开了几乎无限的新世界。当然,这些 API 已经整合到了 Android Studio 中,使得 Android Thing 的开发像其他 Android 应用程序开发一样简单和有趣。

一旦你有了套件和外围设备,你就可以开始开发你的第一个 Things 应用程序,其基础知识在下一节中概述。

创建 Things 项目

Android Things 使用的 API 不包含在标准 SDK 中,因此需要支持库。至少,你需要以下依赖项:

dependencies {
 ...
 provided 'com.google.android.things:androidthings:0.5-devpreview'
}

除了在清单中加入以下条目:

<application ...>
 <uses-library android:name="com.google.android.things"/>
 ...
</application>

大多数 Things 项目将需要更多的东西,这取决于使用了哪些外围设备以及项目是否将使用 Firebase 进行测试。查看提供的示例是了解需要哪些依赖项的好方法;以下片段取自 Things 门铃示例:

dependencies { provided 'com.google.android.things:androidthings:0.4-devpreview' 
  compile 'com.google.firebase:firebase-core:9.4.0'
  compile 'com.google.firebase:firebase-database:9.4.0'
  compile 'com.google.android.things.contrib:driver-button:0.3'
  compile 'com.google.apis: google-api-services-vision:v1-rev22-1.22.0'
  compile 'com.google.api-client: google-api-client-android:1.22.0' exclude module: 'httpclient'
  compile 'com.google.http-client: google-http-client-gson:1.22.0' exclude module: 'httpclient' }

在设置 Android Things 项目时,下一个主要的区别可以在清单文件中看到,添加以下代码中突出显示的 <intent-filter> 将使项目在测试和调试时成功运行:

<manifest  package="com.example.androidthings.doorbell">

  <uses-permission android:name="android.permission.CAMERA" /> 
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="com.google.android.things.permission .MANAGE_INPUT_DRIVERS" />

  <application android:allowBackup="true" android:icon="@android:drawable/sym_def_app_icon" android:label="@string/app_name">
    <uses-library android:name="com.google.android.things" />
    <activity android:name=".DoorbellActivity">

    <intent-filter>
      <action android: name="android.intent.action.MAIN" />
      <category android: name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <intent-filter> 
 <action android: name="android.intent.action.MAIN" />
 <category android: name="android.intent.category.IOT_LAUNCHER" />
 <category android: name="android.intent.category.DEFAULT" />
 </intent-filter>

    </activity>
  </application>
</manifest>

这些实际上是设置 Android Things 项目时唯一的区别。其他的区别将更多地基于使用哪些外围设备和传感器。像往常一样,更深入地探索 Things 的最佳方式之一是通过提供的示例。虽然可用的示例不多,但数量正在增加,并且它们都是为了帮助我们学习而编写的。

为 Android Things 开发可能对许多开发者来说似乎令人生畏,但 Android Studio 通过其系统镜像、支持库和代码示例的方式使得任何有好点子的开发者都可以廉价快速地开发、测试和生产这样的产品。

总结

在本章中,我们探讨了一些 Android Studio 可以帮助开发者的更加奇特的方式。谷歌在数字世界中的巨大影响力提供了替代技术,比如 Kotlin 语言,并鼓励制造商开发吸引 Android 开发者的技术,使尖端技术可以被具备技能和想法的任何人使用。

Android Studio 不是唯一提供使用不同语言或不同形态因素编码的机会的软件,但 Android Studio 确实使开发者学习新技能变得更简单更容易。

在下一章中,我们将看一下最后的开发阶段之一;测试。这将给我们一个很好的机会来探索 Android Studio 最创新和有用的工具之一:设备监视器和分析器。

第八章:测试和分析

如果有一个理由选择 Android Studio 而不是其他 IDE,那很容易可以说是因为它强大的调试和测试工具。这些工具从简单的 Logcat 报告到基于 JUnit 框架的复杂测试机制。除了帮助我们识别代码中的错误的工具之外,Android Studio 还拥有一系列非常智能的性能监控工具,允许开发人员对项目进行微调并最大化其效率。

本章将依次探讨这些过程,从简单的内联调试调用开始,然后转向不同类型的 JUnit 测试,最后看一下如何在各种条件下监视我们应用程序的性能。

在本章中,您将学习如何:

  • 配置 Logcat 调试过滤器

  • 创建本地单元测试

  • 构建插桩测试

  • 记录 Espresso 测试

  • 测试 UI

  • 执行远程测试

  • 压力测试应用程序

  • 启用高级分析

  • 记录方法跟踪

  • 记录内存分配

  • 检查 Java 堆转储

  • 检查网络流量

Logcat 过滤器

最简单但也最有用的调试技术之一是简单地包含 Logcat 过滤器。这可以用于报告变量值或简单跟踪调用了哪些方法。当跟踪不明显的进程时特别有用,例如对 UI 没有明显影响的服务、广播接收器和回调。

也许最简单的调试工具之一,当我们匆忙时只想检查单个值或事件时非常有用,就是包含一行类似于:

System.out.println("Something happened here"); 

这只是一个临时解决方案,因为输出将被埋在其他 Logcat 文本中。更容易管理的方法是配置 Logcat 过滤器。以下简短的练习演示了如何做到这一点:

  1. 开始一个新项目,或打开一个新项目。

  2. 选择一个活动或片段,并包括以下字段:

private static final String DEBUG_TAG = "tag"; 
  1. 选择您想要检查的方法,并添加一行类似于这里的行:
Log.d(DEBUG_TAG, "Some method called"); 
  1. 使用Alt + 6打开 Logcat 工具。

  2. 从右上角的下拉菜单中选择编辑过滤器配置,并完成结果对话框,如下所示:

过滤器配置

  1. 运行应用程序。

  2. 现在可以使用 Logcat 工具以同样的方式跟踪任何值、活动或事件。

这是迄今为止最不复杂的方式来在代码运行时进行询问,但它有其用途:它可以随时快速应用。这种方法适用于对抗单个错误;一旦我们有了可工作的代码,我们将需要在一些明确定义的条件下进行测试。这就是 Android Studio 基于 JUnit 的测试系统发挥作用的地方。

JUnit 测试

没有开发项目可以完整,直到它经过彻底和严格的测试,而 Android Studio 直接将 JUnit 测试整合到工作区中。正如其名称所示,该框架允许测试单个代码单元。这些通常是单独的模块,但也可能是单个类或方法。

Android Studio JUnit 测试框架提供了两种不同类型的测试。它们如下:

  • 本地单元测试用于在不依赖于 Android 组件或其他代码的隔离环境中测试业务逻辑,尽管可能会模拟一些依赖关系。这些测试在本地 Java 虚拟机上运行,因此比在硬件设备或模拟器上进行测试要快得多。

  • 插桩测试用于测试 Android 框架本身的元素,例如我们的 UI 的行为。这些测试生成一个 APK 文件,因此构建速度较慢。

在大多数开发周期中,我们需要同时使用这两种技术,接下来我们将依次看看每种。

对于几乎所有的项目,我们可以预计将花费大约两倍的时间来测试代码的稳定性,而不是测试功能,我们将在下一节中看到这两者。

本地单元测试

如果您使用项目向导创建了一个 Android Studio 项目,那么两种测试类型的基本测试用例将会自动创建。向导还将包括必要的 Gradle 依赖项。如果您是以其他方式创建的项目,您将需要手动创建测试目录结构并包含 Gradle 依赖项。这些步骤如下所述:

  1. 在您的module/src目录中,创建一个新文件夹,与src/main旁边叫做src/test

  2. 在这个test目录中,重新创建main目录内的文件夹结构,例如:

main/java/com/packt/chapterseven 
  1. 这个目录是您将放置测试类的地方,现在可以从 IDE 的项目资源管理器中访问。

  2. 最后,如果尚未包含,将以下依赖项添加到您的build.gradle文件中:

testImplementation 'junit:junit:4.12' 

如果您使用向导创建项目,那么它将包括一个示例测试类ExampleUnitTest.Java。这个类包含一个用于测试算术的方法:

public class ExampleUnitTest { 

    @Test 
    public void addition_isCorrect() throws Exception { 
        assertEquals(4, 2 + 2); 
    } 
}  

这是一个非常简单的例子,但它仍然是一个很好的方式来初步了解单元测试在这种环境中是如何工作的。最好的方法是使用项目设置向导创建一个项目,或者打开一个以这种方式创建的项目,以便它包含测试类。

尽管它们实际上位于磁盘上,但测试模块可以在 IDE 的项目资源管理器中与常规的 Java 模块一起找到。

从 IDE 访问测试

看到这些测试的最简单方法并探索其他测试功能,就是修改addition_isCorrect()方法,使其失败。assertEquals()方法只是比较两个表达式,并可以设置为失败,如下所示:

public class ExampleUnitTest { 
    int valueA; 
    int valueB; 
    int valueC; 

    @Test 
    public void addition_isCorrect() throws Exception { 
        valueA = 2; 
        valueB = 2; 
        valueC = 5; 

        assertEquals("failure - A <> B + C", valueA, valueB + ValueC); 
    } 
} 

这将产生如下所示的可预测的输出:

单元测试输出

上面显示的运行工具具有许多有用的功能,可以在工具栏中找到。特别是左侧第三个图标允许我们在进行任何更改时自动重新运行测试。主工具栏允许我们过滤和排序通过和忽略的测试,以及导入和导出结果,可以保存为 HTML、XML 或自定义格式。

单击“单击以查看差异”链接将打开一个非常有用的失败比较表格,当多个测试失败时非常有用。

测试可以像其他代码一样运行,通常只需在主工具栏中点击运行图标,但运行菜单和代码编辑器左侧的运行测试图标包括调试选项和显示类覆盖窗口的选项。这些编辑器图标特别有用,因为它们可以用来运行单独的方法。

提供的示例使用了 JUnit 的assertEquals()断言。我们有许多类似的 JUnit 断言和其他可用的结构,完整的文档可以在junit.org上找到。

上面的例子是自包含的,并没有告诉我们如何使用这些类来测试我们的应用程序代码。下面的例子演示了如何做到这一点:

  1. 在默认包中创建一个 Java 类,其中包含一个函数,就像这里的一个函数:
public class PriceList { 

    public int CalculateTotal(int item1, int item2) { 

        int total; 
        total = (item1 + item2); 

        return total; 
    } 
} 
  1. 按照以下方式在test包中创建一个新类:
public class PriceListTest { 

    @Test 
    public void testCalculateTotal(){ 

        PriceList priceList = new PriceList(); 
        int result = priceList.CalculateTotal(199, 250); 

        assertEquals(449,result); 
    } 
} 

与第一个例子不同,上面的代码演示了如何在测试代码中整合业务逻辑。

一旦我们有了几个测试,有时有必要控制这些测试运行的顺序,特别是如果我们希望在每次测试运行开始时运行准备代码。这可以通过一系列 JUnit 注释来实现,如下所示:

@BeforeClass 
@Test(timeout=50) 
public void testSomeMethod() { 
... 

前面的配置注解将导致该方法仅运行一次,在调用类中的所有其他方法之前运行,并且在 50 毫秒后失败。@Before可以用来导致一个方法在每次其他测试之前执行,还有相应的@After@AfterClass注解。

org.junit包中还有许多其他断言和其他类,完整的文档可以在以下链接找到:

junit.sourceforge.net/javadoc/org/junit/package-summary.html#package_description

通常,您会希望一起运行相同的一组测试类。与其每次分别运行它们,不如重新创建一组测试并作为一个运行,类似以下代码:

@RunWith(Suite.class)

@SuiteClasses({

        someClassTest.class,

          someOtherClassTest.class })

并不总是可能或者希望完全隔离地测试每个单元。通常,我们需要测试一个单元与 Android 和其他 Java 接口和类的交互。这通常是通过创建模拟依赖来实现的。

正如读者所知,有许多种方法可以创建模拟对象和类,从从头开始构建它们的繁琐任务到使用现成的第三方框架。在大多数情况下,这第二个选项更可取,也许唯一的例外是一些完全重新定义 UI 的全屏游戏。否则,对于 Android Studio 用户来说,最简单,也可能是最好的选择是 Mockito。

Mockito 是一个强大的 Java 框架,虽然它很容易集成到 Android Studio 中,但它并不特定于它,许多读者可能已经从其他 IDE 中熟悉它。关于这个主题可以涵盖很多内容,但这超出了本书的范围。当然,Mockito 需要在我们的build.gradle文件中声明为依赖项,方法如下:

testImplementation 'org.mockito:mockito-core:2.8.9' 

幸运的是,不需要创建模拟依赖来能够调用 Android API。如果android.jar方法的默认返回值足够,那么我们可以通过将以下片段添加到build.gradle文件的 Android 部分来指示 Gradle 执行此操作:

testOptions { 
  unitTests.returnDefaultValues = true 
} 

Mockito 提供了结构来模拟我们可能需要测试业务逻辑的大多数 Java 类,但归根结底,我们正在开发一个 Android 应用程序,并且需要在真实设备和模拟器上进行测试。一旦我们确信我们的模型在隔离状态下运行良好,我们需要看看它在现实世界中的表现如何。

测试 UI

尽管在这里分开考虑,仪器化测试也可以是单元测试。有许多非 UI Android 类需要我们进行测试,尽管这些可以被模拟,但这可能是一个耗时的过程,特别是当我们知道这些类已经完全实现在我们的设备和模拟器上时。如果我们愿意牺牲模拟测试的快速构建时间,那么我们可能会插入我们的设备并启动我们的模拟器。

开发中难以模拟的一个方面是 UI 模拟和交互,一般来说,当我们想要测试我们的布局与物理手势时。幸运的是,我们有一些非常方便的工具和功能可供使用,帮助测试和优化我们的设计。

测试视图

在仪器化 UI 测试的核心是 Android 测试支持库。这包括 JUnit API,UI Automator 和 Espresso 测试框架。在 Android Studio 上设置 Espresso 几乎没有任何难度,因为如果您是通过项目设置向导生成的项目,它会默认作为依赖项包含在内。如果不是,您需要将以下内容添加到您的build.gradle文件中:

androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 

    exclude group: 'com.android.support', 
           module: 'support-annotations' 

}) 

如果您的测试设备上设置了开发人员动画选项,例如窗口和转换动画比例,您需要在测试期间禁用它们,以使 Espresso 能够顺利工作。

简而言之,Espresso 允许我们执行三项基本任务:

  1. 识别和访问视图和其他 UI 元素。

  2. 执行活动,如点击和滑动。

  3. 验证断言以测试代码。

最好的方法是通过一个简单的例子来看看它是如何工作的。与单元测试类似,插装测试需要放置在正确的磁盘位置才能被 Android Studio 识别,如下所示:

\SomeApp\app\src\androidTest

插装测试位置

以下步骤演示了我们如何执行刚才提到的三项任务:

  1. 在活动中创建两个视图,如下所示的代码;这里使用主活动:
<EditText 
    android:id="@+id/editText" 
    . . . 
    /> 

<Button 
    android:id="@+id/button" 
    . . . 
    /> 
  1. androidTest目录中创建一个测试类,如下所示:
@RunWith(AndroidJUnit4.class) 
@LargeTest 
public class InstrumentedTest { 

    private String string; 

    @Rule 
    public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>( 
            MainActivity.class); 

    @Before 
    public void init() { 
        string = "Some text"; 
    } 

    @Test 
    public void testUi() { 

        onView(withId(R.id.editText)) 
                .perform(typeText(string), 
                        closeSoftKeyboard()); 

        onView(withId(R.id.button)) 
                .perform(click()); 

        onView(withId(R.id.editText)) 
                .check(matches(withText("Some text"))); 
    } 
} 
  1. 请注意,IDE 将 Espresso 术语标识为斜体:

斜体的 Espresso 术语

  1. 运行测试,可以从编辑器的左边栏或运行菜单中运行。

  2. 应用程序将在测试设备上打开,string将被输入到编辑框中,按钮将被点击,活动将被完成并关闭。

  3. 然后可以在 IDE 中查看测试结果。

在前面的代码中有一两个需要指出的地方,特别是对于新手来说。ActivityTestRule用于访问我们活动中的小部件,调用closeSoftKeyboard();后者并不是严格必要的,但是,如果您运行测试,您会看到它确实像人们想象的那样关闭软键盘。

在运行插装测试时,平台会使用一个测试清单,如果您从模板创建了项目或者正在处理一个示例,它将已经包含在内。这将位于磁盘上的以下目录中:\SomeApplication\app\build\intermediates\manifest\androidTest\debug

这些测试中使用的几乎所有库都需要导入,尽管代码编辑器擅长发现缺少的导入,但也了解需要哪些库是很好的。以下是前面测试所需的库列表:

android.support.test.filters.LargeTest; 
android.support.test.rule.ActivityTestRule; 
android.support.test.runner.AndroidJUnit4; 

org.junit.Before; 
org.junit.Rule; 
org.junit.Test; 
org.junit.runner.RunWith; 

android.support.test.espresso.Espresso.onView; 
android.support.test.espresso.action.ViewActions.click; 
android.support.test.espresso 
        .action.ViewActions.closeSoftKeyboard; 
android.support.test.espresso.action.ViewActions.typeText; 
android.support.test.espresso.assertion.ViewAssertions.matches; 
android.support.test.espresso.matcher.ViewMatchers.withId; 
android.support.test.espresso.matcher.ViewMatchers.withText;

可以通过在build.gradle文件中包含以下依赖项来在 JUnit 测试中包含 Hamcrest 断言匹配器:

Implementation 'org.hamcrest:hamcrest-library:1.3'

Espresso 提供了许多其他操作,例如滚动和清除文本,以及输入和点击。Espresso 的详细文档可以在以下链接找到:

google.github.io/android-testing-support-library/docs/

测试列表和数据

前面的示例使用onView()来识别我们想要使用其 ID 进行测试的视图,对于我们已经命名的组件来说这是可以的;然而,列表中的项目不能如此明确地识别,因此我们需要另一种方法。在处理列表时,例如可回收视图和下拉列表框时,Espresso 提供了onData()方法来识别列表项。

要查看此操作,请在应用程序活动中添加一个下拉列表框,如下所示:

public class SomeActivity extends AppCompatActivity { 

    ArrayList<String> levelList = new ArrayList<String>(); 
    TextView textView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 

        . . . 

        Spinner spinner = (Spinner) findViewById(R.id.spinner); 

        levelList.add("Easy"); 
        levelList.add("Medium"); 
        levelList.add("Hard"); 
        levelList.add("Impossible"); 

        ArrayAdapter<String> adapter = new ArrayAdapter 
                <String> 
                (MainActivity.this, 
                        android.R.layout.simple_spinner_item, 
                        levelList); 
        spinner.setAdapter(adapter); 

        spinner.setOnItemSelectedListener 
                (new AdapterView.OnItemSelectedListener() { 

            @Override 
            public void onItemSelected(AdapterView<?> 
                    parent, View view, int position, long id) { 

                Snackbar.make(view, "You selected the" 
                        + levelList.get(position) 
                        + " level ", Snackbar.LENGTH_LONG) 
                        .setAction("Action", null) 
                        .show(); 
            } 

            @Override 
            public void onNothingSelected(AdapterView<?> parent) { 

                Snackbar.make(view, "Nothing selected" 
                        ,Snackbar.LENGTH_LONG) 
                        .setAction("Action", null).show(); 

            } 
        }); 
    } 

现在我们可以使用onData()编写一个测试来询问小部件:

@RunWith(AndroidJUnit4.class) 
@LargeTest 
public class InstrumentedTest { 

    private String string; 

    @Rule 
    public ActivityTestRule<MainActivity> 
            testRule = new ActivityTestRule<>(MainActivity.class); 

    @Before 
    public void init() { 

        string = "Medium"; 
    } 

    @Test 
    public void testSpinner() { 

        onView(withId(R.id.spinner)) 
                .perform(click()); 

        onData(allOf(is(instanceOf(String.class)), is(string))) 
                .perform(click()); 

        onView(withId(R.id.spinner)) 
                .check(matches(withText 
                (containsString("Medium")))); 
    } 
} 

即使您已将 Hamcrest 作为 Gradle 依赖项包含在内,但是工作室的快速修复功能不会启动,测试代码中需要包含以下导入:

import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString;

import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is;

记录测试

在前面的部分中,我们看到 Android Studio 为测试我们的代码提供了一套全面的工具,但编写这些测试是耗时的,除了最琐碎的项目之外,还需要许多单独的测试。幸运的是,Android Studio 提供了一种半自动化的方式来构建测试,使用我们自己的 UI 交互来创建、识别和执行测试的代码元素。

以下简单的练习展示了如何通过手动编写之前的测试来执行测试:

  1. 打开在上一个练习中创建的下拉菜单项目,或者创建一个新的项目。

  2. 从“运行”菜单中选择“记录 Espresso 测试”。

  3. 从下拉菜单中选择一个项目。这将在“记录您的测试”对话框中反映出来。

  4. 点击“添加断言”按钮。

  5. 点击下拉菜单,完成对话框,如下所示:

记录您的测试对话框

  1. 保存并运行测试。

如您所见,IDE 已经接受了我们的屏幕手势并将它们转换为代码:

@Test 
public void someTest() { 
    ViewInteraction appCompatSpinner = onView( 
      allOf(withId(R.id.spinner), 
        childAtPosition( 
          childAtPosition( 
            withClassName(is("android.support.design.widget.CoordinatorLayout")), 
            1), 
          0), 
        isDisplayed())); 
    appCompatSpinner.perform(click()); 

    DataInteraction appCompatTextView = onData(anything()) 
      .inAdapterView(childAtPosition( 
        withClassName(is("android.widget.PopupWindow$PopupBackgroundView")), 
           0)) 
      .atPosition(1); 
    appCompatTextView.perform(click()); 

    ViewInteraction textView = onView( 
      allOf(withId(android.R.id.text1), withText("medium"), 
        childAtPosition( 
          allOf(withId(R.id.spinner), 
            childAtPosition( 
              IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class), 
            0)), 
         0), 
      isDisplayed())); 

    textView.check(matches(withText("medium"))); 
} 

这段代码可能不够高效或用户友好,但节省的时间可能是值得的,而且归根结底,所有的测试都是临时的,一旦我们对代码满意,就会被取消。

读者可能已经注意到,当从“选择部署目标”对话框运行测试时,还有一个“云测试”选项卡。这个功能允许我们直接从 IDE 访问 Firebase 测试实验室。

远程测试

在为 Android 应用程序进行一般发布时,希望尽可能在许多不同的设备配置和平台版本上进行测试。在大量真实设备上进行测试是不切实际的,虚拟设备似乎是唯一的选择。幸运的是,Firebase 提供了一个基于云的测试实验室,允许我们在各种真实设备和模拟器上测试我们的应用程序。

Firebase 是一个功能强大、完整的基于云的应用开发套件,具有许多有用的功能,如文件托管和实时崩溃报告。在本章中,我们将专注于 Firebase 产品之一,即测试实验室。

IDE 中有 Firebase 助手,这是开始的最简单方式,可以在“工具”菜单中找到:

Firebase 助手

在将 Android Studio 连接到 Firebase 之前,请使用您的 Google 帐户登录firebase.google.com/.

点击“了解更多”链接将允许您直接从 IDE 连接到 Firebase。这将带您完成一个快速向导/教程,最终点击“连接到 Firebase”按钮。

现在,我们可以通过从“运行”|“编辑配置...”菜单中打开“运行/调试配置...”对话框来配置我们的基于云的测试:

测试配置

这些测试现在可以像任何其他项目一样启动,使用运行图标或菜单项,您将从测试输出中看到一个链接,可以查看结果的 HTML 版本,如下所示:

Firebase 输出

值得注意的是,尽管许多人更喜欢它,但 Firebase 并不是测试 Android 应用程序的唯一云设备,感兴趣的读者应该查找亚马逊网络服务AWS)设备农场、Xamarin 测试云、Sauce Labs、Perfecto 等其他设备。

前面概述的方法演示了我们可以应用于我们的代码的各种测试技术,以及 Android Studio 可以加快和自动化这一重要但常常令人沮丧的开发方面。在转向更有趣的主题之前,还有一种测试需要一点解释,尽管不严格属于 IDE 的一部分,但应用程序 Exerciser Monkey 仍然是一个非常有用的小工具。

压力测试

Android 应用程序 Exerciser Monkey 是一个方便的命令行应用程序压力测试工具。它通过执行(或注入)一系列随机输入操作,如点击、输入和滑动来工作。这就像把你的应用交给一个孩子,看看他们能不能把它弄坏。所有开发人员都明白,用户可以,也会尝试用他们的应用做出绝对荒谬和不可预测的事情,而除了坐在那里尝试复制每种可能的手势组合之外,Exerciser Monkey 是我们能够预测不可预测的最接近的方法。

Monkey 非常简单易行:只需在sdk/platform-tools目录中打开命令提示符,然后输入以下命令:

adb shell Monkey -p com.your.package -v 5000

在这里 5000 是您想要执行的随机操作的数量,输出将类似于以下片段:

. . . 
:Sending Touch (ACTION_DOWN): 0:(72.0,1072.0) 
:Sending Touch (ACTION_UP): 0:(70.79976,1060.0197) 
:Sending Touch (ACTION_DOWN): 0:(270.0,1237.0) 
:Sending Touch (ACTION_UP): 0:(284.45987,1237.01) 
:Sending Touch (ACTION_DOWN): 0:(294.0,681.0) 
:Sending Touch (ACTION_UP): 0:(301.62982,588.92365) 
:Sending Trackball (ACTION_MOVE): 0:(-3.0,-1.0) 
. . . 

可以在developer.android.com/studio/test/monkey.html找到包含所有 Monkey 命令行选项的表格。

测试我们的业务逻辑,它如何与系统的其余部分结合,以及在各种条件下在各种设备上的行为,是任何开发生命周期的重要部分。然而,一旦我们确信我们的代码表现如我们所愿,我们就可以继续审查它执行这些任务的效率。我们需要问问我们的工作有多有效,它是否包含内存或资源瓶颈,或者是否不必要地耗尽电池。为了做到这一点,我们需要求助于 Android Profiler。

性能监控

我们可能已经消除了代码中的所有问题,但仍然有很多微调要做,而 Android Studio 最具创新性的功能之一——Android Profiler,正是让我们能够做到这一点。

Android Profiler 不适用于使用 C++开发的模块。

Android Profiler 是在 Android Studio 3.0 中引入的,取代了以前的 Android Monitor。在最基本的级别上,它监视实时 CPU、内存和网络使用情况。这使我们能够在不同条件和配置下测试我们的应用,并改善其性能。它可以从 View | Tool Windows 菜单或工具窗口栏中访问。

性能监控

这种基本监控与以前版本的 Android Monitor 没有什么不同。这是因为诸如方法跟踪和内存分配检查之类的功能对构建时间有负面影响。可以通过 Run/Debug Configurations 对话框轻松启用高级分析,该对话框可以通过 Run | Edit Configurations...菜单找到。

高级性能监控

分析器现在显示特定事件信息,以及我们将在下面探讨的其他一系列功能。

CPU 分析

Android Profiler 提供的检查比其前身 Android Monitor 更深入,允许详细检查线程活动、UI 事件和单个方法的性能。CPU 分析器还允许我们记录方法跟踪,以及一些复杂的检查工具,帮助我们使程序更有效率。

通过单击 CPU 时间轴中的任何位置,可以看到 CPU 高级分析功能。然后,它将在显示的底部显示线程活动时间轴。

像这样实时观察我们应用的行为可以非常有启发性,但是,为了最好地看到发生了什么,我们需要记录一段活动时间。这样,我们就可以检查单个线程。

以下简短的练习演示了如何记录这样的方法跟踪:

  1. 单击 CPU 时间线中的任何位置以打开高级 CPU 分析器。

  2. 决定要记录哪些操作。

  3. 此窗格顶部有两个新的下拉菜单。选择插装而不是采样,并保持其他设置不变。

  4. 如果您计划进行长时间的记录,请缩小视图。

  5. 单击记录图标并执行您计划的操作。

  6. 再次单击相同的图标以停止。

记录的 CPU 方法跟踪

记录样本两侧的耳朵可以拖动以调整记录的长度。

正如练习所示,有两种记录方式,即插装和采样;它们的区别如下:

  • 插装记录会精确记录方法调用的时间。

  • 采样记录会定期对内存使用情况进行采样。

正如您将看到的,有四个选项卡可以表示这些数据,位于工具窗口底部。调用图和火焰图以图形方式显示方法层次结构,而自上而下和自下而上则将此信息显示为列表。

单击这些图表中的任何方法将打开该方法的源代码。

能够详细检查程序流程非常有帮助,可以节省大量不必要的调试,但我们需要考虑的不仅仅是处理器时间;我们还需要密切关注我们的应用程序消耗了多少内存。

内存分析器

充分了解我们的应用对设备 CPU 的影响只是一个考虑因素。作为开发人员,我们必须在不知道目标设备的内存能力的情况下创建应用,并且,此外,我们无法知道这些设备在我们的应用运行时正在使用内存的其他用途。

为了帮助我们规划内存使用并避免泄漏,Android Studio 配备了强大的内存分析器。这使我们能够查看 Java 堆并记录内存分配。只要启用了高级分析功能,高级内存分析器就可以通过单击实时时间线上的任何位置以与处理器分析器相同的方式打开。

高级内存分析器

如前图所示,分析器还显示自动垃圾收集。这样的清理也可以通过分析器工具栏中的垃圾桶图标手动执行。这还包括用于记录内存分配和捕获 Java 堆转储(下载图标)的按钮。

获取内存转储就像单击图标并等待一会儿收集数据一样简单。堆转储显示了在转储堆时正在使用的对象,并且是识别内存泄漏的好方法。探索堆转储的最佳时间是在进行了长时间 UI 测试后,查找应该已经被丢弃但仍占用内存的对象。

Java 堆转储

单击转储列表中的类将在编辑器中打开相应的源代码。

这样的内存转储非常有用,可以观察我们的对象消耗了多少内存,但它们并没有告诉我们他们如何使用这些内存。要查看这一点,我们需要记录内存分配。这是通过与 CPU 记录相同的方式完成的,即通过单击记录图标。这个方便的内存检查工具需要更多解释,引导我们到第三个和最后一个分析工具,网络分析。

网络分析器

这个分析器和前两个操作的方式没有太大的区别。与记录网络活动不同,只需单击并拖动到您感兴趣的时间线区域。然后在下面的窗格中列出涉及的文件,并在选择它们时提供详细信息:

高级网络分析器

高级网络分析器提供了一种很好的方式来识别低效的网络使用。在需要网络控制器经常打开和关闭无线电以下载小文件的情况下,最好避免这种情况,而是优先下载多个小文件。

网络分析器以及其他两个分析器都是节省时间的工具的绝佳示例,这使得 Android Studio 成为开发移动应用程序的不错选择。对应用程序进行彻底测试和微调往往可以决定一个平庸的应用程序和一个成功的应用程序之间的差异。

总结

在本章中,我们看了一下测试和分析我们的应用程序的过程。我们不仅了解了如何利用 JUnit 集成来测试自己的业务逻辑的完整性,还了解了如何整合诸如 Mockito 和 Espresso 之类的工具来测试平台本身,以及诸如 Firebase 之类的资源来在更广泛的设备范围内进行测试。

除了测试我们的代码和用户界面之外,我们还需要一种测试我们的应用程序,硬件性能以及 CPU,内存或网络使用是否存在问题的方法。这就是 Android Studio 内置的分析器派上用场的地方,它允许我们详细检查和记录我们应用程序的性能。

我们的应用程序现在运行顺畅,并经过性能调整,我们可以看看开发的最后阶段,构建,打包和部署。Android Studio 允许我们使用 Gradle 构建系统简单地创建签名 APK,包括不同风味的 APK,并简化签名和安全性。

第九章:打包和分发

在应用程序开发过程中,编译和构建 APK 文件是我们做的很多次的事情,除了包含各种依赖项之外,我们基本上认为我们的构建自动化系统 Gradle 是理所当然的。尽管如此,读者不会忽视 Gradle 实际上所做的事情是非常复杂和复杂的。

我们可以认为 Gradle 是理所当然的原因之一是它使用一种称为约定优于配置的过程来配置每个构建。这确保在几乎所有情况下,Gradle 会为每个项目选择最明智的配置选项。只有当我们覆盖这些设置时,Gradle 才变得有趣和有用。例如,我们可以使用它从同一个 Studio 项目构建应用程序的移动和平板版本。

生成编译后的 APK 文件绝不是我们旅程的最后一步,因为我们仍然可以进行大量的测试和分析。Android Studio 的 APK 分析器极大地帮助了这些过程。

一旦我们的测试完成并且对我们的产品满意,我们将通过生成签名的 APK 文件进入旅程的最后阶段,准备发布。这一步并不是一个复杂的过程,Android Studio 会在每一步中帮助开发人员。

在本章中,您将了解以下内容:

  • 了解构建过程

  • 创建产品风味

  • 从 Eclipse 导入 Gradle 构建

  • 分析 APK 文件

  • 清理项目

  • 生成签名的 APK

  • 注册 Google Play 应用签名

  • 配置自动签名

Gradle 构建配置

正如读者所见,Gradle 脚本通常有一个项目(或根)文件和一个或多个模块级别文件:

Gradle 脚本

我们被告知不要在根脚本的注释中编辑此文件;除非我们有所有模块通用的配置选项,否则最好保持不变。

模块级别的脚本对我们来说更有趣,以下是典型脚本的分解。

第一行只是声明了使用 Gradle 插件:

apply plugin: 'com.android.application'

接下来,声明了面向 Android 的 API 级别和构建工具版本:

android { 
    compileSdkVersion 27 
    buildToolsVersion "27.0.0" 

默认配置设置定义了 Android 清单文件的元素,并在此处编辑它们将在下一次构建或同步后自动反映在清单中,如下所示:

defaultConfig { 
     applicationId "com.example.someapp" 
     minSdkVersion 21 
     targetSdkVersion 27 
     versionCode 1 
     versionName "1.0" 
     testInstrumentationRunner 
         "android.support.test.runner.AndroidJUnitRunner" 
} 

构建类型部分配置了ProGuard,这是一个用于最小化和混淆我们代码的工具。

Proguard 对 APK 大小的影响通常可以忽略不计,但混淆的影响不容小觑,ProGuard 可以使我们的 APK 几乎不可能被反向工程。

buildTypes部分包含了在构建应用程序的发布版本时是否以及如何在 APK 文件上运行 ProGuard 的说明:

buildTypes { 
     release { 
         minifyEnabled false 
         proguardFiles 
             getDefaultProguardFile('proguard-android.txt'), 
             'proguard-rules.pro' 
        } 
    } 
} 

可以从proguard.rules.pro文件中编辑 ProGuard 规则,该文件覆盖默认规则,可以在sdk\tools\proguard中找到。

请注意,默认情况下minifyEnabled设置为false。这是一组非常有用的函数,可以剥离我们的代码中的冗余部分,通常会导致更小的 APK 文件,通常应该设置为true

另外,添加shrinkResources true也是一个好主意,它对我们的资源文件执行类似的操作。

最后,我们将专注于依赖项部分,我们已经非常熟悉。在这里,模块的lib目录中的任何.jar文件都包括在内:

dependencies { 
    implementation fileTree(dir: 'libs', include: ['*.jar']) 
    androidTestImplementation('com.android.support 
        .test.espresso:espresso-core:2.2.2', { 
        exclude group: 'com.android.support', 
            module: 'support-annotations' 
    }) 
    implementation 'com.android.support: 
        appcompat-v7:26.0.0-beta2' 
    testImplementation 'junit: 
        junit:4.12' 
    implementation 'com.android.support.constraint: 
        constraint-layout:1.0.2' 
    implementation 'com.android.support: 
        design:26.0.0-beta2' 
} 

命令行选项

许多从其他 IDE 迁移的读者可能已经从命令行运行 Gradle 脚本,当然,在 Android Studio 中也可以这样做,尽管 Studio 将其很好地整合到工作区中,因此不需要退出 IDE 以这种方式执行命令。

有两个方便的工具窗口可以帮助我们完成这项任务,Gradle 工具窗口和 Gradle 控制台,两者都可以从“查看|工具窗口”菜单中找到。

Gradle 工具窗口

前面截图中的分解提供了 Gradle 扮演的角色的概览,但要真正掌握它,我们需要通过一个简单的示例来工作,下一节将演示如何配置 Gradle 以从单个项目中生成不同的产品口味。

产品口味

一般来说,想要创建自定义版本或口味的应用有两个原因:

  • 当我们为不同的形态版本创建版本,比如手机和平板电脑时

  • 当我们希望在 Play 商店中有两个不同版本的应用可用时,比如付费版和免费版

这两种情况都可以通过配置我们的构建文件来解决,这可以针对debugrelease APKs 都可以做。新的口味和构建类型都可以使用它们各自的对话框进行配置,这些对话框可以在 Build 菜单下找到:

构建选项

一如既往,最好亲自看看这些过程是如何运作的。在下面的示例中,我们将创建两个产品口味,代表应用的免费版和付费版。要遵循的步骤如下:

  1. 在 Android Studio 中启动一个只有一个模块的新项目。

  2. 在您的values文件夹中创建两个新目录,分别命名为paidfree

  3. 这些不会在 Android 标签下的资源管理器中可见,但可以通过切换到项目视图来找到。

通过在导航工具栏中选择values并从下拉菜单中选择paidfree,可以快速实现这一快捷方式。这将自动打开项目视图并展开以显示我们的新文件夹。

  1. 按照以下方式创建两个合适的strings.xml文件:
<resources> 
    <string name="app_name">Product Flavors Pro</string> 
    <string name="version">Pro</string> 
</resources> 

<resources> 
    <string name="app_name">Product Flavors Free</string> 
    <string name="version">Free</string> 
</resources> 
  1. 打开build.gradle文件,并像这样完成它:
apply plugin: 'com.android.application' 

android { 

    . . . 

    defaultConfig { 

        . . . 

        flavorSelection 'full', 'paid' 
        flavorSelection 'partial', 'free' 
    } 
    buildTypes { 
        release { 

            . . .  

        } 
    } 

    productFlavors { 
        flavorDimensions "partial", "full" 

        paid { 
            applicationId = "com.example.someapp.paid" 
            versionName = "1.0-paid" 
            dimension "full" 
        } 

        free { 
            applicationId = "com.example.someapp.free" 
            versionName = "1.0-free" 
            dimension "partial" 
        } 
    } 
} 

dependencies { 

    . . .  

} 

现在使用 Build Variants 工具窗口来选择随后构建的两种口味中的哪一种。

许多读者可能已经从 Eclipse IDE 迁移到 Android Studio。导入 Eclipse 项目相对简单,但导入 Gradle 构建文件则不那么简单。可以通过以下build.gradle根文件实现:

buildscript { 
    repositories { 
        mavenCentral() 
    } 
    dependencies { 
        classpath 'com.android.tools.build:gradle:3.0.0' 
    } 
} 
apply plugin: 'com.android.application' 

android { 
     lintOptions { 
          abortOnError false 
      } 

    compileSdkVersion 27 
    buildToolsVersion "27.0.0" 

        defaultConfig { 
            targetSdkVersion 27 
        } 

    sourceSets { 
        main { 
            manifest.srcFile 'AndroidManifest.xml' 
            java.srcDirs = ['src'] 
            resources.srcDirs = ['src'] 
            aidl.srcDirs = ['src'] 
            renderscript.srcDirs = ['src'] 
            res.srcDirs = ['res'] 
            assets.srcDirs = ['assets'] 
        } 

        debug.setRoot('build-types/debug') 
        release.setRoot('build-types/release') 
    } 
} 

能够创建不同版本的 APK 而无需创建单独的项目是一个很大的时间节省,Gradle 使这变得非常简单。然而,大多数情况下,我们可以让 Gradle 自行处理其工作。

诱人的想法是测试过程将随着 APK 的生成而完成。然而,Android Studio 提供了一个神奇的工具,允许我们分析已完成的 APK。

APK 分析

APK 分析器是 Android Studio 最方便的功能之一;正如其名称所示,它允许我们分析 APK 文件本身,甚至通过提取资源和 XML 执行一定程度的逆向工程,并允许我们比较不同版本。

APK 分析器也可以在 Build 菜单下的 Analyze APK...中找到。每次在设备或模拟器上运行项目时,都会生成一个调试 APK。这可以在项目目录下找到;\SomeProject\App\build\outputs\apk\debug

分析器显示其输出如下:

APK 分析

分析器的输出包含大量信息,从其大小和压缩的 Play 商店大小开始。可以一目了然地看到哪些资源占用了最多的空间,并在可能的情况下进行修正,例如使用矢量图而不是位图。

classes.dex文件允许我们探索我们的类和导入库所消耗的内存。

APK 类分析。

分析器最有用的功能之一是能够比较两个 APK 并排放在一起,可以使用窗口右上角的按钮实现这一点。

如果 APK 分析器不够用,那么主文件菜单中还有 Profile 或 Debug APK...的选项。这将打开一个新项目并解开 APK,以便完全探索甚至调试。

除了 MakeBuild 和 Analyze 之外,构建菜单还有其他有用的条目,例如,Clean Project 项目可以从构建目录中删除构建工件,如果我们想要与同事和合作者在互联网上共享。要进行更彻底的清理,可以使用本机文件浏览器在项目文件夹中打开命令提示符,或者从文件|工具窗口菜单中选择终端。

以下命令将清理您的项目:

gradlew clean 

这个操作可以节省的空间通常是非常令人印象深刻的。当然,下次构建项目时,它将花费与第一次一样长的时间。

在开发周期的绝大部分时间里,我们只关心 APK 的调试版本,但迟早我们需要制作一个适合发布的 APK。

发布应用程序

开发移动应用,即使是相对简单的应用,也是一个漫长的过程,一旦我们测试了所有的代码,消除了任何问题,并完善了我们的用户界面,我们希望能够尽快简单地将我们的产品上架。Android Studio 将所有这些过程都纳入了工作空间。

正如读者所知,向发布迈出的第一步是生成已签名的 APK。

生成已签名的 APK

所有 Android 应用程序在安装到用户设备上之前都需要数字证书。这些证书遵循通常的模式,即在每次下载时都包含一个与我们自己的私钥对应的公钥。这个过程保证了用户的真实性,并防止其他人制作其他开发者作品的更新。

在开发过程中,IDE 会自动生成一个调试证书供我们使用,仅在开发过程中使用。这些证书可以在:\SomeApp\build\outputs\apk\debug中找到。

有两种方法可以创建这些身份证书:我们可以管理自己的密钥库,或者我们可以使用 Google Play 应用签名。我们现在将看看这两种技术,首先是自我管理。

管理密钥库

无论是我们自己管理密钥库还是由 Google 代表我们管理,流程都是一样的,如下面的步骤所示:

  1. 单击构建菜单中的 Generate Signed APK...条目。

  2. 完成以下对话,使用非常强大的密码。

生成已签名的 APK 对话框

  1. 如果您正在创建新的密钥库,将会出现新密钥库对话框,必须按照以下方式完成:

新密钥库对话框

  1. 最终对话框允许您选择构建类型和 APK 目标文件夹,以及您可能创建的任何版本。确保选择 V2(完整 APK 签名)框。

最终 APK 配置

  1. 最终的 APK 将存储在...\app\release\app-release.apk中。

V2 签名版本的选择是一个重要的包含。在 API 级别 24(Android 7.0)中引入的 Signature Theme v2 提供更快的安装速度,并保护免受黑客逆向工程我们的 APK 的影响。不幸的是,它并不适用于所有版本,但是在适用时非常值得应用。

管理我们自己的密钥库通常是一直以来的做法,只要我们保持密钥的安全,这是一种完全可以接受的管理证书的方式。然而,使用 Google Play 进行应用签名提供了一些明显的优势,是值得考虑的。

Google Play 应用签名

使用 Google Play 签署我们的应用程序的主要优势是,Google 会维护关键信息,如果不幸丢失,可以检索。关于这个系统非常重要的一点是,一旦采用,就没有退出选项。这是因为这本身可能代表着可能的安全漏洞。

要启用 Google Play 应用签名,请按照前面的步骤准备您的签名 APK,然后打开 Google 开发者控制台。从“发布管理”菜单中可以找到 Google Play 应用签名。

Google Play 应用签名

然后将打开服务条款对话框。

Google Play 应用签名服务条款

要注册 Google Play 应用签名服务条款,您需要按照这里概述的步骤进行操作:

  1. 首先,使用Play 加密私钥(PEPK)工具对您的签名密钥进行加密,该工具可以从左侧导航栏中的控制台下载。

  2. 创建第二个上传密钥并将其注册到 Google。

  3. 使用此密钥对应用进行签名以发布并上传到 Google Play。

  4. 然后,Google 使用此密钥对您进行身份验证,然后使用加密密钥对应用进行签名。

有关该过程的更多信息,请单击服务条款对话框上的“了解更多”。

只要我们愿意承诺自己,注册应用签名服务提供了比传统方法更安全的系统。然而,当我们决定签署我们的应用程序时,始终可以通过配置 Gradle 等方式更好地控制该过程。

自动签名

每次我们签署应用程序或变体时,都会为我们自动创建签名配置。幸运的是,这些配置可以根据我们的具体目的进行定制。例如,我们可能希望在每次构建时自动签署应用程序。Android Studio 通过“项目结构...”使这成为可能,该选项可以在主文件菜单中找到。

以下练习演示了如何自动签署应用程序的发布版本:

  1. 打开项目结构对话框,如前所述,或者通过在项目资源管理器中选择模块的下拉菜单中的“打开模块设置”,或者选择模块并按F4

  2. 选择“签名”选项卡,单击“+”图标,并填写相应字段。

签名配置

  1. 接下来,打开“构建类型”选项卡,选择调试或发布类型,输入“签名配置”字段,如下图所示,并进行其他设置,例如启用缩小。

选择构建类型和签名配置

在发布之前还有一些其他准备工作要做。必须对发布 APK 进行更多测试,并收集各种促销资源和材料,但从 Android Studio 的角度来看,签名的发布 APK 几乎就是最终产品了。

摘要

签名 APK 的生成是一个漫长的过程的最后一步。每个应用程序从一个想法开始,经历了无数次的设计、开发和测试,最终被放置在 Android Play Store 等商店的货架上。

Android Studio 旨在在这一过程的每一步上帮助开发人员,而 Google 投入如此多的产品的原因之一是,通过投资未来的开发人员并使他们更容易将想法付诸实践,Android 平台只会变得更好。

在这本书中,我们探讨了专门为 Android 开发创建的唯一集成开发环境,并且我们看到了这种专业化的方法为开发者带来了许多好处。布局编辑器的视觉直观特性以及约束布局可以只需点击鼠标一两下就设计完成,这将让大多数开发者对仍在使用其他集成开发环境的人感到遗憾。

在 Android Studio 中,编码也变得不那么繁琐,它具有简单的代码补全和重构功能。再加上 Kotlin 作为官方开发语言的整合,选择 Android Studio 对许多移动开发者来说似乎是唯一的选择。甚至使用 Android Studio 编译和测试应用程序也可以更快更容易,当然,使用该集成开发环境为可穿戴设备和物联网等新颖形态进行开发也变得更加容易。

在整本书中,我们探讨了选择 Android Studio 3 的优势。这个集成开发环境当然还在不断发展,毫无疑问,作为谷歌认真投入的项目,它将在未来多年持续增长和改进。在许多方面,Android Studio 3 只是一个开始,希望这本书能帮助读者掌握这段旅程中的一小步。

posted @ 2024-05-22 15:13  绝不原创的飞龙  阅读(10)  评论(0编辑  收藏  举报