安卓可穿戴设备高级教程-全-

龙哥盟 / 2024-10-06 / 原文

安卓可穿戴设备高级教程(全)

原文:Pro Android Wearables

协议:CC BY-NC-SA 4.0

零、简介

欢迎阅读 Pro Android 可穿戴设备这本书,在这里你将学习如何为智能手表设备开发应用。将有一本名为 Pro Android IoT (物联网)的后续书籍,将涵盖其他 Android APIs,如 Android TV、Android Auto 和 Android Glass,因此在这本书中,我只能专注于探索智能手表设备市场。

智能手表和独立电视电视机持续爆炸的原因是一个基本的经济学问题。现在有数十家制造商生产智能手表,包括传统手表品牌,如西铁城、劳力士、卡西欧、豪雅、天美时和 Fossil,以及所有主要的消费电子巨头,包括索尼、三星、LGE、华硕、华为和摩托罗拉,它们现在有多种智能手表型号。这产生了难以置信的大规模竞争,从而压低了价格,使这一价值主张难以辩驳。我今天在谷歌上搜索了 Android Wear 手表,发现了两款最令人印象深刻的智能手表,摩托罗拉 MOTO 360 和华硕 ZenWatch,售价不到 200 美元。对于你手腕上用玫瑰金和小牛皮制成的电脑(ZenWatch)或漂亮的碳黑钢手镯(MOTO),这是一个非常合理的价位。我预计智能手表的价格将在 150 美元至 450 美元之间,并在未来继续增加销量,同时增加屏幕分辨率(480 至 640 像素)、处理器内核(2 至 4 个)和系统内存(1 至 2 GB)。

这本书将涵盖如何为爆炸式增长的智能手表市场开发应用,它包括谷歌发布的新的 Watch Faces API ,允许开发人员将他们的应用创建为手表表面设计本身!既然这就是手表的用途,我将详细讨论 Watch Faces API,以便您的智能手表应用可以向用户提供它们的功能,同时告诉他们时间、日期、天气、活动、通知等等。你将学习如何使用 Google Play 服务,并制作 Android Wear 应用,这些应用包含在你的智能手表以及智能手机或平板电脑上运行的组件,称为配套活动应用

第一章在您设置 Wear 生产工作站之前,查看 Android Wear 和可穿戴概念和设计考虑事项,包括您的 IDE、SDK 和新媒体内容开发应用第二章。我将在第三章中讨论 Android Wear 的新功能,在你了解 IntelliJ 想法之前,在第四章中为你的 Wear 项目创建一个基础。在第五章中,您将设置 IntelliJ IDEA,也称为 Android Studio,用于生产准备,确保所有 SDK 和仿真器都是最新的,并创建用于圆形或方形表盘测试的 avd。

在第六章中,你将通过查看 Android Watch Faces API 及其所有功能和 UI 设计考虑事项,为开始编码做好准备。在第七章中,你将实际编写你的手表脸服务和手表脸引擎类。这些驱动了你将在后续章节中实现的表盘基础设施。

在第八章中,您将把您的表盘计时引擎放在适当的位置,学习时间和时区类,以及实现 BroadcastReceiver 对象来管理这些类(对象)的实现。在第九章中,你将实现核心的手表界面 API 方法,控制不同的手表界面渲染风格和事件处理。

在第十章中,您将了解矢量图形以及如何使用 onDraw()方法在智能手表表面“渲染”设计,在第十一章中,您将了解光栅图形以及如何使用 BitmapDrawable 对象和 PNG32 位图素材将数字图像添加到您的智能手表设计中。在第十二章中,您将学习数字图像技术,该技术将允许您优化用于适应不同智能手表显示屏颜色限制的颜色数量,从而为您的智能手表应用设计获得最逼真的效果。

在第十三章中,您将了解谷歌移动服务(GMS)API 以及如何访问 Google Play 服务,以便您的 Wear 应用能够比使用原生 Android 和 Android Wear APIs 做更多的事情。在第十四章中,你将在你的代码中实现 Android Wear 数据 API,创建一个 Watch Face 工具类来管理你的用户设置。

在第十五章中,您将学习如何为真实世界的硬件设备建立测试环境,了解 Android 调试桥(ADB ),以及如何为您的硬件设备实现 USB 设备驱动程序。

在第十六章中,您将学习如何处理 API 弃用以及类和方法调用代码更新,因为您删除了弃用的 Time 类,并将其替换为 Calendar 和 GregorianCalendar 类代码,从而使您的应用更加高效。

最后,第十七章介绍了 Android 物联网 API 和其他 Wear API 特性,供您的智能手表应用考虑,例如分别使用语音和 GPS APIs 的语音识别和位置跟踪。借助本书中的信息,您将可以使用 Android Wear 和 Android Watch Faces APIs 开发智能手表应用!

一、Android 可穿戴设备简介:概念、类型和材质设计

欢迎阅读 Pro Android 可穿戴设备书籍!这本书将向你展示如何为那些在你普通的智能手机和平板电脑之外的 Android 设备开发 Android Studio 应用。这本书还包括可以戴在你身上的设备的 Android 开发,这就是为什么这些 Android 设备通常被称为“可穿戴设备”。

如果你正在寻找为 Android 设备开发,如 iTV 集、4K iTV、游戏机、机器人或其他设备,那么你想要的书是 Pro Android IoT (Apress,2016)书。该标题涵盖了被称为“物联网”的 Android 设备的开发,包括不戴在身上的设备,以及更普通的平板电脑和手机之外的设备。

本章将介绍不同类型的可穿戴设备,以及它们的功能和流行用法,您将了解可穿戴设备的术语、概念、差异化因素和重要技术,并仔细了解您可以为可穿戴设备开发哪些类型的 Android 应用。我将在这里介绍所有与可穿戴设备相关的知识,以便您可以在第二章中专注于设置您的工作站,然后在第三章的中了解 Android 可穿戴设备的功能。我还会解释一下可穿戴设备和 Android 可穿戴外设的区别。

我还将讨论 Android 5 中添加的新材质设计功能,因为这些功能可用于可穿戴设备的应用开发,你将看到你可以用这些功能做的所有很酷的事情!

可穿戴技术定义:什么是可穿戴?

术语可穿戴设备,以及术语可穿戴技术可穿戴设备,表示基于嵌入式计算机硬件的消费电子技术,该硬件内置于穿戴在人体外部的产品中。这包括衣物和配饰,包括珠宝(如手表)和防护服(如眼镜),以及袜子、鞋子、帽子和手套等衣物,以及可以舒适佩戴在身体某处的运动、健康和健身产品。

可穿戴设备通常具有少量的通信能力,这将允许设备穿戴者实时访问信息。数据输入功能是可穿戴设备的必要功能,因为它允许用户访问可穿戴设备的功能,并使用它来运行您将在本书的过程中学习开发的应用。数据输入通常采用触摸屏界面、语音识别(也称为语音输入)的形式,或者有时通过使用可穿戴设备本身内置的硬件按钮。

由于有了云,本地存储对于可穿戴设备来说是不必要的,尽管一些设备具有 micro SD(安全数字)卡或在链接的配套设备上存储数据。可穿戴设备能够执行许多与手机和平板电脑相同的应用任务;事实上,许多可穿戴设备要求可穿戴设备在蓝牙 4.x 技术的操作范围内与另一个 Android 设备“结合”(本章稍后将对此进行详细介绍)。

与当今市场上的手持技术相比,Android 可穿戴设备在“传感器输入”方面往往更加复杂。这是因为可穿戴设备将提供智能手机或平板设备通常没有的传感和扫描功能。这方面的例子包括生物反馈和生理功能跟踪等功能,如脉搏、心率、锻炼强度、睡眠监测等。

可穿戴硬件设备的示例包括智能手表、智能眼镜、纺织品(也称为智能织物)、帽子、便帽、鞋子、袜子、隐形眼镜、耳环、头带、助听器和珠宝,例如戒指、手镯和项链。可穿戴技术往往指的是可以毫不费力地穿上和脱下的东西。值得注意的是,可穿戴设备概念的版本在本质上更加激进,例如植入设备,如微芯片甚至智能纹身。在本书中,我不会讨论这些非主流设备类型的应用开发。因为普通大众将主要使用智能手表,所以我将把重点放在这类可穿戴设备上。最终,无论设备是佩戴在身体上还是整合到身体中,这些 Android 可穿戴设备的目的都是提供对消费电子产品的持续、方便、便携、无缝和免提访问。

可穿戴应用开发:什么类型的应用?

可穿戴技术的用途仅限于你的想象力,这些应用的影响将是深远的,这就是为什么你首先购买了这本 Pro Android 可穿戴设备的原因!

可穿戴应用将在许多方面影响广泛的行业垂直领域。许多行业都在采用 Android 可穿戴设备,其中包括医疗保健、制药、教育、健身、娱乐、音乐、老龄化、残疾、交通、金融、企业、保险、汽车和游戏行业,并且该列表每天都在增长。

这些行业中的可穿戴应用的目标应该是将功能强大的便携式电子设备和计算机无缝融入个人的日常生活。在出现在消费市场之前,可穿戴设备主要用于军事行动以及制药、医疗保健、体育和医药行业。

十多年前,医学工程师们开始讨论一种可穿戴设备,这种设备可以以智能衬衫或智能腕带的形式监控患者的健康。其中至少有一项是当今的主流技术,其目的是监测生命体征,并将生物反馈信息发送到应用或网站进行数据跟踪和分析。

你可以在可穿戴设备上创建的 Android 应用的类型仅限于你的想象。这是因为可穿戴设备与更先进的硬件“配对”,因此具有智能手机或平板电脑所具有的相同硬件功能,以及智能手机和平板电脑所不具备的一些传感器!

一个合乎逻辑的应用垂直领域是健康和福利监控;由于这些心率传感器,可以创建 Android 可穿戴设备应用来帮助用户生活中与健康相关的领域,例如他们在健身房的锻炼,在工作或旅行时跟踪他们的饮食,获得足够的睡眠,每天步行或跑步足够的英里数,以及帮助用户改善健康或至少保持健康的类似应用。

另一个合乎逻辑的垂直应用是社交媒体,因为目前的趋势是在一天的任何时候都与每个人保持联系,同时也结交新朋友或商业伙伴。机器人通过 Wi-Fi 或 4G 随时连接到互联网,因此这些类型的穿戴应用在可穿戴设备上也会非常受欢迎。

当然,游戏、手表人脸或皮肤、娱乐消费也将是可穿戴设备的海量应用垂直领域。让我们来看看这方面,这样你就可以知道如何应用你将学到的东西了!

Android 可穿戴乐趣:增强现实、教育娱乐和游戏化

虽然可穿戴技术可能会对社交媒体连接、健康、节食或健身领域产生重大影响,但可穿戴技术领域也有望对休闲游戏、音频视频(AV)娱乐、教育娱乐和增强现实(AR) 市场产生重大影响。将日常任务变成游戏乐趣的可穿戴应用,通常被称为游戏化 ,也是一个重大的市场机会。

AR 最初被称为混合现实,可以利用可穿戴技术。AR 使用 Android 平台中的 i3D OpenGL 功能,创建一个逼真的沉浸式 3D 环境,与你周围的现实世界实时同步,这要归功于你的 Android 5 应用中的 Java 7 代码。Android 4.4 和更早的版本使用 Java 6,而 Android 5 使用 Java 7。使用数字视频、数字图像或基于全球定位卫星(GPS)定位的技术,将虚拟现实或交互式 3D (i3D)与实际现实相结合,无论如何都不是什么新鲜事。AR 应用是可穿戴应用的最高级类型。

在世纪之交之前,人们就已经开始考虑通过使用可穿戴设备来交付 AR。重要的是,AR 硬件原型正在从笨重的技术(如 Oculus Rift 使用的巨大护目镜)转变为小巧、轻便、舒适、高度移动的 3D 虚拟现实系统。

下一个即将出现在可穿戴设备上的最复杂的应用将是可穿戴游戏。你可以期待很快在市场上看到为智能手表和智能眼镜开发的休闲游戏。仔细的优化是创建一个在可穿戴设备上运行良好的游戏应用的关键,我将在本书中讨论这个主题。

可穿戴设备的另一种复杂类型的娱乐应用是 AV 应用。在可穿戴设备上播放数字音频或数字视频也需要仔细的优化,以及拥有一副好的蓝牙高保真耳机的用户,幸运的是,这些耳机目前由十几家主要的电子产品制造商制造。

最后,更复杂的可穿戴应用之一是定制智能手表的面部或皮肤。这些可以把用户整天看着的表盘变成他们想要的样子。当然,你也可以创建大量基于文本的应用,比如办公工具或方便的秘籍管理器;这些在可穿戴设备上会很好用!

Android 可穿戴应用的未来需要反映时尚、功能、实用性、视觉和用户界面(UI)设计的无缝集成。我将在整本书中讨论如何做到这一点,在你把第二章第 ?? 节的开发工作站放在一起,并在第五章第 ?? 节的 ?? 中创建了模拟器之后,这样你就有了可穿戴开发的基础。

主流可穿戴设备:智能手表和智能眼镜

流行的消费电子产品制造商争相生产两种主要(即主流)类型的可穿戴设备。

智能手表是目前最受欢迎的可穿戴设备,市场上已经有数百种价格适中的型号。几个世纪过去了,手表已经成为国际时尚宣言。因此,这是最受欢迎和最有用的 Android 可穿戴设备也就不足为奇了。

另一种流行的可穿戴类型是智能眼镜,谷歌(Google)和 Vuzix (M100)等公司已经发布了数十种产品。让我们更仔细地看看这两种类型的可穿戴设备硬件,因为它们将成为运行您将在本书过程中创建的 pro Android 可穿戴设备应用的大多数设备类型。

智能手表:圆形表盘与方形有机发光二极管

消费电子产品中的智能手表可穿戴类型拥有最多的产品,有几十个品牌制造商和数百个实际产品型号,除了一个(Apple Watch)之外,所有产品都运行一种或另一种版本的 Android。为此,我将在本书中重点讨论这些。

还有三星的智能手表 Galaxy Gear 2,它使用 Tizen OS,利用 Linux,HTML5,JavaScript 和 CSS3 进行应用开发。我不会在本书中涉及这些,而是专注于三星 Gear S。

Android 5 Wear 软件开发套件(SDK)支持两种不同的智能手表表面类型,圆形和方形,因为手表通常有这两种配置。在第五章中,您将为这两种智能手表类型创建 Android 虚拟设备(avd)(软件仿真器)。

智能眼镜:眼镜和其他智能眼镜制造商

智能眼镜可穿戴消费电子产品类别是下一个增长最快的可穿戴产品类别。品牌眼镜制造商正在争先恐后地进入这个可穿戴领域,所以请尽快关注 Luxottica 的智能眼镜。因此,这将是本书的第二个重点。

智能眼镜一般会运行谷歌眼镜开发套件(GDK)或 Android 4.x,2015 年你可以期待 Android 5 智能眼镜可穿戴设备。有很多智能眼镜公司,包括谷歌、Vuzix、GlassUp、索尼、six15 和 Ion。谷歌当然已经停止生产这款眼镜,并承诺未来会推出更好的新产品。

可穿戴应用编程接口

有两个主要的应用编程接口(API),它们都运行在谷歌 Android Studio 下,可用于访问可穿戴设备的功能,这些功能在 Android 5 版本中尚未成为标准,该版本横跨智能手机和平板电脑等主流设备。智能手表 API 叫做 Wear SDK ,智能眼镜 API 叫做眼镜 GDK??。需要注意的是,一些可穿戴设备可以运行完整的 Android 操作系统(OS)。

智能手表的例子是 Neptune Pine 智能手表,它运行完整的 Android 操作系统,谷歌眼镜产品不要求你使用谷歌眼镜 GDK,除非你需要使用谷歌眼镜的特殊功能或想要开发“原生”眼镜应用。换句话说,谷歌眼镜将运行在普通智能手机和平板电脑上运行的 Android 应用。

这意味着 Neptune Pine 和谷歌眼镜可以运行您为其他 Android 设备开发的相同应用。Neptune Pine 产品线的新版本将利用 Wear SDK,这在很大程度上是我将在本书范围内讨论的内容。

Android Studio 1.0:Android Wear SDK

Android Wear SDK 是谷歌创建的用于 Android 可穿戴设备(运行在 Android 上的可穿戴设备)的 API。

它于 2015 年初通过 Android 开发者网站推出,同时推出了几个定制的“垂直”API,包括 Android Auto SDK(用于汽车)、Android TV SDK(用于 2K 或 4K iTV 电视机)和 Android Wear SDK(用于智能手表)。目前,Wear SDK 的目标是智能手表,但后来它可能会扩展到其他可穿戴设备,如鞋子、帽子等。

Android Wear SDK 提供了一个统一的 Android 可穿戴设备开发平台,可以跨多个智能手表产品。在 Wear SDK 推出之前,智能手表制造商必须要么提供自己的 API,就像索尼智能手表 One 和 Two 在 2014 年所做的那样,要么支持完整的 Android 4 操作系统,就像 Neptune Pine 在 2014 年所做的那样。

这里需要注意的是,这个 Android Wear SDK 并没有提供单独的操作系统,实际上是 Android 5 OS 的一个扩展,需要 Android 可穿戴应用的一部分在你的主机 Android 设备上运行。这通常是 Android 智能手机,因为这是最便携的设备,也是连接各种网络和运营商的设备类型。

由于 Android 智能手表代表了可穿戴应用市场的大部分,我将把本书的大部分内容集中在可穿戴应用开发领域,尽管我也会在第十七章中介绍谷歌眼镜。

谷歌眼镜开发套件:安卓或镜像的 GDK

在开发谷歌眼镜可穿戴应用时,你有两种不同的 GDK API 选项。您可以单独使用它们,也可以将它们相互结合使用。此外,还有第三种选择,即不使用任何一种 API 而直接使用 Android 操作系统。让我们仔细看看玻璃的发展。

谷歌眼镜的安卓工作室 GDK:眼镜开发套件

谷歌 Android GDK 是 Android OS APIs(也称为 Android 5 SDK)的一个插件,它允许你构建谷歌所说的玻璃器皿。玻璃器皿包括直接在谷歌眼镜硬件上运行的谷歌眼镜可穿戴应用。

一般来说,如果您需要直接访问谷歌眼镜的独特硬件功能,为您的用户与谷歌眼镜硬件进行实时交互,或者在没有互联网、Wi-Fi、2G、3G 或 4G 蜂窝网络可用的情况下为您的应用提供离线功能,您会希望使用这种 Android GDK 开发方法。

通过将 Android SDK 与 GDK 结合使用,您可以利用广泛的 Android APIs,同时为谷歌眼镜用户设计出色的用户体验。与 Mirror API 不同,使用这个 Android GDK 构建的玻璃器皿可以在 Glass 本身上运行。这允许访问 Glass hardware 的低级、专有(谷歌眼镜独有)产品功能。

仅使用 Android 环境开发谷歌眼镜应用

谷歌设计了 Glass 平台,以使他们现有的 Android SDK 能够在 Glass 上工作。这意味着您可以使用所有现有的 Android 开发工具,您将在第二章中下载并安装这些工具,并且您的玻璃器皿可以使用标准的 Android 应用包(APK)格式交付。

这打开了许多其他 pro Android 开发书籍,如Pro Android Graphics(Apress 2013)或 Pro Android UI (Apress 2014),它们将向您展示如何制作在谷歌眼镜设备上运行良好的可视化 Android 应用。这是因为谷歌眼镜产品旨在运行完整的 Android 操作系统,因此,任何应用都将在其上运行。这意味着你可以开发一个可以在所有安卓设备上运行的应用,包括谷歌眼镜。只要用户不需要在智能手表上运行,除了 Pine 之外,这就允许一次编码,随处交付(高度优化)应用的开发工作流程!

在谷歌眼镜上使用 RESTful 服务:镜像 API

还有一个 API 可以与谷歌眼镜配合使用,完全不依赖于谷歌 Android 操作系统。这就是所谓的 Google Glass Mirror API,也就是通常所说的 RESTful API 。

Mirror API 允许开发者使用他们选择的任何编程语言为谷歌眼镜制作玻璃器皿。RESTful 服务提供了对基于 web 的 API 的简单访问,这些 API 将为应用开发人员完成大部分数据传输的繁重工作。

一般来说,如果您需要使用跨平台的基础设施,如 HTML 或 PHP,需要访问 Google Glass 产品的内置功能,或者需要独立于 Android 操作系统的平台,您会希望使用这个镜像 API。举例来说,这就是你在 iOS 或 Windows 上使用谷歌眼镜的方式。

混合玻璃应用:混合安卓 GDK 和镜像 API

最后,有趣的是,开发者也可以创建“混合”谷歌眼镜应用。这是因为,正如你可能已经怀疑的那样,谷歌眼镜镜像 API 可以与谷歌眼镜安卓 GDK 接口。

这是通过使用菜单项调用镜像 API 代码来完成的,该代码将一个意图对象发送到安卓 GDK API,然后发送到 GDK 应用代码。在本书中,我将使用 Intents,它是一种 Android 平台特定的 Java 对象类型。意图用于在应用、菜单、设备、活动和 API(如 Mirror API)之间进行通信。你甚至可以使用这种混合开发模式来利用现有的 web 属性,这些属性可以启动直接在谷歌眼镜上运行的增强的 i3D 体验。

真正的 Android 或 Android 外设:蓝牙链接

在专业 Android 可穿戴设备的世界中,甚至在某些情况下,在专业 Android 设备的世界中,作为一名开发人员,你需要知道一个区别,Android 产品的营销人员往往会想要隐藏这个区别。这是因为制造一种类型的 Android 设备的成本将非常高(小型化),而制造另一种类型的 Android 设备的成本将非常低,因此利润率将更高,特别是如果公众可以确信产品正在运行 Android 操作系统,而实际上它并没有这样做!

这在 Android 可穿戴产品领域非常明显,包括智能手表和智能眼镜,如谷歌眼镜。一些智能手表是真正的安卓设备;也就是说,它们在智能手表内部有一个计算机处理单元(CPU)、内存、存储、操作系统、Wi-Fi 等。Neptune Pine 就是一个很好的例子。

真正的智能手表设备实际上就像是在你的手腕上戴着一部完整的智能手机,将由电信运营商提供,就像现在的智能手机一样。这款真正的 Android 智能手表将是你唯一的 Android 设备,你将不再需要携带智能手机。嵌入式计算机小型化的进步最终将允许所有智能手表做 2014 年 Neptune Pine 所做的事情,将成熟的 Android 设备放在某人的手腕上。

如果你想知道,我从 TrueHD (HDTV)行业术语中借用了这个“真实”的 Android 描述。TrueHD 是 1920×1080 的分辨率,它是一个必要的描述性修饰符,因为市场上还有另一种更低的 1280×720 分辨率,称为 just HD(我称之为伪 HD)。

其他智能手表不是真正的 Android 设备,可以被描述为现有智能手机、平板电脑或平板电脑的“外设”,这些智能手表使用蓝牙技术成为 Android 设备的扩展,Android 设备具有 CPU(处理器)、内存(应用运行时)、数据存储和电信(Wi-Fi 接入和 4G LTE 蜂窝网络)硬件。

外围 Android 设备显然需要不同的应用开发工作流程,并具有不同的数据优化和测试程序,以实现最佳的性能和用户体验。

显然,因为这本书将着眼于开发一些更受欢迎的 Android 可穿戴设备,所以我将更详细地讨论这个“远程 Android 外设”开发问题,并看看如何设计和优化可穿戴外设应用。

我只是想让你知道,现在有两种完全不同的方法来进行 Android 开发:板载或原生 Android 应用,以及远程或双向(后端)通信 Android 应用功能。

随着蓝牙可穿戴设备和第二屏幕技术的出现(在Pro Android IoT【Apress,2016】一书中有所涉及),随着时间的推移,这将成为 Android 应用开发的一个重要区别,这些扩展 Android 产品将继续出现在市场上。

最重要的是,您需要知道哪种消费电子设备正在托管 CPU、内存、存储和电信硬件,哪种消费电子设备正在托管触摸屏、显示器和输入,以及哪种技术(以及它的速度)正在将这两者连接在一起。这对你优化应用性能的能力很重要,因为用户体验(UX)是基于应用的响应速度和易用性。

可穿戴应用设计:Android 5 材质设计

Android 5 于 2014 年与 Android TV、Wear 和 Auto SDK 插件一起发布,采用了全新的 UI 范式。谷歌称之为材质设计,因为它更懂 3D。纹理或“蒙皮”一个 2D 或 3D 对象涉及到使用媒体设计行业中通常称为的材质

谷歌创建了 Material Design 作为跨谷歌 Chrome OS 和 Android OS 平台,以及跨运行 Chrome OS(Linux 之上的 HTML5、CSS3 和 JavaScript)或 Android 5 OS(64 位 Linux 内核之上的 Java 7、CSS3 和 XML)的消费电子设备的最终用户交互、动画动作和视觉设计的深远 UI 设计指南。

Android 5 操作系统中的一个很酷的功能是支持所有类型的 Android 5 设备上的应用的材质设计,如果你读过《绝对初学者的 Android 应用》一书*(2014 年 4 月),你可能已经知道了。*

在本章以及本书的其余部分,你将了解到如何为 Android 可穿戴设备应用使用材质设计。Android 5,也称为 Android API Level 21(以及更高版本),提供了一些新的组件和新的 OS 功能,专门针对材质设计。

这包括新的 Android 5 主题、用于新查看功能的 Android View 小部件、改进的阴影和动画 API 以及改进的可绘制性,包括更好的矢量缩放、使用 8 位 alpha 的 32 位 PNG 着色以及自动颜色提取 API。我将在本章的剩余部分更详细地讨论所有这些。

安卓材质设计主题:光明与黑暗

这个 Android 5 材质设计主题提供了新的 Android 5 符合风格,用于您的 Android 5 应用。由于 Android Wear SDK 是 Android Studio 1(Android 5 plus IntelliJ)的一部分,这些新主题也将适用于 pro Android 可穿戴设备。你将在第二章第一节安装 Android Studio 以及其他一些开源内容开发软件,在第三章第三节探索 Android Studio 1.x 和 IntelliJ,在第四章第五节探索 Java 7。如果你是为 Android 4 开发,你会使用 HOLO 主题**;**如果是为 Android 5 开发,会使用材质主题。

两个主题。赫萝和主题。材质提供深色和浅色版本。您可以使用自己定义的自定义调色板自定义材质主题的外观,以匹配品牌标识。通过使用材质主题属性,你可以给 Android 操作栏和状态栏着色。

你的 Android 5 小工具有一个新的用户界面设计和触摸反馈动画。您可以自定义这些触摸反馈动画,以及应用的活动过渡。Android 中的活动是应用的一个逻辑部分或 UI 屏幕。我假设你已经了解 Android 的行话,因为这是一本中级到高级水平(专业)的书。

定义耐磨材质主题:使用样式属性

就像以前版本的 Android 一样,材质主题是使用 Android Style 属性定义的。各种材质主题的示例将使用 XML 定义,使用以下 XML 1.0 标记:

@android:style/Theme.Material                     (the default dark UI version)
@android:style/Theme.Material.Light               (the light UI version)
@android:style/Theme.Material.Light.DarkActionBar (a light version with a dark version Actionbar)

正如我提到的主题。材质 UI 风格(或主题)只有 Android 5,API 级及以上才有。v7 支持库为一些 5 之前版本的视图小部件提供了带有材质设计风格的主题,并支持在 Android 5 之前定制调色板。

定义耐磨材质主题调色板:项目标签

如果你想定制你的材质设计主题的原色,以适应你的可穿戴应用品牌,你可以使用 <项目> 标签来定义你的定制颜色,嵌套在 <样式> 标签内,嵌套在 <资源> 标签内,嵌套在 themes.xml 文件内。您创建了一个 AppTheme ,其父属性继承自主题。材质父主题,并使用 colors.xml 文件添加您的自定义颜色引用,该文件使用 xml 标记结构保存十六进制颜色数据。这应该是这样的:

<resources>
    <style name="AppTheme" parent="android:Theme.Material">
        <item name="android:colorPrimary">@color/primary</item>
        <item name="android:colorPrimaryDark">@color/primary_dark</item>
        <item name="android:colorAccent">@color/accent</item>
    </style>
</resources>

同样,我假设您了解基本的 Android (Java 和 XML)开发。这里应用使用的样式名是 AppTheme ,它引用了主题的样式。Material 并设置自定义颜色值,设置在一个 colors.xml 文件中,使用 < item > 标签包含您的主题样式常量— colorPrimarycolorPrimaryDarkcolorAccent 。Android 很容易使用这些主题常量,所以你所要做的就是引用它们的自定义颜色值。

定制可穿戴材质主题状态栏:状态栏颜色

您还可以轻松定制主题的应用状态栏。材质,您可以指定另一种适合可穿戴应用品牌的颜色,并提供适当的颜色对比度来显示白色状态图标。要为你的应用状态栏设置自定义颜色,当你扩展一个主题时,添加一个 android:statusBarColor 属性 。材质样式定义。使用前面的示例,您的 XML 应该如下所示:

<resources>
    <style name="AppTheme" parent="android:Theme.Material">
        <item name="android:colorPrimary">@color/primary_color</item>
        <item name="android:colorPrimaryDark">@color/primary_dark</item>
        <item name="android:colorAccent">@color/accent_color</item>
        <item name="android:statusBarColor">@color/status_bar</item>
    </style>
</resources>

如上所述,如果你没有特别指定,statusBarColor 常量将继承colorPrimaryDark 常量的值。你也可以使用 Android 5# aarggbb32 位十六进制颜色数据值的 alpha 通道组件在状态栏后面进行绘制。如果你想深入研究 Android graphics,可以看看《Pro Android Graphics*(Apress 2013)这本书。*

例如,如果你想将状态栏显示为完全透明,你可以使用一个**@ Android:color/transparent**常量,它将 alpha 通道设置为零(关闭或 #00000000 )。然而,这并不是一个好的 UI 设计实践,因为你可能在状态栏后面有一个白色的背景,这样会使状态栏图标不可见。

所以你真正想做的是在背景(图像、照片、3D、2D、艺术品)上创建一个带颜色的状态栏。您应该使用深色渐变来确保白色状态图标可见。为此,您可以使用 Android WindowManager 将 statusBarColor 设置为透明,并将 WindowManager 对象的 windowTranslucentStatus 属性的数据值设置为 true。LayoutParams 类(对象)FLAG _ TRANSLUCENT _ STATUS常量。还可以通过 Java 代码使用**window . setstatusbarcolor()**方法实现状态栏动画或半透明的淡入或淡出。

作为一个 UI 设计原则,你的 Android 状态栏对象应该总是与操作栏有一个清晰的界限,除非你在这些栏后面设计自定义 UI 图像或新媒体内容,在这种情况下,你应该使用你的变暗渐变,这将确保图标仍然可见。当您自定义 UI 导航(操作栏)和状态栏时,您应该使它们都透明,或者只更改状态栏的透明度。在所有其他情况下,导航栏应该保持黑色。

自定义可穿戴材质主题:单个视图主题

Android 风格和主题不仅可以用于在全球范围内定制整个可穿戴应用的外观和感觉,还可以用于设计本地屏幕的风格和主题,这些屏幕是应用的组成部分。

你可能会问,为什么要在 Android 5 中为一个单独的视图对象或活动对象开发一个风格或主题呢?

答案可以在 UI 设计的概念模块化 中找到,这是 Android 可穿戴应用开发的一个基石。一旦你使用 XML 文件开发了一个风格和主题,你就可以在任何需要的时候应用它,这可能会在你的可穿戴应用开发过程中多次出现。这样,您只需做一次 UI 设计工作(创建一个模块),然后就可以多次应用它。这也确保了每次都以完全相同的方式应用主题或样式。如果你需要更详细地了解 Android 5 开发的用户界面设计,本书Pro Android UI(2014 年出版)深入讨论了所有的 Android 用户界面设计问题。

你的 XML 用户界面布局容器定义(Android 布局容器从视图组子类化)中的 UI 元素(Android 窗口小部件可以引用一个 android:theme 属性 或者一个 android:style 属性。这允许您以模块化的方式引用预构建的样式和主题资源。

引用其中一个预构建的样式或主题属性将应用该 UI 元素以及该 UI 设计元素中的任何子元素。这项功能对于在可穿戴应用 UI 设计的特定部分更改主题调色板非常有用。

Android 材质设计视图小部件:列表和卡片

Android 5 API 提供了两个全新的视图子类(widgets)。这些可用于显示使用材质设计主题、样式、动画和特殊效果的信息卡或可回收清单。

RecyclerView widget 是 Android ListView 类的即插即用增强版。它支持许多布局类型,并提供性能改进。 CardView widget 允许你的可穿戴应用使用具有一致外观和感觉的“卡片”来显示上下文信息。

在我继续讨论阴影、动画和特殊效果(如可绘制的着色和颜色提取)之前,让我们先仔细看看新的 UI 设计工具。

Androidrecycle viewClass:优化(回收)列表查看

Android RecyclerView 是一个 UI 设计小部件,它是 Android ListView 小部件的一个功能更加丰富的版本。RecyclerView 用于显示大量应用数据列表。该类的独特之处在于视图中包含的数据可以非常高效地滚动。这是通过 RecyclerView ViewGroup(一个布局容器)子类实现的。在任一时刻,它在其 ViewGroup 布局容器中保存有限数量的数据(视图)对象。

这种内存优化原则与数字视频流的工作原理非常相似,只在系统内存中保留当前使用的数据列表部分,这使得该类更快,内存效率更高。当您的数据集合中的数据在运行时会根据应用最终用户的操作或网络事务发生变化时,您可能希望利用 Android 的 RecyclerView 小部件。

RecyclerView类通过向开发人员提供大量软件组件来实现所有这些,可穿戴开发人员可以在他们的代码中实现这些组件。其中包括用于在列表中定位数据视图项目的布局管理器、为数据视图项目操作添加视觉效果的动画,以及为可穿戴应用实现此 RecyclerView 小部件设计自定义布局管理器和动画的灵活性。

Android CardView 类:索引卡组织范例

Android CardView 类是 ViewGroup 布局容器类,扩展了 FrameLayout 类。Android FrameLayout 类允许您显示单个视图 UI 元素(小部件),因此 CardView 将是一个 FrameLayout 单个视图的集合,它是一堆 3x 5 索引卡的范例。这个类允许你在虚拟卡上显示可穿戴应用的任何信息数据,这些虚拟卡在 Android(应用、可穿戴、电视或 auto SDK)平台上具有一致的外观。

您的 CardView 小部件还可以为该 CardView 布局容器中的每张卡片添加阴影和圆角,尽管是 CardView 本身而不是每张单独的卡片添加阴影和圆角。要创建一个有阴影的卡片,需要使用一个card _ view:card levation属性 。

如果您的用户使用的是 Android 5 (API Level 21)或更高版本,CardView 类将访问实际高程属性并自动创建动态阴影,对于更早的 Android 版本,它将基于更早的版本创建可编程的阴影实现。

如果您想增强 CardView 小部件的外观,您可以提供一个自定义的圆角半径值,比如说 6dip,它将使用 cardCornerRadius 属性为 CardView 中的每张卡片创建圆角。

如果你想在你的 CardView 小部件后面显示一个背景图像,你可以提供一个自定义的背景颜色值,比如#77BBBBBB,它将使用cardbbackground color属性为你的 CardView 中的每张卡片创建一个浅灰色的透明背景颜色。

如果你想在 CardView 窗口小部件后面有一个阴影,你可以提供一个自定义的 elevation 值,比如说 5dip,这将使用 cardElevation 属性在 CardView 中的每张卡片后面创建一个漂亮的、高度可见的阴影。在使用 cardElevation 属性之前,您需要将名为 cardUseCompatPadding 的填充兼容性常数设置为 true 值,以便 CardView 计算 dropshadowing (elevation)效果。

要访问这些 CardView 属性,必须为 LinearLayout 父布局容器类和嵌套的 CardView 子布局容器类导入自定义 XMLNS(或 XML 命名架构)。这是通过在每个布局容器标记中使用以下 XML 参数来实现的:

xmlns:card_view=[`schemas.android.com/apk/res-auto`](http://schemas.android.com/apk/res-auto)

一旦设置好了,您就可以使用上面指定的属性,方法是在它们前面加上一个 card_view: 修饰符,这样您的 cardCornerRadius 属性就可以写成例如card _ view:cardCornerRadius

为了实现我在本节前面概述的三个示例,LinearLayout 父布局容器的标记(包含一个包含 TextView 对象的 CardView)类似于下面的 XML:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              xmlns:card_view="http://schemas.android.com/apk/res-auto" >
    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/my_card_view"
        android:layout_gravity="center"
        android:layout_width="180dip"
        android:layout_height="180dip"
        card_view:cardCornerRadius="6dip"
        card_view:cardBackgroundColor="#77BBBBBB"
        card_view:cardUseCompatPadding="true"
        card_view:cardElevation="5dip" >
        <TextView android:id="@+id/info_text"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent" />
    </android.support.v7.widget.CardView>
</LinearLayout>

现在让我们来看看材质设计中的特效应用,并学习投影和动画视图部件。

Android 材质设计效果:阴影和动画

虽然 Android 一直有一个阴影和动画功能集,它与 Android View 对象(称为 Android widgets)一起工作,但材质设计通过提供更高级的阴影将这些效果提升到了一个新的水平,以便屏幕上的所有东西都有一个 3D z 轴元素。例如,除文本对象之外的视图对象现在可以访问投影,并且现在有高级的过渡动画效果,如曲线插值运动和在屏幕上模拟 3D 效果的东西,如波纹。

Android 材质设计 3D 效果:自动视图阴影

除了 x 和 y 属性之外,从 Android 中的View类继承而来的 Android widgets 现在增加了第三个特性,z 轴属性。这个属性称为仰角属性,定义了视图对象的高度,进而决定了其阴影的特征。

正如许多熟悉 3D 的人所知,这将 Android UI 设计从 2D 带入了激动人心的 3D 领域。这使得照片般逼真的 i3D UI 设计成为可能,而在以前,只有“平面”的 2D UI 设计才是可能的。

添加这个新的 elevation 属性是为了表示视图对象相对于 UI 设计中它上面和下面的其他视图对象的高程或高度。Android 操作系统将使用 elevation 属性(或者如果您喜欢,属性或特征)来确定阴影的大小,因此具有较大 elevation 值的视图对象(小部件)应该投射较宽的阴影,使小部件看起来处于较高的高度。

UI 设计中的视图小部件也将通过 z 值获得它们的绘制顺序。这通常被称为 z 顺序z 索引,被分配了较高 z 值的 UI 视图对象将总是出现在被分配了较低 z 值的其他视图对象(窗口小部件)之上。

Android 材质设计动画:用户界面的触摸反馈

Android 5 中升级的动画 API 允许你为你的 UI 控件(小部件)创建自定义的触摸反馈动画。它们允许根据视图小部件状态的变化触发这些动画效果,还允许当用户在应用的活动屏幕之间导航时进行活动转换。

动画 API 的材质设计相关增强功能允许您使用新的触摸反馈动画来响应视图小部件上的触摸屏事件。这些实现了新的波纹元素( RippleDrawable 类),并且可以被定义为有界(包含在视图内)或无界(发散出视图边界)。使用 XML 定义它相当简单,尽管它也可以使用 Java 7 来定义。

要定义有界波纹触摸反馈动画,请在视图小部件标签的 android:background 参数中引用它,如下所示:

<Button android:id="@+id/my_rippling_muscles_I_mean_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="?android:attr/selectableItemBackground" />

要定义无界波纹触摸反馈动画,再次使用 View 小部件标签中的 android:background 参数引用它,如下所示:

<Button android:id="@+id/my_rippling_abs_I_mean_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="?android:attr/selectableItemBackgroundBorderless" />

触摸反馈动画现在集成在几个标准视图小部件子类中,比如Button类。一个新的 API 允许您自定义这些触摸反馈动画,以便您可以将它们添加到您的自定义视图中。

用户现在可以使用圆形显示动画来隐藏和显示视图小部件,这为 Android 操作系统增加了另一个级别的惊喜因素。熟悉 2D 和 3D 图形的人会知道,这是通过将动画圆形(ShapeDrawable)剪辑形状应用到两个 UI 小部件视觉效果之间的渲染管道中来实现的。

Android 材质设计过渡:增强的活动过渡

在活动过渡动画区域,您现在可以使用自定义活动过渡动画在活动之间切换。每当用户使用 Intent 对象从一个活动转移到另一个活动时,Android 操作系统都会应用自定义活动转换。

这些活动动画旨在与其他材质设计动画效果结合使用。我怀疑添加这些是为了在整个 Android 5 可穿戴应用中统一 UX 的材质设计。现在,视图和活动对象都提供了预建的动画!你也可以创建自定义的过渡动画,就像在 Android 5 之前一样。

Android 5 材质设计中有四种不同类型的预建活动动画:进入退出控制全屏动画效果,shared elementerSharedElementExit 控制 UI 元素的本地化 UI 元素效果,这些元素是共享(在两个活动屏幕上)。

当用户从可穿戴应用中的另一个活动屏幕切换到新的活动屏幕时,将触发进入活动动画,因此,该进入转换确定活动中的视图窗口小部件如何进入屏幕。例如,在分解过渡中,视图小部件从屏幕外部进入屏幕,飞向屏幕中心。确保在你的活动中使用图形设计来增强这种过渡的特殊效果。

相反,当用户退出他们的可穿戴应用中的活动屏幕时,可以触发退出类型的活动动画。退出过渡将决定可穿戴活动中的视图小部件如何退出屏幕。例如,使用分解过渡示例,在退出过渡中,您的可穿戴应用视图窗口小部件将退出屏幕,远离中心,与进入分解过渡动画相反。

如果两个活动屏幕之间有共享的 UI 元素,那么共享元素转换可以应用于这个 UI 设计场景。正如您可能已经猜到的,这些被称为 SharedElementEnter 和 SharedElementExit 转换。SharedElement 转换决定了在两个活动屏幕之间共享的视图小部件将如何处理这些活动屏幕之间的转换。

例如,如果两个活动屏幕具有相同的数字图像,但其位置或分辨率不同,则 changeImageTransform 共享元素过渡将在两个活动屏幕之间平滑地转换(重新定位)或缩放(调整大小)图像。

还有其他共享元素转换,比如 changeBounds 共享元素转换,它将动画显示任何目标活动视图小部件的布局边界的任何变化。还有一个 changeTransform 共享元素转换,可以动画显示两个正在转换的活动屏幕之间目标视图小部件的缩放(大小)和旋转(方向)的任何变化。最后,有一个 changeClipBounds 共享元素转换,它将为目标活动视图小部件的剪辑路径边界中的任何变化制作动画,这些小部件被分配了剪辑路径。

Android 5 现在支持三种主要的进入和退出转换;这些也可以与共享元素转换结合使用。有爆炸过渡,它将视图小部件移入或移出屏幕中心,滑动过渡,它将视图小部件滑入或滑离场景边缘,以及渐变过渡,它通过改变视图小部件的不透明度值来添加或移除它们。

您可能想知道是否有任何方法可以创建自定义过渡。这是通过使用 Visibility 类创建一个定制的 transition 子类来实现的,Visibility 类是Transition类的一个子类,就是为了这个目的而创建的。

Android 材质设计运动:增强的运动曲线或路径

那些对 3D 动画软件(如 Blender3D)或数字视频编辑软件(如 Lightworks)有经验的人应该熟悉使用运动曲线来控制视频剪辑或场景中 3D 对象移动的速度变化率的概念。

这被称为运动曲线,因为它允许你精确地控制某物随时间移动的方式,这在电影制作、角色动画和游戏设计中很重要。还有另一个密切相关的工具叫做运动路径。运动路径定义了对象如何在 2D 或 3D 空间中移动。

因此,运动曲线是用于定义时间动画属性(动画速度随时间的变化)的工具,而运动路径是用于定义空间动画属性的工具。

Android 操作系统中的动画,以及新的 Android 5 材质设计,都依赖于这些使用插值器类的运动曲线。这个类现在可以用来定义更复杂的 4D 运动曲线插值(第四维,或 4D,意味着随时间的变化)。“材质设计”现在添加了运动路径来支持 2D 空间运动模式,因此现在不仅可以控制运动的速率,还可以控制运动发生的位置。

路径插值器类是一个基于贝塞尔的新插值器子类,贝塞尔是一种复杂类型的曲线定义数学。贝塞尔曲线在 Android 5 中已经实现为路径(类)对象。这个插值器类可以在 1 乘 1 的正方形中指定贝塞尔运动曲线,锚点在 0,0 和 1,1,并带有自定义的 x,y 控制点,这是开发人员使用 pathInterpolator 类的构造器方法参数指定的。您通常会使用 XML 资源定义一个 PathInterpolator 对象,如下所示:

<pathInterpolator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:controlX1="0.5"
    android:controlY1="0"
    android:controlX2="1"
    android:controlY2="1" />

这个 Android 5 操作系统为材质设计规范中的三个基本的新运动曲线提供了 XML 资源。这些将使用标记,并在您的 XML 标记或 Java 代码中引用,使用@interpolator/路径引用头,如下所示:

@interpolator/linear_out_slow_in.xml
@interpolator/fast_out_linear_in.xml
@interpolator/fast_out_slow_in.xml

Android 材质设计动画状态变化:statelistantimator

Android 状态变化,如手机上的信号指示器,现在可以使用新的 Android 5 材质设计功能集进行动画制作。这将允许可穿戴应用开发人员在其 Android 可穿戴应用设计中添加更多令人惊叹的元素。

新的 Android StateListAnimator 类允许开发人员使用 ObjectAnimator 对象,每当视图对象的状态改变时,这些对象就会被 Android 操作系统触发(运行)。将 StateListAnimator 设置为 XML 资源的方式利用了 <选择器> 标签(在不同的状态中进行选择)和 <设置> 标签(创建选择集),以及定义状态的 <项目> 标签和定义对象动画的 <对象动画器> 标签。一个简单的 pressed=truepressed = falsestatelistantimator 选择器对象以这种方式建立,使用四层(深层)嵌套 XML 结构:

<selector xmlns:android="http://schemas.android.com/apk/res/android" >
   <item android:state_pressed="true" >
      <set>
         <objectAnimator android:propertyName="translationZ"
             android:duration="120"
             android:valueTo="5dip"
             android:valueType="floatType" />
      </set>
   </item>
   <item android:state_pressed="false"
         android:state_enabled="true"
         android:state_focused="true" >
      <set>
         <objectAnimator android:propertyName="translationZ"
             android:duration="120"
             android:valueTo="0"
             android:valueType="floatType" />
      </set>
   </item>
</selector>

要将定制的状态变化动画附加到视图中,使用 XML 资源文件中的<selector>元素定义一个 ObjectAnimator,如上所示。接下来,使用Android:stateListAnimatorXML 参数在您希望受其影响的视图对象 XML 标记中引用它。

要将动画 StateListAnimator 中的这种状态变化分配给 Java 代码中的视图,您应该利用一个**animation inflater . loadstatelistanimator()方法调用,然后记住使用View . setstatelistanimator()**方法调用将这个 ObjectAnimator 分配给您的视图。

重要的是要记住,每当你的可穿戴应用主题扩展一个主题。材质材质设计主题,UI 按钮对象默认启用 z 动画(设置和激活)。为了避免 UI 按钮对象中的这种行为,在 XML 标记中将这个Android:statelistantimator属性设置为数据值 @null ,或者在 Java 代码中设置为“ null

接下来,让我们看看 Android 5 在 Android 4.x 中已经存在的大量 Android Drawable 类中添加了什么,以便能够通过动画矢量、位图图形、着色功能、颜色提取和新状态动画图形来提供这种大大增强的材质设计功能。

Android 材质设计图形处理:Drawables

还有一些新的用于材质设计的可绘制 API 功能,使得使用 Android 可绘制对象来帮助您为您的应用实现流畅的材质设计变得更加容易。新的 Android Drawable 类包括 VectorDrawable、AnimatedVectorDrawable、RippleDrawable、Palette 和 AnimatedStateListDrawable,所有这些我将在本节中讨论。

这些新的 Android 5 可绘制类为 Android 操作系统添加了可扩展矢量图形(SVG)路径支持、变形、i3D 波纹特效、调色板颜色提取和多状态可绘制对象的动画过渡。Android 5 增加了一些非常强大的类,涉及到 2D 矢量和位图图形!

Android 5 可绘制调色:。setTint( ) 和*。setImageTintMode( )*

使用 Android 5 及以上版本,可以着色 BitmapDrawable 对象以及 NinePatchDrawable 对象。您可以对整个数码图像对象进行着色,或者将这种着色效果限制在某些像素上。这是通过定义 alpha 通道来“掩盖”着色效果来实现的。

您可以使用 Android 颜色类资源或引用这些颜色类资源的 Android 主题属性来着色这些可绘制对象。通常,您只需创建一次这些资源,然后在您的可穿戴应用中使用它们,使用着色功能根据需要给它们着色,以匹配您的主题。正如您所想象的,您可以使用它来进行优化,因为您在整个应用中使用的图形图像资源要少得多。

您可以使用为可穿戴应用的 Java 代码中的 BitmapDrawable 或 NinePatchDrawable 对象应用色调。setTint( ) 方法。还可以在 XML UI 布局容器定义中设置色调颜色和色调模式。这是通过使用视图对象标签中的 android:tintandroid:tintMode 参数(属性)来实现的。

Android 5 矢量可绘制对象: VectorDrawable

新的 Android 5 VectorDrawable类和使用该类创建的对象可以在不损失任何质量的情况下放大或缩小。这是因为,与位图图像不同,矢量图像由代码和数学(线条、曲线、填充和渐变)组成,而不是像素。因此,如果发生缩放,矢量图像实际上将被渲染以填充可用于显示它的像素数量。矢量图像在缩放时不会像基于像素的图像那样被重新采样,而是会被重新渲染以适应新的屏幕分辨率,无论是 320 x 240 的智能手表还是 4096 x 2160 的 iTV。

出于这个原因,矢量图像可以从可穿戴设备一直使用到 4K 独立电视,具有相同的视觉质量。使用位图图像是不可能的。因为矢量图像是基于文本的,所以它的数据密度至少比位图图像高一个数量级,因为矢量是代码(数学和文本),而不是数据密集型像素值的数组。

正如你可能想象的那样,矢量图像非常适合单色应用图标。矢量图像只需要一个图像资源,与位图图像格式相反,位图图像格式需要为每个屏幕密度提供一个资源文件。要创建一个矢量图像,您需要在一个<vector> XML 父标签中为形状定义 SVG 数据。定义彩色填充正方形的 SVG 矢量图像的 XML 标记如下所示:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:height="320dip"
        android:width="320dip"
        android:viewportWidth="160"
        android:viewportHeight="160" >
        <path android:fillColor="#AACCEE"
              android:pathData="M0,0 L0,100 100,100 100,0 Z" />
</vector>

SVG 图像使用 VectorDrawable 对象封装在 Android 5 中。有关 SVG 路径命令语法的信息,请参见 W3C 网站上的 SVG 路径参考(http://www.w3.org/TR/SVG/paths.html)。我在《Java 8 游戏开发入门(2014 年出版)一书中也深入讨论了这一点,因为 Java 8 和 JavaFX 拥有广泛的 SVG 路径支持。

你还可以通过制作 VectorDrawable 对象的 SVG path 属性动画来模拟流行的多媒体软件类型,称为扭曲变形,这要归功于另一个全新的 Android 5 类 AnimatedVectorDrawable。

接下来让我们仔细看看所有新的 Android 5(自动)颜色提取功能,这些功能是由 Android Palette 类提供的。

Android 5 自动调色板提取:调色板

Android 5 增加了一个新的调色板类,它有助于一个颜色提取算法,允许开发人员从你的应用中的位图图像素材中自动提取突出的颜色。Android 支持库 r21 和更高版本包括 Palette 类,它允许您在版本 5 之前的 Android 应用版本中从图像中提取突出的颜色,例如 Android 3.x 和 Android 4.x 应用。调色板将从位图图像的色谱中提取以下类型的突出颜色:

  • 响亮的
  • 充满活力的黑暗
  • 明亮的光线
  • 柔和的
  • 柔和的暗色
  • 柔和的光线

Palette 类是一个助手类,它帮助开发人员提取六种不同的颜色分类(如上所列)。要使用这个助手类,您可以将想要调色板化的位图对象传递给调色板类的**。生成【位图图像】的方法,使用下面的方法调用:**

Palette.generate(Bitmap imageAssetName);

请确保在加载图像资源的后台线程中执行此操作。如果不能使用后台线程,也可以调用调色板类的**。generateaync()**方法,提供一个监听器代替,像这样:

public AsyncTask<Bitmap, Void, Palette> generateAsync (Bitmap bmp, Palette.PaletteAsyncListener pal)

您还可以使用调色板类中的 getter 方法从图像中检索突出的颜色,如**。getVibrantColor( )** 或**。getMutedColor( )** 。一个**。generate( )** 方法将返回一个 16 色调色板。如果需要更多,可以用另一个(重载)。generate( ) 方法采用如下格式:

Palette.generate(Bitmap image, int numColorsInPalette);

我查看了这个调色板类的源代码,似乎没有任何你可以要求这个类提供的最大颜色数(大多数调色板最大值是 8 位颜色,或 256 种颜色)。这为这个类提供了一些非常有趣的应用,因为它不依赖于 8 位颜色。调色板中要求的颜色越多,处理时间就越长。这就是为什么有一个 AsyncTask < > 版本的**。generate( )** 方法调用!

要在可穿戴设备应用的 IntelliJ 项目中使用 Palette 类,您需要将以下 Gradle 依赖项添加到应用的模块中:

dependencies { ... (default Gradle dependencies remain in here)
               compile 'com.android.support:palette-v7:21.0.+'  }

Android 5 状态动画:一个AnimatedStateListDrawable

除了新的 Android RippleDrawable 类和 VectorDrawable 类之外,还有一个全新的 AnimatedStateListDrawable 类,它可以创建 StateListDrawable 对象之间的动画过渡。

Android AnimatedStateListDrawable 类允许您创建可绘制对象的动画状态列表(因此有了类名),该类在引用的视图小部件的状态变化之间调用动画。Android 5 中的一些系统小部件将默认使用这些动画。下列范例显示如何使用 XML 资源来定义 AnimatedStateListDrawable:

<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/pressed"
          android:drawable="@drawable/drawable_pressed"
          android:state_pressed="true" />
    <item android:id="@+id/focused"
          android:drawable="@drawable/drawable_focused"
          android:state_focused="true" />
    <item android:id="@id/default"
          android:drawable="@drawable/drawable_default" />
    <transition android:fromId="@+id/default"
                android:toId="@+id/pressed" >
          <animation-list>
                <item android:duration="85"
                      android:drawable="@drawable/asset1" />
                <item android:duration="85"
                      android:drawable="@drawable/asset2" />
          </animation-list>
    </transition>
    <transition android:fromId="@+id/pressed"
                android:toId="@+id/default" >
          <animation-list>
                <item android:duration="85"
                      android:drawable="@drawable/asset2" />
                <item android:duration="85"
                      android:drawable="@drawable/asset1" />
          </animation-list>
    </transition>
</animated-selector>

<动画选择器> XML 定义的上半部分定义了状态,使用 <条目> 标签指定每个状态,下半部分定义了过渡,使用(惊奇) <过渡> 标签,其中嵌套了 <动画列表> 标签。

你将从这本书中学到什么

这本书将重点介绍 Android 5 操作系统和 IntelliJ IDEA **、的那些功能,这些功能用于使用 Android Studio 和 Wear SDK 创建 Android 可穿戴应用。如果你需要基础的 Android 5 应用开发知识,或者想学习如何为 Neptune Pine(或另一款不使用 Wear SDK 的智能手表)创建可穿戴应用,可以看看我的书绝对初学者的 Android 应用(第三 e 2014,Apress)。

这本书的第一部分将为这本书的其余部分奠定基础,包括这一章涵盖可穿戴类型、概念和 Android 操作系统的新 Android 5 材质设计。然后,您将建立一个开发工作站,浏览 Android 的可穿戴功能,并了解新的 IntelliJ IDEA。在本书中,您还将设置用于测试可穿戴应用的模拟器。

本书的第二部分将向您展示如何使用 Wear SDK 为智能手表创建可穿戴应用。您将了解 Android 技术中对于可穿戴应用开发人员来说非常重要的领域,例如创建和交付可穿戴应用、通知、数据层、同步和用户界面布局设计、使用卡片和列表。

这本书的第三部分将解释如何使用 Android Watch Faces API 创建智能手表面孔。您将了解如何创建手表面孔服务,如何将手表面孔绘制到屏幕上,如何设计 Android 手表面孔,如何优化手表面孔以获得最佳性能,如何显示手表面孔设计中的信息(数据),以及如何为手表面孔创建手表面孔应用配置屏幕。

摘要

在第一章中,你看了一下可穿戴设备的类型和概念,并了解了谷歌为 Android 5 操作系统添加的许多新功能。你看了蓝牙 LE,材质设计,新的可绘制类型,高级 3D 比如 OpenGL ES 3.1。

在下一章中,您将把您的开发工作站和所有开源软件放在一起,您将能够使用它们来开发您的高级 pro Android 可穿戴应用。

二、设置 Android5 可穿戴设备应用开发工作站

现在你已经有了一些关于可穿戴设备的基础知识,以及 Android 5 增加了什么让可穿戴应用令人难忘,本章将帮助你建立另一种类型的基础。您的开发工作站是实现 Pro Android 可穿戴设备应用开发目标的最重要的硬件和软件组合。在这里,我将花一些时间预先考虑您将需要的硬件和软件基础设施,您将需要将一个专业的、全面的、Android 软件开发工作站与您的软件开发箭筒中的一打箭放在一起(奇怪的类比组合,不是吗?罗宾汉和棒球)。然后,无论你开发什么类型的可穿戴应用,你都将拥有这本书剩余部分所需的一切!

我们还将摆脱所有那些关于组装一个 100%专业的 Pro Android 可穿戴设备生产工作站的繁琐任务。

因为本书的读者通常希望使用相同的 Android 可穿戴应用软件开发环境进行开发,所以我将在本章概述所有步骤,以组装一个完全精心设计的 Android Studio 开发工作站。你需要这样做,因为本书的所有读者都需要平等地体验你在本书课程中学到的一切。您将了解在哪里下载以及如何安装这个星球上最令人印象深刻的一些开源软件包!

创建 Android 工作站的工作流程

看完硬件需求后,你要做的第一件事是下载并安装整个 Java 软件开发包 (SDK) ,甲骨文称之为 Java SE 7 JDK (Java 开发包)。从 Android Studio 1.0 开始,Android OS 使用 Java 标准版(SE)版本 7 更新 71。Android Studio 1.2 使用 Java 7 update 79,在我对这本书进行第二次编辑时,我刚刚升级到它。当你读到这篇文章时,它很可能使用的是比那更高的版本!这就是软件开发的本质。

需要注意的是,Java 版本 8 也存在,并于 2014 年第二季度发布。Java 8 包含了强大的 JavaFX APIs,将 Java 编程语言变成了强大的新媒体引擎。Java 7 对 JavaFX 的支持确实存在于 Android OS 之外,如果你想使用 JavaFX,有一个让 JavaFX 新媒体应用在 Android 5 下工作的工作流程。因此,开源开发(Android OS、XML、Java7、Java8、JavaFX、HTML5、CSS、JavaScript 和 OpenGL)的未来已经到来!

你将下载并安装的第二个东西是 Android Studio ,你可以从谷歌 developer.android.com 的网站获得。Android Studio 1.0 实际上是一个软件包,由IntelliJ IDEA**(Android 集成开发环境)和一个 Android 开发工具 ADT 5 插件组成。在 Android 4.4 之前,Eclipse IDE、Android SDK 和 ADT 插件都是单独安装的,这使得安装很困难。**

这个 ADT 插件现在是 Android Studio 不可或缺的一部分,桥接****Android SDK,它也是 Android Studio 下载的一部分,带有 IntelliJ IDEA 的版本 14。ADT 插件将这种 IntelliJ Java 思想融入 IntelliJ Android Studio 思想。值得注意的是,IntelliJ 仍然可以用于简单的 Java SE 7 应用开发。IntelliJ 还支持 Java 8 和 JavaFX。

在你的 Android Studio 可穿戴设备应用开发环境建立之后,你将下载并安装新媒体素材开发工具,这些工具将与 Android Studio 结合使用(但不在 Android Studio 之内),用于数字图像编辑(GIMP)、数字视频编辑和特效(EditShare Lightworks)、数字音频混音、甜味和编辑(Audacity)以及 3D 建模、渲染和动画(Blender)等。

您将下载和安装的所有这些软件开发工具将接近匹配昂贵的付费软件包的所有主要功能集,例如来自 Apple (Final Cut Pro)、Autodesk (3D Studio Max)或 Adobe (Photoshop、Premiere 或 After Effects)的软件包。

开源软件可以免费下载、安装甚至升级,并且每天都在不断增加功能,变得越来越专业。在过去的十年或二十年里,你会完全惊讶于开源软件包变得多么专业;如果你还没有经历过这些,你将会以一种主要的方式经历这些!

Android 开发工作站:硬件基础

因为您将在本章中整合本书期间使用的 Pro Android 可穿戴设备应用开发工作站的基础,所以让我们先花点时间回顾一下 Android Studio 1.0 开发工作站的硬件要求,因为这是影响您开发性能(速度)的因素。这显然和软件本身一样重要,因为硬件运行软件。

Android Studio 的最低要求包括 2GB 内存、 2GB 硬盘空间、720p 高清 ( 1280 乘 800 )显示屏。接下来,让我们讨论一下使用 Android Studio 可穿戴工作站需要什么,从将 1280 x 800 的高清显示器升级到1920 x 1080的真正高清显示器开始!

我建议至少使用一个英特尔 i7 四核处理器,或者一个 AMD 64 位八核处理器,至少有 8GBDDR3 1600 内存。我用的是八核 AMD 835016GBDDR3 1866 。英特尔也有一个六核 i7 处理器。这相当于拥有 12 个内核,因为每个 i7 内核可以托管两个线程;同样,对于 64 位操作系统线程调度算法来说,i7 四核应该看起来像八个内核。

也有高速 DDR3 1866DDR3 2133 时钟速度内存模块组件可用。数字越大,表示内存访问速度越快。要计算内存循环的实际兆赫速度,将该数字除以 4 (1333 = 333Mhz,1600 = 400Mhz,1866 = 466Mhz,2133 = 533Mhz 时钟速率)。内存访问速度是一个主要的工作站性能因素 ,因为您的处理器通常会受到处理器内核访问它需要处理的数据(在内存中)的速度的“瓶颈”限制。

当您的工作站运行时,所有这些高速处理和内存访问都在您的工作站内部进行,因此保持一切冷却也很重要,这样您就不会遇到“散热问题”我建议使用宽全塔式机箱,带120 毫米200 毫米散热风扇(至少一两个),以及 CPU 上的系留液体感应散热风扇。需要注意的是,系统运行的温度越低,运行的速度就越快,持续的时间也就越长,所以在工作站上安装大量的静音风扇吧!

如果你真的想要最大的性能,特别是在模拟 AVDs (Android 虚拟设备)进行快速原型开发或测试时,我将在第五章中介绍,安装一个 SSD (固态硬盘)驱动器作为主硬盘驱动器,您的应用和操作软件将从这里加载。为您的 *D:* 硬盘使用传统的 HDD (硬盘驱动器)硬件,用于较慢的数据存储。

就操作系统而言,我使用的是 64 位 Windows 8.1 操作系统,它的内存效率相当高。Linux 64 位操作系统的内存效率极高。我推荐使用 64 位操作系统,这样你就可以寻址 3GB 以上的内存了!

Android 开发工作站:软件基础

为了创建一个全面的 Android 应用开发工作站,您将安装我将在本书后面向您介绍的所有主要类型的开源软件。首先,您将安装 JavaSE 7、Android Studio 以及 Gimp、Lightworks、Blender3D 和 Audacity,它们也都是开源软件包和编程语言(Android 使用 Java、XML、CSS 和 HTML5)。因此,你将组装一个 100%开源的工作站(除非你使用的是 Windows 8.1 操作系统,这是一个付费软件)。我还在本章末尾推荐了其他的免费软件,这样你就可以组装一个大型生产工作站了!

因为开源软件已经达到了付费开发软件包的专业水平,也因为我希望你们所有人都能够参与进来,所以我也会使用开源。使用开源软件包,如 Java、IntelliJ、Blender3D、GIMP、Audacity、Lightworks 等,您可以组装一个免费的新媒体应用开发工作站,与付费软件工作站相媲美,后者可能要花费您所在国家的几千个货币单位(仅是为了使本书国际化)。

对于那些刚刚购买了新的 Pro Android 可穿戴开发工作站 PC 并打算从头开始组装整个开发软件套件的人,我将介绍一个完整的工作流程,从 Java 7 开始,然后添加 Android Studio,最后是每个主要类型的各种媒体内容开发软件包:数字图像、数字视频编辑、3D 和数字音频编辑。如果你有 Macintosh,大多数开源软件都支持该平台以及流行的 Linux 发行版,甚至 Oracle 的 Open Solaris。

Java 7:为 Android Studio 安装基础

你要做的第一件事是访问 Oracle 网站,下载并安装最新的 Java 7 JDK 环境,在撰写本书时,该环境是 Java SE 7u71 、,如图图 2-1 所示。

9781430265504_Fig02-01.jpg

图 2-1 。Oracle TechNetwork 网站 Java SE 7 下载部分;为您的操作系统找到最新的 Java SE 7 JDK

网址在图 2-1 的地址栏里,或者你可以简单 Google Java SE 7 JDK 下载。这将为您提供 Java web 页面的最新链接,我也将把它放在这里,以防您想简单地剪切和粘贴它:

[www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html](http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html)

将网页右侧的滚动条拉到页面中间,显示 Java SE 开发工具包 7u71 (或更高版本7u 79)下载链接表,如图 2-1 最底部的所示。你也可以阅读位于下载链接表正上方的新 CPU 和 PSU Java 发布版本的解释;例如,在本书中,我将使用 Java 7u71。

一旦您点击此下载链接表格左上角的接受许可协议 单选按钮,链接将变为粗体,您将能够点击您想要使用的链接。如果您使用的是 Windows,并且您的操作系统是 64 位,请使用 Windows x64 链接,否则请使用 Windows x86 链接。我在我的 Windows 7 和 Windows 8.1 工作站上使用的是这些链接中描述的“Windows x64”,这是 Windows 的 64 位版本。

确保使用这个 Java SE 开发工具包 7u71 下载链接,不要使用 JRE 下载(Java 运行时版)链接。JRE 是 JDK 7u71 的一部分,因此您不必担心单独获得 Java 运行时。如果您想知道,您将确实使用 JRE 来启动和运行 IntelliJ IDE,,并且您将使用该软件包内的 JDK 来提供 Java 核心类基础,该基础用作 Android OS 基于 Java 的 API 类的基础。

确保而不是从普通(当前、最新的 Java)下载页面下载 JDK 8u25 或 JDK 8u25 捆绑包,其中包括 NetBeans 8.0,因为 Android 5 使用 Java 7u71 和 IntelliJ IDEA,而不是【the NetBeans 8.0.1 IDE 作为其 ADT 插件,所以在您的工作过程中要非常小心这个特定的初始 Java 7 JDK 基础软件安装步骤!

实际上,我在 JavaFX 开发中使用了不同的 Windows 7 工作站,其中安装了 Java SE 8u25 和 NetBeans 8,我还有另一个 HTML 开发工作站,其中安装了 Java SE 8u25 和 NetBeans 8.0(仅限)。

在运行此安装之前,您应该使用您的 Windows 控制面板,通过添加或删除程序 (XP 和更旧版本)或程序和功能 (Windows Vista、7 和 8.1)工具,删除您的旧版本 Java。

这将是必要的,尤其是如果您的工作站不是全新的,那么您最新的 Java SE 7u71 和 JRE 7u71 是目前安装在 Android Studio 可穿戴工作站上的唯一 Java 版本。

安装可执行文件下载完成后,打开它,双击 EXE 文件启动设置对话框,在系统上安装最新的 Java SE 7u71 JDK,如图 2-2 左侧所示。点击下一个按钮进入自定义设置对话框,如图 2-2 中间所示。再次点击下一步按钮,进入提取安装程序进度对话框,如图图 2-2 右侧所示。提取安装软件后,您可以选择安装文件夹。

9781430265504_Fig02-02.jpg

图 2-2 。Java SE 7 JDK 提取;单击“下一步”按钮继续提取

使用图 2-3 左侧显示的目标文件夹对话框中默认的C:\ program files \ Java \ JRE 7,然后点击下一步按钮。这将在该文件夹中安装 Java 运行时版本(JRE)版本。

9781430265504_Fig02-03.jpg

图 2-3 。Java 7 JDK 安装;单击“下一步”按钮进行安装,然后关闭按钮

有趣的是,出于某种原因,安装程序没有要求您指定 JDK 文件夹名称,可能是因为它希望 Java JDK 始终是一个固定的名称。这个 JDK 文件夹将被命名为C:\ program files \ Java \ JDK 1 . 7 . 0 _ 71,你会注意到在内部 Java 7 实际上被称为 Java 1.7.0。因此 Java 6 应该是 1.6.0,Java 8 应该是 1.8.0。例如,如果您正在使用搜索工具查找 Java 版本,或者只是为了炫耀,了解这一点是很有用的!

一旦你点击下一步按钮,你将得到 Java 安装进度对话框,如图 2-3 中的中间所示。一旦 Java 7 完成安装,你将最终看到你的完成对话框,可以在图 2-3 的右侧看到。恭喜你!您已经成功安装了 Java!

请记住,您没有下载 JRE 的原因是因为它是 JDK 安装的一部分。这个 Java Runtime Edition 是一个可执行程序(平台),一旦您的 Java 软件被编译成应用,它就可以运行该软件,因此需要最新的 JRE 来运行 IntelliJ,正如您现在所知道的,IntelliJ 是 100%完全使用 Java SE 平台编写的。

一旦 Java 7u71(或更高版本)JDK 安装在您的工作站上,您就可以从developer.android.com网站下载并安装最新的 Android Studio 软件安装程序。随着你安装的每一层 Pro Android 可穿戴设备开发软件,这变得越来越令人兴奋!

您还可以使用相同的程序和功能控制面板中的添加或删除程序工具,您可能最近使用过它来删除旧的 Java 版本,甚至确认新的 Java 安装是否成功,以删除当前可能安装在您的 Android 开发工作站上的任何 Android 4 开发环境的任何旧版本。

现在你已经准备好在 Java 7 之上添加第二层 Android 可穿戴应用开发软件(Android Studio 1.0 和 IntelliJ)。

Android Studio 1.0:下载 Android 5 创意

此过程的第二步是访问developer.android.com网站,从 Android 开发者网站的/sdk/文件夹下载并安装 Android Studio 软件安装程序文件,网址如下:

[`developer.android.com/sdk/index.html`](https://developer.android.com/sdk/index.html)

在 Android 开发者网站的主页上,点击网站主页左下方的Get SDK按钮即可进入该页面。这将把你带到本网站的 SDK 部分,如在图 2-4 的右侧所示。你也可以从 Android Studio 概述页面进入网页,如图 2-4 左侧的。Android Studio 概览页面位于以下 URL:

[`developer.android.com/tools/studio/index.html`](http://developer.android.com/tools/studio/index.html)

一旦进入 Android Studio SDK 页面,点击左下方的下载 Android Studio 灰绿色按钮,下载 Android Studio for Windows,如图图 2-4 所示。这将带你到一个下载页面。

9781430265504_Fig02-04.jpg

图 2-4 。Android Studio 下载链接(左)和下载页面(右);点击绿色的下载 Android Studio 按钮

如图 2-5 中的所示,实际的 Android Studio 下载页面在顶部包含一个部分,概述了 Android 软件开发工具包许可协议。该协议通常被称为最终用户许可协议(EULA), ,它规定了您可以和不可以使用 Android 5 Studio SDK、IDEA、软件、工具、编解码器和 APK(应用包)做什么。

9781430265504_Fig02-05.jpg

图 2-5 。选择“我已经阅读并同意条款和条件”复选框,然后单击蓝色的下载 Android Studio for Windows 按钮

仔细阅读本网页的条款和条件部分,如有必要,与您的法律部门一起,然后点击底部声明旁边的复选框,内容为:我已阅读并同意上述条款和条件,如图 2-5 中以红色突出显示的内容。

一旦这个复选框被激活(选中),你现在应该可以看到一个蓝色的下载 Android Studio for Windows 按钮。下载站点会自动检测您的操作系统,以及您的操作系统支持的位级别(32 位或 64 位)。这应该与工作站相匹配,所以如果你有 64 位处理器(CPU),你也应该有 64 位操作系统。

如果您下载了用于 Windows x64 或 Linux x64 的 Java 7u71 JDK,那么您将拥有 64 位版本的 Android Studio 相反,如果您为 32 位 x86 操作系统选择了 Java 7u71,那么您将拥有 32 位版本的 Android Studio。

激活后,点击下载 Android Studio 蓝色按钮,开始下载过程。如果你的网络连接速度很慢(调制解调器或 ISDN),这可能需要一两个小时。如果有 DSL 或 4G 连接,大概需要半个小时。如果你有光纤连接,应该只需要一两分钟。

下载完成后,您将启动一个软件安装程序,该程序将设置 Android 5 SDK 和集成开发环境(IDE ),以便在您的 Pro Android 可穿戴设备开发工作站上使用。这些文件以前是用 ZIP 文件安装的,所以这是一个很大的改进!值得注意的是,这些文件的大小也是随时变化的,所以如果在你写这本书的时候和你写这本书的时候之间 SDK 发生了变化,它们可能会有所不同。

在这个 Android Studio“捆绑包”在版本 4 中可用之前,设置这个 Android IDE 是一个复杂的过程,需要大约 50 个或更多的步骤。这些包括安装 Java SDK,然后是 Android SDK,然后是 Android 插件,然后配置插件以查看 Android SDK。现在设置 Android 5 开发环境简单多了!

新的捆绑方法通过包括 IntelliJ IDE 以及所有 Android SDK 和插件组件来完成所有这些 Android SDK 和插件配置,这允许所有这些配置工作由 Google 的人员提前完成,而不是由在家的开发人员完成。

安装 Android Studio: IntelliJ IDEA 和 Android SDK

下载完成后,您需要做的第一件事是找到您刚刚下载的文件。它应该在你的操作系统的下载文件夹中,或者在我的情况下,我为这个下载指定了我的软件文件夹,所以我导航到那个文件夹找到然后启动它。

如果您不知道浏览器在下载文件后将其放在了哪里,您也可以右键单击位于浏览器下载进度窗口中的下载文件,并选择文件夹中的视图选项。如果右键单击不起作用,文件名旁边应该有一个向下箭头,它会显示一个下拉菜单项列表。

下载进度选项卡通常位于每个浏览器的底部状态栏区域,或者可以通过下载菜单选项或浏览器右上角的图标来访问(通常是三个黑色条形,表示可以通过该图标访问菜单列表)。

在我的情况下,这个文件被称为android-studio-bundle-135.1641136.exe(我告诉过你它是一个软件包),我将在我的 64 位 Windows 8.1 工作站上使用它。一旦找到 EXE 文件,右击并从上下文菜单中选择以管理员身份运行选项。这将启动一个 Android Studio 设置对话框,如图 2-6 左侧所示。

9781430265504_Fig02-06.jpg

图 2-6 。启动 Android Studio 安装程序(左),选择默认组件(中),同意许可(右)

一旦你有了欢迎来到 Android Studio 设置对话框,点击下一步按钮,这将带你到选择组件对话框,如图 2-6 中间的所示。接受默认组件安装选择,然后单击下一步按钮。这将把你带到一个许可协议对话框,如图 2-6 右侧的所示。点击我同意按钮,将进入配置设置对话框,如图 2-7 左侧所示。接受 Android Studio 和 Android SDK 的默认安装位置,然后单击下一个按钮继续。

9781430265504_Fig02-07.jpg

图 2-7 。接受默认区域设置(左),接受 Android Studio 名称,点击安装(中),安装对话框(右)

选择开始菜单文件夹对话框中,如图 2-7 中间所示,确保文件夹名为 Android Studio ,然后点击安装按钮开始安装过程。然后你会看到一个安装对话框,如图 2-7 右侧所示,它会显示哪些组件被提取并安装在你的工作站上。如果您想查看进度条之外的更多细节,请单击该对话框中的显示细节按钮。

一旦提取和安装过程完成,你会看到完成 Android Studio 设置对话框,显示在图 2-8 的左侧。保持选择启动 Android Studio 选项。接下来,单击完成按钮,这将启动 Android Studio,这样您就可以确保安装创建了一个可用的 IDE。在图 2-8 的中间和右边部分显示了启动屏幕和设置向导。你可以看到 Android SDK 已经安装并且是最新的,所以你完成了!

9781430265504_Fig02-08.jpg

图 2-8 。选择启动 Android Studio 复选框(左),启动屏幕(中),确认安装组件(右)

点击完成按钮,然后启动 Android Studio,如图 2-9 左侧所示。在对话框的左下方,你会看到一个检查立即更新链接,你应该点击这个以确保你的 Android Studio 想法是完全最新的,它应该是最新的,因为你刚刚下载并安装了它!正如你在图 2-9 的右上方看到的,我点击了链接,得到了一个检查更新进度条,然后是更新信息对话框,在图 2-9 的右下方看到,告诉我我有最新版本的 Android Studio。

9781430265504_Fig02-09.jpg

图 2-9 。检查更新(左下角突出显示)、进度条(右上角)、更新信息对话框(右下角)

在本章的剩余部分,您将下载并安装您需要的其他工具。

专业数字影像软件:GIMP 2.8.14

GIMP 是一个类似于 Photoshop 的数字图像软件包,目前的版本是 2.8.14。3.0 版本预计将于 2015 年推出。

GIMP 有许多重要的工具来创建您的 Pro Android 可穿戴应用所需的数字图像素材,包括数字图像处理、遮罩、alpha 通道透明、自动路径创建、矢量(SVG)路径处理、图层合成等等。

要访问 GIMP 网站,您可以在 Google 搜索框中输入 GIMP,也可以输入该网站的 URL,直接进入 GIMP 主页。非营利组织的网址(。ORG) GIMP 2.8.14 网站采用以下格式:

www.gimp.org/

当 GIMP 2 主页出现时,如图 2-10 中的所示,你会在页面的中上方看到一个橙色的下载按钮,在网站主页的右上方还有一个橙色的下载链接。您将点击其中任何一个来访问 GIMP 2.8.14 www.gimp.org/downloads页面。

9781430265504_Fig02-10.jpg

图 2-10 。转到gimp.org站点,点击橙色的下载按钮来获得 GIMP 的最新版本

可以下载专门为 Windows Vista、XP、7、8.1、Linux(通用)、Mac OS/X、BSD、Solaris、Debian、Mandriva、Fedora、Open SUSE、Ubuntu 编译的版本。GIMP 支持这些操作系统的 32 位和 64 位版本。只需点击操作系统的下载链接,将软件下载到您的 Pro Android 可穿戴应用开发工作站。

直到最近,GIMP 2 还是托管在 SourceForge 上,但是该公司决定自己托管在 HTTP 和 Torrent 服务器上,因为一些关于广告客户选择加入的活动,GIMP 的创建者并不支持。我使用标准的 HTTP 链接启动了 64 位 Windows 8.1 GIMP 2.8.14 下载,效果非常好。接下来,我们来看看 Lightworks 12 数字视频编辑和特效开源软件包。

下载完成后,启动安装程序 EXE,,它应该被命名为gimp-2.8.14-setup-1.exe,除非有更高版本可用(2.10 版本预计将于 2015 年在 Q1 发布,3.0 版本将于 2015 年晚些时候发布)。你可以右键点击一个安装程序,然后以管理员身份运行来启动它。

专业数字视频编辑:Lightworks 12

EditShare Lightworks 曾经是一款(昂贵的)付费数字视频编辑和特效软件,时至今日,它与领先的数字视频编辑包(FinalCut Pro X 和 After Effects)展开了正面竞争。

您可以在 EditShare 网站上的www.editshare.com或 Lightworks 网站上的www.lwks.com找到关于这款领先的数字视频编辑 FX 软件包的更多信息,您也可以在那里注册获得该软件的副本,一旦完成,您就可以下载该软件供自己免费使用。

当 EditShare 将 Lightworks 开源后,它成为了第三个免费开源软件(第一个是 GIMP,第二个是 Blender3D ),能够在其新媒体制作类型(数字视频编辑、合成、特效应用和压缩)中与付费软件包进行功能竞争。事实上,EditShare 因其处理速度和创新的用户界面方法赢得了 NAB“最佳展示”奖。

Lightworks 是第一批重写代码以在 GPU(图形卡,如 nVidia GeForce 或 AMD ATI 镭龙)上运行的软件包之一。GPU 处理效果和编码的速度比 CPU 快一个数量级,包括 GIMP 在内的许多其他软件包现在也在代码中实现基于 GPU 的处理。

一旦您在此 Lightworks 网站上注册,您将能够为您的公司创建一个视频编辑器配置文件,并登录以便能够为您在本章中组装的内容开发工作站下载一份 Lightworks 12.1。因为 EditShare Lightworks 是一款非常有价值的软件,你需要注册才能获得它,我并不反对,因为这款软件以前的价格高达四位数。

一旦你注册成为一名骄傲的 Lightworks 12 用户,你可以点击位于网站菜单右上角的下载按钮,你会看到三个不同操作系统版本的标签——Mac、Linux 和 Windows——以及该软件的工具和文档标签。

单击与您的操作系统的位级别相匹配的红色下载按钮,或者如果您想要正在开发的最新测试版本,请单击蓝色下载按钮。下载后,使用默认设置安装它,为它创建一个快速启动图标,并启动它以确保它与您的工作站正常工作,就像您对其他软件所做的那样。

在图 2-11 中可以看到 2014 年第四季度发布的 EditShare Lightworks 版本 12 的下载页面。单击代表您的操作系统的选项卡,然后下载 75MB 软件安装程序。我下载了适用于 Windows 8.1 的 Lightworks 64 位版本 12.0.2。

9781430265504_Fig02-11.jpg

图 2-11 。转到lwks.com网站,注册并点击与您的系统匹配的操作系统选项卡和下载按钮

现在你已经有了 2D 图像和视频编辑软件包,你可能也应该有 3D 和数字音频编辑软件包了!

专业 3D 建模和动画:Blender

接下来让我们来看看世界上最流行的开源软件包之一, Blender 3D 建模 、渲染和动画 软件包,它可以在 Blender 网站的以下 URL 找到:

www.blender.org/

Blender 有一个非常活跃的开发社区,并且每个月都有更新,有时甚至会更频繁。我可以想象,在你读到这本书的时候,会有一个更高版本的软件,但是,这一节中的所有内容仍然适用。也有用 Blender 制作的 3D 电影,以防万一你想掌握这个软件,然后成为一个主要的电影制作人!

在图 2-12 中可以看到,Blender.org主页上还有一个下载 Blender 2.72b 蓝色按钮,同样会带你进入 Blender 下载页面,在这里你可以选择 32 位或 64 位版本的 Blender for Windows。Blender 也适用于 Linux、Mac、Unix 和 Solaris。

9781430265504_Fig02-12.jpg

图 2-12 。转到blender.org网站,点击云图标上的蓝色下载按钮

这个站点将自动检测您当前运行的操作系统版本,由于我运行的是 Windows 8,您将在图 2-13 中看到,Blender for Windows 选项卡被选中,显示一个蓝色的主要软件下载区域。

9781430265504_Fig02-13.jpg

图 2-13 。第blender.org/download/页;选择顶部的操作系统选项卡,然后单击您的操作系统类型的安装程序链接

正如你所看到的,世界上有许多不同的下载服务器托管这个非常受欢迎的软件包,所以如果你在荷兰或德国,你可以点击这些链接更快地下载软件(或者至少离你更近)。

因为我有运行 Windows XP (32 位)、Windows Vista (32 位)、Windows 7 (64 位)和最近的 Windows 8.1 (64 位)的新媒体内容制作工作站,所以我已经下载了这两个版本的 Blender,如果你回头看一下图 2-13 中显示的截图,就可以看到这一点。单击与您的操作系统配置(32 位或 64 位)匹配的版本(操作系统位级),然后下载 Blender 的相应版本。

一旦你的下载完成,启动安装程序 EXE,它应该被命名为blender-2.72-windows32.exeblender-2.72-windows64.exe,除非更新的版本已经可用(版本 2.8 预计在 2015 年推出)。安装完成后,右键单击图标或可执行文件,选择固定到任务栏选项,创建一个快速启动图标。

专业数字音频编辑:Audacity 2.0.6

Audacity 项目 托管在sourceforge.net上,这是一个开源软件开发网站,如果你还不知道这个网站,你可能会发现搜索你感兴趣的软件非常有趣!要访问 Audacity 项目,进入audacity.sourceforge.net网址,你会看到一个下载 Audacity 2.0.6 链接, 如图图 2-14 所示。

9781430265504_Fig02-14.jpg

图 2-14 。转到 SourceForge 网站上的audacity.sourceforge.net页面,点击下载 Audacity

请注意,32 位 Audacity 支持几十年前的操作系统,如已有十多年历史的 Windows 2000 和现在已有近十年历史的 Windows XP。我希望你的 Android 可穿戴设备开发工作站使用的是 Windows 7 或 Windows 8.1 操作系统,因为 Windows 8.1 的内存效率现在几乎和 Linux 一样高了!

一旦下载了 Audacity 安装程序文件,您就可以启动它并继续安装这个功能丰富的数字音频编辑软件。它问你的第一件事是你想用什么语言运行这个软件,我选择了默认语言,英语。然后我点击了一个下一个按钮,读取信息。然后,我再次单击 Next 按钮,接受默认的安装位置,并创建了桌面图标。最后,我点击了安装按钮,得到了安装进度条对话框,以及关于 Audacity 项目的更多信息,还有最后一个对话框,在那里我可以点击完成按钮来退出安装软件。

如果你愿意,你可以按照我用 Blender 做的相同的工作过程,通过右键单击 Audacity 2.0 图标并选择固定到任务栏,在任务栏上放置一个快速启动快捷图标。您可以通过将启动图标拖动到任务栏上您喜欢的任何位置来重新定位它们。

现在 Audacity 已经安装好了,您可以启动音频编辑软件并确保它在您的系统上工作。通过快速启动图标或双击桌面上的图标来启动 Audacity。你应该看到一个新的空白项目,如图 2-15 所示,在桌面上打开。在本书的后面,你将使用 Audacity 为你的 Android UI 元素对象添加音效, ,比如你的按钮和 ImageButton 对象。

9781430265504_Fig02-15.jpg

图 2-15 。启动 Audacity,确保它能在您的可穿戴设备开发工作站上正常运行

接下来让我们下载领先的开源用户界面设计原型软件包 Pencil 2.0.5。这适用于 Windows、Linux 和 Macintosh,并以所谓的“模板”包的形式支持 Android 和 HTML5 用户界面,这些包适用于 Android 4、iOS、Dojo JS、Ext JS Neptune,将来可能还会支持 Android 5。如果你喜欢绘制 UI 设计,你会喜欢铅笔!

专业 UI 设计线框化:铅笔项目 2.0.5

接下来,您将下载并安装一个名为 Pencil 的用户界面(UI) 线框化原型化工具,目前版本为 2.0.5。在谷歌上搜索 Pencil 或直接进入以下网址,可以在图 2-16 的顶部看到:

http://pencil.evolus.vn

9781430265504_Fig02-16.jpg

图 2-16 。进入pencil.evolus.vn网站,点击橙色下载按钮下载 Pencil 2.0.5

当 Pencil 项目主页出现时,点击橙色的下载按钮,下载 22MB 软件安装程序可执行文件。这应该被命名为类似于 Pencil-2.0.5.win32.installer.exe 的**。Pencil 只使用 32 位二进制,因为软件不需要 64 位提供的功能。**

一旦下载完成,启动安装程序,当它完成时,右键单击你的桌面图标或开始菜单图标(甚至在一个可执行文件上)并选择固定到任务栏选项来创建软件的快速启动图标快捷方式。我的 Windows 8.1 任务栏如图图 2-17;注意我已经添加了字体**、计算器文本文件管理工具。

9781430265504_Fig02-17.jpg

图 2-17 。显示快速启动任务栏,带有关键的操作系统工具、新媒体软件和 Android Studio

现在,为了彻底起见,让我们安装一个完整的商业生产软件套件,以防您需要为未来的 Android 可穿戴设备软件开发项目整理报价、电子表格甚至合同!

专业商业软件套件:OpenOffice 4

为了 100%确保您的 Android 开发工作站已经安装了您的专业 Android 可穿戴设备应用开发业务所需的一切,让我们以另一个名为 OpenOffice 4 的软件包来结束这一令人印象深刻的专业软件安装,该软件包最初来自 Java 的制造商 Sun Microsystems,被 Oracle 收购,并在开源后移交给 Apache。

在谷歌上搜索 Apache Open Office,或者访问www.openoffice.org网站,然后单击我想下载 OpenOffice 4 链接,或者访问 Open Office 4.1.1 下载页面,该页面位于以下 URL,如果您想直接在浏览器中键入该 URL:

[`www.openoffice.org/download/index.html`](https://www.openoffice.org/download/index.html)

正如你在图 2-18 中看到的,网站可以检测到你正在使用的操作系统和位级别,以及你正在使用的语言!

9781430265504_Fig02-18.jpg

图 2-18 。Apache OpenOffice 4.1.1 下载页面,已经自动检测到我的 64 位 Windows 8.1 操作系统

一旦你进入/download/页面,如图 2-18 所示,点击绿色箭头为你的操作系统下载最新版本的 office 套件,在我的例子中是 Windows 8.1(该网站为我自动检测)。这个下载差不多有 135MB,包含了六个以上的生产力软件包,包括一个文字处理器、一个电子表格和一个数据库。

下载完成后,启动你的 installer EXE,完成后右击图标或可执行文件,选择 Pin to Taskbar 选项,为软件创建快速启动图标快捷方式。

接下来,我将告诉你一些其他的开源和负担得起的 3D 软件包,我用它们来为我的客户创造新的媒体内容。

其他开源和负担得起的媒体软件

还有很多其他的开源软件包,如果你想要的话都有,包括 SketchUp (建筑渲染) TerraGen3 (虚拟世界创作)true space(3D)Wings3D、 Bishop 3D、 POV Ray 3.7 (3D 渲染) Rosegarden (音乐作曲、MIDI 和配乐) Qtractor (声音

还有一些非常实惠的 3D 软件包你也应该看看,包括 NeverCenter SILO 2.3(四边形 3D 建模)Moment of Inspiration3(NURBs 3D 建模) Vue 3D (3D 世界生成) Hexagon 2.5(多边形 3D 建模)【Auto-Des-Sys Bonzai (3D 建模)以及 NewTek Lightwave 3D (3D 建模、3D 动画

我在每个新媒体内容制作工作站上安装了这些专业开源软件包。这允许你为你所有的制作人创建完全加载的 3D 制作工作站,唯一的成本是硬件,你可以在沃尔玛花 300 到 600 美元买到。

恭喜你!您现在已经组装了专业级的 Android 可穿戴应用和新媒体内容开发工作站,您现在可以利用它来创建专业的 Android 可穿戴应用,并在世界未发现之前开发用户界面设计和用户体验!

接下来,让我们快速概述一下您将在本书的课程中学习的一些内容,现在您已经了解了可穿戴设备、Android 5 材质设计,并设置了您将用于开发可穿戴应用的 Pro Android 可穿戴设备开发工作站。

摘要

在这一章中,您完全设置了您的全面的 Pro Android 可穿戴设备 Android Studio 应用开发工作站,学习了硬件要求(以及您真正应该拥有的东西)以及关于开源软件的所有内容,在过去的十年或二十年中,开源软件最终变得像付费软件包一样专业,这些软件包可能很容易就要花费数千美元!

您下载并安装了 Java SE 7、Android Studio、新媒体内容制作软件、(可选)用户界面设计原型、商业生产力工具,您下载并安装了世界上最令人印象深刻的开源软件包。

你这样做是为了给你的 Pro Android 可穿戴设备和设备应用开发工作流程打下基础,你将在本书中进行这项工作。我决定让你先安装所有的软件,而不是在你进行的时候安装这些软件包。我这样做是为了防止你想在实际阅读本书之前探索这些强大、令人兴奋的新媒体内容制作软件包的许多功能。我认为这很公平。

这个过程最棒的一点是,你是通过使用开源、100%免费的商业用途和专业级应用软件包来完成的,如果你仔细想想,这是非常惊人的。

你从下载安装甲骨文 Java SE 7u72 JDK 或者 Java 开发包开始,这是 Java 7 编程语言的 SDK。这个 Java JDK 是使用 Eclipse 以及为消费电子设备的 Android 操作系统平台开发应用软件所必需的。

然后你访问了 Android Developer 网站,下载并安装了 Android Studio Bundle ,它在你的 Java SE 7 编程软件开发环境之上构建了 IntelliJ IDEA 以及所有的 Android Developer Tool 插件。

接下来,您下载并安装了 GIMP 2.8.14 ,这是一个强大的数字图像编辑包,可用于 Windows、Linux 和 Macintosh 操作系统。

然后你下载并安装了 Lightworks 12 ,这是一个数字视频编辑和特效包,最近作为开源发布,目前可用于所有流行的操作系统。

然后,您下载并安装了 Blender 2.72 ,这是一款适用于 Windows、Linux 和 Mac OSs 的专业 3D 建模、渲染和动画工具。

接下来,您下载并安装了 Audacity 2.0.6 ,这是一个可用于 Windows、Linux 和 Mac OSs 的开源数字音频编辑工具。

然后您下载并安装了 Pencil 2.0.5 ,这是一个流行的 UI 线框和原型工具,可用于 Windows、Linux 和 Mac OSs。

最后,您安装了 Apache Open Office 4.1.1 ,只是为了确保您拥有一个完全完善的开源工作站。

在下一章中,您将了解 Android 5 的所有新功能,从而为 Android 5 操作系统为您的定制可穿戴设备应用开发提供的功能打下坚实的基础。**

三、Android 可穿戴设备的基础:Android 5 中新的可穿戴功能

现在,您已经组装了一个 Android 开发工作站,上面安装了那些有价值的(但免费的)、专业级的开源软件包,是时候看看 Android 5 为可穿戴开发添加的所有新东西了。我在第一章中彻底讨论了材质设计的增加以及可穿戴的类型和概念,因此本章将涵盖更多技术和“幕后”的增加,如新媒体硬件和编解码器支持,新技术,如最新的蓝牙OpenGLWebKitWebAudioWebRTC 平台,以及其他您希望了解和了解的东西,以便您的 Android

当然,我会在本书中尽可能多地利用 Android 5 的这些功能,为你的可穿戴设备应用添加更多功能。也就是说,我在这里只有几百页来完成这一点,而 Android OS 有数千个类、方法和常数,所以我将专注于 Android 5 的功能,以及这些功能如何应用于可穿戴设备应用。我这样做主要是因为这是一本关于专业 Android 可穿戴设备的书,因此,我假设你已经熟悉 Android 4 以及之前的 Android 操作系统版本。本章将介绍 Android 5 电源管理、网络连接、视频、音频和 3D 新媒体、网络媒体渲染、超高清摄像头支持、数据存储、锁屏通知和通知元数据、屏幕共享、屏幕捕捉和 Android 扩展包。

Android 的项目 Volta:电源管理工具

目前,可穿戴设备应用最关键的领域之一是电源管理领域。许多智能手表型号仍在纠结于电池寿命的问题。智能手表制造商正试图让产品在不充电的情况下持续使用数周或数月;然而,更好的电池技术,或者一个简单的智能手表支架,当智能手表在晚上用户睡觉时给电池充电,最终可能会产生更好的解决方案。无论如何,Android 5 解决了其项目 Volta 中的电源问题,该项目专注于通过使用任务调度器(电源管理)和任务最小化(电源优化)API 来使 Android 更加节能。

Android 5 进程调度器:JobScheduler 和 JobInfo

Android 5 增加了一个新的 android.app.job 包,其中包含 JobScheduler ,这是一个类(对象),可以让你通过为 Android 操作系统定义作业(任务处理)来异步运行,从而优化可穿戴设备的电池寿命。异步处理允许任务在“不同步”(无序)的情况下处理,可能会在稍后进行,这样操作系统就可以在较低的级别优化内存和 CPU 的使用。因为作为可穿戴应用开发人员,您不知道用户将运行哪些应用,以及以什么顺序运行,所以作为可穿戴应用开发人员,您将无法实现这种级别的优化,而这个 job API 允许 Android 为您实现这一点。

这些作业(任务)也可以被安排在更优化的条件下进行处理,例如当 Android 设备正在充电并且可以获得无限的电力时,或者在可穿戴应用开发者可以指定的其他类型的条件下,例如当视频正在流动时。

这个 JobScheduler 类 用于保证 Android 可穿戴设备在可穿戴设备开发场景中的电源优化,在这些场景中,可穿戴应用具有可以推迟的非面向用户的任务(后台处理),或者应用具有开发人员希望在可穿戴设备(和主机)插入电源(充电)时处理的任务。

开发人员还可以根据外部资源的可用性安排任务处理,例如,电信(4G LTE)网络接入、蓝牙主机设备或本地 Wi-Fi 连接的可用性。

您还可以使用这个 JobScheduler 类进行批处理。这允许开发人员将调度的任务组合在一起,以便它们可以“批处理”运行批处理在大型机和小型机时代就开始流行了,现在被称为企业计算和服务器!

批处理是计算机同时处理所有任务的地方。这通常是定期完成的,例如每小时、每天、每周或每月。在大型机计算的早期,批处理被安排在晚上,此时 IT 部门正在休息,所有的计算机能力都可以集中在处理上。请注意,您也可以一次调度一个基于时间的任务,因此 JobScheduler 类也可以用于执行例行或基于时间的任务处理。

JobScheduler 对象在 JobService 对象(类)内部运行,该对象是 Android 服务的子类,管理所有需要调度的作业。由 JobScheduler 对象处理的每个“工作单元”是使用一个 JobInfo (类)对象定义的。Android OS 也会生成一个 JobParameters 对象。这包含作业标识、配置和参数。这些是由 Android 操作系统基于所有计划的作业生成的。

JobInfo 对象用于指定开发者希望 Android 操作系统如何处理调度标准。该对象是使用 JobInfo 创建的。Builder 类,它允许开发人员使用 Java Builder 语法并轻松配置调度任务应该如何运行。

您可以安排 JobInfo 任务对象在精确(严格)或特定的条件下运行。这些包括当设备正在充电时开始任务或仅当设备正在充电时处理该任务,当设备连接到未计量的网络时运行或开始任务,每当设备空闲(未被主动使用)时开始任务处理,或在某个截止日期出现之前或在任务处理开始之前具有最小延迟的情况下处理任务。

例如,以下构建器 Java 代码结构将按此顺序创建一个 JobInfo 对象,该对象将在设备重新启动后持续存在,要求设备处于充电状态,定期运行任务,将任务的运行延迟一定的延迟因子,并要求设备连接到未计量的网络(如家庭 Wi-Fi 或 DSL[数字用户线]服务)。

JobInfo uploadTask = new JobInfo.Builder( mJobId, mJobServiceComponent )
                                .[setPersisted](https://developer.android.com/reference/android/app/job/JobInfo.Builder.html#setPersisted(boolean))( true )
                                .[setRequiresCharging](https://developer.android.com/reference/android/app/job/JobInfo.Builder.html#setRequiresCharging(boolean))( true )
                                .[setPeriodic](https://developer.android.com/reference/android/app/job/JobInfo.Builder.html#setPeriodic(long))( intervalUsingLongDataValueInMilliseconds )
                                .setMinimumLatency( minimumLatencyUseLongDataValueMilliseconds )
                                .setRequiredNetworkCapabilities( JobInfo.NetworkType.UNMETERED )
                                .build();

JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(uploadTask);

值得注意的是,如果设备具有“稳定”的电源(即,它已经插上电源(超过两分钟)并且电池寿命处于健康水平),如果 JobInfo 对象的任务处理截止日期尚未到期,系统将运行任何准备运行的计划作业。

Android 5 电池优化器:电池历史工具

还有一个新的ADB shell dumpsys battery stats命令,可以生成关于安卓设备上电池使用情况的统计数据。这些数据使用一个唯一用户 ID (UID) 进行组织。每当调用该命令时将收集的统计数据包括该 Android 硬件设备的电池相关事件和全局电源相关统计数据的历史记录、每个 UID 和系统组件的电源使用的近似值、每个数据包的每个应用移动毫秒数、UID 系统汇总统计数据和 UID 应用汇总统计数据。

如果您想了解更多关于定制工具输出的各种命令行选项的信息,您可能想使用一个名为 - help 的开关(命令行工具使用开关或选项,以一对减号或破折号开头)。我会使用以下命令行格式:

$ adb shell dumpsys batterystats --help

例如,如果您想要打印自 Android 设备上次充电以来给定应用包的电池使用统计数据,您可以运行以下命令行格式来实现:

$ adb shell dumpsys batterystats --charged <your-package-name-here>

您可以在 dumpsys batterystats 命令的输出上使用 Google Battery Historian 工具。这将为命令日志中与电源相关的事件生成一个基于 HTML5 的可视化视图。这些信息让您更容易理解和诊断与电池相关的可穿戴设备的应用编程问题。如果你愿意,你可以在 GitHub 上下载一份谷歌的电池历史记录工具,网址如下:

[`github.com/google/battery-historian`](https://github.com/google/battery-historian)

接下来,我将解释 Android 5 可以连接的不同类型的网络。这对于可穿戴应用显然非常重要。

安卓的网络连接 : NFC 和蓝牙

目前 Android 可穿戴硬件设备的另一个关键支持领域是多数据网络支持的领域。支持更多电信网络的可穿戴设备将更好地与世界连接,因此具有更多有用的可穿戴应用。当您的可穿戴 Android 设备可以找到并接入各种不同的数据网络,如 4G LTE、Wi-Fi、蓝牙和 NFC(近场通信)时,您的可穿戴应用对企业和消费者都变得更有价值。

Android 5 多重网络支持:ConnectivityManager

Android 5 提供了一个全新的多网络 API ,它将允许你的可穿戴应用动态扫描具有特定功能的可用网络。一旦 API 找到一个网络,您将能够建立到它的连接,为您的可穿戴应用的功能提供数据。

当可穿戴应用需要专用网络时,Androidconnectivity manager类提供的功能会非常有用。Android 当前支持的网络可以在 ConnectivityManager 类的常量列表中找到,其中包括 TYPE_BLUETOOTH (蓝牙数据连接) TYPE_DUMMY (虚拟连接) TYPE_ETHERNET (以太网连接) TYPE_MOBILE (移动连接) TYPE_MOBILE_DUN (DUN 移动连接) TYPE_MOBILE_HIPRI (高优先级移动连接) TYPE_MOBILE 如果您想使用特定类型的传输协议传输数据,这个类也很有用。

要从应用中动态选择并连接到数据网络,首先需要创建 ConnectivityManager 对象。接下来,您将使用一个名为 NetworkRequest 的构建器类。构建器 创建网络请求对象,然后指定可穿戴应用感兴趣连接(网络类型)和利用(协议)的网络功能和传输类型。

要扫描各种支持的数据网络,可以使用**。requestNetwork( )** 然后可以将 NetworkRequest 对象和一个 ConnectivityManager 一起传递给方法调用。NetworkCallback 对象。这应该通过以下方式完成。requestNetwork()方法对 Java 编程结构的调用:

.requestNetwork( NetworkRequest, ConnectivityManager.NetworkCallback )

您还可以设置 Android,通过使用**来通知您的应用检测到网络可用性。****registerNetworkCallback()**方法调用在 Java 代码中为进入范围的网络设置一个监听器。这应该使用。使用以下 Java 编程结构进行 registerNetworkCallback()方法调用:

.registerNetworkCallback( NetworkRequest, ConnectivityManager.NetworkCallback )

一旦检测到合适的网络,如果想要主动切换到合适的网络,可以使用 requestNetwork()方法。如果您希望只接收被扫描网络的通知,而不主动切换到它们,那么您可能希望使用. registerNetworkCallback()方法调用。

当 Android 操作系统检测到合适的网络时,它将连接到该网络并调用一个**。** onAvailable( ) 回调方法调用。

如果您想确定有关可用网络的其他信息,可以轮询从 NetworkCallback 对象返回的网络对象。完成此操作后,您可以指示您的应用使用这些选定的网络之一来执行数据传输操作。

Android 5 低能耗蓝牙:蓝牙 LE API

你大概还记得 2014 年 Android 4.3 推出了对蓝牙低能耗 (一个蓝牙 LE API)的平台支持,支持 Wear SDK。在 Android 5 中,用户的 Android 硬件设备也可以充当蓝牙 LE 外围设备,允许它与其他蓝牙设备进行“社交”。本质上,这将 Android 硬件变成了蓝牙集线器!

Android 5 可穿戴设备应用可以利用这一功能,让附近任何支持蓝牙的消费电子设备知道自己的存在。使用这一新功能,您可以构建可穿戴应用,允许设备充当计步器或心脏监视器,如果可穿戴硬件支持这些功能,然后将产生的医疗保健数据与附近的另一个蓝牙 le 设备进行通信。

这个新的蓝牙 LE API 包含在一个名为 android.bluetooth.le 的 Android 包中,你可能已经猜到了。这种新的 API 使您的可穿戴设备应用能够做一些事情,如广播广告,扫描响应,以及与其他蓝牙设备建立连接。

如果您想要使用这些新的广告或扫描功能,您需要将 BLUETOOTH_ADMIN 权限添加到 AndroidManifest.xml 文件中。当您的最终用户从谷歌 Play 商店下载(或更新)您的可穿戴应用时,他们将被要求授予这种“蓝牙连接信息 : 允许应用控制蓝牙,包括向附近的蓝牙设备广播或获取有关这些设备的信息”权限,以用于您的可穿戴应用。这在应用用于连接(双向)其他蓝牙设备之前获得了用户的许可。

要开始蓝牙 LE 广告,以便任何支持蓝牙的设备都能够检测到可穿戴应用,请调用**。** startAdvertising( ) 方法然后传递它一个 Android 广告回调类的实现。

这个广告回调对象将接收关于蓝牙 LE 广告操作成功或失败的报告。Android 5 还增加了一个 ScanFilter 类,这样可穿戴设备就可以只扫描它们感兴趣的特定类型的设备。要开始蓝牙 LE 设备扫描,您需要调用一个**。** startScan( ) 方法,传入你的 ScanFilter 对象列表。您还必须提供对您的 ScanCallback 对象的引用。每当检测到任何指定的蓝牙(设备)广告时,ScanCallback 对象将向可穿戴应用报告。

Android 5 NFC 改进:近场通信

Android 5 还对他们的 NFC 技术实现进行了增强。这些新增功能将使这一强大的 NFC 技术得到更广泛、更灵活的应用。

Android Beam 技术被添加到 Android 4 中,但从未真正应用过可靠的用户体验设计,因为相对于两个不同的 Android 用户来说,该技术很难启动,该技术已被添加到 Android 5 操作系统的 Android 共享图标区域。

只要你想让用户分享数据,你的可穿戴应用就可以在用户的 Android 硬件上调用这个 Android Beam 应用。这将通过调用**来完成。****【invoke beam()**方法。

使用这种方法调用进行设置,避免了用户为了完成数据传输而手动点击其他支持 NFC 的设备,从用户体验的角度来看,这种技术更容易使用。

要通过 NFC 连接传输数据,您需要创建并利用一个 NDEF (NFC 数据交换格式)记录,它使用轻量级二进制格式。这种 NDEF 格式由 NFC 论坛指定,可用于封装类型化数据,以便使用 NFC 连接进行传输和存储。有趣的是,NDEF 是交通不可知论者。

该 NDEF 用于定义消息和数据记录。您的 NDEF 记录将包含类型化数据,比如 URL 或 URI(统一资源标识符)、MIME 类型的媒体、文本消息或自定义应用数据传输“有效负载”一个 NDEF 消息是一个或多个 NDEF 记录结构的容器。这可能需要一点时间来适应,因为通常情况下,数据记录会包含一条消息,而不是相反。我会称之为 NDEF 包,但只要你知道 NDEF 消息是 NDEF 记录的集合,那么如果你想开发使用 NFC 的应用,你就可以开始了!

例如,如果您想创建一个 NFC 消息应用,您可以使用新的**。** createTextRecord( ) 方法创建一个包含 UTF-8 文本数据的 NDEF 记录。这个 Android NdefRecord 类也有一个**。createUri( )** 和一个**。createMime( )** 方法分别用于创建 URI 和 Mime 数据记录类型。

如果你正在开发一个信用支付应用,你现在有能力动态注册你的 NFC 应用 ID ( AID ) 。这可以通过调用一个registeridsforservice()方法来完成。您还可以使用 setPreferredService( ) 方法调用来设置首选的卡仿真服务。当特定的活动在前台(由最终用户使用)时,总是会调用此方法。接下来,我们来看看安卓媒体!

Android 5 媒体:给可穿戴设备增加惊喜因素

除非你的可穿戴应用都是纯功能性的,否则你的可穿戴应用在市场上的成功将取决于你如何实现媒体元素以达到令人惊叹的水平。这将允许你让买家选择你的应用,而不是其他(竞争)可穿戴应用。本节将介绍所有新媒体素材,如数字视频、数字音频和 3D,以及其他领域,如 web 浏览器支持(WebKit)、WebAudio、WebGL、WebRTC、MediaBrowser 和新的超高清(UHD)Camera 2 API。

数字视频播放:媒体控制器和媒体会话

Android 5 增加了一个全新的媒体控制包,名为 android.media.session 。这个包包含了全新的 MediaControllerMediaSession 类,以及这两个类的嵌套类,还有 MediaSessionManagerPlaybackState 类,它们也有嵌套类。

使用新的通知和媒体 API 来确保系统 UI 了解您的媒体播放,并可以提取和显示专辑封面。由于 MediaSession、PlaybackState 和 MediaController 类以及它们的九个工具嵌套类,使用带有 MediaSessionManager 服务的 UI 设计来控制媒体播放现在是一种无缝的开发人员体验。

您将使用一个Context . getsystemservice(Context。MEDIA_SESSION_SERVICE) 调用以获取 MediaSessionManager 对象(类)的实例,并调用。getSystemService(上下文。MEDIA_SESSION_SERVICE)方法从您的上下文对象中删除。这方便地为 MediaSessionManager 服务类提供了显示可穿戴应用正在做什么的上下文(对象)。

MediaSession 类取代了现在已经废弃的 RemoteControlClient 类,并提供了一组统一的回调方法。它们处理走带控制和媒体按钮。如果您的可穿戴应用提供媒体播放,您将需要使用 MediaSession 类(对象)来处理使用这些回调方法的传输控制。值得注意的是,以这种方式编码的应用可以在 Android TV 和 Android Wear SDK 上运行。

您甚至可以使用 MediaController 类创建自己的 MediaController 可穿戴应用。这个类提供了一种“线程安全”的方法,这样你就可以从可穿戴应用的 UI 进程(主 UI 线程)内部监视和控制你的媒体素材回放。要创建媒体控制器,需要指定一个 MediaSession。令牌对象。完成后,您的应用可以与 MediaSession 对象进行交互。你将呼叫**。。停()。skipToNext( )** ,或者**。setRating( )** 方法来控制媒体素材的回放。

这是通过使用媒体控制器来完成的。TransportControls 对象(嵌套类),正如您可能从类名中注意到的,它提供了一个 UI 传输控制对象来与 MediaController 对象一起使用,media controller 对象是 MediaSession 对象的一部分。

另一个 MediaController 嵌套类, MediaController。回调,将允许你注册一个媒体控制器。回调对象。这将监听素材的元数据,并将媒体会话的任何状态变化实时报告给可穿戴应用,以便您的代码能够对其做出响应。

您可以创建多媒体通知,这允许您使用新的通知将回放控制绑定到媒体会话。MediaStyle 类。这个类是 android.app 包的一部分,是通知的子类。Style 类,它允许您将媒体与多媒体设计混合在一起,例如,显示带有传输 UI 的专辑封面。

数字音频播放:增强的音轨精度

Android 5 AudioTrack 类也在 android.media 包中,它为 Java 应用管理和播放单个音频资源。它允许开发人员将 PCM(脉冲编码调制)音频缓冲直接从系统内存流式传输(动态)或播放(静态)到音频硬件,以获得低延迟的用户体验。

这是通过使用以下三种方法之一将数据加载到系统内存中的 AudioTrack 对象来实现的。write()方法调用参数表结构:

.write( byte[],  int, int )
.write( short[], int, int )
.write( float[], int, int, int )

在流(动态)模式下,可穿戴应用使用以下三者之一将连续的数据流写入 AudioTrack 对象。write()方法。动态流模式在播放由于音频持续时间过长或数字音频素材数据压缩特性(例如,24 位采样分辨率下的高频采样率,如 THX、48Khz 16 位或 24 位数字音频)而导致数据量过大而无法一次放入内存的音频数据块时非常有用。

动态音频流也是灵活的,因为它可以在先前排队(加载到系统存储器中)的数字音频播放时被接收或生成。

当处理易于放入系统内存的短声音时,应使用静态音频模式,该模式需要使用最小的等待时间(延迟)进行回放,以确保同步准确性。

因此,当实现需要经常或快速连续播放几次的 UI 或游戏音频素材时,应该使用静态音频模式,尽可能使用低延迟和最少量的系统存储器。

一旦你创建了一个 AudioTrack 对象,Android 操作系统就会为 AudioTrack 对象初始化一个相关的音频缓冲区。缓冲区的大小是在构造 AudioTrack 对象时指定的。这决定了 AudioTrack 对象在用完数据之前将从内存中播放多长时间。对于使用静态模式的 AudioTrack 对象,缓冲区大小是可以使用 AudioTrack 对象播放的声音的最大大小。

AudioTrack 类提供了三个重载的构造函数方法,它们可以采用以下构造函数方法调用参数列表格式:

AudioTrack( int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode )
AudioTrack( int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode, int sessionId )
AudioTrack( AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
            int mode, int sessionId )

对于流模式,数据将以小于或等于总缓冲区大小的块写入音频接收器。AudioTrack 不是最终版本,因此允许子类,但不建议这样使用。这个版本包括了 Android AudioTrack 类的一些值得注意的变化。

您的应用现在可以使用高精度浮点格式和新的 Android 常量 ENCODING_PCM_FLOAT 来提供音频数据。这是为了增加 PCM(也称为 WAVE audio)音频数据格式的动态范围。这允许高清晰度(24 位和 32 位)波数字音频(。WAV 文件)具有更一致的精度以及更多的“净空”

这是因为浮点精度对于在数字音频素材使用的计算类型中提供更准确的结果非常有用。回放端点仍然对数字音频素材使用整数数据格式,因此使用较低的位深度。与 Android 5 一样,部分内部数字音频管道尚未完全重新编码,以利用 100%浮点数据表示。

Android 5 中还有一些强大的数字音频处理算法类,保存在 android.media.audiofx 包内。那些熟悉音频工程的人会因为我简单地提到一些类名而激动不已,这些类名揭示了每个进程的作用。这些令人印象深刻的音频处理算法以类的形式实现,包括 BassBoost、EnvironmentalReverb、均衡器、虚拟化器、PresetReverb、可视化器、声音增强器、噪声抑制器,最后是声音取消器和自动增益控制。

正如你所看到的,Android 非常关注数字视频、数字音频、数字插图以及 3D 渲染和动画等专业多媒体类型,我将在接下来进行解释。伙计们,抓紧你们的帽子,我们就要进入第三维度了!

实时 3D 渲染:OpenGL ES 和扩展包

Android 5 增加了大量强大的交互式 3D 功能,也称为 i3D。这些都是为了增加 Android 操作系统的惊喜因素,以便它可以与市场上所有其他 64 位操作系统竞争,如 iOS 8 和即将推出的 Windows 9。

人们常说“扁平化 UI 设计”是流行趋势;然而,Android 5 OS 似乎在抵制这种趋势,它包括了 3D 涟漪效果,dropshadow 的微调控制(阴影高度,Z 层顺序,视图自动阴影),OpenGL ES 3 支持和 OpenGL 4.4 仿真。

这些功能旨在将 Android 5 置于与 Xbox 和 PlayStation 游戏机平等的竞争环境中。这种 i3D 功能的大部分被用于新的 Android 5 材质设计模式,我在第一章中详细讨论了这一点。来学习一下 OpenGL 和 Android 扩展包吧!

open GL ES 3.1:Android 5 的增强 3D 渲染技术

Android 5 中添加的最强大的 3D 功能之一是包含了最新版本的 OpenGL ES 3.1 。你可能已经猜到,这个“ES”代表嵌入式系统。这个版本的 OpenGL 经过优化,可以在浏览器和嵌入式设备上运行,如智能手表和智能眼镜。

OpenGL ES 3.1 是这种实时 i3D 渲染技术的最新版本。OpenGL 常用于流行的 3D 视频游戏中。大多数新功能都与使 3D 对象看起来逼真的“皮肤”有关。在 3D 建模或动画行业中,这些被称为“纹理贴图”。这些纹理贴图由不同的“着色属性”组成,通常称为“着色器”

高级效果,如动画表面或响应游戏播放或位置的表面,可以使用表面材质中每个着色器“槽”中的“着色器语言”来创建。这有点类似于在图像合成中使用层,只是着色器在数学上要复杂得多,因为它们包含许多允许它们实现照片真实感的属性。其中有颜色透明度光泽(闪亮度)、光照(光晕)、反射度(倒影)、以及凹凸贴图(表面高度)。

OpenGL ES 3.1 支持增强纹理映射 功能,包括“多样本”纹理功能,可在纹理映射应用中产生更高的边缘质量。这类似于你将在本书后面学到的反走样。

OpenGL ES 3.1 还支持模板纹理映射、,用于增强纹理贴图的深度错觉。这些可以使用深度缓冲纹理来创建,而不是实际的 3D 几何体(基础网格或 3D 多边形模型)。这些高级功能使 3D 游戏输出质量可与 PlayStation 4 或 Xbox 等传统游戏平台相媲美。

很明显,谷歌正在用 Android 5 追赶他们的 3D 游戏机竞争对手。市场上已经有几款基于安卓系统的游戏机,包括售价 99 美元的 OUYA、亚马逊 Fire TV 和售价 199 美元的 nVidia Shield。这些最有可能在 2015 年升级到 Android 5。

OpenGL ES 3.1 还支持一种叫做“计算着色器”的东西这些实际上根本不是纹理贴图或着色器,而是一种编程构造,允许开发人员使用图形处理单元(GPU)来执行与 3D 无关的计算。使用 GPU 而不是 CPU 进行数学计算更快、更高效,因为 GPU 针对浮点数学进行了优化,而 CPU 更适合(优化)基于整数的数学。

在游戏应用中,计算着色器的流行实现是使用计算着色器将游戏物理计算卸载到 GPU 上。这样做是因为当前的 GPU 技术比 CPU 技术更强大,至少在解决高度复杂的数学计算时是如此,这些计算通常在 i3D 游戏应用中使用。

许多开源软件包,如 GIMP 3 或 EditShare Lightworks 12,利用计算着色器使其软件以闪电般的速度执行复杂的特效和像素过滤应用。

只要用户在他们的 Android 设备中有 nVidia GeForce 或 AMD 镭龙 GPU,可穿戴应用也可以利用计算着色器。有一些 Android 设备采用了 Tegra K1 微处理器(由英伟达制造,目前在英伟达 Shield 和亚马逊 Fire TV 中使用),因此可以利用这种能力,通过使用这些 OpenGL ES 3.1 计算着色器来使用 GPU 进行非 3D 中心的计算。

OpenGL ES 3.1 还向后兼容 OpenGL ES 2 和 OpenGL ES 3,所以请放心,您现有的 3D 可穿戴应用代码都不会被破解。OpenGL ES 3.1 将提供可选的“扩展”,允许第三方制造商(如 nVidia)添加高级混合模式(纹理层合成特效)和微调的阴影效果,如完整的 OpenGL 4.4 规范中的那些。

事实上,我接下来将要讨论的 Android 扩展包(AEP)就是这种第三方扩展特性的一个典型例子。

Android 5 增加了 Java 接口和对 OpenGL ES 3.1 的原生支持。Android 上 OpenGL ES 3.1 的 Java 接口是通过 GLES31 提供的。如果你使用 OpenGL ES 3.1,一定要在 Android Manifest 文件中用 < uses-feature > 标签声明它,还有 android:glEsVersion 属性,像这样:

<manifest>
          ...
          <uses-feature android:glEsVersion="0x00030001" />
</manifest>

接下来,我们来看看如何将 Android 5 升级到 OpenGL 4.4!

Android 扩展包:使用 OpenGL ES 模拟 OpenGL 4.4

OpenGL ES 3.1 标准还有一个额外的扩展叫做 AEP ( Android 扩展 Pack ) 。AEP 允许在完整的 OpenGL 4.4 版本中发现的特性。这意味着具有类似于《疯狂足球》、《虚幻 4》和《光环 4》中所见 3D 图形的 3D 主机游戏将能够在 Android 5 和支持 GPU 硬件的更高版本硬件设备上运行,如 nVidia Tegra K1。

AEP 的主要特性包括镶嵌着色器几何着色器自适应可伸缩纹理压缩(ASTC),所有这些我都将在本节中介绍。几何着色器是 OpenGL 中的第一个特性,所以让我们先来看看这个概念。

3D 对象的几何图形是底层的 3D 模型,就像形状一样,只是三维的而不是二维的。几何体有时也被称为“网格”,因为没有任何纹理映射,这就是它看起来的样子。3D 几何的另一个术语叫做“线框”,也来自于这种网状 3D 几何的外观。几何体使用“多边形”建模,每个多边形都是三角形(称为“三边形”)或四边形(称为“四边形”)。

几何着色器允许底层网格变得更加精细,而无需添加任何多边形。这允许使用“低多边形”建模方法创建 3D 模型,这降低了数据占用空间(更小的文件大小)。几何着色器允许低多边形网格看起来像高多边形网格,更平滑。几何着色器使用 GPU 来应用称为“镶嵌”的优化算法,这将添加更多的“顶点”

每个多边形由“顶点”(空间中的点)组成,这些顶点是用“边”连接的元素三角形有三个顶点和三条边,四边形有四个顶点和四条边。值得注意的是,一个四边形可以被一分为二(对角),形成两个三角形。一个四边形建模器的例子是 NeverCenter SILO2 软件,一个三角形建模器的例子是 Blender3D 软件。

渲染管道(一个 3D 层堆栈,如果你愿意的话)通常从 3D 网格(模型)顶部或皮肤上的“顶点着色器”向下通过“镶嵌着色器”,它提供对底层几何着色器如何镶嵌的微调镶嵌控制。

这一切是如何工作的超出了专业 Android 编程标题的范围,但我想在这里简要介绍一下,这样您就可以很好地了解 AEP 有多先进。

最后,ASTC 类似于 Android 4 中添加的高级 WebM 和 WebP 编解码器的更高级的 3D 版本。ASTC 在 OpenGL 4.4 和 OpenGL ES 3.1 中都有,所以它是一项用于流行 3D 视频游戏的先进技术。ASTC 允许更好的 3D 纹理贴图优化,允许应用的数据足迹和用于这些纹理贴图的内存量应用到您的 3D 多边形网格显著减少。

除了 OpenGL ES 3.1,Android 5 还提供了这个扩展包和 Java 接口,以及对使用 AEP 令人难以置信的高级 i3D 图形功能的原生支持。Android 将 AEP 扩展视为一个单独的包。如果存在ANDROID _ extension _ pack _ es31a扩展,您的可穿戴设备应用可以假定此包中的所有扩展都存在。这将使用单个**#扩展**语句启用阴影语言功能。

Android 扩展包的一个 Java 接口提供了 GLES31EXT 。在你的可穿戴设备应用清单中,你可以声明你的应用必须只安装在支持扩展包的设备上,就像这样:

<manifest>
          ...
          <uses-feature android:glEsVersion="0x00030001" />
          <uses-feature android:name="android.hardware.opengles.aep" android:required="true" />
</manifest>

接下来,我们来看看与万维网相关的浏览媒体。

WebKit 媒体 : WebView、WebAudio、WebGL 和 WebRTC

Android 的一个超级强大的特性是 WebKit API,你可以用它来显示 HTML 内容,甚至创建你自己的浏览器。Android WebView 使用 WebKit 渲染引擎来显示网站,Android 5 将其升级到最新版本的 Chromium,这相当于谷歌 Chrome 浏览器版本 37。我刚刚看了我的 Chrome 浏览器,它是 39 版本,所以这是相当新的。我预计谷歌会保持 Android 5 的这一部分是最新的,所以随着 Android 5.x 的每次更新,预计这个版本号会增加,因为在本书写作期间它已经增加到 Android 5.1。

Android WebView 类:PermissionRequest 类

除了将 Android 5 WebView 实现更新到 Chromium-37,Android 5 还将安全增强提升到了一个新的水平,通过大量的错误修复增加了 WebView 类和 WebKit 渲染引擎的稳定性。

您在 Android 5 上运行 WebView 的默认用户代理字符串已经更新,将 37.0.0.0 作为您的最新版本编号方案。

Android 5 新增了一个 PermissionRequest 类 。此类允许您的应用授予 WebView 对象访问受保护资源(如摄像机或麦克风)的权限。这是使用 WebKit APIs 完成的,比如 getUserMedia( ) 。该应用必须声明正确的 Android 权限,才能首先使用这些资源,然后再将这些权限授予 WebView 对象供其使用。

PermissionRequest 类创建您的权限请求对象,当您希望请求访问包含受保护资源的网站内容时,该类是必需的。这个类中有三个常量与您需要访问权限的数据类型相关。

RESOURCE_AUDIO_CAPTURE 资源权限将允许访问音频捕获设备,如麦克风。有一个RESOURCE _ PROTECTED _ MEDIA _ ID资源许可,它将允许您访问受保护的媒体素材,而 RESOURCE_VIDEO_CAPTURE 将允许您访问视频捕获设备,如高清数字摄像机。

PermissionRequest 类的权限请求事件通过一个**onPermissionRequest(PermissionRequest)方法调用来传递,并且可以通过使用一个onPermissionRequestCanceled(PermissionRequest)**方法调用来取消。

PermissionRequest 类具有**。格兰特()。deny( )** 分别授予或拒绝权限的方法。您将在应用的 UI 线程中调用. grant()或. deny()方法来响应权限请求。

还有一个新的**。** onShowFileChooser( ) 方法,允许您在 WebView 中使用带有文件字段的输入表单,这将启动文件选择器,允许最终用户从他们的 Android 设备中选择图像或文件。

这个 Android 5 版本还增加了对几个强大的新媒体相关开源标准的支持,包括 WebAudioWebGLWebRTC 。接下来,我将介绍这三种技术,以及它们将为您的可穿戴设备应用开发带来哪些功能。

网络音频:数字音频合成和实时处理

在这个 WebAudio API 出现之前,web 浏览器还没有强大的数字音频处理和合成能力。HTML5 中引入了一个<音频>标签,允许基本的流式音频播放支持。尽管音频标签功能强大,足以支持音频流,但对该数字音频流的所有处理都必须在服务器端完成(或者在服务器上发布数字音频之前)。

要真正实现强大的数字音频处理,数字音频必须能够响应客户端复杂的数字音频(编程)应用,也就是说,在 web 浏览器或嵌入式操作系统浏览器内部。

WebAudio API 提供了这种客户端处理能力。该 API 旨在支持大量的数字音频处理应用。API 是以模块化的方式设计的,因此随着时间的推移,可以添加更多的高级功能。

WebAudio API 支持各种复杂的数字音频应用,包括游戏和交互式音频设计和合成应用。我会将虚拟合成器、音频序列器和音乐创作软件包括在可以使用 WebAudio API 创建的数字音频应用类型中,这使得它非常令人印象深刻。任何有合适硬件支持的可穿戴设备都可以利用这些优势!

WebAudio 与 WebGL 提供的更高级的图形特性有很好的协同作用,我将在下一节介绍这些特性。WebAudio API 具有模块化数字音频样本路由(在模拟音频中,这将被称为信号路由),提供创建复杂混音的能力,并允许开发人员创建高度详细的特效管道。

这个 WebAudio API 将允许开发者对多个数字音频数据进行编程“发送”(源),以及提交混音。API 通过使用 32 位浮点数据格式进行内部处理来支持高动态范围。

该 API 允许音频开发人员利用样本精确的预定声音回放,具有非常低的延迟。这对于需要高度节奏精度的音乐应用来说是必要的,比如架子鼓和音频音序器。低延迟(延时)使得动态(实时)创建数字音频特效成为可能。

API 允许包络、渐强、渐弱、颗粒(噪声)效果、滤波器扫描或 lfo(低频振荡)等功能的音频参数自动化。开发人员可以灵活地处理音频输入通道,允许实时分离或合并输入音频。

您可以处理从音频或视频媒体元素中提取的音频源,或者通过使用方法调用获得的媒体流处理现场音频输入。getUserMedia( ) 。您还可以集成 WebAudio 和 WebRTC,这将在我介绍完 WebGL 之后介绍。您可以通过使用 WebRTC 的MediaStreamAudioSourceNode方法来处理从远程对等点接收的音频。

开发人员可以使用 JavaScript 执行数字音频合成或数字音频处理,甚至可以通过使用MediaStreamAudioDestinationNode方法将生成的(或处理的)音频流发送到远程对等点。

3D 音频还通过空间化音频得到支持,空间化音频支持广泛的 3D 游戏和沉浸式环境。还有几种音频声相模型,包括 HRTF(热相关传递函数)、等功率和直通声相。

其他值得注意的功能包括距离衰减、多普勒频移、音锥、对障碍物(或闭塞)的支持以及线性卷积引擎,该引擎允许您创建一系列高质量的特殊效果,例如在不同类型的房间(环境声音空间)中模拟音频,如圆形剧场、大教堂、音乐厅、走廊、隧道、洞穴和森林。

WebAudio API 也支持振荡器(音调生成);声音设计的波形效果:失真;非线性音频处理效果;实时、时域和频率分析;音乐可视化支持;高效双二阶滤波器,可用于低通、高通和其它常见滤波器应用;梳状滤波器效果;样本反转;以及用于混合的整体控制和增甜的动态压缩。智能手表是否可以利用这一点是一个与音频硬件功能支持相关的问题。

接下来让我们看看 WebGL ,它甚至比 WebAudio 更强大,适用于 i3D 类型的媒体,而不是数字音频类型。

WebGL 支持:交互式 3D 渲染、着色和动画

WebGL 就是它听起来的样子,OpenGL ES for the web (browser)。最初的 WebGL (1.0)支持 OpenGL ES 1.1 和 2.0 规范,下一个 WebGL 2 (2.0)规范将支持 OpenGL ES 3.0 和 3.1 以及 1.1 和 2.0。WebGL 和 OpenGL 都由 Khronos Group 管理,这是一个管理 i3D 相关技术开源规范的开发人员团体,如 OpenGL、OpenGL ES、OpenCL、OpenSL、OpenSL ES、Collada、WebGL、WebCL、OpenMAX、gITF 和许多其他 3D 相关技术。

您可以在 Khronos 网站和 W3C 网站上找到更多关于 WebGL 规范的信息。这些网站位于以下 URL:

[`www.khronos.org/webgl/`](https://www.khronos.org/webgl/)
[www.w3.org/community/declarative3d/wiki/Related_technologies](http://www.w3.org/community/declarative3d/wiki/Related_technologies)

在使用 WebKit 支持 WebGL 2.0 之前,Pro Android 可穿戴设备开发人员将希望使用之前讨论的 OpenGL ES 3.1(如果需要,可以使用 AEP),而不使用 WebGL,因为使用 Android 的原生 OpenGL ES 会渲染得更快。

WebRTC 支持:可穿戴设备的实时通信

WebRTC 标准是一个免费的开源项目,它为浏览器和移动应用提供了 ?? 实时通信功能。

WebRTC API 为 web 应用开发人员提供了在 web 上编写丰富的实时多媒体应用(如聊天、音频聊天或视频聊天)的能力,而无需插件、下载或安装。它的目的是帮助构建一个健壮的免费 RTC 平台,该平台可以跨多个 web 浏览器以及多个操作系统平台工作。

像 WebAudio 和 WebGL 一样,这是通过使用 WebRTC API 来完成的。WebRTC 组件已经过优化,用于在 web 浏览器和 CE(消费电子)设备(也称为嵌入式设备,如 iTV、智能手表、智能手机、平板电脑、电子阅读器、游戏控制台等)内部进行实时音频和视频通信。

WebRTC API 允许为 web 浏览器、移动平台、平板电脑或电子阅读器平台以及智能手表和智能眼镜等物联网设备开发健壮、专业质量的 RTC 应用。

这个 WebRTC API 允许这些消费电子设备类型中的每一个使用所有平台软件和硬件制造商都同意的一组通用协议来彼此通信。

WebRTC 计划是一个开放的行业项目,目前得到了 Google、Mozilla 和 Opera 的认可和支持。WebRTC API 网站由 Google Chrome 团队维护。

Android MediaBrowser 类:浏览第三方媒体

Android 5 引入了可穿戴应用使用 android.media.browse API 浏览其他应用的媒体内容库的能力。为了公开你的可穿戴应用中的媒体内容,扩展 MediaBrowserService 类。

MediaBrowserService 的自定义类实现(子类)将提供对 MediaSession 的访问。令牌嵌套类。应该这样做,以便您的可穿戴应用可以播放通过 MediaBrowserService 服务类提供的媒体内容,该服务类是通过 Android 操作系统安排的。

为了与这个 MediaBrowserService 对象进行交互,可以使用 MediaBrowser 类(object)。为此,您首先要为您的 MediaSession 类(对象)指定组件名。这将在您创建 MediaBrowser 实例的同时通过使用构造函数方法来完成。

使用 MediaBrowser 对象实例,您的应用可以连接到 MediaBrowserService 对象,您已经“连接”或引用了该对象。这个 MediaBrowserService 对象将获得一个 MediaSession。Token 对象播放新的媒体内容,这是通过 Android 服务公开的。

Android Camera 2 API: UHD 图像处理支持

Android 5 引入了新的 android.hardware.camera2 包,其 API(类、接口、方法、属性和常数)允许开发者实现**(高动态范围图像)摄影捕捉以及 HDRI 处理。已经有一款在表冠上带有 HDRI 摄像头的智能手表型号 Hyetis。随着越来越多的产品添加到这些相机中,您可以预期 HDRI 相机支持将会掀起可穿戴应用开发的风暴。**

现在,您可以在 Java 代码中访问操作系统可用的 HDRI 相机设备。你可以使用一个。** getCameraIdList( ) 方法确定用户的硬件设备可以使用摄像机,然后连接到特定的摄像机硬件设备,使用**。openCamera( )** 方法调用。

一旦完成这些,您就可以开始捕捉 HDRIs,方法是创建一个 CameraCaptureSession 对象,然后指定 Surface 对象,将捕捉到的图像发送到可穿戴应用中进行显示。这些 CameraCaptureSession 对象可以配置为捕捉一个单个图像镜头,或者在一个连拍图像镜头中捕捉多个 HDRIs。

为了在捕捉到新图像时通知可穿戴应用,您实现了 CameraCaptureSession。CaptureCallback 侦听器,并在您的捕获请求中设置它。当系统完成图像捕获请求时,CameraCaptureSession。CaptureCallback 侦听器将接收对的调用。****onCaptureCompleted(),它将为您的可穿戴应用提供一个封装在 CaptureResult 对象中的图像捕获元数据对象。

还有一个 CameraCharacteristics 类(object),它将允许您的可穿戴应用检测给定硬件设备上有哪些高清摄像头功能。该对象具有一个INFO _ SUPPORTED _ HARDWARE _ LEVEL属性,它将包含表示功能级别的数据。

要求所有兼容相机的 Android 设备至少支持INFO _ SUPPORTED _ HARDWARE _ LEVEL _ LEGACY硬件支持数据变量级别。此属性包含对非 HDRI 相机功能的支持,这些功能基本上等同于“已弃用”(已停止使用,但仍受支持)的相机 API。所以基本上这个 Camera 2 API 取代了原来的 camera (Camera1) API。支持INFO _ SUPPORTED _ HARDWARE _ LEVEL _ FULL硬件支持数据变量级别的 Android 设备能够使用超快帧速率手动控制图像捕捉和图像后处理,以及捕捉高分辨率图像

Android 5 通知:锁屏和元数据

Android 5 在其锁屏功能和元数据功能中增加了一些新的通知功能。接下来我们来看看这些。

锁屏通知:隐私保护控制 API

Android 5 锁屏 API 现在有了呈现通知的能力。您的应用用户现在可以通过 Android 操作系统设置,选择是否允许在安全(锁定)的 Android 设备屏幕上看到“敏感”(私人)通知内容。当通知显示在用户的锁屏上时,开发人员可以控制锁屏上可见的详细程度,这代表了开发越来越受欢迎的锁屏应用时非常需要的灵活性增加。

要控制可见度级别,请调用**。** setVisibility( ) 方法然后指定其中一个可见性常量: privatepublicsecret

VISIBILITY_PRIVATE 常量告诉锁屏只能显示最基本的信息,比如通知的图标,但是会隐藏通知的全部内容(因为它被认为是私有的)。 VISIBILITY_PUBLIC 常量告诉你的锁屏允许显示一个通知的全部内容(因为它被认为是公开的),而 VISIBILITY_SECRET 常量告诉锁屏它必须什么都不显示,不包括任何通知文本,甚至通知图标(嘘,这是一个秘密)。当您将 visibility 常量设置为 VISIBILITY_PRIVATE 时,您还可以选择提供锁屏通知内容的“编辑”(有限或删节)版本,这将隐藏任何个人详细信息。

假设您想创建一个短信(SMS 短消息服务)可穿戴应用,并且您想要显示显示“5 条新 SMS 文本消息到达”的通知,并且您需要隐藏实际的消息文本以及发送者身份。

要提供此替代通知,首先使用 Notification.Builder 创建替换通知。创建私有通知对象时,通过 setPublicVersion()方法将替换通知附加到该对象。

通知元数据:智能通知分类

Android 5 现在支持元数据属性,你可以将它与你的可穿戴设备应用通知相关联。这允许您的应用更智能地对这些通知进行排序。要安装这个元数据,您可以在您的通知中调用三个方法中的任何一个(或全部)。构建器当您使用关联的构建器对象构建通知对象时。

三种方法中的第一种,a .setCategory()方法,指示 Android 当 Android 设备碰巧处于优先模式时,如何处理你的可穿戴应用的通知。例如,您可以为通知添加代表即时消息、来电或报警通知的类别。类别是字符串常量。

那个。setPriority()方法设置通知对象的优先级。您可以将通知对象的优先级设置为比普通系统通知更重要或更不重要。如果您的通知对象有声音(或振动),优先级字段设置为 PRIORITY_MAXPRIORITY_HIGH 的通知对象会出现在一个小的浮动窗口中。

那个。addPerson()方法调用将允许可穿戴设备开发人员向通知对象添加一个或多个人。这些人应该与您的通知对象相关。您的可穿戴应用可能会利用这一点向 Android 操作系统发出信号,通知对象应该在来自使用该方法调用添加的人员后被分组在一起。您还可以使用此方法调用,根据优先级标记将来自这些人的通知划分为更重要或不太重要。

更多 Android 5 操作系统增强功能

最后,让我们看看 Android 5 中改进的其他几个领域,这些领域可以在 Pro Android 可穿戴设备应用开发中加以利用。Android 5 OS 的最近应用和文档区已经彻底检修,同样的还有通过目录结构管理文件。此外,还有最重要的第二屏幕功能,它允许 Android 设备相互反串(不得不在书中有争议地旋转一些东西),以便智能手表或智能眼镜可以控制像 UHD·4K 独立电视这样的东西!

最近屏幕:并发文档支持

在 5 之前的 Android 操作系统版本中,“最近”屏幕将只为终端用户最近交互的每个应用显示一个任务。这提供了有限的多任务处理能力,需要在 Android 5 中进行大幅升级。这使得 Android 5 能够更好地与所有其他流行的操作系统竞争。

使用 Android 5,您的可穿戴应用可以打开更多任务,根据需要为应用文档添加额外的并发活动屏幕。该功能将增加急需的多任务支持,让用户只需使用新的 recents 屏幕,就可以在各个活动屏幕(包含用户文档)之间高效切换。这为当前正在使用的 Android 5 应用提供了一致的任务切换用户体验。

“最近通话”屏幕中可能使用的并发任务示例包括任何移动网络浏览器中的多个打开的选项卡、游戏应用中的任何并发游戏会话、业务生产力应用之一中的业务文档或任何消息应用中的文本消息。

你的可穿戴设备应用可以通过使用 Android ActivityManager 来管理最近屏幕上的任务。AppTask 类(或对象)。这是 ActivityManager 类的八个嵌套类之一,activity manager 类是 android.app 包的一部分。这个类很重要,因为它允许你的应用看到可能与你的应用一起运行的所有其他 Android 应用!

这个 Android ActivityManager 类允许可穿戴设备开发人员以各种方式与运行在 Android 操作系统中的所有当前活动的活动对象进行交互。这个类有八个嵌套的类结构,这将允许你与运行在你的用户的 Android 操作系统的化身(当前内存或处理器部署)上的接口。我将在这里介绍它们,因为了解它们可以做些什么,让您的可穿戴设备应用了解它们周围发生的事情,这对您非常重要!

活动经理。AppTask 类允许您管理应用的任务(因此得名)。还有一个 ActivityManager。MemoryInfo 类,它允许您通过使用. getMemoryInfo(ActivityManager)来轮询关于可用内存的操作系统信息。MemoryInfo)方法调用。

活动经理。ProcessErrorStateInfo 类将允许开发人员检索关于处于错误状态的操作系统进程(线程)的信息。活动经理。RecentTaskInfo 将允许您检索有关用户最近开始或访问的任务的信息。

活动经理。runningapprocessinfo 类将允许您检索关于当前正在运行的进程的系统信息,以及 ActivityManager。RunningServiceInfo 类将允许您检索关于当前在 Android 操作系统中运行的特定服务的系统信息。

活动经理。RunningTaskInfo 类将允许您处理关于当前正在操作系统线程管理器中运行的特定任务的系统信息。活动经理。TaskDescription 类将允许您在最近的任务列表中检索关于当前活动的信息。

要插入一个逻辑中断,以便系统将活动视为一个新任务,您可以在使用启动活动时使用您的FLAG _ ACTIVITY _ NEW _ DOCUMENT常量。 startActivity( ) 方法调用。您还可以通过在 AndroidManifest XML 文件中将您的< activity > XML 元素的 documentLaunchMode 属性设置为“intoExisting”或“always”来实现此行为。

为了避免弄乱用户的最近任务屏幕,您可以设定从应用内部显示的最近任务的最大数量,这将限制可以出现在该屏幕内部的任务。为此,您可以设置名为 android:maxRecents父标签的参数。

当前可以指定的最大值是每个用户 50 个任务,除非你在一个低内存的 Android 设备上,在这种情况下,它被设置为 25 个。用户最近屏幕中的任务也可以设置为重启后保持。为了控制这种任务持久性行为,使用 android:persistableMode 参数。

您也可以在“最近”屏幕中更改活动的可视属性。这包括活动的颜色、标签和图标。这是通过调用来完成的。setTaskDescription( ) 活动的方法。

数据存储:目录结构选择支持

另一个令人印象深刻的 Android 5 升级与数据存储访问有关。Android 存储访问框架功能已经扩展到允许开发人员(以及他们的用户)选择整个目录子树。这将允许您的可穿戴应用对包含在给定目录文件夹结构中的所有文档进行读取和写入数据存储访问,而无需用户确认每个要启动的项目!

要选择整个目录子树,您需要创建并发送一个OPEN _ DOCUMENT _ TREEIntent 对象。Android 操作系统将显示所有支持子树选择的文档提供者对象实例。这允许最终用户浏览和选择目录。返回的 URI 对象将引用对所选子树的访问。然后就可以使用了。buildChildDocumentsUriUsingTree()方法调用与结合使用。buildDocumentUriUsingTree( ) 方法调用,然后使用一个**。query( )** 方法调用来探索子树内部的数据。

还有一个新的**。** createDocument( ) 方法调用,它允许您在子树下的任何位置创建一个新的文档或目录结构。如果您的可穿戴应用需要管理一个现有的文档,您会希望使用一个**。renameDocument( )** 或一个**。** deleteDocument( ) 方法调用。您还可以在发出这些调用之前检查 COLUMN_FLAGS 常量来验证提供者对这些调用的支持。

如果您希望实现 DocumentsProvider 对象,并且还希望支持子树数据选择,那么您将希望利用名为的方法。isChildDocument( ) 然后在 COLUMN_FLAGS 属性中设置 FLAG_SUPPORTS_IS_CHILD 常量,要求 Android OS 授予子文档支持。

Android 5 还在共享存储上引入了新的特定于包的目录。您的应用可以将媒体文件放在那里,以便包含在 MediaStore 中。

一个新的**。getExternalMediaDi****RS()方法调用将为所有共享存储设备上的外部媒体存储目录返回一个路径引用**。

应用不需要额外的权限来访问任何返回的路径,这类似于**的功能。****getExternalFilesDir()**方法调用。Android 操作系统将定期扫描位于这些目录中的媒体。您还可以通过使用 MediaScannerConnection 对象(类)来强制操作系统执行这些媒体素材扫描之一,这将允许开发人员随时扫描以检测新媒体内容的存在。该类实现了一个 ServiceConnection 接口,还具有两个嵌套的助手类。

一个媒体扫描连接。MediaScannerConnectionClient嵌套的 helper 类将为开发人员提供所需的接口,以便在建立到 MediaScanner 服务的任何连接时或文件扫描完成时通知客户端 MediaScannerConnection 对象。

另一个 MediaScannerConnection。OnScanCompletedListener 嵌套的帮助器类提供了一个侦听器接口,该接口可用于侦听并通知任何客户端对所请求的媒体文件的扫描结果。

第二个屏幕:屏幕捕捉和屏幕共享

最后,Android 5 OS 通过允许开发人员使用全新的 android.media.projection 包及其强大的 API,为他们的专业 Android 可穿戴应用添加屏幕捕捉和屏幕共享功能,提升了其第二屏幕功能。

第二屏幕功能可能会非常有用,尤其是对于可穿戴设备,正如你所想象的那样!例如,如果您的用户希望在视频会议应用中实现屏幕共享,他们可以使用 2K 或 4K Android TV 功能的 iTV 电视机,观看实物大小的另一场派对。

还有一个**。****createVirtualDisplay()**方法这是一种新的方法,允许你的可穿戴设备应用捕捉你的主屏幕(默认显示)的内容,并将屏幕捕捉数据放入一个表面对象中。然后,您的应用可以通过网络发送这些数据。该 API 仅允许捕获未受保护的屏幕内容,不适用于系统音频捕获。

要开始屏幕捕捉过程,您的应用必须首先通过使用 Intent 启动屏幕捕捉对话框来请求用户的许可,这是通过使用**获得的。****createscrecaptureintent()方法调用。对该方法的调用将返回一个 Intent 对象,该对象必须传递给。****startActivityForResult()方法以便启动屏幕截图。要检索屏幕截图,请使用。****getMediaProjection()**方法。

通过使用**。getmedia projection(int result code,Intent resultData)** 方法调用,然后可以访问 MediaProjection 对象,该对象将从一个成功的。createScreenCaptureIntent()屏幕捕获请求。

恭喜你!您刚刚更新了 Android 5 操作系统添加的关键内容,这些内容将帮助您快速了解 Android 4.4(及之前版本)的知识库。其中许多可以创造性地用于 Pro Android 可穿戴设备应用开发。

摘要

这一章仔细研究了除材质设计之外的所有功能,这在 Android 5 的第一章中有详细介绍。这包括了从操作系统特性到新媒体特性,到新版本的 OpenGL,以及更远的东西(嘿,到无限甚至更远!).

首先,我讨论了 Android 项目 Volta,该项目旨在允许开发人员社区将电源管理和优化掌握在自己手中。这可以通过使用作业调度器作业信息类,结合电池历史工具来完成。

接下来,我讨论了新的 Android 网络连接技术改进,包括 NFC 和蓝牙,以及新的蓝牙 LE APIs。你学习了 Androidconnectivity manager类,以及 NDEFAndroid Beam 技术,以及如何在 Java 7 中实现它们。

接下来,我讨论了 Android 5 新增的所有酷多媒体所带来的所有新的惊喜机会。我讨论了用于数字视频回放的 MediaSessionMediaController 类,以及用于数字音频的 AudioTrack ,您学习了如何使用它们进行新媒体回放。

接下来,我深入研究了 Open GL ES 3.1Android 扩展包,并解释了如何实现它们,将 Open GL 从 3.1 升级到 4.4,将所有新的实时 i3D 渲染能力添加到您的 Android i3D 应用中。

最后,我讨论了 WebKit 支持和 WebView 类,以及对最新 WebAudioWebGLWebRTCAPI 的新支持。最后,我介绍了 Android recents 屏幕的新增功能、新的文件(目录)管理功能、新的第二屏幕屏幕捕捉功能,以及如何在您的专业 Android 可穿戴应用中实现这些很酷的新功能。

在下一章中,你将了解软件开发环境 IntelliJ IDEA,你将在本书的其余部分使用它来开发 Android 5 Wear SDK 应用。这是为了让你在本书的学习过程中,对将要用来编写 Java 7 代码和 XML 标记的工具有一个坚实的基础。**

四、探索 Android Studio:熟悉 IntelliJ IDEA

现在你已经对 Android 中的新特性有了一个全面的概述,包括新的材质设计范例和其他 Android 5 的新特性,本章将看看 Android Studio 的集成开发环境IntelliJ IDEA 。在第二章的中,您已经完成了开发工作站的组装,所以现在剩下要做的就是让您了解 IDE 软件工具的详细情况,您将在本书中使用该工具。一旦解决了这个问题,您就可以在本书接下来的 13 章中专注于 Java 7 编码或 XML 标记。

让我们先看看 IntelliJ 如何让您了解它的所有特性,然后看看如何使用 Android SDK 管理器找出当前安装了哪些 Android APIs。我想向您展示如何确保为 Wear 和 Android TV 安装所有最新的 API,这样您就可以在升级 IDE 方面进行一些练习。

在那之后,你将在这一章的剩余部分学习你的 IntelliJ 思想将如何被布局以及它的各种功能组件。然后,我将解释如何使用 IDEA 的一些特性和功能,以及 IDEA 与 Android SDK 的接口来构建可穿戴应用。我将向您展示如何使用 SDK 管理器,以便您可以安装 API 来开发 Google cloud 可穿戴应用。然后,我将向您展示如何创建 Wear 智能手表应用,以及 Wear 组件的工作原理。

更新 IntelliJ IDEA:使用更新信息对话框

如果您还没有这样做,请使用您在第二章中创建的快速启动图标启动 Android Studio 环境。这将启动 IntelliJ IDEA,它是 Android Studio 的基础,取代了 Android 4 . x 版和之前版本中使用的 Eclipse ADT IDE。

正如你在图 4-1 中看到的,如果 Android Studio 有更新,你的欢迎使用 Android Studio 启动屏幕会在右上角有一个浅绿色的更新信息消息。幸运的是,当我今天发布时,有一个从 1.0.1 到 1.0.2 的更新,所以我可以向您展示更新中涉及的工作流程,因为您很可能会在可穿戴应用开发的某个时间点遇到这种情况。

9781430265504_Fig04-01.jpg

图 4-1 。启动 Android Studio,单击 update 链接,单击 Update and Restart 按钮,然后更新 IDE

点击图 4-1 中红色框中突出显示的蓝色更新链接,启动 Android Studio 更新流程。这将打开一个对话框,显示在图 4-1 的右上角,它会给你当前的构建,当前的版本号,以及补丁的大小。这个补丁只会更新你的 Android Studio 安装中已经改变的部分。这被称为增量更新,这比再次下载完整的 1gb(可能更多)Android Studio 开发环境更可取,因为这是多余的。点击更新重启按钮,你会看到更新进度条对话框,如图图 4-1 右下方所示,你将会更新到 Android Studio 的最新版本。

探索 IntelliJ IDEA:帮助、提示和键映射

在你更新并重新启动 Android Studio (IntelliJ IDEA)后,欢迎使用 Android Studio 屏幕的左下方会显示最新的版本号,是 1.0.2,如图图 4-2 所示。点击图 4-2 左下方的文档和操作选项。这将打开文档和操作面板,显示在图 4-2 的右侧。

9781430265504_Fig04-02.jpg

图 4-2 。一旦 Android Studio 重启,点击文档和操作选项来浏览 IntelliJ 文档

正如你所看到的,有许多对你的想法学习曲线爬升有帮助的领域供你探索,包括阅读帮助文档,你将在接下来看到,每日提示对话框,默认键盘映射参考,,如果你有无限的带宽, JetBrains TV

JetBrains 是这个 IntelliJ 想法的开发者。如果你想为 IntelliJ 开发第三方插件,这超出了本书的范围,也可以选择接受这方面的培训。

点击文档和操作面板顶部的阅读帮助选项,打开他们网站的 IntelliJ IDEA 帮助部分,可以在图 4-3 中看到。有关新功能、帮助系统、快速入门指南、基本思想概念、教程部分和类似参考资料的信息按逻辑分组。

9781430265504_Fig04-03.jpg

图 4-3 。点击阅读帮助选项打开 JetBrains IntelliJ IDEA 第 14 版网络帮助网站

如果你想掌握集成开发环境,我建议你在某个时候通读所有这些材质,在本书的过程中,你将使用集成开发环境来开发 Pro Android 可穿戴设备应用。在本章中,我还将介绍 IntelliJ 思想的基础。

点击获取帮助链接,查看您有哪些关于 IntelliJ IDEA 的帮助选项。正如你在图 4-4 中看到的,点击链接将在网站的左侧内容导航窗格中打开一个子面板,以链接的形式显示更多的细节,你可以点击它来探索 IntelliJ IDEA 帮助系统的不同区域。

9781430265504_Fig04-04.jpg

图 4-4 。单击左侧导航窗格中的获取帮助导航链接,并仔细阅读列出的小节

其中包括如何使用帮助主题、如何使用“日积月累”对话框、如何使用 IntelliJ 在线资源、如何使用 IDEA 生产力指南、如何报告您在 IntelliJ IDEA 安装(或功能)中遇到的任何问题、如何提交关于您希望在未来版本中看到哪些功能的反馈,以及如果您喜欢使用键盘快捷键而不是鼠标和 IDEA 菜单系统,您的按键映射参考。

在这一章的后面,我将回顾一些在概念部分阐述的核心 IntelliJ IDEA 概念,如图 4-4 中用灰色突出显示的。我会这样做,这样在你开始在第五章和本书的整个篇幅中认真使用这些概念之前,你已经对这个想法有了一些核心概念。

如果你是好奇型的,现在就去浏览一些在线资料,这样当我在本书后面开始认真讨论它时,你就熟悉它了。

接下来,让我们看看 Android Studio 启动屏幕上的配置菜单,了解如何使用 SDK 管理器工具来定制您的想法。

配置 Android Studio:使用 SDK 管理器

因为这是一个 Pro Android 标题,所以您可能有一些使用 Eclipse ADT IDE 进行 Android 4.4(和更早版本)应用开发的经验,它包括一个 Android SDK 管理器菜单选项和对话框。在 IntelliJ IDEA 中也可以通过使用配置image SDK 管理器面板序列来访问该工具,如图图 4-5 所示。

9781430265504_Fig04-05.jpg

图 4-5 。单击返回箭头返回到快速入门,然后单击配置选项和 SDK 管理器选项

Android Studio 配置面板 还可以访问您的 IntelliJ IDEA 设置的对话框,管理 IntelliJ IDEA 的插件,并设置导入导出参数。

你真的应该花些时间来探索这些对话。在本书中,您也将使用其中的一些对话框,这样您将获得一些在 Pro Android 可穿戴设备开发工作流程中最大限度地使用 IntelliJ IDEA 的实践经验。

一旦你点击 SDK 管理器选项,如图 4-5 顶部所示,这将打开 Android SDK 管理器对话框,如图图 4-6 所示。请注意,在对话框的底部有一个抓取 URL 进度条,它可以到达下载安全套接字层(dl-ssl)谷歌服务器的存储库,并根据 Android 5 谷歌服务器上的最新 API 检查您想法中的所有内容。这是为了让下一个对话框可以推荐您安装的软件包,以便您可以将您的 Android Studio (IntelliJ 14 IDEA)升级到最新的功能,或硬件平台(Wear SDK、Android TV、Auto SDK 等。)支撑位。

9781430265504_Fig04-06.jpg

图 4-6 。一旦你启动 Android SDK 管理器,你会看到抓取 URL 的进度条 加载 API

正如你在图 4-7 中看到的,这个 Android SDK 管理器将填充那些它认为你应该选中的复选框,以调用更新或新的 API。

9781430265504_Fig04-07.jpg

图 4-7 。Android SDK 管理器对话框(左上,右下)默认谷歌 USB 驱动选择

Android Studio 1.0.2 有更新,或者你可以将其视为 Android Studio 1.0.2 的新 API,这里描述为 Android SDK 工具版本 24.0.2,这是一个内部编号模式 ,显示在 Rev(ision)列中。

此外,还为 Android Wear 或 Android TV 提供了七种新的系统映像,适用于 ARM 和英特尔(Atom)处理器硬件。我按照建议检查了它们,因为在第五章的例子中需要它们,在那里我将介绍你的 AVD (Android 虚拟设备)模拟器的创建。这些可以在图 4-7 的左下方看到,我不得不把它(对话框)剪成两半以适合页面。

在图 4-7 的右侧,可以看到底部有新的 Android 5 支持工具(新的支持资源库和新的支持库),以及新的 Google USB 驱动(版本 11)。支持库及其存储库非常重要,因为它们提供了向后兼容性。使用支持库可以让你为 Android 5 开发,并且仍然支持早期版本的操作系统,如 2.3.7 (Kindle Fire)和 4.x。

你也可以使用这个 Android SDK 管理器对话框来安装“额外的东西”,比如 Google Web Driver,它们显示在图 4-7 的右下角。

一旦你点击 Install 11 Packages 按钮,这些都将被安装(或更新)到你的 Android Studio 中,正如你现在所知道的,这是 IntelliJ IDEA 的 14 版本和 Android 5.0.2 SDK 的融合。如果 Android Studio 是 1.0.2,那么 Android OS 将是 5.0.2。

在你安装任何 API 之前,本质上是新的软件包,你必须同意他们的每个许可“条款和条件”,显示在图 4-8 的选择要安装的包对话框中。选择以红色突出显示的接受许可证单选按钮,并单击安装按钮继续安装。

9781430265504_Fig04-08.jpg

图 4-8 。选择要安装的软件包对话框和以及接受许可协议

我点击了安装按钮,本来应该是一个相当长的安装过程在不到五秒钟内就过去了!在我点击安装按钮几秒钟后,出现了如图 4-9 所示的对话框。这让我有点困惑,所以我等着看是否会弹出下载日志对话框。果然如此,并充满了错误启动!哦天啊!

9781430265504_Fig04-09.jpg

图 4-9 。显示下载 SDK 更新进度对话栏 ,位于对话框底部

我选择安装的每个软件包都出现了“下载中断:错误记录 MAC”的错误,如图 4-10 中的所示。

9781430265504_Fig04-10.jpg

图 4-10 。显示 Android SDK 管理器日志对话框,下载中断:坏记录 MAC 错误

注意日志对话框的左下方,在图 4-10 的中用红色突出显示,说明安装已经完成,而没有安装任何东西!因此,我的预感是正确的,当我谷歌这个错误时,我没有找到这个问题的解决方案,所以我决定尝试以一个系统管理员的身份启动 Android Studio,这样我就有了顶级管理员权限。我将向您展示这个工作过程,因为当软件安装不起作用时,这总是我尝试的第二种方法,因为有时文件试图写入硬盘驱动器,但没有权限这样做!

以管理员身份运行:使用管理员权限 安装

在 Windows 中调用管理员运行命令有两种方式,如图图 4-11 和图 4-12 所示。很少有开发者知道的很酷的快捷方式是右击 Android Studio 任务栏启动图标,选择 Android Studio image以管理员身份运行。这显示在图 4-11 中,还有一个更复杂的 Android Studio image属性方法,我将在下面解释。

9781430265504_Fig04-11.jpg

图 4-11 。右击 Android Studio 图标,选择以管理员身份运行

9781430265504_Fig04-12.jpg

图 4-12 。在 bin 子文件夹中查找 studio64 可执行文件;右键单击并选择属性以查看其属性对话框(如右侧所示)

从同一个菜单中,您还可以选择属性菜单选项,并打开 Android Studio 文件的属性对话框,它会告诉您 EXE 文件的名称以及它在硬盘驱动器上的位置。

有了这些信息,您就可以在 Windows 资源管理器(或 Linux OS 文件管理工具)中右键单击该文件,然后选择以管理员身份运行选项。从那时起,在您再次使用一个配置的image SDK 管理器序列之前,您以管理员身份启动 Android Studio。然后再次尝试更新(升级),这一次,用管理员权限!

图 4-12 显示了带有目标文件夹的 Android Studio 属性对话框,以及您想要使用studio64.exe(64 位版本)作为文件的事实,您将使用与图 4-11 中所示完全相同的菜单序列右键单击并启动。

正如你在图 4-13 中看到的,安装现在正以极其缓慢的速度进行,这意味着这一次,我得到了一个非常不同的结果!这意味着更新可能会成功,所以如果没有日志对话框弹出,以管理员身份运行将解决问题。

9781430265504_Fig04-13.jpg

图 4-13 。使用 SDK 管理器对话框下载任何新的 Android SDK 更新

我想向您展示这个 SDK 管理器 API 更新困境的解决方案,以防您遇到它,因为没有很多网站提供这个关于如何作为管理员启动 IntelliJ IDEA 的快速而简单的变体!

值得注意的是,您不必总是以管理员身份启动 Android Studio 1 . 0 . 2(IntelliJ IDEA 14)来使用开发环境,只有当您尝试更新 SDK 环境并遇到与读取或写入操作系统硬盘驱动器上被视为“敏感”(或特权)的文件相关的错误时才需要这样做。

在第二次尝试之后,我没有弹出 Android SDK 管理器日志对话框(窗口)!相反,在这个安装过程的最后,我看到了 Android Tools 更新的消息对话框,可以在图 4-14 中看到。

9781430265504_Fig04-14.jpg

图 4-14 。一旦你更新了你的 SDK,你会看到 Android 工具更新对话框

该对话框建议关闭 SDK 管理器窗口,然后重新打开它。我把这个建议带到了下一个层次,关闭了 Android Studio (IntelliJ IDEA)并重新启动它,将最新版本加载到内存中。我这样做是为了 100%确保内存中的一切都是 Android Studio 1.0.2“干净的”

让我们再次使用配置image SDK 管理器选项窗格序列,你会看到 Android SDK 管理器显示它现在已经安装。这显示在图 4-15 中,如你所见,在你试图更新或安装的东西旁边有一个文件夹检查已安装图标。

9781430265504_Fig04-15.jpg

图 4-15 。使用配置image SDK 管理器序列来启动 SDK 管理器并确认安装

在启动 Android Studio 时,只要你看到绿色的小更新对话框,你就会想要执行这个工作过程(参考图 4-1 )。

这个绿色的对话框(实际上更像是一个警告贴)会告诉你 Android Studio (IntelliJ IDEA)有一个更新正在等待。这将提醒您还需要使用您的配置image SDK 管理器面板序列,因为 SDK、驱动程序、库、存储库、服务和系统映像的更新几乎总是与 IntelliJ IDEA 本身的每个主要(和次要)更新同步进行!

接下来,我想给你一个 IntelliJ IDEA 的快速概述,并介绍有关它如何工作的概念,以便你在我在本章稍后实际使用该想法创建 Android 项目之前有一个鸟瞰图,以及在第五章当你将创建 AVD 仿真器(虚拟设备)时,你知道如何创建和测试 Android 项目。

学习 IntelliJ IDEA 基础知识:项目和 SDK

IntelliJ IDEA 文档包含两个主要级别。如果你仔细回顾一下,你会发现这一点。其中一些将适用于作为 Android 应用开发人员的您,一些将适用于 Google 的 Android Studio 开发团队,因为它涉及到集成定制的 SDK 并将它们插入 IntelliJ IDEA 以创建平台,如您刚刚升级到的 Android Studio 1.0.2(或更高版本)平台。

IntelliJ 项目级别:开发 Android 应用

当您使用 IntelliJ 思想时,您需要为您的每个应用创建一个“项目”。您将在本章的稍后部分执行此操作,这样您就可以看到所涉及的工作流程。IntelliJ 项目用于将应用组织成功能开发组件素材。作为一名专业的 Android 开发人员,您知道这包括 Java 代码、XML 标记、数字图像、数字视频、数字音频、build.gradle 和 AndroidManifest.xml 文件,以及您刚刚在 SDK 管理器中安装的组件,这些组件也需要在您的应用中,以便为您在 Java 或 Android SDK 代码基础上编码(和标记)的内容提供基础。

一个完成的或“编译的”可穿戴应用的 APK(应用包)可能看起来像一个文件,但它实际上包含大量离散的或“隔离的”模块,这些模块提供类似于 Java 中的包的代码组织。IntelliJ IDEA 项目定义了在任何给定的可穿戴应用中使用哪些模块。

这种项目方法允许开发人员以高度可视化的方式在 ide(集成开发环境)内将项目组合在一起,并使用可视化开发编辑器将这些功能模块联系在一起。这类似于文字处理器,但它是为编写代码而不是业务文档而优化的,并且通常使用缩写 IDE。

澄清一下,这个 IntelliJ IDEA“项目”并不包含您实际的开发素材,在 IntelliJ 术语中称为“工件”,如媒体素材、Java 代码、XML 标记、编译构建脚本、文档等。这些项目是 IntelliJ 思想的最高组织级别,它们定义了项目范围的设置、模块和库的集合,以及对项目中用于创建应用的各种资源(文件)的引用。正如您所看到的,这个 IntelliJ 项目实际上把所有东西都绑定在了一个项目文件中,这当然是 IDE 生成应用所必需的!

IntelliJ 项目文件格式:文件、文件夹和配置

IntelliJ IDEA 将使用纯文本 XML 文件格式存储项目的配置数据及其组成部分。这将使您更容易管理,因为 XML 是一种相对简单的编辑或调试格式,如果需要,您还可以使用这种 XML 数据格式更容易地与他人共享项目配置数据。IntelliJ 开发人员可以使用两种类型的配置数据格式来存储您的 Android Studio 可穿戴设备应用项目配置数据。这些格式包括基于目录和基于文件的格式。

IntelliJ IDEA 基于目录的数据格式

当一个基于目录的数据格式 被用于 IntelliJ IDEA 项目时,你会发现**。IntelliJ 项目文件夹结构中的 idea** 目录。这个。idea 子目录将包含上一节提到的 XML 配置文件的集合。

这些 XML 文件中的每一个都只包含整个项目配置数据的逻辑部分。文件名反映了 IntelliJ IDEA 项目中的功能区域。这样,使用这种逻辑文件命名方法,您要找的东西将很容易找到。

例如,您可能会看到包含与项目编译方式相关的 xml 标记的 compiler.xml 文件,与项目实现编码方式相关的 encodings.xml 文件,或者包含 Android Studio 应用项目所需模块的 modules.xml 文件。

几乎所有这些文件都包含对项目本身至关重要的信息。这将包括文件名和所需组件模块和库的位置,或者可能是编译器设置或其他 IDEA 配置设置。这些文件可以(也应该)保持在版本控制之下,就像项目中的所有其他文件一样。

然而,这种文件命名方法有一个例外,它被命名为 workspace.xml 。这个 XML 文件将包含您所有的首选项设置,例如意见编辑窗格的位置和各种编辑器 UI 窗口的位置。该文件还包含您的 VCS(版本控制系统)和历史设置。

此工作区文件还可以包含与 IntelliJ IDEA 集成开发环境相关的任何其他数据。因此,您可能不希望公开共享这个文件,除非您希望人们模仿您的 IDE!

基于 IntelliJ IDEA 文件的数据格式

还有一种基于文件的配置格式 ,当您希望在一个项目目录中只放置两个配置文件时,应该与 IntelliJ 一起使用。其中一个文件以一个为特色。ipr 扩展名,代表 IntelliJ 项目。该文件将存储主要 IntelliJ IDEA 项目的配置信息。

其他更次要的文件将使用一个**。iws** 扩展名,代表 IntelliJ 工作区,这个文件存储了你的个人工作区设置。这个。iws 文件不应置于版本控制之下,而您的。如果您使用版本控制,ipr 文件可以(也应该)被置于版本控制之下。

基于文件的配置文件格式还可以转换为基于目录的配置文件格式(更多信息,请参见 JetBrains 网站上的“将项目转换为基于目录的格式”)。

IntelliJ 特性 : SDK、语言支持和自动编码

要开发任何类型的应用,您总是需要使用软件开发工具包(SDK)。与 Android Studio 相关的一个例子是 Android SDK,它运行在 Java 7 软件开发工具包之上,Oracle 称之为 JDK。

IntelliJ IDEA 本身不包含任何 SDK 然而,这个 Android Studio 1.x 捆绑的 IntelliJ IDEA 和 Android SDK 是基于 Java 7 JDK 的,如你所知,来自第二章第一章。通常,在您开始编写任何应用代码之前,您必须下载并安装一个受支持的 SDK,然后配置它以便在 IntelliJ IDEA 中使用。

您可以在首次创建应用项目时指定 SDK,甚至可以在以后指定 SDK。要在 IntelliJ IDEA 中定义 SDK,您需要做的是指定 SDK 的名称及其位置。

硬盘驱动器上的位置是磁盘字母和目录路径,通常称为 SDK 的“主”目录,类似于 Java 使用的术语。这将是你安装 SDK 的目录,由于你已经在第二章中安装了 Java 7 JDK,你可能记得对于 Java 来说是C:/program files/Java/JDK 1 . 7 . 0 _ 71

让我们来看看 IntelliJ 思想可以支持(使用)的一些流行的开发环境,以防您计划在 Flash(使用 Flex 或 AIR)以及 Java SE 或 Java ME 中开发应用,当然,使用 Android Studio 1 . 0 . 2(Android 5 . 0 . 2)。也有允许其他开发平台的第三方插件。

IntelliJ 支持的流行 SDK:Android、Java 和 Flash

IntelliJ IDEA 使用的一些最广泛的软件开发平台包括开源的 Java 8 SE、Java 8 ME、Java 7 SE 和 Android Studio,以及 Adobe 的付费软件 Flash,这些软件目前在许多嵌入式设备(智能手机、平板电脑、智能手表、智能眼镜、iTV 电视机、游戏控制台、机顶盒、电子书阅读器、家庭媒体中心)上并不真正受支持,因为 Flash 是非常数据密集型的,而且制造商实施起来也很昂贵(它不是开源的)。

Java 7 或 8 SDK (JDK)涵盖了 Java 桌面(Java SE)和企业(Java EE)应用,以及 Java mobile (Java ME)或 Java Embedded (Java SE Embedded 和 Java ME Embedded)应用的开发。JavaFX 现在也是 Java 7 和 Java 8 的一部分,所以它包含在 Java 的所有版本中。如果你感兴趣的话,我写了【Java 8 游戏开发入门(2014 年出版)来讨论这个话题。

Android SDK,一旦使用 Android Studio 1.x 捆绑包与 IntelliJ 预集成,就用于为 Android 5 和更早的设备开发应用(使用前面讨论的 Android 支持 API 向后兼容库)。

也有 Adobe Flex 和 Adobe AIR SDKs 用于为 Adobe Flash 平台开发应用,到目前为止,Adobe Flash 平台的受欢迎程度已经下降了十年。由于免费用于商业用途,开源平台的使用量激增,从苹果、Adobe 和 MS Windows 等付费平台夺走了市场份额,因为媒体开发商的零成本功能水平相似。这就是为什么你会看到这么多基于 HTML5 和 Android 的设备。

还支持 Flexmojos SDK,它将启动 Adobe Flex 编译器和调试器。需要注意的是,当 Flexmojos 项目打开时,IntelliJ IDEA 会自动创建这个 SDK。

最后,还有“本地”IntelliJ IDEA 平台插件 SDK,用于为 IntelliJ IDEA 开发定制的插件。IntelliJ IDEA 安装本身将充当 IntelliJ IDEA 平台插件 SDK。

IntelliJ 支持的流行语言:Java 8、XML 和 Groovy

现代软件应用的开发目前涉及在一个单一的开发基础设施中使用几种(不相关的)编程语言。这描述了一个叫做多语言编程环境的开发环境。Android Studio 1.x 就是一个很好的例子,因为它使用 Java 7 进行编码,使用 XML 标记进行快速对象定义。

作为一名专业的 Android 开发人员,您知道这些对象后来被“膨胀”, XML 标记结构被转换成 Java 对象。IntelliJ IDEA 是多语言编程的专业 IDE,这也是 Google 在 Android Studio 1.x 和 Android 5(以及更早版本)开发中采用它的原因。

IntelliJ IDEA 的这个免费版本支持的主要编程语言包括 Java 8(和 lambda expression)以及早期的 Java 支持,包括与 Android Studio 一起使用的 Java 7(没有 lambda expression 支持),以及 XML 和 XSL。XML 广泛用于 Android 应用开发,因此 IntelliJ 非常适合 Android 5。IntelliJ 还支持 Groovy ,这通常不用于 Android 应用开发,但是正如您可能想象的那样,Groovy 群体已经找到了一种方法来使 Android 的 Groovy 开发成为现实,所以如果您使用 Groovy,那么您很幸运,所有支持的 IntelliJ 语言都可以一起工作!Android Studio 中的 Gradle 构建系统使用 Groovy 语法。

IntelliJ IDEA 的“终极”版本支持大量高级语言,如 Java Server Pages (JSP 和 JSPX)、Flex、ActionScript、HTML5、CSS3、JavaScript、SQL、PHP、Spring 和许多其他语言。

最终成本大约是 500 美元购买(第一次)或 300 美元升级。创业公司有 50%的折扣,学生和教师也有免费版本。符合开源软件定义以及 IntelliJ 许可和升级(“购买”页面)网页上定义的一组附加标准的非商业操作系统软件项目可以获得免费的开源许可证。也有用于教育和培训目的的免费版本,所以事实证明 JetBrains 是一家相当慷慨的公司!

IntelliJ 自动编码功能:突出显示、格式化和折叠

像 NetBeans (JavaFX,HTML5)或 Eclipse (Java) IDE 软件包一样,IntelliJ IDE 具有许多高级功能,就像文字处理器具有的编写功能一样,只有这些功能用于辅助代码编程。

IntelliJ 代码突出显示、文件模板、代码完成和代码生成

最明显的特点是根据使用情况用不同的颜色突出显示 ,这是文字处理器不会对你的文字做的。语法和错误突出显示在所有的 ide 中都很常见,但是 IntelliJ 看起来是所有 ide 中最专业的,因为它是免费版本的最终(或付费)版本。尽管 IntelliJ IDEA 中使用的默认颜色是行业标准颜色,但如果愿意,您也可以更改代码颜色值。

代码高亮颜色可以在设置对话框的颜色和字体部分中配置,您可以在图 4-5 的右侧看到,从上往下数第二个,您可能已经研究过了,正如我之前建议您做的那样。

还有另一个很酷的特性叫做“文件模板”,它允许 IntelliJ 为 IDE 支持的语言创建我称之为引导 ,或者部分编码(这些也被称为存根 )。这使得 IntelliJ 能够创建“空”的类和方法,例如 Java 7 或 XML 1.0 标记。

所有 IDE 软件中都有另一个流行的特性,叫做代码 完成 ,它会查看您正在编写的代码的上下文,并为您完成部分代码。还有代码生成,它与文件模板的功能密切相关,提供了您的引导代码“片段”,您可以根据需要进行修改。

IntelliJ 代码格式化、代码折叠、搜索和替换、宏和文档

IntelliJ IDEA 不仅为您编写代码并为代码着色,以使事情变得更容易,它还以其他酷的方式格式化、隐藏(和显示)和自动化您的编程。

帮助你组织和更好地了解程序逻辑的特性之一叫做代码折叠 。代码折叠通常会在主要代码块的左侧使用加号(+)或减号(-)图标,例如 Java 7 中的类和方法,以允许您“折叠”这些程序结构,这样您就可以在屏幕上有更多的空间来处理其他代码结构,或者鸟瞰您正在处理的 Java 类的代码结构。

如您所料,IntelliJ 允许您快速访问 Android API 文档,以便您可以快速轻松地研究常量、接口、方法或类。Android 5 API 已经变得如此庞大,以至于这个功能成为你可穿戴应用开发的重要补充。

随着代码变得越来越复杂,另一个变得越来越有价值的自动化特性包括在编辑窗格中使用宏。宏是自动化的工作过程,可以追溯到早期的大型机计算、DOS 和批处理。

还有其他一些有用的功能,比如可以打开控制台窗口,这样就可以在不脱离 IntelliJ 思想的情况下交互式地执行命令。

除了代码编辑辅助功能,IntelliJ IDEA 还支持调试 Java、JavaScript、PHP 和 Adobe Flash (Flex)应用。目前,Mozilla Firefox 和 Google Chrome 浏览器都支持 JavaScript 相关应用的调试。现在,让我们创建一个可穿戴应用!

创建 Android 可穿戴应用 :使用 IntelliJ

现在是使用快速启动快捷方式启动 Android Studio 的时候了,这次不是点击配置或文档和操作选项,而是点击启动一个新的 Android Studio 项目选项,如图 4-16 中的选择。

9781430265504_Fig04-16.jpg

图 4-16 。选择开始一个新的 Android Studio 项目;将你的应用命名为 ProAndroidWearable

将您的应用命名为 ProAndroidWearable 并将【wearables.android.pro.com 设置为您的域,然后单击下一步按钮。在接下来的对话框中,如图图 4-17 所示,从下拉列表中选择Android 4.3 Jelly Bean API Level 18,然后勾选磨损复选框选项。保持选中电话和平板电脑复选框选项(因为这是默认的应用开发选项)。

9781430265504_Fig04-17.jpg

图 4-17 。选择 API 18、穿戴、手机和平板电脑

一旦你点击下一个按钮,你将被带到一个添加一个活动到移动对话框,如图图 4-18 所示。选择 Add No Activity 选项,显示在该对话框的左上角,以便不为上一对话框的电话或平板电脑选项部分创建活动。

9781430265504_Fig04-18.jpg

图 4-18 。在“向手机添加活动”对话框中选择“不添加活动”选项(适用于手机和平板电脑选项)

一旦你点击下一个按钮,你将得到第四个对话框,添加一个活动佩戴对话框,在这里你将选择你的空白佩戴活动选项,正如你在图 4-19 中看到的,显示了圆形和方形 Android Wear 智能手表屏幕(活动)配置。选择空白穿戴活动后,点击下一个按钮,您可以为穿戴式应用命名主活动,并让 IntelliJ 在下一个对话框中设置(生成代码)引导 XML 文件。

9781430265504_Fig04-19.jpg

图 4-19 。在“添加要穿戴的活动”对话框中选择“空白穿戴活动”选项,然后单击“下一步”按钮

为您的新文件选择选项对话框中,可以在图 4-20 中看到,接受 Android Studio 建议的 Android 5 Java 和 XML 文件命名约定,并将您的主 Java 引导MainActivity.java和主 XML 用户界面布局容器文件 activity_main.xml 命名。Wear UI 布局容器 XML 文件分别以 round_ 和 square_ 作为文件名的前缀,这对我来说似乎合乎逻辑。

9781430265504_Fig04-20.jpg

图 4-20 。在“为新文件选择选项”对话框中,接受标准的 Java 和 XML 文件名约定

点击完成按钮后,会出现一个 Windows 安全警告对话框,如图 4-21 左侧所示。点击允许访问按钮,这样 Android Studio 就可以继续启动,用 Gradle 构建一个项目,如图图 4-21 右侧的构建对话框所示。

9781430265504_Fig04-21.jpg

图 4-21 。单击“Windows 安全警报”对话框中的“允许访问”按钮,解除对 Windows 防火墙的阻止

一旦 Android Studio (IntelliJ IDEA)启动你的引导程序项目,你会看到日积月累对话框,也可以使用文档和操作image日积月累面板序列来访问,如图图 4-22 所示。

9781430265504_Fig04-22.jpg

图 4-22 。IntelliJ 启动后,日积月累对话框将会打开

你可以点击下一个提示按钮,在图 4-22 中以蓝色显示,或者你可以点击它左边的上一个提示按钮向后循环提示队列,如果你是一个不墨守成规的人,阅读所有这些关于 IntelliJ IDEA 的热门提示,我强烈推荐。

完成日积月累后,点击下一个提示按钮右侧的关闭按钮,关闭日积月累对话框。然后全屏打开 Android Studio IntelliJ IDEA,这样您就可以接下来查看它,因为它第一次在其中打开了您的 Pro Android 可穿戴设备项目!这种实际使用越来越令人兴奋!

正如你在图 4-23 中看到的,左边有一个项目窗格,里面有你的 ProAndroidWearable 项目结构。中间的编辑器窗格目前有两个选项卡,一个用于 Android 5 中使用的 Java 7 代码,接下来您将看到,另一个用于 XML 用户界面布局标记,这是任何 IDEA 启动时打开的默认选项卡。

9781430265504_Fig04-23.jpg

图 4-23 。一旦你完成了项目创建过程,新项目将会打开

XML 选项卡有一个设计模式,默认情况下在启动时显示,并在编辑窗格的左下方使用一个标有“design”的蓝色选项卡来指示在它的右边有一个标记为“text”的白色标签,您将使用它来手工编写 XML 标记(从头开始),因为这是一个 Pro Android 标题!design 选项卡有一个呈现引擎,用于显示 XML 标记在 Android 硬件设备上的外观。这不如 AVD 仿真器精确,您将在第五章中设置 AVD 仿真器,在那里您将学习如何创建和配置仿真器。

IntelliJ 中的设计编辑器提供了一个拖放式用户界面,用于添加 UI 小部件和设置它们的属性(如果您喜欢这些术语,可以称之为属性或参数)。这些小部件在左侧的面板窗格中,可以拖动到智能手机预览界面,将它们添加到设计中。

当一个给定的 UI 小部件被选中进行编辑时,每个小部件的属性显示在图 4-23 的右下角。该窗格名为属性 ,将包含所选用户界面小部件支持的任何属性(或参数,也称为属性)以供进一步定制。

还有组件树窗格,在图 4-23 中 XML 编辑区的右上方,显示了当前正在使用的 widget 组件,比如那些已经添加到当前设计预览中的 UI widgets。

这个窗格给你一个鸟瞰图,本质上是顶级的 UI 设计层次结构。这是为复杂的 UI 设计层次结构使用而提供的,就像 Java 编辑窗格中提供的代码折叠一样,以便在代码变得更复杂时使事情变得更容易。

接下来,单击 XML 编辑器窗格左下方的文本选项卡,使其变为蓝色,并向您显示创建预览的 XML 标记结构,您会在中注意到,图 4-24 现在显示在 XML 编辑区域的右侧,而不是中间。

9781430265504_Fig04-24.jpg

图 4-24 。单击底部的文本选项卡,展开工程窗格文件树,并选择加入或退出使用统计

接下来,使用 IntelliJ 左侧项目窗格中的向右箭头图标打开该窗格中所有已关闭的文件夹。您的项目已经有了一个 Java 文件、四个 PNG 图像素材、三个 XML 布局文件、一个包含字符串值的 XML 文件和六个 Gradle 脚本,它们用于“构建”您的可穿戴设备应用。

当我在第七章谈到 Java 代码和 XML 标记时,你将开始了解关于这个 Java 代码(和 XML 标记)正在做什么的细节。

接下来,单击 IntelliJ IDEA 中间编辑窗格左上角的 MainActivity.java 选项卡,查看使用新的 Android 项目系列对话框为您创建的 Java 代码。

让我们来看看 Android Studio 为您创建的 bootstrap Java 代码,看看它是从哪里获得的。您的第一个 Java 包语句来自右侧的图 4-16 所示的对话框,它将您的公司目录字段与您的应用名称字段连接起来,以创建以下 Java 包语句:

package com.pro.android.wearables.proandroidwearable;

然后是导入语句。这些导入了四个 Android 类: BundleActivityTextViewWatchViewStub 。这些是创建活动对象、保存应用状态和创建 UI 小部件(视图对象)所需要的。这四个初始导入语句的 Java 代码(随着您添加更多的小部件和函数,将会添加更多)如下所示:

import android.app.Activity;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.widget.TextView;

生成的 Java 代码的重要部分是 MainActivity 类,它是 Android Activity 类的子类。你知道这是类层次的方法是因为它是通过 Java extends 关键字的使用来表示的:

public class MainActivity extends Activity {
    private TextView mTextView;
    @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);
            }
        });
    }
}

让我们来看看这个 Java 代码体的内部是什么,如图 4-25 所示。我将在这里介绍这一点,这样您就知道这些 Java 类和您的文件中的 XML 标记之间的相互联系了。第一行代码声明了一个名为 mTextView 的 TextView 小部件,因此现在使用了一半的 import 语句,因为 Activity import 用于 MainActivity 子类,TextView import 用于这个 TextView 小部件。

9781430265504_Fig04-25.jpg

图 4-25 。单击顶部的 MainActivity.java 选项卡,展开项目窗格文件树,并选择加入或退出使用统计数据

作为一名专业的 Android 开发人员,您可能知道,您必须始终为您为 Android 应用创建的活动@Override 一个 onCreate( ) 方法 。如果你需要回顾 Android 开发,我刚刚在 2014 年第三季度为绝对初学者写了第三版 Android 应用 (Apress),非常详细地涵盖了所有内容。这个方法调用使用您导入的 Bundle class 来创建状态内存(内存中与活动实例相关的所有状态或设置)Bundle,它被恰当地命名为 savedInstanceState 。如果 Android 因为任何原因不得不将您的活动从内存中交换出来,这个小 Bundle 对象将允许 Android OS 完全按照用户离开时的样子重建您的活动,因此用户甚至不知道该活动已经暂时从系统内存中删除。

你在里面看到的第一样东西。onCreate()方法的结构是super.onCreate(savedInstanceState);语句。这将通过使用父 Activity 类来创建(或者使用 Bundle 对象重新创建,如果 Android 已经从内存中删除了该活动的话)Activity 对象。onCreate()方法,使用 Java super 关键字从父 Activity 类调用该方法。

下一个代码语句是每个 Android 应用必须设置用户界面布局容器的语句。这是您的 setContentView()方法 调用,它引用了 activity_main.xml 文件,您之前在 activity_main.xml 选项卡中看到了该文件。这就是 XML UI 定义如何连接到可穿戴应用的 Java 7 代码。

下一行代码是使用第四个 WatchViewStub import 语句将可穿戴应用的 Android 应用部分连接到可穿戴应用本身,因此现在您已经使用了所有导入的类。您创建一个名为 stub 的 WatchViewStub 对象,并通过使用 findViewById( ) 方法 调用,用已经在 activity_main.xml 文件中定义的 watch_view_stub UI 小部件加载它。

接下来,使用一个. setOnLayoutInflatedListener()方法 将一个侦听器附加到名为 Stub 的 WatchViewStub 对象。在 watch_view_stub XML 布局定义膨胀后,该方法内的代码将被触发

这个构造将确保可穿戴设备 UI 定义在。onLayoutInflated()方法 结构被执行。注意这里面。setOnLayoutInflatedListener()方法,一个新的。onLayoutInflatedListener()对象是在参数区创建的。

这个。onLayoutStubListener()方法 通过使用包含该方法的 WatchViewStub 类的路径引用来调用。这是非常密集的 Java 代码,它设置了您的整个侦听器结构,以及在 UI 布局膨胀后它将要做什么。这些都是只用了六行 Java 代码就完成的。

在 onLayoutInflated()方法中,将使用 ID 为 text 的 TextView UI 小部件的 XML 定义来加载 mTextView TextView 对象。如您所见,没有 TextView 小部件或任何其他 ID 为 text 的小部件,因此您需要打开其他 XML UI 布局容器文件之一。这是通过右键单击项目窗格中的 rect_activity_main.xml 文件名并选择跳转到源代码菜单项来完成的,这在 IntelliJ 行话中的意思是“在中央编辑区域的选项卡中打开文件”

正如你在图 4-26 的 rect_activity_main.xml 选项卡中看到的,有一个名为 @+id/text 的 TextView 小部件,这是 Java 代码在。使用 findViewById()的 onLayoutInflated()方法结构。

9781430265504_Fig04-26.jpg

图 4-26 。右键单击 rect_activity_main.xml 文件,选择跳转到源菜单选项以在选项卡中打开

如果您想查看 rect_activity_main.xml 选项卡中的 UI 定义是如何在 activity_main.xml UI 定义中引用的,如图 4-24 中的所示,请查看第 7 行。如果您想在 IntelliJ 中打开行号,右键单击 XML 标记窗格左侧的灰色列,并选择显示行号选项。第 7 行类似于下面的标记:

app:rectLayout="@layout/rect_activity_main" app:roundLayout="@layout/round_activity_main"

这两个应用:对 rectLayout 和 roundLayout 属性的引用是 Android Wear 查看的属性,以确定将哪个 UI 设计用于矩形(方形)或圆形的智能手表。

现在你已经知道了在 bootstrap 项目中所有的东西是如何连接在一起的,接下来你需要做的就是确保你的 AVD 仿真器已经就位,并准备好用于本书的其余部分。恭喜你!你进步很大。

摘要

在本章中,您了解了 IntelliJ IDEA 的所有内容,以及 Android Studio 如何将他们的 Android 5 SDK 和 Java JDK 结合起来,将其变成一个定制的 Pro Android 可穿戴设备应用集成开发环境软件工具。

您还学习了如何使用 SDK 管理器来查看安装了哪些 SDK,以及安装本书中需要的其他功能。你知道了鲜为人知的以管理员身份运行的技巧,除了你试图将 Android Studio 1.0.1 更新到 Android Studio 1.0.2 时遇到的情况之外,它还可以解决许多其他失败的安装场景。幸运的是,有一个更新可用,所以我可以向您展示这个小技巧,让您在整个操作系统板的读或写权限。

您已经了解了 IntelliJ IDEA 版本 14 的所有内容:如何更新它,如何了解它的特性,以及它的项目结构、文件、文件夹层次结构和配置文件。您了解了 SDKs IntelliJ 将支持什么,以及什么版本的 IntelliJ(免费与付费)支持哪些编程语言。您了解了无数的编程特性,这些特性将使您的编码工作变得更加容易。

最后,您创建了自己的 ProAndroidWearable 应用,这样您就可以探索智能 IDE 了。您了解了 Java 7 和 XML 标记,并了解了 Java 代码的作用以及它是如何链接到(连接到)XML 标记 UI 定义文件的。

在关于 AVD 的下一章中,您将确切地了解如何为定制的可穿戴设备设置 Android AVD 仿真器,并获得一些关于 AVD 创建、配置、设置和定制的经验。

五、Android 虚拟设备:设置可穿戴设备应用模拟器

现在你已经对 Android Studio 的特性有了一个大概的了解,本章将讨论 Android 虚拟设备(AVD),它是一个软件模拟器。

这些之所以重要,是因为它们允许你更快地测试你的 Pro Android 可穿戴设备应用,而不必在每次你想测试应用的 Java 代码或 XML 标记时,通过 USB 电缆将你的 APK 文件传输到硬件设备(尽管你确实在第四章中安装了最新的 USB 驱动程序,以防你想这样做)。

让我们首先为方形和圆形佩戴硬件(智能手表外设)创建 avd。您将为基于英特尔 x86 的智能手表和基于 ARM EABI7 的智能手表执行此操作。这是可能的,因为 x86 和 ARM 系统映像是您在更新到 Android OS 5.0.2 时使用 SDK 管理器在第四章中安装的。

之后,你将在本章的剩余部分学习你的 AVDs 如何与 IntelliJ IDEA 中的 Android 5 可穿戴设备应用开发项目的其余部分进行接口。您将了解如何在开发过程中的任何给定时间选择使用哪个 AVD 仿真器来测试您的应用。这对于 Wear 应用开发非常重要,因为您将需要创建方形和圆形版本的 UI 设计,正如在第四章中详细讨论的那样,当您了解到 WatchViewStub 对象如何允许 Android 操作系统检测和提供针对用户智能手表形状的正确 UI 设计时。

使用 AVD 管理器:创建磨损模拟器

如果您还没有这样做,请使用您在第二章中创建的快速启动图标启动 Android Studio 开发环境。这将启动 IntelliJ IDEA 并显示你在第四章中创建的 ProAndroidWearable 项目。点击 IDE 顶部的工具菜单,如图图 5-1 所示,点击 Android 菜单,显示带有 AVD 管理器选项的子菜单。还要注意,AVD 管理器下面是 SDK 管理器选项,所以如果您想知道如何在项目模式下进入 IntelliJ 后访问 SDK 对话框,这是您将如何完成的。

9781430265504_Fig05-01.jpg

图 5-1 。使用 IntelliJ 中的工具image Android image AVD 管理器菜单序列并启动 AVD 管理器

这将打开 AVD 管理器对话框和一个当前安装的 AVD 仿真器列表,称为你的虚拟设备屏幕,如图图 5-2 所示。

9781430265504_Fig05-02.jpg

图 5-2 。使用 AVD 管理器对话框左下角的创建虚拟设备按钮创建 AVD

该对话框列出了当前安装在 IntelliJ IDEA 中的基本 avd 和默认软件安装,包括摩托罗拉的 Android 5 操作系统兼容的 Nexus 5 API Level 21,用于英特尔凌动 x86 处理器仿真。对于那些没有听到行业新闻的人来说,谷歌几年前收购了摩托罗拉。

在对话框的左下方,有一个标记为“创建虚拟设备”的按钮。点按此按钮可调出第二级 AVD 创建。

AVD 管理器中的第二级对话框用于选择虚拟设备配置对话框中的硬件,如图 5-3 中的所示。它列出了所有当前预定义的手机平板电脑佩戴、电视硬件,并通过对话框左侧的类别按钮对其进行分类和组织。正如您在图 5-3 中看到的,“真正的智能手表”并不是智能手表的外设,而是被列在手机部分,因为它们可以作为一部手机绑在您的手腕上。我在图 5-3 中高亮显示了 Neptune Pine 智能手表,这样你就可以看到属性是如何列在右边的。

9781430265504_Fig05-03.jpg

图 5-3 。请注意,Neptune Pine 智能手表是一款完整的 Android(手机)设备,不在 Android Wear 之下

点击对话框左侧的 Wear 按钮,查看 IntelliJ IDEA 中列出了哪些内置的 Android Wear 设备选项。

Android Wear Square 有预定义的智能手表仿真器,Android Wear Round 也有,如图图 5-4 所示。让我们首先为英特尔凌动(x86)处理器创建 Android Wear Round AVD 仿真器。

9781430265504_Fig05-04.jpg

图 5-4 。选择 Android Wear Round 硬件仿真器,然后单击蓝色的“下一步”按钮来定义它的使用

选择 Android Wear Round 条目,在图 5-4 中以蓝色显示选中,然后点击蓝色下一步按钮。这将带你进入第三层,进入系统映像对话框,如图 5-5 所示。请注意,该对话框中列出了两种不同的硬件版本,ARM 和 Intel 处理器。您将为这两种类型的穿戴硬件创建仿真器,因为有些智能手表产品基于 ARM,有些基于 Intel。选择 Lollipop x86 API Level 21 选项,然后单击下一步进入系列中的下一个对话框,在这里您可以为英特尔凌动 x86 Android Wear Round 智能手表 AVD 仿真器定义仿真器选项。

9781430265504_Fig05-05.jpg

图 5-5 。为 Android Wear 选择一个英特尔 x86 Lollipop 系统映像,该映像是使用 SDK 管理器安装的

对话框系列中的第四层是验证配置对话框,如图 5-6 中的所示。您将在仿真性能部分看到两个复选框,一个用于 GPU 仿真,另一个用于存储最后一个仿真器状态的内存快照,用于更快地加载仿真器。我假设你接受了我在第二章中的建议,并且有一个 SSD 硬盘,所以使用 主机 GPU 选项更值得选择,因为它允许 3D OpenGL 被模拟。需要注意的是,这两个复选框不能同时选中。你将不得不在这两个重要的模拟器津贴之间做出选择,所以要小心选择!

9781430265504_Fig05-06.jpg

图 5-6 。选择使用主机 GPU 以使仿真器图形能够在工作站的图形适配器上运行,然后单击完成

一旦你点击完成按钮,如图 5-6 右下方所示,你将被带到你的 虚拟设备对话框,如图 5-7 所示。

9781430265504_Fig05-07.jpg

图 5-7 。一旦您单击完成,Android Wear Round API 21 x86 AVD 将在虚拟设备中列出

再次点击对话框左下方的创建虚拟设备按钮,如图图 5-7 所示,接下来我们创建一个 Android Wear Square API 21 AVD。在选择硬件对话框中,如图图 5-8 所示,这次选择 Android Wear Square 设备,如蓝色选中所示。请再次注意,右侧列出了有关该 AVD 仿真器规格的信息,在本例中为尺寸、比率、 HDPI 密度和 280 DP

9781430265504_Fig05-08.jpg

图 5-8 。选择 Android Wear Square 硬件模拟器,然后单击下一步按钮定义它以供使用

点击蓝色下一个按钮,将打开系统镜像对话框,如图图 5-9 所示。再次选择一个 Lollipop API Level 21 x86 系统镜像。

9781430265504_Fig05-09.jpg

图 5-9 。为 Android Wear 选择一个英特尔 x86 Lollipop 系统映像,它是通过 SDK 管理器安装的

点击蓝色下一个按钮,进入验证配置对话框,如图图 5-10 所示。我再次使用使用 主机 GPU 选项,因为我有一个高级 3D 显卡,我是一个 3D 建模师和动画师,我计划最终在我的 Android 应用中使用 OpenGL。值得注意的是,许多对话框在升级过程中“滞后”了一个版本。因此,即使你在第四章中确保你是 5.0.2“干净的”,诸如更新对话框文本反馈信息的细节,例如,现在是 Android 5.0.2 x86 的 Android 5.0.1 x86,并没有进入当前正在使用的对话框 UI 标签的更新!

9781430265504_Fig05-10.jpg

图 5-10 。选择使用主机 GPU 以使仿真器图形能够在工作站的图形适配器上运行

点击完成,返回虚拟设备对话框,如图图 5-11 所示。

9781430265504_Fig05-11.jpg

图 5-11 。用于圆形和方形智能手表的英特尔凌动 Android Wear AVD 仿真器已经问世

我建议使用与基于英特尔凌动的智能手表完全相同的工作流程,这也可以在图 5-4 到 5-11 中看到,并创建使用 ARM 处理器技术的 AVD 仿真器。如果你这样做了,当你需要它们进行 Android Wear Round 和 Android Wear Square 测试时,你将拥有所有四个仿真器。在本书中,我将遵循“第一次就做对”的工作流程,所以当你在那个对话框中时,做好你需要的一切。

图 5-12 显示了你的虚拟设备对话框,在这四个 Android Wear AVD 模拟器都被创建之后。

9781430265504_Fig05-12.jpg

图 5-12 。适用于圆形或方形智能手表的英特尔凌动或 ARM Android Wear AVD 仿真器,随时可用

现在,您已经有了为 Android Studio IntelliJ IDEA 创建 ARM 和英特尔 AVD 仿真器的一些实践,您将为测试您的 Pro Android 可穿戴应用做好准备。

接下来,让我们看看如何使用这些 AVD 仿真器来测试您在第四章中创建的 ProAndroidWearable 应用。

使用穿戴模拟器:测试你的可穿戴应用

Android Studio IntelliJ IDEA 中的仿真器设置是通过运行/调试配置对话框访问的,当您更新和探索 Android 5 的 IntelliJ 高级集成开发环境时,您可能已经仔细阅读过第四章中的配置对话框。

正如你在图 5-13 中所看到的,这也可以从你的项目内部访问,通过使用一个运行image编辑配置菜单序列,它以蓝色突出显示。单击 IntelliJ IDEA 顶部的运行菜单,并选择编辑配置菜单项以打开对话框。

9781430265504_Fig05-13.jpg

图 5-13 。使用运行image编辑配置菜单序列来设置您想要使用的 AVD 仿真器

在图 5-14 中可以看到,主要有三个选项卡:通用LogCat仿真器。在这里,您将看到如何使用 LogCat (日志编目),因为您开始产生需要在本书的过程中纠正的错误。LogCat 是你在开发工作过程中真正需要考虑的东西,这就是为什么我没有在第四章中讨论 LogCat 的功能。

你可能认为仿真器选项卡是你需要设置 AVD 仿真器的地方,然而,事实上在常规选项卡的底部有一个目标设备 部分,在那里设置该功能用于 IntelliJ。

正如你在底部的图 5-14 中看到的,在 IntelliJ 中有几个选项可以设置用于测试你的可穿戴设备应用的目标设备。从使用仿真器 AVD 到使用你在第四章中更新的 USB 驱动程序,再到指示 IntelliJ 就使用哪个设备(仿真器或真实硬件)来测试你的 Android Wear 应用的 Java 代码和 XML 标记进行投票。

9781430265504_Fig05-14.jpg

图 5-14 。在常规选项卡中,选择模拟器目标设备单选按钮,并设置下拉选择器

如果您想手动选择目标设备,每次使用跑步或跑步佩戴图标或菜单序列时,您都需要选择位于显示选择器对话框选项旁边的单选按钮。每次你用这个配置设置在 IntelliJ 中开始运行磨损或调试操作时,Android Studio IntelliJ IDEA 都会为你显示一个选择设备对话框。

如果选择 USB 设备选项,IntelliJ 将在应用启动时检测插入的 USB 设备,然后用于测试目的。如果你有一个智能手表,这将是你的最佳选择!

因为我不能假设所有的读者都将拥有一个 Wear 智能手表,所以在本书的过程中,我将主要使用 Android Wear Square 和 Android Wear Round AVD 模拟器来测试可穿戴设备应用。这就引出了第三个仿真器选项。

如果您选择第三个仿真器单选按钮,您将能够从下拉菜单中选择首选 Android 虚拟设备设置。这是用您在本章前面创建的 AVD 仿真器填充的。

我将首先选择 Android Wear Round API 21 (英特尔凌动),您接下来将测试它,以确保它能够正常工作。在本章中,您还将测试您的 ARM AVD 仿真器和 Android Wear Square AVD,以便了解它们作为硬件仿真器的外观和工作方式。

如果您打算更改 AVD 模拟器,并在 USB 驱动程序(外部硬件设备)和使用 AVD 模拟器之间来回切换,您将需要尝试使用显示选择器对话框单选按钮选项。这将允许你在每次点击运行图标(看起来像一个绿色视频播放器传输按钮)或使用运行image运行穿戴菜单序列来测试你的应用时,选择你想要如何测试可穿戴设备应用。

使用 IntelliJ Run:在 Round Wear 中运行可穿戴应用

让我们使用 Run image Run 菜单序列,执行名为 ProAndroidWearable 的 bootstrap 可穿戴应用,它是您在第四章中创建的。我得到一个错误,显示在截图左下方的图 5-15 中。我还在这张截图中显示了运行image运行菜单序列。正如你所看到的,我的系统,碰巧是 AMD FX-8350,似乎在运行英特尔 HAXM x86 仿真时遇到了问题。这可能是由于英特尔和 AMD 在 CPU 和 GPU 市场上的激烈竞争,因此,发生这种情况并不奇怪。

9781430265504_Fig05-15.jpg

图 5-15 。使用运行image运行菜单序列尝试并启动 AVD 注意输出中的错误(底部)

让我们来看看让英特尔 HAXM 正常工作的正确工作过程,以防 HAXM 错误信息发生在您身上!

安装英特尔硬件扩展管理器:intelhaxm.exe

打开操作系统文件管理工具,使用搜索栏,输入英特尔 axm 或【intelhaxm.exe 搜索词,如图 5-16 顶部红色突出显示的。这将为您找到intelhaxm-android.exe文件。

9781430265504_Fig05-16.jpg

图 5-16 。使用文件管理工具搜索 intelhaxm.exe 文件,然后右键单击该文件并选择以管理员身份运行

您实际上可以右键单击该文件(结果),如图 5-16(??)所示,并使用“以管理员身份运行”菜单选项在您的系统上安装英特尔硬件加速执行管理器 (HAXM) ,以消除您在 Android Studio IntelliJ IDEA 中遇到的 HAXM 错误。

在我的系统上,我在一个 VT 不支持对话框中收到了可怕的“此计算机不支持英特尔虚拟化技术”错误消息,如图 5-17 左侧所示。对我来说幸运的是,在这种情况下,我安装了所有的 AVD 类型,并且仍然可以在我的 Android 可穿戴应用开发中使用 ARM AVD。我可以继续写这本书!

9781430265504_Fig05-17.jpg

图 5-17 。如果你像我一样使用强大的 AMD 8 核 FX 处理器,你就要倒霉了!

一旦我点击 OK 按钮,如图 5-17 左侧所示,我收到这个“英特尔硬件加速执行管理器安装向导提前结束”对话框,如图 5-17 右侧所示,于是我点击完成

如果您没有看到图 5-17 中所示的对话框,请安装英特尔 HAXM,这应该可以解决您可能在 IntelliJ IDEA 的运行/调试/编译输出窗格和 IDEA 底部标签中得到的任何错误消息(这些显示在图 5-15 中的)。

切换 AVD:在圆形佩戴臂 AVD 中运行应用

因为我无法模拟这款英特尔凌动 Android Wear Round AVD,所以我回到运行/调试配置对话框,如图 5-18 中的所示,使用图 5-13 中的运行image编辑配置菜单。我从下拉列表中选择了 Android Wear Round API 21 2 选项。因为我在几分钟前刚刚创建了所有这些 avd,所以我知道这是 ARM 版本,所以我可以使用这个仿真器向您展示如何测试您的 ProAndroidWearable 应用。

9781430265504_Fig05-18.jpg

图 5-18 。选择您的 ARM Android Wear Round API 21 2 AVD(请注意,英特尔 AVD 现在以红色标记)

这次我将让您点击 IntelliJ 顶部的绿色播放(运行)图标 。正如你将在本章中看到的,有几种方法可以运行你的可穿戴设备应用。一旦你点击运行图标,你会看到在图 5-19 底部显示的输出窗格,它显示了大量与在 AVD 仿真器内运行你的应用相关的技术信息。最后一行写着“创建窗口”(包含仿真器),位于 30,30 (操作系统桌面的右上角),大小适合一个 320x320 圆形 AVD 仿真器。如果你想看一看这是什么样子,它显示在图 5-20 中,在我的系统上第一次启动!

9781430265504_Fig05-19.jpg

图 5-19 。点击顶部绿色的运行(播放)图标,运行 ARM 模拟器;请注意输出选项卡(底部)中的统计数据

当你第一次运行和启动 Android Wear AVDs 时,它完全模拟了你第一次运行任何智能手表产品时的体验。你将看到的屏幕序列可以在图 5-20 中看到,其中一个屏幕显示 Android Wear 应用不在你的手机上。

9781430265504_Fig05-20.jpg

图 5-20 。首次发射显示精确的磨损仿真;当你第一次打开智能手表时,它会显示这个

如果您使用鼠标将模拟器屏幕滑动到一侧,您将看到其他屏幕,建议您从谷歌 Play 商店安装 Android Wear 应用,以便 Wear 智能手表外围设备可以正常工作。需要注意的是,这不是必需的;这只是模拟器在试图模拟真实世界的 Android Wear 体验时异常准确。还要注意在图 5-20 的最右边,如果你没有用 AVD 仿真器做任何事情(一分钟后),也会显示一个项目 Volta 电源关闭(关闭)屏幕。

如果关闭 AVD 仿真器,单击红色 X 关闭窗口,并使用 IntelliJ 中的运行进程简单地重新启动它。我将向您展示另一种直接运行您的应用的方法,即使用右键单击上下文相关的基于菜单的工作流程。点击MainActivity.java标签,如图 5-21 中上部所示,然后在编辑窗格的右侧点击右键,选择运行‘主活动’菜单选项,再次启动您在运行image编辑配置运行/调试对话框中选择的 AVD。

9781430265504_Fig05-21.jpg

图 5-21 。从项目中启动 AVD 的另一种方法是在代码区域右键单击并选择 Run 'MainActivity '

第二次启动 AVD 时,你会看到一系列不同的 AVD 启动屏幕,这些屏幕会告诉你正在加载、针对穿戴设备优化、正在启动等应用的数量。你还会看到一个穿戴启动标志屏幕,以及一个智能手表屏幕,上面有云与充电的时间和指示器,以及“Ok Google”,如图图 5-22 所示。

9781430265504_Fig05-22.jpg

图 5-22 。AVD 优化和启动应用,显示 Android Wear 启动徽标,以及 Ok Google time 屏幕

一旦你获得了默认的 Android Wear 主屏幕(时间),你就可以在屏幕上滑动鼠标并下拉不同的手表表面,如图 5-23 的最左侧所示。也可以上下滑动;这将滚动不同的选项,如图 5-23 的的第二个窗格所示。

9781430265504_Fig05-23.jpg

图 5-23 。一旦模拟器启动,滑动屏幕找到开始按钮,然后滑动找到 ProAndroidWearable 应用

向下滚动并找到蓝色的开始选项,然后点击它进入应用屏幕,可以在图 5-23 的第三个窗格中看到。

您可以上下滚动启动应用屏幕,直到找到您的 ProAndroidWearable 应用,当您单击它时,它将启动。这将运行引导应用并显示 Hello Round World!消息。这个文本字符串对象包含在一个 TextView 小部件中,这个小部件可以在 activity_main.xml 文件中引用的 round_activity_main.xml 文件中找到。

我用浅蓝色突出显示了图 5-19 中的标记引用。我是通过在 XML 文件引用上单击鼠标光标来做到这一点的,所以在这个引用中寻找一个插入条。这是 Android 操作系统查看您正在使用的仿真器(或智能手表硬件)的地方,然后调用正确的 UI 布局设计 XML 定义文件,该文件将适合该类型的智能手表外观!作为开发人员,你将提前定义这一点,在本书的课程中,我将专注于智能手表的 UI 设计。

切换 AVD:在方形佩戴臂 AVD 中运行应用

进入 IntelliJ,使用运行image编辑配置工作流程,如图图 5-13 (此处不重复截图),选择Android Wear Square API 21 2(ARM,除非你的 Intel AVDs 在工作,你自己选择)。然后使用图 5-15 、图 5-19 或图 5-21 中所示的运行方法,这次在 Android Wear Square AVD 仿真器中运行可穿戴应用,以便您可以看到用户体验与 Android Wear Round AVD 仿真器相比如何。

请注意,每当您更改 Android 虚拟设备仿真器时,都会出现一个“等待 adb”进度条对话框,因为 IntelliJ IDEA 需要将新的 AVD 仿真器系统映像定义加载到系统内存中。这个进度条显示在图 5-24 中,如果您有一个带 SSD 硬盘的快速多核系统,它应该最多只显示几秒钟。

9781430265504_Fig05-24.jpg

图 5-24 。如果你在 IntelliJ 中切换到一个不同的 AVD 仿真器,你会得到等待 adb 进度条

在这个进度条消失之后,你的 Android Wear 方形模拟器会出现在和之前的 Android Wear 圆形模拟器相同的位置(你桌面的右上方)。正如你在图 5-25 中看到的,会出现一个稍等(加载)的屏幕,然后你会看到和你在 Android Wear Round 模拟器上看到的相同的连接屏幕。

9781430265504_Fig05-25.jpg

图 5-25 。当你第一次启动 AVD 时,它会模拟真正的智能手表会做什么

使用 Android Wear Round AVD 模拟器,如果你不使用模拟器,就像 Project Volta 与你的智能手表一样,如果你在启动后一分钟左右不使用它,你的 AVD 就会自动关闭。

出于这个原因,我建议在你的 Android Wear 模拟器中启动 ProAndroidWearable 应用后立即测试它!人们可能会认为,开发人员会急切地想看看他们的 Java 编程逻辑或 XML UI 设计是否工作正常,因此让 AVD 进入“省电”模式很少会成为问题。

一旦你的模拟器已经加载,你将得到一个“配对!”屏幕,模拟穿戴设备与智能手机或平板电脑的配对,如图 5-26 左侧所示。该屏幕消失后,您将看到一个您一切就绪!屏幕,告诉你向上滑动进入,显示在图 5-26 的右侧。

9781430265504_Fig05-26.jpg

图 5-26 。一旦你得到配对的屏幕,你就一切就绪了!屏幕,向上滑动!

如果你在这一点上向下滑动屏幕,你会看到一个列表,列出了你可以用可穿戴设备做的各种事情,就像你用 Android Wear Round AVD 做的一样,如左侧窗格中的图 5-27 所示。找到开始图标并点击它来加载设备上的应用。接下来,找到你的 ProAndroidWearable 应用,点击它。一旦你这样做了,你会看到你的应用正在运行,如图 5-27 中的右窗格所示。

9781430265504_Fig05-27.jpg

图 5-27 。找到开始菜单和 ProAndroidWearable 应用,并启动它运行

因为有圆形和方形智能手表,所以你需要在这些模拟器上测试你的应用,以确保你的应用在这两种类型的屏幕上都可以工作。一些顶级制造商,如 LG 电子,甚至拥有这两种类型的屏幕。LG 目前正在提供 G(方形)和 R(圆形)智能手表产品。

摘要

在本章中,您学习了如何为您的 Pro Android 可穿戴设备 IntelliJ 开发环境创建软件仿真。这些被称为 Android 虚拟设备,也被称为 AVDs ,使用 IntelliJ IDEA 中的运行/调试对话框创建,并使用运行image编辑配置菜单序列访问。

首先,您学习了如何使用 AVD 管理器来创建在 Android Studio 中使用的 AVD 模拟器(IntelliJ IDEA)。您为英特尔凌动处理器和 ARM 处理器创建了 Android Wear 圆形和 Android Wear 方形 AVD 仿真器。这样做是为了让您有四个不同的模拟器用于您的 Android 开发工作流程。

当我试图为英特尔凌动运行 Android Wear Round AVD 时,我在 IntelliJ 的输出窗格中收到一条错误消息。因此,我借此机会向您展示了如何在工作站上找到并安装英特尔的硬件加速执行管理器( HAXM ),以防您遇到同样的问题。

我发现英特尔不支持 AMD 处理器(这并不奇怪),所以我将不得不在我的 Pro Android 可穿戴设备开发工作流程中使用这些 ARM AVDs。接下来,您学习了如何实际使用这些 AVD 来运行您的 ProAndroidWearable 应用,以及如何导航 AVD 仿真模式。

在本书的下一部分,你将开始学习关于如何开发 Android 可穿戴应用和添加可穿戴功能的细节。

六、Android 表盘设计简介:考虑因素和概念

现在,您已经掌握了一些关于 Android 5 及其新功能、材质设计、Android Studio IntelliJ IDEA 和 AVD 仿真器的“基础知识”,并且已经设置、更新和配置了一个开源 Pro Android 可穿戴设备应用开发工作站,现在是时候进入正题,了解 Android 5 API 中直接应用于并影响可穿戴设备应用开发的各个部分了。

你已经在第四章中学习了如何创建一个全新的 ProAndroidWearable 应用(我想给你一个好的开始),所以在这一章你将开始学习最流行的 Android Wear SDK 应用。这被称为表盘 API ,由谷歌发布,使开发者能够创建定制的表盘,将适用于所有智能手表型号。

因为人们对如何在 Wear SDK 中创建表盘非常感兴趣,尽管它很复杂,但它为创建其他一些更高级类型的可穿戴应用提供了基础,所以我将在本书中详细介绍表盘。我将在第十七章中讲述更多高级 Android 可穿戴开发主题。我们先来看一下表盘设计考虑因素;之后,在接下来的几章中,你将学习一些高级的图形设计概念。

表盘设计:注意事项和指南

谷歌的 Android Wear SDK 最近在 Android Studio 1.x 中引入了一个 Watch Faces API ,允许开发者创建定制的表盘。这使得开发人员可以使用定制的设计来创建智能手表“皮肤”或“面孔”,这些设计基于 Java 代码、XML 用户界面和新媒体素材(如 SVG(形状)、PNG(图像)和 UI 小部件)的组合。

这些 Watch Faces 应用可以简单地以一种新的独特方式告诉用户现在是什么时间,它们还可以向手表佩戴者显示上下文相关的信息,如通知、天气、健康信息、收到的短信、电话呼叫者姓名以及用户希望实时访问的类似信息,以改善他们的日常生活。Watch Faces API 允许 Android 5 开发者创建一个将所有这些数据集成到一个无缝用户体验中的设计。

手表面向用户界面:艺术与功能的无缝融合

您的 Watch Faces UI 设计将从您的 activity_main 开始。 xml 文件,您在第四章中创建了该文件,然后在 square_activity_main 中从前进到更定制的设计。 xmlround _ activity _ main . XML文件如您所知,这些文件保存了您不同的 watch faces UI 设计类型,我将很快介绍这些类型。

正如你可能想象的那样,表盘设计将图形、算法和数据完美融合,创造出一种视觉用户体验,以一种美丽的方式向用户告知各种类型的信息,而无需任何额外的观看工作。

作为 Watch Faces API 开发人员,您的目标是创建优雅、清晰、有条理且有吸引力的用户界面布局,能够适应不同的智能手表显示类型、屏幕形状和边框大小。

在本章和本书的其余部分,您将学习如何设计 Watch Faces UI,以及如何为用户提供颜色和外观选项。这将使您的用户能够使用适合他们生活方式的穿戴式智能手表设备来创建他们自己的个性化手表面孔用户体验。

您还需要考虑 Android OS 用户界面元素将如何与 Watch Faces 应用设计进行交互,包括电池电量、蓝牙或 4G LTE 信号指示灯等系统图标。在本章的后面,我将讨论在你的表盘设计的 UI 设计布局中定位它们的各种选项。

手表面临电源使用:互动和环境模式

Android Watch Faces API 要求开发者为他们的 Watch Faces 应用提供两种不同的用电模式。这些被称为交互(彩色)模式和环境(灰度)模式。你的表盘 UI 设计需要考虑这些不同的功耗模式。我将讨论如何优化这两种模式的设计。

通常,如果表盘设计在环境模式下看起来很专业,那么在交互(色彩)模式下会看起来更原始。相反的情况并不总是如此,因为在交互模式下形成对比的某些颜色在灰度模式下可能会显示出相同的灰度,因此,Watch Faces API 图形设计将比您想象的要复杂得多。

观看面孔互动模式:全彩色,30 FPS 动画

你的 Watch Faces 智能手表皮肤 UI 设计中最高级别的模式是交互模式,它允许你的手表 Faces 设计使用全彩和动画。在交互模式下,当用户移动手腕看手表表面时,他们的屏幕会切换到交互模式。

您的手表表面(或皮肤)图形设计可以在交互模式下使用全彩色像素和高帧率动画。这并不是说你应该使用高帧率动画,如果你可以避免这样做的话,因为它使用电源的速度比静态交互模式的表盘设计要快得多。

观看面孔环境模式:每分钟更新的灰度

Watch Faces 智能手表皮肤 UI 设计中的下一个最高级别模式将是环境模式。这种模式将通过使用更少的颜色(在选定的几个型号上)或使用灰度或黑白来代表您的手表表面 UI 设计,帮助智能手表设备节省电力。您的 UI 设计可以让您的用户清楚地看到,他们的智能手表屏幕处于环境模式,只使用灰度颜色作为表盘设计组件。

重要的是不要在环境模式中使用大量的纯白(完全打开#FFFFFF 白色像素颜色值)。这是因为与黑色背景相比,它会耗尽电池寿命,并会分散用户的注意力。

在环境模式下,智能手表显示屏每分钟只会更新一次。出于这个原因,手表表面 UI 设计应该只在环境模式下更新小时和分钟。只有在互动模式下,并且只有在表盘需要时,才应该显示秒。

当智能手表设备切换到环境模式时,Android 操作系统会提醒你的表盘应用,正如你将在本书的这一部分中看到的那样。您可以创建一个专门用于此模式的表盘环境模式设计。因此,如果你真的想设计 Watch Faces 应用,你需要在像素级别设计你的图形,以适应所有这些不同的模式,包括那些超低功耗模式,我将在本章的下一节介绍。

手表表面节能:低位和烧伤保护

今天所有最新的 Android Wear 智能手表设备都利用了各种不同的显示屏硬件技术。其中包括 LCD (液晶显示器) LED (发光二极管)有机发光二极管(有机发光二极管) AMOLED (有源矩阵有机发光二极管),高通 Mirasol 前光技术。

这些显示器像素阵列制造方法中的每一种都有其自身的优点和节能考虑。为你的表盘设计环境模式显示屏的一个重要考虑因素是它如何影响电池寿命甚至屏幕老化,这可能会影响一些屏幕技术,如 AMOLED 和有机发光二极管。

如果您想在 Java 编码和 XML 设计中获得细节,可以配置 watch face 应用,根据每个设备上可用的屏幕类型显示不同的环境设计图形。我将在本书的这一部分讨论所有这些屏幕类型常数和更复杂的手表表面 API 考虑因素,以便您可以为所有屏幕类型的手表表面设计最佳的整体设计。

低位超节能:考虑因素和技术

当处于环境模式时,诸如有机发光二极管或透反射 LED 的一些显示屏技术中的像素将是“开”或白色,或者“关”或黑色。在这些情况下的环境模式,即在这些屏幕类型上,如华硕 ZenWatch,通常被称为“低位”环境模式。

当为低位环境模式设计时,仅使用黑白颜色,避免使用灰度值,因为它们在低位显示器上不起作用。确保在使用低位环境模式的设备上测试您的 UI 设计。

这意味着您需要在绘制样式中禁用抗锯齿,我将在本书的后面讨论这一点,本书涵盖了图形设计概念,如抗锯齿、alpha 通道、混合模式以及实现这些概念所需的工作流程。

老化保护和预防:考虑因素和技术

AMOLEDs 和 OLEDs 等显示屏会产生像素老化,就像旧的阴极射线管(CRT)屏幕长时间显示同一图像一样,在设计时,您应该最大限度地减少白色像素着色,这既是为了提高能效,也是为了最大限度地减少显示屏老化效应。

当这些类型的显示屏在环境模式下运行时,操作系统会将显示屏的内容周期性地移动几个像素,这将有助于避免这种像素老化现象。

对于低比特环境模式设计,最小化屏幕老化的关键是保持 90%的像素为黑色。用低位环境模式中的轮廓形状替换常规环境模式设计中的实心灰度形状,以提供老化保护。

另一个好主意是替换用像素(矩阵)图案填充的灰度图像区域。对于模拟(圆形)表盘设计,挖空一个中心(指针相遇的地方)区域,这样当手表处于低位环境模式时,可以避免手表中心的像素老化。

手表表面 UI 设计形状:方形与圆形

如果你要成为一名专业的 Watch Faces API 开发人员,那么你需要学习如何优化你的 Java 代码、XML 标记、UI 设计和图形设计,以适应方形和圆形设备。这可以通过使用适合两种形状的单一设计或者通过提供两种不同的设计来实现。

如果你提供两种不同的设计,那么你可以使用 Android 操作系统的功能来检测脸型。在本书的这一部分,我将讨论如何在 Java 代码和 XML 标记中做到这一点。

正如你所料,一些表盘设计概念在这种或那种格式下可能会更好。然而,通过一点巧妙的设计,您可以创建一个混合设计,允许用户使用手表表面,而不管他们的智能手表使用的显示屏格式。

让我们回顾一下一些 Watch Faces API 设计指南,它们将帮助您的 Watch Faces 应用跨越方形和圆形设备。

手表表面概念设计:创造一个灵活的设计概念

表盘设计的视觉功能应适用于圆形和方形两种形式。你的表盘的视觉功能应该足够灵活,无需任何调整就能以任何一种形式显示。

然而,一些手表表面设计概念最终将需要不同的执行,即不同的 Java 代码、不同的 XML 标记、不同的图形设计和不同的动画,跨越方形和圆形屏幕。

手表外观风格设计:使用一套通用的设计风格

类似于您将 CSS3 用于 HTML5 应用和网站,或者将样式、主题和材质设计用于 Android 应用,使用一组风格化的颜色、线条粗细、阴影、渐变、图形设计元素、动画和其他设计元素来绘制方形和圆形 Watch Faces 应用版本之间的视觉联系。

通过使用相似的调色板或一致的视觉元素,您的方形和圆形表盘的整体 UI 设计的外观将看起来像是为每个表盘形状定制的,同时仍然保留表盘应用设计是同一视觉系统的一部分的感觉。

表盘设计类型:圆形模拟与方形数字

你的一些表盘设计概念可能会采用传统模拟时钟的格式,因为这是几个世纪以来最受欢迎的计时外观,它采用径向(圆形)方式,带有时针、分针和秒针的中心支点。

使用这种传统的表盘设计方案,开发人员将有机会吸收圆角区域,这些区域在将这种设计转换为方形表盘格式时将不可避免地出现。

这将给你的表盘设计过程带来创造性的火花,因为你试图发现创新和有吸引力的方式来扩展和探索这个额外的表盘屏幕“房地产”这可能包括装饰元素、操作系统指示器、动画元素、功能用户界面元素、日期相关指示器或日历日期信息。

手表界面集成:吸收操作系统功能

Android Watch Faces API 要求您在手表 Faces 的设计中吸收所有必要的操作系统功能,以便您的设计适应基本的 Android Wear UI 元素,如状态、状态和通知。

这些由操作系统渲染(控制)的 UI 元素为你的 Watch Face 用户提供关于他们可穿戴硬件的状态信息(电源、信号等)。)并显示来自运行 Wear 外设应用的用户手机或平板电脑上的服务的各种类型的通知。

出于这个原因,有一个在逻辑位置显示关键操作系统 UI 元素并在表盘设计中明确定义的表盘设计工作流程是很重要的。例如,确保操作系统提供的 UI 元素和消息不会被任何 Watch Faces 图形设计或 UI 设计元素所掩盖。

Android 通知:CardView UI 布局消息

Android“Cards”是 Android 5 中的一个新通知系统,它在可穿戴外设和其主机移动设备之间架起了信息桥梁。这是大多数可穿戴应用通知终端用户各种事情的方式。用户可能会在他们的可穿戴设备上收到关于电子邮件或文本消息的通知。作为一名表盘开发者,你需要在你的设计中支持大卡片和小卡片。您的 watch faces 应用可以指定您对卡片大小的偏好,但用户可以覆盖此设置。用户也可以通过向下滑动来暂时隐藏卡片。

一张 Peek 卡片 是通知流中最上面的卡片,将会出现在智能手表屏幕的底部。一个可变 Peek 卡有一个高度属性,由通知内的文本量决定。一个小 Peek 卡会给表盘设计留下更多空间。圆形模拟指针的表盘通常使用小 Peek 卡。如果可变 Peek 卡的最大高度上方的时间清晰可见,如果您愿意,您可以选择使用可变 Peek 卡。可变 Peek 卡将显示更多通知信息,但是,下半部分带有信息的手表表面最好使用小 Peek 卡。

需要注意的是,当 Peek 卡的边界(尺寸)发生变化时,您的 Android 5 操作系统会通知您的表盘设计(Java 代码)。因此,如果 Peek Card Bounds 对象发生变化,您的 Java 代码将交互地重新排列表盘设计中的用户界面和图形设计元素,从而使交互重新排列成为必要。

Android 硬件状态指示器:硬件模式状态

Android 状态指示器用于告诉用户可穿戴设备的状态,例如它还剩多少电量,它是否正在充电,或者它是否处于飞行模式。在设计表盘时,你需要考虑这些指示器图标如何融入表盘的视觉设计。

智能手机或平板电脑上的 Android 状态指示器出现在状态栏中,这并不奇怪;然而,在表盘设计中,它们可以放置在可穿戴设备显示屏周围的几个固定位置。

Android CardView 类用于在材质设计下创建新的“卡片”范例。如果您需要支持更大的 Peek 卡,状态指示器图标的指示器位置应该靠近表盘设计的顶部或中心。如果你把硬件状态图标放在表盘设计的底部,操作系统将被迫使用小的 Peek 卡。

如果表盘周边包含重要的视觉元素,例如装饰元素、刻度或数字,则将指示器置于表盘设计的中央。

安卓热门词汇布局:OK 谷歌短语

将 Android 操作系统功能集成到 Watch Faces UI 设计中要记住的另一个重要因素是,要为 Android“热门词汇”留出空间。Android 的热门词汇是短语 OK Google ,它在启动时显示,告诉用户他们可以通过使用语音识别技术与手表进行交互,包括预定义的语音命令。

当你的用户打开他们的可穿戴设备时,这个热门词汇会在智能手表显示屏上出现几秒钟。在用户说 OK Google 五次后,热门词汇不再出现,因此,Android 操作系统 UI 元素的放置没有状态图标或卡片那么重要。

你应该避免使用 Android hotword 位置来掩盖手表外观设计的重要 UI 元素。还有针对热门词和状态指示器的“背景保护”UI 元素设置,可以帮助增加对比度(可读性)。应该打开这些 UI 元素选项,除非您的设计是定制的,使 UI 元素以最大对比度出现在它的顶部(例如,使用没有图案的深色)。

Android 外设连接:Wear Companion 应用

Android Wear companion 应用是“主机”智能手机或平板电脑与“外围”Android Wear 智能手表硬件之间的桥梁。此应用将允许您的用户访问应用中的所有表盘设计,并允许他们从包含的设计中进行选择,以及更改他们的设置,如颜色、数字、风格、动画、功能等。

观察面孔清单:你不需要提供一个启动图标

所有可用的 Watch Faces 应用都可以从 Android Wear 配套应用或您捆绑的第三方应用中访问。因此,您的应用启动器图标适用于捆绑的外设(智能手机或平板电脑)应用,而不是 Watch Faces 应用。

因此,任何 Android Wear Watch Faces 应用都不需要在 AndroidManifest.xml 文件中声明独立的应用启动器图标。

观察面孔控制面板:您的设置对话框面板

如果您的设计有需要使用设置对话框设置的有用选项,您的手表面孔设计也可以有一个手表面孔设置面板。设置对话框(或面板)可以使用表盘本身或使用安装在用户智能手机或平板电脑上的配套应用的(更大)显示屏来访问。

您应该在表盘上设计表盘设置,以限制开或关(称为二进制)选择。你也可以使用 ListView objects (类)来实现可滚动的设置列表。

智能手机或平板电脑上 Wear companion 应用的设置可能包括更复杂的配置项目,除了您在 watch face UI 设计上提供的基本设置之外。

在大多数情况下,你可以使用标准的 UI 布局容器类(UI 组件),比如 Android CardView 类,来设计一个设置对话框或者设置面板,这一点你将在本书后面学到。

当你成为一名更高级的 Android Watch Faces API 开发人员时,一旦你建立了设计表盘的可靠工作流程,你可能还想探索其他更复杂、更有创意的设置选项设计。

观察面孔功能:功能数据集成

现在我已经介绍了表面设计规则和表单,接下来我将介绍表面函数。您的表盘设计可以向用户显示“上下文相关”的数据,如外面的天气、月相或代表夜间、日出、正午或日落的颜色以及类似的文本数据表示,这将把原始数据转化为表盘设计图形。

表盘设计通常会通过改变表盘设计的颜色、样式或图形设计来可视化不同类型的外部数据。这是使用 Java 代码和 XML 标记完成的。让我们来看看为表盘设计增加功能的诸多考虑因素。

数据可视化:您希望用户看到的数据

在涉及数据集成的表盘设计中,第一步是图形设计,它将赋予数据本身以生命或视觉效果。

决定你的表盘将如何定义你的观众对你想作为表盘设计的一部分显示的数据的看法。你对数据的视觉概念化应该容易被用户识别或理解。此外,您的手表设计试图可视化的数据需要得到真实用户日常需求的支持,以及将这些数据应用于现实世界的需求。

你需要想一想,当最终用户看到表盘设计后,你想让他们知道什么。他们会理解你的设计试图用你选择的颜色、风格变化或图形设计来传达什么吗?

一旦你设计了你的数据可视化并对其进行了测试,以确保用户能够识别你的设计是如何可视化他们感兴趣的数据的,接下来你需要做的就是确定表盘将如何获得你要可视化的数据。

数据整合:表盘设计与数据的融合

如果您要可视化数据,而不仅仅是一天中的时间,请设计 watch faces 应用,使其包含与时间数据密切相关的其他有用类型的数据,这些数据是 watch faces 的核心。

时间数据中包含的逻辑数据类型当然包括日期,可能还有计时器(秒表)、闹钟、日历约会、时区功能、天气预报、月相,甚至可能还有位置或健身数据。

您还需要找到一种方法,将数据可视化与您的表盘设计无缝集成,当观众快速浏览表盘应用时,创造性地激发他们使用颜色、文本、样式或自定义图形设计来消费您正在可视化的数据。

您的设计在可视化和整合外部数据方面的出色表现将直接关系到表盘应用的受欢迎程度。因此,你需要避免在基于时间的表盘上覆盖额外的数据,而不是使用灵感设计,以巧妙的方式将其无缝集成到整体设计中。

在设计数据集成时,您需要考虑如何通过您用于表盘的设计来表达这种类型的数据。例如,如果您正在设计一个与天气相关的表盘,请使用反映当天当前温度范围的颜色渐变,因此 80 到 100 度的范围可以用橙色到红色来表示,20 到 40 度的范围可以用白色到浅蓝色来表示。

数据同化:使用简单、统一的设计目标

一旦决定了如何可视化数据,并达成了手表外观概念决策,就该使用 Java 代码、XML 标记和数字图像(例如 GIMP)来实现您想要的 UI 设计了。

最受欢迎的表盘设计最终将是简单而优雅的设计。能够通过简单的一瞥传达大量信息的表盘将在服装市场大受欢迎。能够提供表达不同类型数据的统一设计的表盘将被认为是谷歌 Play 商店中最“天才”的表盘。

为了精心制作一个单一的表盘数据可视化“信息”,你必须在设计中对你想要可视化的最重要的数据进行排序。例如,不要试图在表盘上显示一个全面的天气预报,你可以使用一个图形设计来显示你外出时天空的样子(晴天、多云、星星、雨、雪等等)。

如果你显示的是基于文本的信息,在任何给定的时间尽量减少屏幕上的字符数。例如,如果您正在添加日历功能,而不是试图显示整个月的日历事件,您的设计应该只显示一个或最多两个即将到来的事件。

利用“逆向归约”的过程,我将在下面讨论,你将能够在设计中精心制作一个单一的数据表达。

观察面部发展:从基础开始,然后逐步增加

确保你的腕表表盘设计工作流程从仔细考虑你的腕表表盘将为你的最终用户提供什么开始。这将使您深入了解最终用户的需求和期望,从而构建出一个成功的 watch face 应用,这也是本书讨论的主题。

让其他智能手表爱好者来运行一个概念,看看关于设计概念的共识是否是好的和受欢迎的,这总是一个好主意。你还应该在开发时彻底测试你的表盘设计,甚至实施一个“beta 测试计划”,并让其他智能手表用户确认你对表盘设计的任何设计假设。

在餐巾纸上画出表盘设计的草图,然后让一两个智能手表最终用户告诉你他们对设计的看法,以及他们会用它来做什么,这可能是一个很好的主意。

不要假设你会在第一次尝试时就开发出一个史诗般的表盘设计,因为这不太可能发生。您需要结合不同的设计场景,尝试不同类型数据的表盘设计和数据组合。在开始编码之前,你还应该确保用实际的手表屏幕来测试你的表盘设计。

手表表面图形设计:多媒体概念

让我们来看看 Android 5 中不同类型的图形概念和支持,您可能会在表盘设计中使用它们。你将在 Watch Faces 设计中使用的主要素材是矢量插图,在 Android 中称为形状和渐变,以及位图图像,它们使用 JPEG、WebP 和 PNG 等图形文件格式,以及动画,它们支持矢量 动画位图动画,并将这两种类型的多媒体带入第四维(随时间移动)。

让我们先了解一下最具数据压缩性的图形技术,矢量插图,它仅使用代码完成,然后介绍 Android 中支持的位图和图形格式以及核心数字图像概念,最后研究如何在 Android 中实现动画,使用 XML 标记结合位图图像资源和矢量代码。在我介绍完所有这些内容之后,您就可以开始研究 Java 编码、XML 标记和创建手表外观所需的图形设计工作流程了!

矢量观察面孔:使用 SVG、形状和渐变

可用于表盘设计的数据优化最多的新媒体素材类型是数字插图,俗称矢量图形 。如果你熟悉 Adobe Illustrator 或者 InkScape,那么你已经知道矢量图形涉及线条曲线笔画填充渐变

矢量图形的主要开源文件格式称为SVG??,代表可缩放矢量图形 。所有流行的开源平台都支持 SVG 数据,包括 Android,以及 HTML5 和 JavaFX (Java 8)。

作为一名专业的 Android 开发人员,您知道 Android 中的多媒体素材是使用 Drawable 对象来表示的。矢量形状将使用 ShapeDrawable 类来创建矢量形状的轮廓,并可以使用 GradientDrawable 类的渐变填充形状。您的 Watch Faces 应用可以仅使用 Java 代码,或结合 XML 定义文件使用 Java 代码,在智能手表屏幕上创建这些可缩放的矢量图形元素。

值得注意的是,整个 Watch Faces 应用只能使用 SVG 创建,这意味着它们将是 100%的 Java 代码和 XML 标记。因此,这些应用 APK 文件的文件大小会特别小,因为 APK 文件中没有新的媒体素材(数字图像文件)存储。

某些表盘设计元素特别适合可伸缩的矢量元素。例如,线条可用于时针、分针和秒针,文字可用于数字和罗马数字,圆形元素可用于表圈或计时元素。

通常会有一些矢量图形元素、位图和动画的组合用于手表外观设计,所以接下来让我们看看 Android 的 BitmapDrawable 对象,以及其他支持的数字图像格式,甚至一些重要的数字图像概念,以便您在本书的剩余部分更好地理解我所谈论的内容。

位图观察面:位图格式和图像概念

因为你将使用数字图像来设计你的表盘,并且数字图像也是你在 Android 中的动画绘制对象的基础,我将花一些时间来为那些不是专业数字图像编辑的人提供理解本书概念所需的基础知识。Android 5 支持许多流行的开源数字图像文件格式,其中一些格式,如 GIF,已经存在了几十年。接下来让我们看看这些。

Android 数字图像格式支持:PNG、JPEG、WebP 和 GIF

Android 5 支持的数字图像格式从你几十年前的 Compuserve 图形信息格式( GIF ) 和古老的联合图像专家组( JPEG )格式,到最近的便携式网络图形( PNG ) 和网络照片( WebP )格式。我将按照起源的顺序介绍这些内容,从旧的(不太理想的)GIF 到最新的 WebP 格式。

Android 5 操作系统仍然支持 Compuserve GIF,但是不建议日常使用。GIF 是一种无损数字图像文件格式,因为它不会丢弃任何图像数据来达到更好的压缩效果。GIF 压缩算法被称为编解码器 (编码器-解码器),不像其他格式那样精细(意思是:强大)。它只允许索引颜色,我将在本章稍后介绍。也就是说,如果已经创建了所有图像资源,并且它们使用 GIF 格式,您仍然可以毫无问题地使用它们,除了手表表面的视觉质量下降之外。

Android 支持的下一个最古老的数字图像文件格式是 JPEG,它使用真彩色深度而不是索引颜色深度。JPEG 是一种有损的数字图像文件格式。该术语来源于这样一个事实,即它“丢弃”或丢失原始图像数据,以便能够实现更小的文件大小。JPEG 格式可以将影像压缩到或超过一个数量级(或者十倍,如果你想知道的话)。

需要注意的是,原始图像数据(称为“raw”或未压缩图像数据)在通过 JPEG 编解码器编码进行压缩后是不可恢复的。因此,在通过 JPEG 数字图像压缩算法运行图像之前,您应该确保保存您的原始(未压缩)图像。

如果你在压缩后放大 JPEG 图像,你会看到变色的区域,而这些区域在原始图像中是不存在的。JPEG 图像数据中的这些退化区域在数字图像行业中被称为压缩伪像。使用有损图像压缩时会出现压缩伪像。

这是 JPEG 文件格式不是 Android 中强烈推荐使用的数字图像格式的主要原因。Android 5 应用开发中最推荐使用的图像格式是 PNG 文件格式。PNG 在数字图像行业读作“ping”。PNG 既有索引颜色版本,称为 PNG8 (或 PNG5,如果你只需要使用 32 种颜色),你将在本章的后面发现,也有真彩色版本,称为 PNG24 (无 alpha 通道)或 PNG32 (有 alpha 通道)。我将在本章的后面讨论数字图像 alpha 通道的概念,因为它非常重要。

我使用的 PNG8 和 PNG24 编号扩展表示颜色支持的位深度,因此带有 alpha 通道的 truecolor PNG 在技术上可以称为 PNG32。同样,使用 16 色的 PNG 应该说是 PNG4,使用 64 色的 PNG 应该称为 PNG6,使用 128 色的 PNG 应该称为 PNG7 等等。推荐 Android 5 使用 PNG 格式的原因是它使用无损压缩。这将提供高图像质量以及良好的数字图像数据压缩效率。

最新的图像格式是在 Google 收购 ON2(WebP 图像格式)时添加到 Android 5 中的。Android 2.3.7 支持这种格式,支持图像读取或回放,Android 4.0 或更高版本支持图像写入或数字图像文件保存。

如果你想知道,Android 中的图像写入支持将与你的 Android 相机一起使用,因此你的用户可以通过远程网络服务器将图像保存或写入他们的 SD 卡或云。WebP 是 WebM 视频编码器文件格式的静态图像版本。WebM 在业内也被称为 ON2 VP8 视频编解码器,它被谷歌收购,然后宣布(也称为“发布”)开源可用性。

手表表面数字图像的基础:像素

数字影像由 2D 阵列或网格组成。这些包含通常称为像素?? 的数据元素。这个行业术语是单词图片(有人称这些为“pix”)和元素(如果你缩短单词元素,就得到时髦的单词“els”)。

您的数字图像素材中的像素数量用一个叫做分辨率 的术语来表示。这是图像的宽度(用 X 轴的 W 或 X 表示)和高度(用 Y 轴的 H 或 Y 表示)的像素数。图像素材的分辨率通常使用两个(X 和 Y)数字表示,中间有一个“X”,或者使用单词“by”,例如 800x480 或作为 800 乘 480 像素。

要找到 2D 图像中的像素总数,只需将宽度像素乘以高度像素。例如,HDTV 分辨率为 1920 x 1080 的图像包含 2,073,600 个像素,即超过 200 万个像素。这也被称为两百万像素。图像中的像素越多,可以说其分辨率越高,从而提供更高的视觉质量。

就像数码相机一样,从 300 万像素的智能手机相机到 7500 万像素的数码单反相机,数码图像网格或阵列中的像素越多,使用图像可以达到的质量水平就越高。这就是 4K UHDTV 屏幕越来越受欢迎的原因,这种屏幕的分辨率为 4096×2160。Android 通过 4K UHDTV 支持智能手表分辨率。

面向数字图像的手表形状:图像纵横比

更复杂的方面(无意双关!)的数字图像分辨率将是图像纵横比 ,这一概念也适用于 Android 5 设备硬件显示器。纵横比是宽度与高度的比率**,或者 W:H ,或者如果你喜欢用 X 轴和 Y 轴来思考,它将是 X:Y 。纵横比将定义图像或显示屏的形状,即图像或显示屏可能是方形或矩形(通常称为宽屏)。手表表面有一个正方形的长宽比。**

1:1 纵横比的显示器(或数字图像)是完美的正方形,2:2 或 3:3 纵横比的图像也是如此。重要的是要注意,是这两个数字之间的比率**定义了图像或屏幕的形状,而不是数字本身。这就是为什么这被称为纵横比,尽管它通常被简称为图像“纵横比”。

图像宽高比通常表示为宽高比冒号两边可以达到的最小的一组或一对数字。如果你在高中时注意过,当你学习最低(或最少)公分母时,长宽比数学将非常容易。

我通过继续将每边除以 2 来执行数学入学考试。以一个相当常见的 1280 乘 1024 SXGA 分辨率为例,1280:1024 的一半就是 640:512;一半的话,就是 320:256;一半是 160:128;其中一半也是 80:64。一半是 40:32;一半是 20:16;一半是 10:8,一半是 5:4,所以 SXGA 屏幕使用的是 5:4 的纵横比

有趣的是,所有上述纵横比都是相同的纵横比,因此都是有效的!因此,如果你想采取真正简单的方法,用冒号替换图像分辨率中的“x ”,你就有了图像的长宽比,尽管像我在这里所做的那样,将其提取到最低的格式,这要有用得多,并且是行业标准的做法。

最初的个人电脑屏幕使用更方形的 4:3 宽高比,早期的 2:3 宽高比 CRT 电视机也几乎是方形的。冒号两边的这些数字大小越接近,图像或屏幕纵横比就越方。永远记住,相同的数字代表一个正方形的长宽比,除非其中一个数字是 1。2:1 宽屏幕显示,3:1 宽屏幕显示将是彻头彻尾的全景,如果它真的出现的话!

当前的显示器市场趋势当然是向宽屏和超高分辨率显示器发展。Android 5 手表表面可能会改变这种趋势,回到方形长宽比。方形屏幕正被用于各种新的消费设备,其中之一是 Android 5 智能手表。

给你的数码图像上色:RGB 色彩理论

现在,您已经了解了数字图像像素,它们在 2D 矩形阵列中的排列方式,以及定义矩形形状的长宽比,下一个逻辑方面(同样没有双关语)是如何为每个像素分配颜色值。像素颜色由三种颜色定义:红色、绿色和蓝色(或 RGB)。这些在每个像素中以不同的量存在。Android 显示屏利用加色颜色,这是每个 RGB 颜色平面 的光波长可以加在一起的地方。加色用于创建数千万种不同的颜色值。这用于流行的 LED、LCD 和有机发光二极管显示器,后者用于智能手表、智能手机、iTV 电视机或平板电脑。加色与减色相反,减色用于打印机。

你可以用来混合的 RGB“色调”或光强的数量,决定了你能够复制的颜色总量。在今天的数字设备中,我们可以为每种 RGB 颜色产生 256 级光强度。为每个图像像素生成颜色,因此对于每个 RGB 数据值,图像中的每个像素将具有 256 级颜色强度。这些 RGB“板”或“平面”中的每一个对于每种 RGB 颜色将使用一个字节的数据。

手表表面数字图像的颜色量:颜色深度

用于表示数字图像素材中颜色数据的位数被称为该图像的色深 。重要的是要注意,在数字图像中,少于八位可以用来表示图像中的颜色量。这只适用于当你使用“索引”颜色模型时,我将在这一节讨论。

数字图像行业中有几种常见的颜色深度,我将在这里概述最常见的颜色深度,以及在 Android 操作系统中使用它们的数字图像文件格式。最低色深存在于八位索引彩色数字图像格式中。索引彩色图像将使用每像素 256 个总颜色值,并将使用 GIFPNG8 图像格式来包含这些索引彩色数字图像数据。

索引彩色图像没有(RGB)色彩平面,因此它通常比真彩色 RGB 图像小三倍。相反,它使用多达 256 个最大颜色值的“调色板”来表示数字图像中的所有颜色。这个调色板是使用压缩算法(编解码器)来“挑选”的,该算法会在数字图像中找到最常用的颜色。

一个 24 位颜色或真彩色深度图像具有每个 RGB 色板(也称为颜色平面)的完整八位颜色数据值。这些真彩色图像每像素能够显示 1600 万种潜在颜色。这被计算为 256 × 256 × 256,等于 16,777,216 种颜色。

使用 24 位色深将为您提供最高的数字图像质量水平,这就是为什么 Android 更喜欢使用 PNG24 或 JPEG 图像文件格式。因为 PNG24 是无损的,这意味着它在压缩过程中不会损失任何质量,所以它提供了最高质量的压缩和最低的原始数据损失,以及最高质量的颜色深度。

因此,就 Android 而言,PNG24 是首选的数字图像格式。这是因为使用 PNG 可以在所有 Android 5 应用中产生最高质量的视觉效果。

值得注意的是,目前存在更高色深(每个 RGB 通道 16 位数据)的图像,这是 i3D 游戏行业流行的。这种颜色深度被称为 HDRI(高动态范围图像)。

在表盘上显示颜色:使用十六进制记数法

现在你已经知道了什么是色深,颜色是由任何给定图像中三种不同颜色通道的组合来表示的,让我们看看,作为程序员,我们将如何在 Android 应用中表示这三种 RGB 颜色值,以便我们能够创建可见色谱中的任何颜色。

需要注意的是,在 Android 5 操作系统中,颜色不仅用于称为 BitmapDrawable 对象的数字图像素材,还用于可扩展的矢量图形,如颜色填充和渐变,这一点您在前面已经了解过。颜色数据值也用于设置 UI 颜色,例如用户界面屏幕中使用的背景颜色值,或者用于用颜色填充字体轮廓的 textColor 值。

在 Android 5 中,不同级别的 RGB 颜色强度使用十六进制符号表示为数据值。十六进制记数法是基于几十年前用来表示 16 位数据值的最初的 Base16 计算机记数法。十进制记数法将从 0 计数到 9,而十六进制记数法将从 0 计数到 F,其中 F 表示十进制值 15。从 0 到 15 的计数给出了 16 个数据值。

为了告诉 Android 你给它的是十六进制值,你可以在 Base16 值的前面加上磅符号 ,也称为散列标签,就像这样: #FFFFFF 。这个十六进制记数法数据值代表一种颜色白色,因为如果你将红色、绿色和蓝色光波长混合在一起,你得到的光颜色将是白色,有时被认为根本没有颜色!

因为这种 24 位十六进制表示中的每个槽代表一个 16 进制值,所以要获得每种 RGB 颜色所需的 256 个值将占用其中的两个槽,因为 16 × 16 等于 256。因此,对于一个 24 位的映像,在您的 hash 标签后需要 6 个槽,对于一个 32 位的映像,在您的 hash 标签后需要 8 个槽。在本章的下一节,我将介绍什么是 32 位图像,以及它们的用途。

十六进制数据槽以下列格式表示 RGB 值: #RRGGBB 。因此,对于白色,该十六进制颜色数据值表示中的所有红色、绿色和蓝色通道处于全开的最大亮度或 FF,这将是 16 × 16,并且对于每个 RGB 颜色通道是完整的 256 个数据值。正如你所看到的,我使用了不同的行业术语(颜色通道、颜色平面、颜色板),你会发现这些术语在图形设计行业中被广泛使用。如果你愿意,所有这些数字图像术语可以互换使用。

如果你把所有的颜色加起来,你会得到白光。以不同的量,它们会产生颜色!颜色黄色由打开的红色和绿色通道以及关闭的蓝色通道表示,因此黄色的十六进制符号表示将是 #ffFF00 ,其中红色和绿色通道槽将完全打开,使用 FF 表示颜色强度(级别)值 256,蓝色通道槽完全关闭,使用 00 表示零值。

正如我在本节前面提到的,还有一个 32 位图像色深,其数据值使用一个 ARGB 颜色通道模型来表示。在这个模型中,A 代表 alpha ,是 alpha 通道 的简称。在本章的下一节,我将更详细地介绍图像 alpha 和 alpha 通道的概念,我还将介绍更高级(相关)的概念像素混合

您的 ARGB 颜色通道模型数据值的十六进制符号数据槽将保存以下格式的数据:# aarggbb。因此,为了表示完全不透明的白色,十六进制颜色数据值表示中的所有 alpha、红色、绿色和蓝色通道都应该处于最大亮度(和最大不透明度)。使用 FF 值将 alpha 通道设置为完全不透明,因此完整的十六进制值将是 #FFFFFFFF

另一方面, 100%透明的阿尔法通道由设置为零的阿尔法槽表示。因此,一个完全透明的图像像素可以配置为#00FFFFFF,或#0000000,甚至#00F7D9C4,如果你喜欢。

这里需要注意的是,如果一个图像 alpha 通道被设置为透明的,那么由最后六个十六进制数据槽值表示的每个像素颜色数据值甚至都无关紧要!这是因为对于特定像素的 ARGB (alpha 通道加颜色)数据值,100%透明度数据值将覆盖使用“像素需要与完全透明的设置合成”的任何颜色值。

在表盘中表现透明度:使用 Alpha 通道

这一节将介绍数字图像如何在被称为图像合成 的过程中合成在一起。这是由被称为数字图像排版师的专业图形艺术家完成的。

数字图像合成是将不止一层数字图像(一张照片)混合在一起的过程。这样做是为了获得更复杂的图像。显示屏上的合成图像看起来就像是一个单独的图像。事实上,图像合成实际上是一个堆栈中的集合,由多个无缝合成的数字图像组成。为了能够实现无缝的图像合成,每个层中使用的图像需要使用与图像中的每个像素相关联的 alpha 通道(透明度级别)数据值。

您可以使用图像中每个像素的 alpha 值来精确控制该像素与具有相同图像坐标或位置的其他像素的混合,但是在其他层上,在该特定图像层的上方或下方。正是因为这种层堆叠范式,我将这种合成称为 3D,因为这些层沿着 z 轴堆叠,可以说具有特定的 Z 顺序。不要将此与 3D 建模软件(如 Blender3D)混淆,因为数字图像合成(层)堆叠的最终结果仍然是 2D 数字图像素材。

像所有通道一样,alpha 通道也支持 256 级透明度。它们使用 ARGB 数据值的十六进制表示形式中的前两个数据槽来表示,该十六进制表示形式有八个数据槽(32 位),而不是用于表示 24 位图像的六个数据槽。24 位图像可以视为 32 位图像,带有不透明的 alpha 通道数据。除非需要透明度值,否则不要使用 32 位图像格式!

为了与图像合成相关,24 位图像不使用 alpha 通道,也不会用于图像合成,除非它是合成层堆栈中的底板(或背板)。另一方面,32 位图像将被用作其他需要在某些像素位置显示(通过透明度值)的内容之上的合成层。这些位于 24 位背板顶部的 32 位合成图像层使用像素透明度来创建最终的合成数字图像。所以你可能想知道如何有一个阿尔法通道,并使用数字图像合成因素到手表表面图形设计。

一个主要的优势是能够将看起来像单个图像的东西分割成多个组件层。这样做的原因是为了能够将 Java 代码逻辑应用到各个层元素,以便控制您无法单独控制的表盘设计的组件部分

手表面孔中的算法图像合成:混合模式

图像合成还有一个更强大的方面,叫做混合模式 。如果您熟悉 Photoshop 或 GIMP,您会知道数字图像合成中的每个图层都将被设置为使用不同的混合模式。混合模式是算法,指定一个图层的像素如何与之前的图层(在该图层下面)混合(数学上)。

这些像素混合算法考虑了你的透明度,它们可以用来实现几乎任何你想要实现的合成效果。混合模式可以在 Android 5 中使用 PorterDuff 类实现。这个 PorterDuff 类为表盘设计者提供了与 Photoshop(或 GIMP)提供给数字图像工匠相同的混合模式。

与 Android 的主要区别在于混合模式可以使用定制的 Java 7 编程逻辑进行交互控制。对于我们这些 Watch Faces 开发者来说,这是令人兴奋的部分。一些强大的 Android PorterDuff 类混合模式包括异或屏幕叠加变暗变亮相乘,或者相加。Apress 的Pro Android Graphics(2013)标题涵盖了如何在一个完整的图像合成管道中实现 PorterDuff 混合模式,如果你有兴趣深入 Android 5 的这一领域的更多细节。

屏蔽手表表面数字图像:利用阿尔法通道

alpha 通道最常见的用途之一是“遮盖”数字图像的某个区域。这创建了可以在图像合成层堆栈中使用的层。这对于观察表面设计显然很重要,因为指针、数字或装饰性组件等组件会用到它。

蒙版 是一个提取主题的过程,本质上是从你的源图像中剪切出主题,这样就可以把它放置(粘贴)到自己的透明层上。在本书的过程中,我将解释使用 GIMP 执行这个屏蔽过程的工作过程。

遮罩过程会在自己的图层上生成图像的一部分。被遮罩的主体将与源图像的其余部分隔离,但由于图层的透明度,它看起来好像仍在最终的图像合成中。一旦被遮罩的图像元素有了自己的 alpha 通道,您就可以对该元素进行旋转、着色、缩放或移动等操作,而不会影响图像合成的其余部分。

表盘设计的含义是显而易见的,这就是为什么我在这里介绍这一基础材质,也是为什么你将在本书后面使用蒙版工作流程,这样你就可以获得一些蒙版经验。

遮罩工作流程允许您将图像元素(主题材质)放入其他图像中使用,如手表脸或用于特殊效果应用。数字图像软件(Photoshop 和 GIMP)有许多工具和功能,专门用于蒙版,以及后来的图像合成。如果不创建蒙版,你就无法真正有效地进行图像合成,因此这是图形设计师和 Pro Android 可穿戴设备(和 Watch Faces)开发人员需要掌握的重要领域。

蒙版过程中最重要的考虑是在蒙版对象周围获得一个平滑但清晰的边缘,这样当你将它“放入”一个新的背景图像时,它看起来好像一开始就属于那里。

屏蔽的关键是正确的选择工作流程。使用数字图像软件选择工具(GIMP 2.8 中有半打这样的工具)以适当的方式和最佳的工作流程是“拉”出“干净”图像蒙版的关键(这是一个额外的很酷的行业术语,让你四处折腾,让你看起来既艺术又懂技术)。

如果要遮罩的对象周围有颜色一致的区域,这会使遮罩过程更容易。您可以在蓝屏或绿屏上拍摄对象,然后您可以使用“魔棒工具”和阈值设置来选择除对象之外的所有内容,然后反转选择集,以获得包含对象的选择集。

其他 GIMP 选择工具包含复杂的算法,可以查看图像中像素之间的颜色变化。这些在边缘检测中非常有用,可以用于其他类型的选择工作过程。

GIMP 剪刀边缘检测选择工具将允许您沿着您想要遮罩的对象的边缘拖动光标,而边缘检测选择工具的算法自动(基于其算法)放置选择边缘的精确、像素完美的位置。

平滑手表表面边缘:反走样的概念

抗锯齿是一种通常使用算法实现的成像技术。它的作用是找到图像中两种相邻颜色相遇的地方,并混合锯齿状边缘周围的像素。消除锯齿将沿着两个彩色区域之间的边缘添加混合颜色,以在视觉上平滑沿着(以前的)锯齿状边缘的混合颜色。这使得当图像缩小时,当像素不是单独可见时,锯齿边缘看起来更平滑。抗锯齿的作用是让你的眼睛看到更平滑的边缘,以消除通常所说的“锯齿”消除锯齿提供了令人印象深刻的效果,对需要看起来更平滑的边缘上的像素使用非常少(七或八个)的中间平均颜色值。

我说的中间色或平均色指的是一些颜色或色谱,它介于沿一条边相交的两种颜色之间。我创建了一个抗锯齿的可视化示例来展示效果。正如你在图 6-1 中看到的,我在黄色背景上创建了一个看似平滑的红色圆圈。我放大到那个圈的边缘,抓拍了一张截图。我把它放在缩小的圆圈旁边,以显示在圆圈边缘相互邻接的红色和黄色值之间的抗锯齿(橙色)值。请注意,有七个或八个平均颜色值。

9781430265504_Fig06-01.jpg

图 6-1 。黄色背景上的红色圆圈(左)和显示抗锯齿的放大视图(右)

获得很好的抗锯齿效果的最好方法是使用正确的图像遮罩工作过程,对您可能使用的任何给定的选择工具使用正确的设置。实现你自己的抗锯齿效果的另一个技巧是在包含有锯齿状边缘的对象的透明层上使用具有非常低的模糊值(0.15 到 0.35)的高斯模糊工具。这将提供你在图 6-1 中看到的同样的反走样,不仅如此,它也将“模糊”你的阿尔法通道(蒙版)本身的透明度值。这将允许您使用任何背景图像消除 32 位图像对象的锯齿,您可能会尝试对其进行无缝合成。我将在本书的第十二章的中向你展示这些使用 GIMP 2.8 的很酷的数字图像合成技术,所以准备好学习如何成为一名数字图像合成 Android 可穿戴表盘设计师和开发者吧!接下来我们来看图像优化!

优化你的手表表面:数字图像压缩因子

有几个技术因素影响数字图像压缩,这是一个使用编解码器的过程,算法会查看您的图像数据,并找到一种方法将其保存为使用较少数据的文件。编解码器的编码器本质上是在图像中找到“数据模式”,并将它们转换成编解码器的解码器部分可以用来重建原始图像的数据形式。

有一些方法可以用来获得更高质量的图像压缩结果,这应该导致更小的文件大小以及更高的图像质量。具有小文件大小和高质量水平的图像可以说实现了高度优化的数据足迹

这是优化数字影像的一个主要目标,即尽可能减少数据占用空间,同时获得高质量的视觉最终效果。让我们首先讨论对数据占用空间影响最大的图像属性,并研究这些方面如何对给定数字图像的数据占用空间优化有所贡献。有趣的是,这与我在本章后半部分所讲述的数字图像概念的顺序相似!

对您生成的图像文件大小(您的数据足迹)最重要的影响因素是像素数或数字图像的分辨率。这是合乎逻辑的,因为需要存储每个像素,以及这些像素的 RGB 颜色通道的颜色值。因此,在保持图像细节的同时,图像分辨率越小,图像文件就越小,因为数据越少。

原始(未压缩)图像大小可以使用以下公式计算:宽度×高度×颜色通道。因此,对于 24 位 RBG 图像,有三个(RGB)颜色通道,对于 32 位图像,有四个(ARGB)颜色通道。因此,一个未压缩的真彩色(24 位)VGA 图像将有 640 × 480 × 3,等于 921,600 字节的原始未压缩数据。如果将 921,600 除以 1,024(千字节中的字节数),将得到原始 VGA 图像中的千字节数(900 KB 的偶数)。

正如您所看到的,颜色深度因此是图像中数据足迹的第二个最重要的因素,因为图像中的像素数乘以一个(8 位)或两个(16 位)或三个(24 位)或四个(32 位)颜色数据通道。这可能是索引彩色影像仍然被广泛使用的主要原因,通常是通过 PNG8 图像格式。像 PNG8 这样的无损压缩算法不会丢失任何图像数据(质量),并且 PNG8 通常比 PNG32 少使用四分之一的数据,比 PNG24 少使用三分之一的数据,因此仅使用 PNG8 就可以减少 200%到 300%的数据占用空间。

可以增加图像数据占用空间的最后一个概念是 alpha 通道,因为添加 alpha 会为正在压缩的图像添加另一个八位颜色通道(透明度)。如果您需要 alpha 通道来定义透明度,为了支持您的图像的未来合成需求,没有其他选择,只能包括这些 alpha 通道数据。只要确保您没有使用 32 位图像格式来包含具有空(未使用的)alpha 通道的 24 位图像。

有趣的是,大多数用于遮罩图像中对象的 alpha 通道都可以很好地压缩。这是因为 alpha 通道包含具有很少灰度值的白色(不透明)或黑色(透明)填充区域。仅有的灰度值存在于黑色和白色之间边缘的像素中。这些反走样的面具。alpha 通道中的这些灰度值是抗锯齿值,如您所知,它们用于为图像合成提供视觉上平滑的边缘过渡。

这是因为在 alpha 通道图像遮罩中,八位透明度渐变是用定义 alpha 通道透明度级别的白色到黑色光谱(渐变)定义的。蒙版中每个对象边缘的灰度值实际上是将对象的颜色与目标背景图像中的颜色进行平均(混合)。这实质上提供了使用任何背景图像的实时反走样。

在表盘中使用索引彩色图像:抖动像素

如果用于创建图像的颜色变化不大,索引色图像可以模拟真彩色图像。索引彩色图像使用八位数据来定义图像颜色,使用 256 种最佳选择的颜色调色板,而不是三个 RGB 颜色通道。根据图像中使用的颜色数量,仅使用 256 种颜色来表示图像可能会导致一种称为条带的效果,即相邻颜色之间的转换不平滑。索引彩色图像编解码器有一个选项来纠正这种情况,称为“抖动”抖动 是沿着图像中两个相邻颜色区域之间的边缘创建点图案的过程。这会让你的眼睛误以为使用了第三种颜色。只有当 256 种颜色中的每一种都与其他 256 种颜色中的每一种相邻时(否则会更少),抖动才会为我们提供 65,536 种颜色(256 乘以 256)的感知数量。

您可以看到创建额外颜色的潜力,并且您会惊讶于索引颜色图像在某些情况下可以实现的结果,即对于某些图像。我拍摄了一张真彩色图像,比如图 6-2 中的所示,并保存为索引色图像,向你展示抖动效果。看看这张奥迪 3D 图像中驾驶员侧后挡泥板上的抖动效果,因为它包含一个颜色渐变,当我将其保存为索引颜色时,它将显示抖动效果。

9781430265504_Fig06-02.jpg

图 6-2 。真彩色源图像使用 16,777,216 种针对 8 位 PNG8 优化的颜色

我设置编解码器对 PNG8 图像进行编码,如图图 6-3 ,使用五位颜色(32 色),这样你就可以清晰的可视化抖动效果。正如您所看到的,通过抖动算法,许多点图案被添加到相邻的颜色之间,从而产生了附加颜色的感觉。

9781430265504_Fig06-03.jpg

图 6-3 。显示压缩设置为 32 色(五位色)的索引彩色图像中的抖动效果

有趣的是,在压缩 8 位索引彩色图像时,您可以选择使用少于 256 种颜色。这样做通常是为了减少数据占用空间。例如,使用 32 色可以获得良好效果的图像实际上是五位图像(PNG5),尽管该格式通常称为 PNG8。请注意,您还将设置使用抖动的百分比。我通常选择 0%或 100%设置,但是你可以在这两个极端值之间的任何地方微调抖动效果。你也可以选择你的抖动算法类型。我使用扩散抖动,因为它可能会沿着不规则形状的渐变产生更平滑的渐变效果,就像你在奥迪挡泥板上的图 6-3 中看到的那样。

正如您可能想象的那样,抖动会将数据模式添加到图像中,这对编解码器的算法压缩来说更具挑战性。因此,抖动会增加几个百分点的数据占用空间。请务必比较应用和不应用抖动的文件大小,以确保抖动提供改进的视觉效果。

现在我已经介绍了静态数字图像的概念和技术,在我完成本章之前,a 将提供一些关于 Android 5 如何使用 Animation 和 AnimationDrawable 类(对象)的信息,以允许您使用动画将您学习的数字图像带到下一个级别。

动画手表表面:动画和动画可绘制

Android 5 OS 既有位图动画,又称帧动画,又有矢量动画,俗称程序动画。矢量动画在 Android 行话中被称为补间动画。Android 中的动画由两组不同的类处理。 AnimationDrawable 类处理帧动画,使用 /res/drawable 项目文件夹保存动画资源,而 Animation 类处理矢量动画,使用 /res/anim 项目文件夹保存程序动画定义。

手表表面的帧动画:AnimationDrawable 类

Android AnimationDrawable 类是实现通常称为“动画书”动画的方法,允许您快速连续播放一系列位图帧,以创建运动的幻觉。AnimationDrawable 类使开发人员能够使用强大的第三方工具,如 Blender 或 Lightworks,在 Android 5 的之外创建动画素材**。如果您想在 Android 中创建动画,您应该使用仅使用 Java 代码和 XML 的过程动画类。Apress 的 Pro Android UI (2014)标题非常详细地涵盖了这两个动画主题。**

使用 AnimationDrawable 类相当简单,因为您所要做的就是使用 GIF、JPEG、WebP 或 PNG 图像资源定义您的位图帧。这是通过使用 XML 格式来定义每个帧资源的文件名以及它在屏幕上显示的持续时间来完成的。然后,使用 Java 7 代码将这个 XML 文件“膨胀”成一个 AnimationDrawable 对象,然后可以使用 Java 7 方法来控制帧动画的新媒体资源的播放,并从这一点开始在 Watch Faces 应用中播放。与向量动画相比,帧动画资源使用更多的内存和更少的处理器,因为它很容易“翻转”帧,但这些帧必须保存在内存中才能做到这一点。向量动画使用非常少的内存来保存动画“移动”的代码,但处理器需要计算和“渲染”移动,从而创建动画数字插图。

表盘的补间动画:动画类

Android Animation 类是用代码(过程)而不是像素实现动画的一种不同方式。这种类型的动画允许定义什么叫做变换 。其中包括平移(移动)旋转(方向变化)缩放(大小变化)。

矢量动画允许开发者使用 AnimationSet 类定义复杂的变换“集合”。这些包括逻辑分组变换,包括移动(平移)、定向(旋转)和调整大小(缩放)。

Android 操作系统使用用户设备上的处理器将这些渲染到屏幕上,创造出一种运动的错觉。一个动画类让开发者能够在 Android 5 内部创建动画素材,只使用 XML 和 Java 代码,不需要外部的新媒体素材。有趣的是,程序动画不仅可以用于制作矢量形状、渐变和文本的动画,还可以变换位图资源,包括在帧制作动画时。我将在这一章的下一节,也就是我喜欢称之为“混合动画”的那一节讲述这个问题

使用 Animation 类不像使用 AnimationDrawable 那么容易,因为您必须使用 XML 标记或 Java 代码定义相当复杂的转换结构。这通常通过 XML 定义文件来完成,该文件用于定义分组旋转、缩放和移动变换的层次结构。这些利用了 Android 动画类的力量,其中包括一个动画动画集(用于分组),当然还有旋转动画缩放动画平移动画alpha 动画类。正如你可能已经从本章的前一节中猜到的,这个 AlphaAnimation 类也允许你程序化地制作不透明度的动画,这将允许你淡入和淡出你的动画组件!这五个专门的转换类都是一个动画超类的直接子类**,所以这六个类将无缝地协同工作。**

使用 Java 7 代码,您的过程动画 XML 定义文件也将被“膨胀”到一个动画对象中,之后,Java 7 方法可用于从您的 Watch Faces 应用中的该点开始控制矢量动画的新媒体资源的回放。记住,Android 5 用的是 Java 7。

与帧动画相比,矢量动画资源使用更多的 CPU 处理资源和更少的系统内存资源。这是因为用户的硬件设备处理器正在使用数学、数据和代码实时渲染矢量动画。这需要进行大量的处理,但是只有很少的内存来保存随时间推移而被处理的向量和代码变量。

手表表面的混合动画:组合的力量

有趣的是,在我结束这一章之前,可以把你的 AnimationDrawable frame 动画 XML 定义和你的基于动画类的矢量动画 XML 定义结合起来。

这是通过将矢量动画应用于包含运行帧动画的 UI 元素来实现的。如果你设置正确,你将能够使用所有的 Android 动画类来实现更加复杂和奇妙的动画效果。

摘要

在这一章中,你学习了手表表面设计的注意事项和指导方针,你将需要在本书的其余部分创建手表表面。

您了解了手表面临的重要节能考虑,以及一些智能手表制造商(如华硕)使用的互动模式和环境模式,以及低位模式。你看了手表外观设计形状,以及如何在手表外观设计中吸收 Android 5 操作系统的功能,如硬件状态图标、热门词汇和通知卡消息。您还了解了创建专业应用所需的高级手表面孔设置对话框和数据集成注意事项。

接下来,我花了一些时间来确保您了解多媒体概念,我将使用这些概念来帮助您创建 Pro Android 可穿戴设备应用。这样,我在这一章中介绍了你需要的所有基础知识。

在下一章中,您将开始学习如何将 Java 代码和 XML 标记放在适当的位置,以形成 Watch Faces 设计和 Watch Faces 应用的基础。**

七、为穿戴设计表盘:创建表盘代码基础

现在,您已经掌握了有关 Android 表盘设计、数字图像和 Android 动画概念的基础知识,您可以开始编写表盘应用了,使用的是您在第章第 4 中开始的引导 Java 代码和 XML 标记。

因为在早期的代码中没有新的 Android 项目工作流程来创建 Watch Face Bootstrap 项目基础架构,所以本章将向您展示如何变形一个标准的 Wear 项目 Bootstrap 基础架构,将其转换为 Watch Faces 项目。在这个过程中,您将了解如何在 Android 中设置 Watch Face,它需要什么权限,Watch Faces 和 AndroidManifest XML 文件与标准应用有何不同,等等。

让我们首先看看 Gradle Build 配置文件及其引用的存储库和依赖项,然后向您的 Wear 和移动应用 AndroidManifest.xml 文件添加权限条目。

一旦这些就绪,您将学习如何创建一个新的 Java 类。这将创建表盘服务引擎,它们将成为你的表盘设计和表盘处理代码的基础。之后,您将创建一个新的 /res/xml 资源目录,并创建一个 watch_face.xml 文件。创建动态壁纸范例需要这个文件,动态壁纸范例用于使 Watch Faces API 可操作。接下来,您将修改一些 Watch Face 预览可绘制素材,并将您的服务类和十几个相关参数添加到您的 AndroidManifest XML 文件中。我们开始吧!

分级脚本:设置分级构建依赖关系

如果您还没有这样做,请使用您在第二章中创建的快速启动图标启动 Android Studio 开发环境。这将启动 IntelliJ IDEA 并且显示你在第四章中创建的 ProAndroidWearable 项目。点击 IDE 左侧面板中 Gradle Scripts 文件夹旁边的箭头将其打开,如图图 7-1 所示。

9781430265504_Fig07-01.jpg

图 7-1 。打开你的 ProAndroidWearable 项目中的 Gradle Scripts 文件夹,打开项目 build.gradle

右键单击主build . gradle(Project:ProAndroidWearable)文件,如图 7-1 左侧用蓝色突出显示的,选择跳转到源代码菜单选项,或者如果你愿意,简单地使用 F4 功能键快捷键。这将在 IDEA 的编辑区打开顶层(主)项目 Gradle 配置文件,如图 7-1 右三分之二所示。

这个顶级 build.gradle 文件将在 dependencies 部分使用一个(绿色)类路径引用,通过使用com . Android . tools . Build:Gradle:1 . 0 . 0,引用 Android 存储库服务器上的 Android Gradle 构建工具

如果 Android Gradle 构建工具已经更新,那么版本编号可能会有所不同。这都是由 Android 新项目系列对话框正确设置的;在图 7-1 中要注意的重要事情是一个重要的信息:*注意:不要把你的应用依赖放在这里;它们属于单独的模块 build.gradle 文件。*因为一个 Android Wear 项目有一个 Wear 和一个 Mobile 组件,正如你在左边的 IntelliJ 项目导航器窗格中看到的,每个应用组件都有自己独特的 Gradle 构建脚本文件,我将在下面讨论。

将 Gradle build 依赖项放在与每个 Wear 和移动应用组件匹配的文件中很重要,这样 Gradle Build 才能正常工作。

接下来,右键单击移动build . gradle(Module:mobile)文件,图 7-2 左侧以蓝色突出显示的,选择跳转到源代码菜单选项,或者如果您愿意,也可以直接使用 F4 功能键快捷键。

9781430265504_Fig07-02.jpg

图 7-2 。打开第二个 build.gradle 模块:ProAndroidWearable 项目中的移动 gradle 构建脚本

这是您的移动应用组件的 build.gradle 文件,它将包含您的应用 ID ,您的包名和类名的串联,即com . pro . Android . wearables . proandroidwearable,以及在 API 级别 18 (Android 4.3)和 API 级别 21 (Android 5)的目标 SDK 版本设置的最低 SDK 版本规范,如

在图 7-2 的底部,您将看到 Gradle 依赖关系部分,引用 wearApp 项目(':wear') 项目类型和 Android 支持库的编译依赖关系,您在第四章中安装了该库,使用com . Android . Support:app compat-V7:21 . 0 . 3表示,以及 Google Play 服务支持库,使用

需要注意的是,您将会看到一些项目,比如那些仍在使用 Eclipse ADT IDE 的项目,它们在 Android 清单文件中设置了 Google Play 服务。这可以使用下面的元数据标签 : 来完成

<application>
    <meta-data android:name="com.google.android.gms.version"
               android:value="@integer/google_play_services_version" />
</application>

因为您在 build.gradle 文件中设置了这个编译依赖项,所以您不需要在 Android Manifest XML 文件中包含这个标记,您将在本章中转换这个文件以便与 WatchFaces API 一起使用。稍后我会解释 <元数据> 标签是做什么的!

最后,右键单击 wearbuild . gradle(Module:wear)文件,如在图 7-3 左侧以蓝色突出显示的,并选择一个跳转到源菜单选项,或者如果你喜欢,简单地使用 F4 功能键快捷键。

9781430265504_Fig07-03.jpg

图 7-3 。打开第三个 build.gradle 模块:在您的 ProAndroidWearable 项目中使用 gradle 构建脚本

这是智能手表(Wear SDK)应用组件的 build.gradle 文件,正如您所见,它还将包含您的应用 ID(??)以及 SDK 最低版本(??)规范。注意在这个 Gradle Build 规范文件中,这是为 Wear 设置的,在 API 级别 21 (Android 5),与 API 级别 21 (Android 5)的目标 SDK 版本相同,如图 7-3 上半部分的 build.gradle 文件的 android 部分所示。

在图 7-3 的底部,你会看到梯度依赖部分。这个 Wear Gradle 构建规范将引用 Android Wear 支持库,而不是引用 Android 支持库、,而是引用 com . Google . Android . Support:wearable:1 . 1 . 0 存储库路径以及文件和版本字符串串联。有趣的是,Android 支持库位于 com . Android . Support;Wear 也在 com.google.android.support!

请注意,您将需要再次引用 Google Play 服务支持库,使用您在移动 Gradle 构建中使用的相同的编译语句。

Android 权限:Watch Face 使用-权限

单击左侧面板中 Gradle Scripts 文件夹旁边的向下箭头,关闭该文件夹,然后单击 wear 旁边的箭头打开该文件夹。接下来点击manifest文件夹旁边的箭头,同样打开该文件夹,显示 AndroidManifest.xml 文件,如图图 7-4 所示。右键单击**/wear/manifest/androidmanifest . XML文件,如图 7-4 左侧蓝色高亮所示,选择跳转到源**菜单选项,或者如果您愿意,可以简单地使用 F4 功能键快捷键。如你所见,这将打开 AndroidManifest.xml 文件,该文件是由你在第四章中使用的一系列新的 Android Studio 项目对话框创建的。

9781430265504_Fig07-04.jpg

图 7-4 。添加两个< uses-permission >标签,用于提供 _ 背景和唤醒 _ 锁定穿戴清单

在指定智能手表硬件的< uses-feature >标签之后(或者之前,如果你愿意的话)添加两个 < uses-permission > 标签。uses-permission 属性是使用这个 XML 标记设置的,它定义了您的应用将从 Android 操作系统请求使用哪些权限。

注意,这些子标签需要“嵌套”在您的 <清单> 父标签中。这是因为< uses-permission >标签将访问 Androidmanifest . permission类中包含的常量值。如果你是好奇型的,想在一个地方看到 Android 操作系统允许的所有权限,请访问以下 URL:

[`developer.android.com/reference/android/Manifest.permission.html`](http://developer.android.com/reference/android/Manifest.permission.html)

您将使用的权限之一没有列出,我将在下面解释原因!

uses-permission 属性使用 android:name 变量来设置预定义的操作系统常量,这些常量用于指定在 Android OS 中使用某些硬件(或软件)功能所需的权限。

如果您使用的常量不是 Manifest.permission 类的一部分,那么该常量需要以存储库路径开头,因此您会注意到在图 7-4 顶部突出显示的 PROVIDE_BACKGROUND 常量使用了一个com . Google . Android . permission . PROVIDE _ BACKGROUND常量引用,而 WAKE_LOCK 权限常量引用使用了一个 android.permission.WAKE_LOCK,这是一个较短的引用路径,直接访问现在可以在 Manifest.permission 类中看到的常量

如果您访问我之前包含的 Manifest.permissions URL,您会看到 WAKE_LOCK 常量用于启用允许使用 Android PowerManager 唤醒锁功能的权限,该功能可以防止硬件设备处理器休眠,并防止您的(智能手表)屏幕变暗。您需要添加到清单中的 XML 标记应该如下所示:

<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

接下来,点击移动旁边的箭头打开该文件夹。接下来点击你的Manifest文件夹旁边的箭头打开那个文件夹,显示另一个 Android Manifest 文件,如图图 7-5 所示。右键单击这个**/mobile/manifest/androidmanifest . XML文件,如在图 7-5 左侧以蓝色突出显示的,并且选择一个跳转到源**菜单选项,或者如果你愿意,简单地使用你的 F4 功能键快捷键。将这两个相同的标记也添加到这个清单文件中,因为您的可穿戴权限需要是移动(手机或平板电脑)权限集的子集(或与之相等)。

9781430265504_Fig07-05.jpg

图 7-5 。添加< uses-permission >标签用于移动清单的提供背景和唤醒锁

现在您已经准备好为您的 Watch Faces 应用创建核心类,您将把它命名为 ProWatchFaceService.java,并将使用 Android CanvasWatchFaceService 类作为它的超类。越来越令人兴奋了!

画布表面服务:一个表面引擎

本节将介绍驱动手表脸服务基础的类, CanvasWatchFaceServiceCanvasWatchFaceService。引擎,以及它们在 Java 和 Android 类层次结构中的位置。您还将了解它们是如何在您当前的 ProAndroidWearable 项目代码库中实现的,您将把它转变为一个专业的 Watch Face 项目,这样,如果您想使用 Android 5 WatchFaces API 进行开发,您就知道如何做到这一点。

CanvasWatchFaceService 类:概述

AndroidCanvasWatchFaceService类是一个公共抽象类 ,这意味着你必须对它进行子类化才能使用它。你将在本章的下一节中做这件事,在我提供了这个类本身及其来源的鸟瞰图之后,这将告诉你关于它正在做什么以及它从哪里获得这样做的能力的相当多的信息。

Java 类层次结构相当复杂,如下所示:

java.lang.Object
  > android.content.Context
    > android.content.ContextWrapper
      > android.app.Service
        > android.service.wallpaper.WallpaperService
          > android.support.wearable.watchface.WatchFaceService
            > android.support.wearable.watchface.CanvasWatchFaceService

当然,Java 中的每个类和对象都是基于 java.lang.Object 的,而在 Android 中,服务类是基于上下文类的,因为服务将需要关于您的服务(在本例中是 WatchFaceService )试图完成什么的上下文信息。注意 WatchFaceService 是从 WallpaperService 继承而来的子类,它告诉你 Watch Face API 是基于 Android Wallpaper API 的,这就是为什么在本章中你必须实现 Wallpaper 对象和 BIND_WALLPAPER 功能。

CanvasWatchFaceService 类是一个基类,用于创建使用画布对象在显示屏上绘制手表表面的手表表面应用。这个类提供了一个无效屏幕刷新机制,类似于 invalidate( ) 方法调用。方法调用是 Android 的基础,可以在视图类中找到,它允许视图对象被刷新。

创建 ProWatchFaceService 子类 : extends 关键字

现在让我们使用这个抽象的 CanvasWatchFaceService 类,并在这个 ProAndroidWearable 项目中创建一个全新的 Java 类。点击向右箭头图标打开你的 /wear/java 文件夹。接下来右键点击com . pro . Android . wearables . proandroidwearable(包)文件夹,然后选择 New image Java Class 菜单序列,如图 7-6 中蓝色部分所示。为了节省空间,我还将创建新类对话框放在了右边的截图中。从种类下拉菜单选择器中选择选项,输入 ProWatchFaceService名称,最后点击确定按钮,创建新的 ProWatchFaceService WatchFace 服务和引擎子类。

9781430265504_Fig07-06.jpg

图 7-6 。右键单击您的包文件夹并使用新的image Java 类来创建 ProWatchFaceService 类

一旦你创建了新的 Java 类,它将在一个编辑标签中为你打开,如图 7-7 的右三分之二所示。bootstrap 类为您提供了以下“空”公共类 Java 代码:

package com.pro.android.wearables.proandroidwearable;
public class ProWatchFaceService {...}

您需要做的第一件事是添加 Java extends 关键字,您将在 ProWatchFaceService 类名之后插入该关键字,引用 CanvasWatchFaceService 超类,并为您的 ProWatchFaceService 类提供 Android CanvasWatchFaceService 超类的所有功能、算法、方法、变量和特性。

如果你输入 Java extends 关键字和这个超类名的前几个字母,IntelliJ IDEA 会为你下拉一个助手选择器对话框,如图图 7-7 所示,你可以找到并双击你的 CanvasWatchFaceService(Android . support . wearable . watch face package)选项,让 IntelliJ 为你完成代码的编写。现在您有了空的 ProWatchFaceService 子类,并准备在其中编写引擎。

9781430265504_Fig07-07.jpg

图 7-7 。使用 extends 关键字从 CanvasWatchFaceService 创建 ProWatchFaceService 类的子类

在编写 WatchFaceService 引擎类之前,让我们快速看一下 CanvasWatchFaceService。Engine 了解它能做什么。

CanvasWatchFaceService。发动机类别:发动机

CanvasWatchFaceService。引擎是一个公共类,它提供了调用 onDraw( ) 方法的绘制引擎,以实际完成在智能手表屏幕上绘制(或在某些情况下制作动画)手表表面的繁重工作。Java 类层次仍然来自 WallpaperService。然而,Engine 远没有 CanvasWatchFaceService 复杂,看起来如下:

java.lang.Object
  > android.service.wallpaper.WallpaperService.Engine
    > android.support.wearable.watchface.WatchFaceService.Engine
      > android.support.wearable.watchface.CanvasWatchFaceService.Engine

这个类提供了一个使用 onDraw()在画布上绘制的手表表面的实际实现。您将需要实现**。onCreateEngine( )** 方法,以便让您的类返回可用的引擎实现。这个类的构造函数方法将与 onCreateEngine()方法 结合,让 Android OS 返回新的引擎对象。

这是使用下面的 Java 构造函数方法结构来完成的,您将在本章的下一节中实现它:

public CanvasWatchFaceService.Engine onCreateEngine() { return new Engine(); }

该类有许多强大的方法,您将在高级 Watch Face Java 代码开发中使用这些方法,一旦您在本章的后面建立了 Watch Face 代码基础,您将在本书的其余部分中添加这些方法。

最重要的方法之一是 invalidate( ) 方法,,它使引擎重新绘制表盘屏幕。该方法将调度对 onDraw(Canvas,Rect) 方法的调用,请求引擎绘制动画或时间更新的下一帧。

有几个处理画布表面的 onSurface( ) 方法调用,包括您的**。onSurfaceChanged(Surface holder holder,int format,int width,int height)** 方法,在这里你可以定义当一个表面表面发生变化时会发生什么,以及 **。onSurfaceCreated(Surface holder holder)方法,在该方法中,您可以定义每当创建手表表面时发生什么。还有就是。onsurfacererawedned(Surface holder holder)**方法,在这里您可以定义每当需要重画手表表面时发生什么。

还有就是**。** postInvalidate( ) 方法,该方法请求 Android 操作系统发布一条消息,安排调用。onDraw(Canvas,Rect)方法,请求它绘制下一帧。现在让我们创建引擎类!

创建私有引擎类:使用 onCreateEngine()

在 ProWatchFaceService 类(在花括号内)中,您需要使用 Java @Override 关键字实现一个 CanvasWatchFaceService()构造函数方法。键入@Override 然后按回车键输入新的一行代码,然后键入 Java public 关键字并开始键入构造函数方法名 CanvasWatchFaceService()。

当 IntelliJ 弹出方法插入帮助器对话框时,如图图 7-8 所示,选择**CanvasWatchFaceService(Android . support . wearable . watch face)**选项,让 IntelliJ IDEA 为你编写 Java 代码语句。键入句点和单词 Engine,以访问完整的 CanvasWatchFaceService.engine 类路径。

此时,您所要做的就是在 public 关键字和类名之后添加上一节中讨论的 onCreateEngine()方法调用。在花括号内,添加**return new Engine();**语句,创建您的表盘引擎的构造就创建好了。这将使用以下 Java 代码结构来完成:

public CanvasWatchFaceService.Engine onCreateEngine( ) {
   return new Engine( );
}

9781430265504_Fig07-08.jpg

图 7-8 。Add @Override,键入 Java 关键字 public 和字母 C,然后选择 CanvasWatchFaceService

一旦你把这个公共的 CanvasWatchFaceService。Engine onCreateEngine()就位,如图图 7-9 所示无误,你可以为你的引擎私有内部类编写一个结构,它将保存 onDraw()方法结构。

9781430265504_Fig07-09.jpg

图 7-9 。添加 onCreateEngine()方法调用并返回 new Engine();结构内部的语句

使用 Java private 关键字在名为 Engine 的 ProWatchFaceService 类中创建一个私有内部类,并使用 Java extends 关键字对 CanvasWatchFaceService 进行子类化。引擎类。你得到的结构应该看起来像下面的 Java 代码:

private class Engine extends CanvasWatchFaceService.Engine {  // An Empty Class Structure  }

正如你在图 7-10 中看到的,代码是没有错误的,你已经准备好在这个私有引擎类中编写你的公共 void onDraw( ) 方法了。

9781430265504_Fig07-10.jpg

图 7-10 。编写一个名为 Engine 的空私有类来扩展 CanvasWatchFaceService。引擎等级

在 Engine 类中(在花括号中),您需要实现一个公共的 void onDraw()方法,再次使用 Java Override 关键字。键入@Override,然后按 Return 键输入新的一行代码。然后键入 Java public 关键字和一个 void Java 返回类型,带有名为 watchface 的 Canvas 对象参数的 onDraw( ) 方法名,名为 rect 的 Rect 对象参数,以及你的两个花括号,如图 7-11 所示。

9781430265504_Fig07-11.jpg

图 7-11 。用 Canvas watchface 和 rect 对象参数编写空的公共 void onDraw 方法

在图 7-11 的底部可以看到,在表盘下面有一个绿色波浪下划线高亮。如果你把鼠标放在它上面,你会看到 IntelliJ 所关心的“参数‘watch face’从未被使用”。您现在可以忽略这个绿色(轻度警告级别)代码高亮显示,因为稍后您将调用这个 watchface Canvas 对象的. drawColor()方法。

还要注意 IntelliJ 添加到 Canvas 和 Rect 对象(类)中的红色。这意味着在代码中使用这些语句之前,您需要编写一个 import 语句。将 Canvas 和 Rect 对象传递到。CanvasWatchFaceService 中的 onDraw()方法。引擎超类使用 Java super 关键字,你将看到如何让 IntelliJ 为你编码这些导入语句。耐心是一种美德!

在 onDraw()方法的主体中,也就是在左花括号和右花括号中,键入 Java super 关键字和一个句点字符以打开 IntelliJ helper 对话框,显示您的超类中可以使用的方法。图 7-12 显示了 onDraw(Canvas canvas,Rect bounds)选择;一旦你双击这个,IntelliJ 会为你写一个 Java 代码语句,生成一个红色波浪线(严重错误级别),我接下来会讨论这个(这也是我为什么拿这个具体的工作过程)。

9781430265504_Fig07-12.jpg

图 7-12 。在 onDraw 方法内部,使用 Java super 关键字调用 onDraw(Canvas,Rect)方法

正如你在图 7-13 中看到的,如果你将鼠标悬停在红色波浪状错误高亮上,IntelliJ 会告诉你你的问题是什么。在这种情况下,显示 android.graphics.Canvasandroid.graphics.Rect 的包和类,并显示错误“引擎中的 onDraw(Canvas,Rect)无法应用”。

这里使用了 import 语句的包和类名部分,这一事实应该会在您的思维过程中触发一个“import 语句”,所以您唯一需要知道的是如何让 IntelliJ 对这些进行编码。

9781430265504_Fig07-13.jpg

图 7-13 。将鼠标悬停在突出显示的红色波浪错误上,查看 Canvas 和 Rect 类的问题

正如你在图 7-14 中看到的,如果你把鼠标放在画布上或者你的公共 void onDraw(Canvas watchface,Rect rect)方法声明中的 Rect 上,一个有点神秘的,缩写的?android.graphics.Rect?Alt+Enter”消息。我会把这个隐晦的信息翻译成“问题:为你导入你的 android.graphics.Rect 包和类?如果是,按 Alt 键,同时按回车键,我来编码!”你会发现,如果你将鼠标悬停在这些红色的类名上,并按下建议的 Alt-Enter 击键序列,IntelliJ 将为你编写两个导入语句,如你在图 7-15 中看到的。

9781430265504_Fig07-14.jpg

图 7-14 。将鼠标悬停在代码中的红色画布和 Rect 类名上,并使用 Alt+Enter 导入这些类

现在是时候在 onDraw()方法的主体中使用 watchface Canvas 对象了。您将调用一个**。**draw color(int color)方法关闭此对象将画布的颜色设置为黑色。当然,这优化了表盘设计的功耗,因为黑色像素的功耗为零!

输入 watchface 对象名称,然后输入一个句点键,然后输入几个 drawColor 字符,会弹出一个帮助对话框,包含以字母 C 开头的绘制方法,如截图右下方的图 7-15 所示。双击 drawColor(int color) 选项,调用 watchface Canvas 对象的方法。现在,您所要处理的就是您的 Color 类参数,您将把它传递到方法调用中,并且您将完成基本引擎结构的编码,这将绘制黑色(空)的手表表面!

9781430265504_Fig07-15.jpg

图 7-15 。键入 watchface Canvas 对象,并使用句点和 drawC 调出方法助手对话框

您可能已经注意到,我的方法是在试图获得更复杂的代码之前,先获得一个空的 Java 代码结构并使其工作。这是因为 Java 是复杂的语言,Android 5 是复杂的操作系统;因此,我通常从最底层的功能编码开始。

我的开发方法是从空的开始逐步构建,但是没有错误的代码构造具有所有需要的导入语句和编码语法(关键字、括号、花括号等)。)恰当地就位。

正如你在图 7-16 中看到的,我已经输入了一个颜色类名(对象)并按下句号键调出颜色常量助手对话框,这样我就可以找到我想用于表盘背景色的颜色。

出于省电的原因,我选择了 Color 的颜色类常量。黑色(android.graphics) ,如图 7-16 中蓝色选中所示。

9781430265504_Fig07-16.jpg

图 7-16 。在 watchface.drawColor()方法中,键入颜色类并选择黑色

双击颜色后。黑色常量在帮助器对话框中,你会在 Java 代码编辑器窗格的左边看到一个灯泡,如图 7-17 左侧所示。您可以将鼠标悬停在图标上,或者单击图标旁边的下拉箭头,打开 IntelliJ 为您提供的关于您刚刚生成的这行代码的解决方案。

这条消息说“为 android.graphics.Color.BLACK 添加静态导入”,所以看起来 IntelliJ IDEA(或 Android Studio)希望您为 Color 而不是 Color 类添加导入语句。黑常数本身!

创建 Java 静态导入语句是为了提供一种类型安全的机制,用于在 Java 代码中包含常量,而不需要引用最初定义为常量的整个(在本例中为 Color)类。

9781430265504_Fig07-17.jpg

图 7-17 。单击灯泡下拉箭头,并添加建议的静态导入

恭喜你!现在,您已经为您的 ProWatchFaceService 及其附带的引擎类建立了 Java 代码基础,包括您的核心 onDraw()方法,该方法目前在智能手表显示屏上绘制一个空白的黑色空手表表面。

这个核心。onDraw()方法最终会在必要时更新您的手表表面。诚然,这目前是一个空的 Java 结构,尽管它确实为您提供了一个空白的黑屏(画布)来创建您的 Watch Faces 设计,因此,您已经将它放在适当的位置,并且您正在一步步地学习这个 Watch Faces API 是如何实现的,以及它在幕后是如何工作的,这仍然是非常重要的。

你的代码现在没有错误了,这可以在图 7-18 中看到。您可以看到 color 类的三个导入语句,一个静态导入语句和一个不需要的(在 IntelliJ 中使用灰色显示)导入语句。这表明引用了 Color 类,但不需要完整的 import 语句。IntelliJ 有一个代码image优化导入特性,一旦应用开发完成,你可以用它来删除所有不必要的导入语句。

9781430265504_Fig07-18.jpg

图 7-18 。完成了私有类引擎和五个导入语句,一个静态

既然已经有了提供核心外观处理功能的 Java 代码,那么让我们完成所有 XML 基础标记。还有很多设置工作要做!

Watch Faces API 设置的 xml 部分将包括一个全新的 /res/xml 目录,XML 壁纸对象定义,您将完成移动应用和 wear 应用 AndroidManifest.xml 文件的变形,从默认的 Wear 应用引导到 Watch Faces API 应用兼容性。完成此操作后,您将能够创建面部图像预览。

表盘 XML 资源:创建和编辑 XML 文件

尽管您已经添加了与 Watch Faces API 相关的权限,并使用 Java 7 代码创建了 Watch Faces 呈现引擎,但是仍然需要使用 XML 标记来解决一些问题。

这些包括创建 XML 资源文件夹、壁纸对象定义,以及将<服务>声明添加到 AndroidManifest.xml 文件中。您还将添加(或删除)一些其他 XML 标记和参数条目,以便在两个(移动和穿戴)Android 清单 XML 定义文件中进行一些关键的调整。这将向您展示如何将 bootstrap Wear 应用“变形”为 Watch Faces API 应用,这也是本书本章的全部内容!让我们开始处理所有这些 XML 标记,这样您就可以完成这一基础章节,然后开始创建手表外观设计!

表盘壁纸:用 XML 创建壁纸对象

接下来你需要创建的是 /res/xml/watch_faces.xml 文件,它定义了你的壁纸对象并包含了手表 faces 的设计。正如您在图 7-19 中看到的,项目文件夹中还不存在一个 /res/xml 文件夹,因此您需要右键单击 /wear/res 文件夹,并使用NewimageAndroid resource directory菜单序列来创建保存 watch_faces.xml 文件所需的目录,您接下来将创建该文件来保存 XML****对象父标签和 xmlns

9781430265504_Fig07-19.jpg

图 7-19 。右键点击/res 文件夹,选择新建image安卓资源目录

调用该菜单序列后,将会看到一个新建资源目录对话框,如图图 7-20 所示。选择您的资源类型xml ,并将目录名设置为 xml 。重要的是要注意,如果您首先选择一个资源类型,IntelliJ 将为您命名目录。其他一切保持不变,然后点击 OK 按钮创建 XML 文件夹,如您所见,在图 7-21 中已经成功创建。

9781430265504_Fig07-20.jpg

图 7-20 。选择一个 xml 资源类型,将把这个目录命名为/res/xml

右键单击 new /res/xml 文件夹,选择新建image XML 资源文件,如图图 7-21 所示,新建 watch_faces.xml 壁纸文件。

9781430265504_Fig07-21.jpg

图 7-21 。右键单击/res/xml 目录,并选择新建image XML 资源文件

调用菜单序列后,你会看到一个新建资源文件对话框,如图图 7-22 所示。选择你的源设置,然后设置文件名watch_face.xml 。保持其他一切设置不变,然后单击 OK 按钮创建 watch_faces.xml 定义文件,正如您在图 7-23 中看到的,该文件已成功创建,并已在 IntelliJ IDEA 内的编辑窗格中打开进行编辑。

9781430265504_Fig07-22.jpg

图 7-22 。将文件命名为 watch_face.xml,并将源设置为 main(保留默认值)

使用下面的 xml 标记进入 < xml > 版本容器标签和 <壁纸> 父标签,可以在图 7-23 的右半部分看到:

<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />

这将在 Android 中创建壁纸对象,用于您的手表表面。

9781430265504_Fig07-23.jpg

图 7-23 。右键单击 watch_face.xml,使用跳转到源代码在选项卡中查看它

现在,您已经将能够在 wear Android Manifest XML 文件中创建<服务>声明所需的一切就绪,这将声明您的 ProWatchFaceService(和引擎)以供使用,并使用父<服务>标签内的<元数据>子标签引用所有其他表盘图像预览和壁纸对象。

声明 WatchFace 服务:XML 标签

在 IntelliJ 中点击您的 wear 应用的 AndroidManifest.xml 选项卡,这样您就可以声明您的服务以在 Watch Face 应用中使用。如图 7-24 中 IntelliJ 的标题栏所示,它显示了当前正在编辑的文件的路径。使用此功能可确保您编辑的是正确的(wear) AndroidManifest,而不是移动 AndroidManifest。我已经将 < uses-feature > 和****标签及其参数放在一行代码中,以便为您接下来将创建的<服务>父标签及其子标签结构腾出空间。

<服务> 标签本身有半打参数来配置它的使用,从 BIND_WALLPAPER 权限开始,它允许你的服务对象“绑定”绑定是指与你在上一节创建的壁纸对象建立“实时刷新”或实时更新的连接。您还需要给<服务>标签一个服务类名,即**。ProWatchFaceService** ,还有一个 Pro 手表脸的标签。最后,您需要为allowed embedded选项设置一个 true (on)标志,并为必需的 taskAffinity 参数(属性)添加一个空字符串值。这可以使用以下 XML 标记来完成:

<service
  android:permission="android.permission.BIND_WALLPAPER"
  android:name=".ProWatchFaceService"
  android:label="Pro Watch Face"
  android:allowEmbedded="true"
  android:taskAffinity="" >
  <meta-data
    android:name="android.service.wallpaper"
    android:resource="@xml/watch_face" />
  <meta-data
    android:name="com.google.android.wearable.watchface.preview"
    android:resource="@drawable/preview_pro_square" />
  <meta-data
    android:name="com.google.android.wearable.watchface.preview_circular"
    android:resource="@drawable/preview_pro_circular" />
  <intent-filter>
    <action android:name="android.service.wallpaper.WallpaperService" />
    <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
  </intent-filter>
</service>

9781430265504_Fig07-24.jpg

图 7-24 。添加定义服务的<服务>父标签和<元数据>子标签

父标签中还有三个子标签以及一个子标签。我将首先介绍意图过滤器,因为只有一个,而且非常重要。意图过滤器子标签有两个自己的(嵌套的)子标签。一个是用于一个动作,它是一个壁纸服务类,是 WatchFaceService 类的超类。这是 ProWatchFaceService 类的 CanvasWatchFaceService 超类的超类。另一个子标签用于动作的类别,不出所料,它是一个 WATCH_FACE 常量。

我在这里详细介绍 CanvasWatchFaceService 类(及其嵌套的引擎类)的原因是,当需要查看意图过滤器设置时,您将理解 WallpaperService 是可绑定服务类的最高级类型,WATCH_FACE 类别实际上是为这个 WallpaperService 超类指出了一个 WatchFaceService 子类类型。

接下来,您需要添加 <元数据> 子标签,为这个 Watch Faces 服务声明定义某些东西。其中最重要的将定义 Wallpaper 对象,它是在上一节中使用 watch_face.xml 文件创建的,并在名为 android.service.wallpaper 的元数据标记中引用,该标记使用**Android:resource = " @ xml/watch _ face "**参数引用语法引用/res/xml 中的 XML 资源文件。

另外两个子标签将为表盘圆形和方形版本预览提供可绘制素材。接下来让我们来创建它们。

square watch face 预览的子标签将使用名称值com . Google . Android . wearable . watch face . preview,并引用您将在下一节创建的 XML 资源文件,该文件将使用**Android:resource = " @ drawable/preview _ pro _ square "**参数引用语法。注意在 name 参数中,默认的表盘屏幕形状类型是方形,而圆形将被编码为 preview_circular

注意在图 7-24 中 IntelliJ 是绿色波浪下划线突出单词 watchface,就像它对 Java 对象名做的一样。原来 IntelliJ 认为这是一个拼写错误,所以,希望在某个时候 IntelliJ 会更新它的字典,并在 watchface 和 WatchFaces API 中添加正确的拼写!

round watchface 预览的子标签将具有名称值com . Google . Android . wearable . watch face . preview _ circular和 XML 引用,该 XML 引用指向您将在下一节中使用**Android:resource = " @ Drawable/preview _ pro _ round "**参数引用语法创建的可绘制资源文件。

接下来,您需要创建这些 watchface.preview 素材并将其添加到正确的/res/drawable 文件夹中,因此,在我用一个完全加载(但为空)并配置好的 Watch Face API 应用结束本章之前,让我们来看一些数字图像素材放置工作,以向您展示这是如何完成的。

观看脸部图像预览:使用可绘制资源

下一步,你需要做的是删除下半部分图 7-24 中显示的手表表面预览图像的标签中的红色错误指示器文本高亮,将两个(方形和圆形)手表表面预览 PNG 图像复制到项目的 wear 部分的正确/res/drawable 文件夹中。我将使用第二部分后面一章中的预览图,包括图形设计素材创建和集成到表盘设计、代码、UI 设计、XML 标记和应用中。

预览图像的图像分辨率需要为 320 x 320 像素。这意味着这些图像是 HDPI(高密度像素图像)分辨率,因此需要进入**/wear/src/main/RES/drawable-hdpi**文件夹,以便 Android 5 找到它们。该文件夹可以在图 7-25 的左侧看到,也可以在图 7-26 的左侧看到。

在图书的资源库中找到 Pro Android 可穿戴设备档案中的 preview_pro_squarepreview_pro_circular PNG 图片,复制到/**AndroidStudioProjects/ProAndroidWearable/wear/src/main/RES/drawable-hdpi/**文件夹中,如图图 7-25 所示。这些预览图像将与 watch faces 配套应用一起使用,该应用运行在手机或平板电脑上,向用户逐个像素地显示表盘的样子(表盘使用 320 像素)。

9781430265504_Fig07-25.jpg

图 7-25 。将 preview_pro_circular 和 preview_pro_square 文件复制到 drawable-hdpi

一旦数字图像素材被正确复制到您的/wear/src/main/RES/drawable-hdpi/文件夹中,图 7-24 中出现的红色错误代码高亮将会消失。这可以在图 7-26 的右侧看到,现在 AndroidManifest.xml 标记也没有错误了。在这一章中,你已经取得了很大的进步!

9781430265504_Fig07-26.jpg

图 7-26 。一旦将 PNG 资源复制到/res/drawable 文件夹中,错误就会消失

摘要

在本章中,您学习了如何通过使用 IntelliJ IDEA 将您的 Pro Android 可穿戴 bootstrap 应用变形为表盘应用,从而为您的表盘应用创建基础。

首先,您了解了项目中的 Gradle 脚本需要如何进行分层,以及如何设置它们以获得成功的 watch faces 应用层次结构。您了解了依赖性和存储库,以及如何使用 Gradle 设置 Google Play 服务。

接下来,您添加了创建 Watch Faces 应用所需的 Android 权限,并了解到您需要处理两个 Android 清单 XML 文件:一个用于可穿戴外围设备,另一个用于手机或平板电脑“主”移动设备。

之后,您学习了 Android CanvasWatchFaceService 类及其 Java 类层次结构,描述了它是如何使用上下文启动服务的对象,该服务创建用于创建 Watch Faces 引擎的壁纸,该引擎使用画布绘制到智能手表显示屏。有趣的是,一个 Java 层次结构可以向我们展示 Google 是如何在 Android 5 操作系统中实现他们的 WatchFaces API 的,不是吗?

然后你了解了 Android 的 CanvasWatchFaceService。引擎类及其层次结构,以及如何将其实现为 ProWatchFaceService 类的私有类。您学习了如何实现?onDraw()方法,并创建了一个空的 Watch Faces 服务引擎,该引擎将用于本书涵盖 Watch Faces API 的第二部分的剩余部分。

接下来,我讨论了手表外观 XML 定义,您创建了一个/res/xml 文件夹,然后创建了一个 watch_face.xml 文件,该文件定义了一个用于手表外观设计的墙纸对象。

然后,我讨论了 Wear Android 清单,您精心制作了服务入口,它定义了新的 ProWatchFaceService.java 类以及其他关键内容,如墙纸对象、意图过滤器和预览图像。

最后,您将手表表面预览图像放到一个可绘制的 HDPI 文件夹中,这样所有的 Java 代码和 XML 标记都变成了绿色,因此没有错误!

在下一章中,您将开始向公共 ProWatchFaceService 类和私有 Engine 类添加 Watch Face 特性。

八、表盘计时引擎:使用时区、时间和广播接收器

现在您已经有了 CanvasWatchFaceService 子类和私有的 Engine 类,并且已经将 Gradle 脚本、XML 素材、Wallpaper 对象和预览素材安装到了它们应该安装的位置,您已经准备好开始学习更高级的 Java 工具类和 Android 类以及实现 WatchFaces API 的方法。

随着你在本书中逐步完成表盘的制作过程,主题会变得更加深入。在本章中,您将深入了解绘图表面,设置您的表盘样式、时区管理、time 对象和时区广播类,为了创建功能性表盘设计应用,需要实现这些内容。

您还将详细了解一下 WatchFaceStyle 类及其 WatchFaceStyle。构建器嵌套类。这些类将允许你建立和配置你在第六章中学到的必要参数。

你还会看到 Android 与时间相关的类和方法,比如时间时区,以及 Android SurfaceHolder 类,最后是 Androidbroadcast receiver类和方法。这一章充满了关于 Java 和 Android 类的信息,所以,让我们开始吧!

你的 watch face Surface:Android Surface holder

你的表盘设计将通过使用一个 Androidsurface holderobject 来“托管”。这个对象是兼容的,可以与安卓画布对象和你的协同工作。onDraw( ) 方法,这两个方法都已经放在代码中,随着本章的深入,它们将扩展您对的使用。

在您用代码实现 SurfaceHolder 接口之前,我想给您一个概述。然后我将回顾一下 WatchFaceStyle 类,最后介绍关键的 WatchFaceService 和 WatchFaceService。引擎类,它包含了您将在接下来的几章中实现的大多数重要的 WatchFaces API 方法。

Android SurfaceHolder 界面:手表表面

Android WatchFaces API 使用一个 SurfaceHolder 对象作为最底层的对象来保存你的手表 Faces 设计。SurfaceHolder 是一个公共抽象 Java 接口,所以它定义了你需要实现的方法或常量,以便创建手表表面应用。它是android . View . surface holder包的成员,因为它是一种 Android 视图。

SurfaceHolder 对象旨在方便持有(承载)显示屏绘图表面。此界面允许您控制显示绘图表面的格式或大小,以及编辑其表面上的像素。

有一些方法可以用来监测表面的任何变化。通常使用 SurfaceView 类来访问这个接口,然而,WatchFaces API 通过来访问它。onCreate( ) 方法。我将在 WatchFaceService 中介绍这个方法。本章的引擎部分简要讨论了 SurfaceHolder 和 WatchFaceStyle 类。

Android SurfaceHolder 接口包含三个嵌套类:两个是可以用来确定何时发生了对 WatchFace 表面的改变的接口,一个是用于坏表面类型异常错误处理的类。

表面固定器。回调接口可以在您的 Java 代码中实现,它允许您的 Watch Face 应用接收有关 SurfaceHolder 对象更改的信息。还有第二辆 SurfaceHolder。回调 2 实现 SurfaceHolder 的接口。回调接口,提供可以接收的附加回调。

表面固定器。BadSurfaceTypeException 类提供了一个从。lockCanvas()方法,该方法是从其类型已设置为SURFACE _ TYPE _ PUSH _ BUFFERS的 SurfaceHolder 对象调用的。

这个常量通常不会影响 Watch Faces 的设计和应用开发,我在这里包含它只是为了完整地涵盖这个类在 Android 操作系统中的所有实现。

该接口还包含大约十几个公共方法,其中几个可以用于 Watch Faces 设计。这些方法是抽象的,因此您必须用自己独特的代码来实现它们。它们在表 8-1 中列出。你可以在这里熟悉他们,这样你就知道他们能做什么。

表 8-1 。SurfaceHolder 接口方法以及数据类型、方法调用结构和用途

|

方法类型

|

方法结构

|

方法目的

抽象虚空 addCallback(SurfaceHolder。回拨回拨) 添加回调接口
抽象曲面 getSurface() 访问曲面对象
抽象直肠 getSurfaceFrame() 获取当前尺寸
抽象布尔 正在创建( ) 正在创建表面?
抽象画布 lockCanvas() 编辑表面像素
抽象画布 lockCanvas(Rect dirtyRect) 肮脏的直肠锁画布
抽象虚空 removeCallback(SurfaceHolder。回调 cback) 移除回拨
抽象虚空 setFixedSize(int width,int height) 使表面固定宽×高
抽象虚空 setFormat(内部格式) 设置像素格式
抽象虚空 setKeepScreenOn(布尔 ScreenOn) 保持屏幕显示选项
抽象虚空 setSizeFromLayout() 允许调整表面大小
抽象虚空 unlockCanvasAndPost(画布) 完成编辑表面

现在您已经对 SurfaceHolder 接口有了一个大致的了解,让我们看看如何使用 SurfaceHolder 为您将用于 Watch Faces API 设计的 Canvas 对象提供一个表面(Holder)。

一个 SurfaceHolder 对象:onCreate(surface holder surface)

如果您还没有这样做,请使用您在第二章中创建的快速启动图标启动 Android Studio 开发环境。这将启动 IntelliJ IDEA,并显示您在第四章中创建的 ProAndroidWearable 项目,其中您在第七章中添加了手表面孔。

关闭除ProWatchFaceService.java选项卡之外的所有编辑选项卡,如图图 8-1 所示,因为在本章中您将会在该选项卡上工作。您将从 WatchFaceService 添加方法。引擎类,你会在这一章的后面学到,第一个是**。onCreate( )** 方法,如图图 8-1 所示。

9781430265504_Fig08-01.jpg

图 8-1 。向私有引擎类添加一个公共 void onCreate()方法,并在参数列表中键入 Sur

键入@ Override public void onCreate() methodinfra structure,并在参数区域内键入 Sur 以触发 IntelliJ 调出一个助手对话框,其中包含该方法调用可以使用的所有对象(类)类型。找到**SurfaceHolder(Android . view package)**选项,双击它将 surface holder 对象插入到方法调用中,并将其命名为 surface ,因为这就是它的作用(holding)。

创建 SurfaceHolder 对象(名为 surface)的公共 void onCreate()方法结构将采用以下 Java 结构,如图 8-2 所示:

@Override
public void onCreate(SurfaceHolder surface) { Your Java method body will go in here }

9781430265504_Fig08-02.jpg

图 8-2 。键入 Java super 关键字,并选择 onCreate (SurfaceHolder)选项将其添加到

正如你在图 8-2 中看到的,如果你简单地在方法体中键入 Java 关键字 super句点键,IntelliJ IDEA 会提供一个方法助手对话框。您可以使用此对话框仔细阅读可用于的所有方法。onCreate()方法。

双击onCreate(surface holder)void选项并插入。onCreate()方法调用您的超类,使用您在公共 void onCreate(surface holder surface)方法构造中创建的表面对象。这会将名为 surface 的 SurfaceHolder 对象传递给超类。onCreate()方法,用于在更高级别进行处理。这就是你如何创建一个 SurfaceHolder 并在你的 WatchFace 中实现它。

你会在图 8-3 中注意到,如果你开始在 super.onCreate()方法调用参数区域中键入你的 SurfaceHolder 对象名(surface ), IntelliJ 会找到这个对象名,以及所有其他可用的兼容对象类型,并把它们放在一个大的帮助器对话框中供你选择。

9781430265504_Fig08-03.jpg

图 8-3 。在中键入一个 s。onCreate()方法参数区,并从对话框中选择表面对象

现在你已经用这个把你的表面支架放好了。onCreate()方法调用,让我们使用 WatchFace Style 类构建一个 WatchFace 样式。

设置手表表面样式:Watch Face Style。建造者

WatchFaces API 包含您的 WatchFaceStyle 和 WatchFaceStyle。构建器类,用于构建和配置 Watch Faces 设计,满足您在第六章中了解到的许多 Android 设计需求。

这些考虑因素定义了你的 WatchFace 将如何吸收 Android 操作系统的考虑因素,例如状态栏指示器的位置,peek 卡支持,Google hotword 指示器的位置,等等。

Android WatchFaceStyle 类:设计手表的外观

Android WatchFaceStyle 类是扩展Object 类并实现 Java Parcelable 接口的公共类。这个类是Android . support . wearable包的一部分。这个 WatchFaceStyle 类的层次结构表明(由于层次结构中缺少任何其他类),它是由 Google 的 Android 开发团队“临时编码”的。创建该类是为了提供观察面孔样式,其层次结构如下所示:

java.lang.Object
  > android.support.wearable.watchface.WatchFaceStyle

需要注意的是,这个 android.support.wearable 包中的可穿戴支持库类可能会发生变化。WatchFaceStyle 类提供了允许描述和配置手表外观的常量和方法。该类中概述的参数定义了 WatchFaceService 将如何在您的自定义手表表面设计上绘制 Android 的操作系统用户界面(UI)元素。

这个类的一个实例将被传递到一个方法中,您将在下一节中使用这个方法,称为setWatchFaceStyle(WatchFaceStyle),用于您在ProWatchFaceService.java类中的私有引擎类中的 onCreate 方法。

为了构造您的 WatchFaceStyle 对象实例,您将使用 Java new 关键字来创建一个新的 WatchFaceStyle。生成器对象。这是在 setWatchFaceStyle()方法中使用来自您的类的上下文对象来完成的。

因此,WatchFaceStyle 类包含一个单独的嵌套类,名为 WatchFaceStyle。建造者。我将在本章的下一节详细介绍这一点。当你构建一个 WatchFace 样式时,你也将在后面的章节中使用这个嵌套类。

WatchFaceStyle 类包含 11 个重要的手表外观常量,您将在手表外观设计和应用开发中使用这些常量。这些显示在表 8-2 中,以及它们的正确名称和功能描述。

表 8-2 。WatchFaceStyle 类常量名称及其预期功能

|

WatchFaceStyle 常量名称

|

WatchFaceStyle 常量函数

环境 _ 窥视 _ 模式 _ 隐藏 当手表处于环境模式时隐藏窥视卡
环境 _ 峰值 _ 模式 _ 可见 当手表处于环境模式时,显示 peek 卡
背景 _ 可见性 _ 中断 简要显示了中断时的 peek 卡背景
背景 _ 可见性 _ 持久性 显示始终用于中断的 peek 卡背景
峰值模式短 使用单行文本显示查看卡
峰值模式变量 使用多行文本显示 peek 卡
PEEK_OPACITY_MODE_OPAQUE 使用不透明/实心背景显示 peek 卡
窥视 _ 不透明 _ 模式 _ 半透明 以半透明背景显示 peek 卡
保护 _ 热门词汇 _ 指示器 在 Hotword 中使用半透明黑色背景
保护状态栏 使用半透明黑色背景状态栏
保护整个屏幕 屏幕使用半透明的黑色背景

此 WatchFaceStyle 类包含允许您访问有关 WatchFaceStyle 的信息的公共方法。这些大多是**。get( )** 方法,这些方法与。稍后您将使用嵌套的 WatchFaceStyle 访问 set()方法。生成器类。接下来我会报道这堂课。

有一个**。describeContents( )** 描述 WatchFaceStyle 内容的方法,以及一个**。equals(Object otherObject)** 方法,用于比较 WatchFaceStyle 对象。有一个**。getComponent( )** 方法,该方法为指定样式的表盘组件获取一个值,并且您的标准继承了**。toString( )** ,。hashCode( ) 和**。writeToParcel( )** 方法。

getter 方法包括一个**。getambientpekmode()方法,显示手表处于环境模式时主 peek 卡将如何显示,以及一个。getBackgroundVisibility( )** 方法显示如何为主 peek 卡设置背景显示。。getcardppeekmode()方法显示主 peek 卡将进入您的表盘显示屏多远,以及。gethotworindicatorgravity()方法显示您在表盘屏幕上放置 OK Google hotword 的位置。。getPeekOpacityMode( ) 显示一个 Peek 卡不透明度设置,和一个**。getShowSystemUiTime( )** 显示您的 WatchFaceStyle 是否配置为在表盘上显示系统时间。

还有一个**。getStatusBarGravity( )** 方法调用,允许您轮询并找出屏幕上状态栏图标的位置,以及一个**。getViewProtectionMode( )** 允许您对表盘屏幕上的元素添加透明黑色背景色的设置进行投票,以便它们在表盘上可读。

最后,还有一个方法叫做**。getshowunreadcountimindicator()轮询 WatchFaceStyle 对象,以确定 WatchFaceStyle 对象是否包含一个指示符,如果设置为 true 值,则该指示符显示还有多少张未读的 peek 卡**要由用户从未读的 peek 卡输入流中读取。

接下来,我们来看看 WatchFaceStyle。生成器嵌套类及其许多外观样式方法。在创建和构建 WatchFaceStyle 对象时,您将在下一节后面的章节中使用这些方法中的大部分。

Android WatchFaceStyle。构建器类:构建样式

Android WatchFaceStyle。构建器公共静态类也有一个扩展对象,表明它也是为构建你的 WatchFaceStyle 对象而临时编写的。该类提供了用于创建 WatchFaceStyle 对象的“生成器方法”。Java 类层次结构如下所示:

java.lang.Object
  > android.support.wearable.watchface.WatchFaceStyle.Builder

这是另一个可穿戴支持库类,可以在 android.support.wearable 包中找到,与 WatchFaceStyle 类一样,它也将在未来随着智能手表制造商改变其产品功能的方式而发生变化。例如,随着消费电子产品组件小型化的进展,以及这些小型化组件的价格随着时间的推移不断下降,更多的智能手表将不可避免地从外设转向完整的 Android 设备(如 Neptune Pine)。

WatchFaceStyle 的公共构造函数方法 。构建器对象使用一个服务,特别是您的 WatchFaceService 子类,在本例中是一个 ProWatchFaceService 类,以及它的上下文对象(this),作为它的参数,使用以下格式:

WatchFaceStyle.Builder(Service service)           // This is the Generic Constructor Method Format
WatchFaceStyle.Builder(ProWatchFaceService.this) // This is our specific Constructor Method Format

一个 Java 这个关键字包含了 ProWatchFaceService 类的上下文,这个上下文对象包含了关于这个类的所有相关信息

这个 WatchFaceStyle。构建器类包含十个公共方法调用。我将向你展示如何在你的私有引擎类的中实现这些。onCreate( ) 方法。在本章的下一节中,当你把你的 WatchFaceStyle 对象添加到ProWatchFaceService.java类中时,你会做到这一点。

WatchFaceStyle 类中的核心方法是您的**。build( )** 方法,它实际上构建了 WatchFaceStyle 对象,使用了一种只读数据(对象)格式。这是因为 WatchFaceObject 应该在启动时使用。onCreate()方法,然后在 watch face 应用执行期间简单地读取。

。setambientpekmode(int ambientpekmode)方法允许您指定 peek 卡的可见性设置。这将决定当您的表盘处于环境模式时,是否会显示 Peek 卡。该方法使用表 8-2 中列出的前两个常数来确定表盘处于环境模式时是否显示或隐藏 Peek 卡。在这里,您将设置在环境模式下显示 peek 卡。

**。setBackgroundVisibility(int background visibility)**方法允许您指定如何显示 peek 卡的背景。该方法使用表 8-2 中列出的后两个常量来确定是否持续显示 Peek 卡背景。

**。setcardppeekmode(int peekMode)**方法允许你指定当一个表盘显示时,一张 peek 卡将覆盖表盘多远。该方法使用表 8-2 中列出的第三两个常数来确定有多少 Peek 牌将显示在你的表盘屏幕上。

**。setPeekOpacityMode(int peekOpacityMode)**方法允许你指定一个 Peek 卡片背景的不透明度为纯色或半透明背景。该方法使用表 8-2 中列出的第四个两个常数来确定 Peek 卡背景是纯色(不透明)还是半透明。

**。setview protection(int view protection)**方法允许你在 watch faces 屏幕上给 UI 元素添加深色半透明背景效果。该方法使用表 8-2 中列出的最后三个常量来确定状态栏、热门词或两者(整个屏幕)是否将被“保护”

**。sethotworindicatorgravity(int hotworindicatorgravity)**方法允许您设置一个位置常数(或多个常数)以将热门词定位在表盘上。参数使用一个或多个标准重力常数值。

**。setStatusBarGravity(int Status bargravity)**方法允许您设置表盘屏幕上状态栏图标的位置。该参数还使用一个或多个标准 Android 重力常数值。

。setShowSystemUiTime(boolean showSystemUiTime)方法调用允许你指定操作系统是否会在你的表盘设计上绘制时间。该方法的参数是一个简单的值。

。setshowunreadcountimindicator(boolean show)方法允许您设置是否在状态栏图标旁边显示一个指示器,该指示器显示有多少张未读卡片等待读取。该方法的参数是一个简单的值。您将在本章开发的手表 face 应用中使用一个真实值。

建立你的手表表面:使用。setWatchFaceStyle()

接下来让我们构建 WatchFaceStyle 对象,这样您就可以了解如何创建这个必需的 watch face 对象,它定义了您的 watch face 将如何吸收 Android OS 功能。您将把这个对象放在第super.onCreate(surface);行代码之后,这将为手表表面创建 SurfaceHolder 对象。

添加一行代码,键入单词集,启动 IntelliJ 方法助手对话框,如图 8-4 中的所示。找到您的**setWatchFaceStyle(WatchFaceStyle)WatchFaceStyle)**方法,双击它,将其添加到您正在创建的 Java 代码中,以实现 WatchFaceStyle 对象。

9781430265504_Fig08-04.jpg

图 8-4 。在 super.onCreate()后添加一行代码,键入 set,并选择 setWatchFaceStyle 方法选项

在 setWatchFaceStyle()方法参数区域内,您将为该 WatchFaceStyle 嵌套您的构造函数方法。使用 Java new 关键字的生成器对象。这创建了一个更密集(也更复杂)的 Java 构造,但它也更紧凑,允许您使用不到十几行 Java 编程逻辑来构造和配置 WatchFaceStyle 对象。这甚至可能是一行(非常长的)Java 代码;然而,我将使用 IntelliJ IDEA 中的十几行来格式化它,以大大提高可读性!

setWatchFaceStyle()方法调用的主要 Java 构造和新的 WatchFaceStyle。嵌套在其中的 Builder()构造函数方法看起来像下面的 Java 代码,它也显示在图 8-5 中:

setWatchFaceStyle( new WatchFaceStyle.Builder(ProWatchFaceService.this) );

9781430265504_Fig08-05.jpg

图 8-5 。添加一行,键入一个句点以弹出方法帮助器对话框,并选择 sethotworindicatorgravity

将光标放在 WatchFaceStyle 之后,按 Return 键添加一行代码。Builder()方法的右括号,在 setWatchFaceStyle 语句的结束括号和分号之前,按句点键,选择一个**。sethotworindicatorgravity(int hotworindicatorgravity)**方法从 IntelliJ 弹出方法帮助器对话框,如图图 8-5 所示。

在参数区输入重力和一个周期,如图图 8-6 所示。

9781430265504_Fig08-06.jpg

图 8-6 。在参数区域中键入重力对象,并使用句点键调出辅助对象对话框

双击重力。底部常量将 OK Google hotword 放置在表盘设计的底部,如图图 8-6 所示。接下来使用一个竖条 | 字符添加另一个重力。方法调用参数区的 CENTER_HORIZONTAL 常量。这将使热门词汇在表盘设计的底部水平居中。确保在竖线分隔符和重力常量之间没有空格,因为这将被编译器视为多个参数,而不是一个“统一的”重力连接参数。

接下来,让我们添加另一行代码。按下 period 键,从弹出的助手对话框中选择**setShowSystemUiTime(boolean showSystemUiTime)**选项,然后输入 false 的值,因为您将需要使用应用代码来控制所有显示的表盘时间。setWatchFaceStyle()方法结构现在应该类似于下面的 Java 7 编程逻辑:

setWatchFaceStyle( new WatchFaceStyle.Builder(ProWatchFaceService.this)
        .setHotwordIndicatorGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL)
        .setShowSystemUiTime(false)
);

接下来,我们来设置 Peek 卡配置设置,从背景可见性方法和常量值开始,如图图 8-7 所示。

9781430265504_Fig08-07.jpg

图 8-7 。添加一个 setBackgroundVisibility()方法,在里面输入 WatchFaceStyle,选择 BACKGROUND _ VISIBILITY _ INTERRUPTIVE

在下面添加一行代码。setShowSystemUiTime()方法并在您的中添加。setBackgroundVisibility( ) 方法调用。在方法参数区,输入 WatchFaceStyle ,按句点键打开助手对话框。使用此方法的BACKGROUND _ VISIBILITY _ INTERRUPTIVE常量值,当出现 Peek 卡时,允许您的表盘设计至少部分可见。这将使用以下 Java 代码结构来完成:

setWatchFaceStyle( new WatchFaceStyle.Builder(ProWatchFaceService.this)
        .setHotwordIndicatorGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL)
        .setShowSystemUiTime(false)
        .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
);

接下来,我们把**加进去。setcardpekmode()。setpeekocapacitymode()**方法调用完成 Peek 卡如何在交互模式下工作的配置。之后,您可以添加其他四种配置方法。使用表 8-2 中包含的 WatchFaceStyle 常量的 Peek 卡方法调用配置如图 8-8 中的所示,应该类似于下面的 Java 代码:

setWatchFaceStyle( new WatchFaceStyle.Builder(ProWatchFaceService.this)
        .setHotwordIndicatorGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL)
        .setShowSystemUiTime(false)
        .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
        .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
        .setPeekOpacityMode(WatchFaceStyle.PEEK_OPACITY_MODE_TRANSLUCENT)
);

9781430265504_Fig08-08.jpg

图 8-8 。添加。build()方法调用方法链的末尾来构建 WatchFaceStyle 对象

让我们添加最后四个 Watch Faces style 配置方法和常量,这样您就可以获得一些实践经验,为您的 Watch Faces API 实现和利用所有 Watch Faces style 对象属性。

接下来,您将在表盘处于环境模式时启用 Peek Cards,将状态栏定位在表盘设计的顶部中心,打开状态栏和 Hotword 的视图保护功能,并显示未读 Peek Cards 消息计数器。

必须结束使用 Java 点标记法的方法调用链的最后一个方法调用是。build()方法。请注意,您可以将这些单独的行链接在一起,作为一长行代码。我只是使用点连接符将链中的方法排列起来,以获得更好的可读性,这可以在最终的 Java 构造中看到,如图 8-8 所示:

setWatchFaceStyle( new WatchFaceStyle.Builder(ProWatchFaceService.this)
     .setHotwordIndicatorGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL)
     .setShowSystemUiTime(false)
     .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
     .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
     .setPeekOpacityMode(WatchFaceStyle.PEEK_OPACITY_MODE_TRANSLUCENT)
     .setAmbientPeekMode(WatchFaceStyle.AMBIENT_PEEK_MODE_VISIBLE)
     .setStatusBarGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL)
     .setViewProtection(WatchFaceStyle.PROTECT_STATUS_BAR|WatchFaceStyle.PROTECT_HOTWORD_INDICATOR)
     .setShowUnreadCountIndicator(true)
     .build( )
);

接下来,让我们看看 Android(和 Java)计时相关的类,因为这些类提供了获取系统时间和更新表盘设计的功能,所以它可以实时显示正确的时间!

设置表盘时间:与时间相关的类

现在,您已经完全满足了您在第六章中了解到的操作系统功能同化要求,并创建了 Watch Faces SurfaceHolder 和 WatchFaceStyle 对象,它们是形成 WatchFace 设计的核心表面并使用 CanvasWatchFaceService 和 CanvasWatchFaceService 在其上显示所需操作系统功能所必需的。你在第七章中放置的引擎类,是时候学习 Java 7 和 Android 5 类和包中与时间相关的特性了。

这是您需要建立的下一个基础,以便您的表盘设计可以采用系统时间和时区值,并使用您决定实施的任何视觉设计范式将它们转换为表盘设计的表面显示。

Java 时间工具类:时间单位和时区

因为这个 WatchFaces API 旨在创建显示时间的应用,所以让我们仔细看看与时间相关的类。这些可以在 java.util 和 java.util.concurrent 包中找到,它们控制时间单位的转换,如小时、分钟、秒、毫秒、微秒、纳秒等,以及 GMT 时区之间的转换。

在时间单位之间转换:使用时间单位类

公共枚举时间单元类是 java.lang.Enum <时间单元> 类的子类,是 java.util.concurrent 包的一部分。类是一个枚举类,用于枚举 Java 7 应用中使用的时间单位,即提供时间单位的数字表示形式。

这个 TimeUnit 类的 Java 类层次结构如下所示:

java.lang.Object
  > java.lang.Enum<TimeUnit>
    > java.util.concurrent.TimeUnit

TimeUnit 类(和对象)用于以任何指定的持续时间粒度单位表示持续时间。您将在 WATCH_FACE_UPDATE_RATE 常量中使用它来表示秒和毫秒,在简要讨论了这些 Java 7 TimeUnit 和 TimeZone 类之后,您将把它放入适当的位置。

这个类为开发人员提供了可以在离散时间单位之间转换时间的实用方法,因此得名。您可以使用这些方法来实现使用这些时间单位的计时和延迟操作。

需要注意的是,时间单位本身并不维护时间信息!它只是帮助开发人员组织或使用跨各种上下文对象单独维护的时间表示。因此,TimeUnit 对象可以被认为是一个实时的时间转换过滤器,适用于所有的声音设计师、视频特效编辑或动画师。

支持的时间单位属性在该类中表示为枚举常量。其中包括小时分钟毫秒微秒纳秒

一纳秒定义为千分之一微秒,一微秒定义为千分之一毫秒,一毫秒定义为千分之一秒,一分钟定义为 60 秒,一小时定义为 60 分钟,一天定义为 24 小时。

TimeUnit 类包含十几个用于控制和转换时间的方法,包括**。托米利斯(长持续时间)**,你将在 ProWatchFaceService.java 课上用到它。

还有半打其他的。此类中的 to()方法,包括。今天( ),。toHours(),。toSeconds(),。toNanos()和。托米克罗斯()。所有的。to( ) TimeUnit 类方法在参数区域采用一个长数据值。

接下来,我们来看看 TimeZone 类,这样你不仅可以将时间转换成不同的单位,还可以转换地球周围的时间!这对表盘设计的国际兼容性非常重要。

时区之间的转换:使用 TimeZone 类

Java 公共抽象时区工具类扩展了一个 java.lang.Object 类。这意味着 TimeZone 工具类是临时编写的,以提供时区支持。该类是 java.util 包的一部分。这个 TimeZone 类有一个已知的直接子类 SimpleTimeZone,但是您将使用这个顶级 TimeZone 类。如果你想知道什么是已知的直接子类,它是 Java API 的一个直接子类。因此,你的直接子类将会是未知的直接子类,直到 Oracle 正式把它们变成 Java 9 的永久部分!

该时区类的 Java 类层次结构如下所示:

java.lang.Object
  > java.util.TimeZone

TimeZone 类创建一个对象,用于表示一个时区偏移量。需要注意的是,TimeZone 类还会为您计算出夏令时调整,这很方便。

通常通过调用来获得 TimeZone 对象。getDefault( ) 方法,该方法基于 Watch Face 应用当前运行的时区创建一个 TimeZone 对象。例如,对于在我居住的圣巴巴拉运行的 WatchFace 应用。getDefault()方法基于太平洋标准时间(也称为 PST)创建一个 TimeZone 对象。您可以使用获得一个时区对象的 ID。getID( ) 方法调用,像这样:

TimeZone.getDefault( ).getID( )

如果愿意,还可以通过调用。getTimeZone()方法与。getID()方法调用,使用以下 Java 点链接方法调用结构:

TimeZone.getTimeZone( ).getID( )

为了正确使用这种方法,您需要知道时区 ID 值。例如,太平洋时区的时区 ID 是“美国/洛杉矶”如果您想创建一个加载了太平洋标准时间的 TimeZone 对象,您可以使用下面的 Java 语句:

TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");

如果您不知道所有支持的时区 ID 值,您可以使用**。getAvailableIDs( )** 方法,然后遍历所有支持的时区 ID 值。您可以选择一个受支持的时区 ID,然后获取该时区。如果您想要使用的时区 ID 不是由当前支持的时区 ID 值之一表示的,则开发人员可以指定自定义时区 ID,以便生成自定义时区 ID。自定义时区 ID 的语法是 CustomID : GMT 符号小时:分钟

当您创建 TimeZone 对象时,它将创建您的自定义时区 ID 属性。此 NormalizedCustomID 将使用以下语法进行配置:GMT 符号(一位数或两位数)小时:(两位数)分钟。其他可接受的格式包括 GMT 标志小时分钟GMT 标志小时

符号可以是加号或减号(连字符),两位数小时的范围在 00 到 23 之间。分钟总是使用两位数字,从 00 到 59。

数字将始终是以下数字之一:0、1、2、3、4、5、6、7、8、9。

小时必须在 0 到 23 之间,分钟必须在 00 到 59 之间。例如,“GMT+10”表示比 GMT 早 10 小时,而“GMT+0010”表示比 GMT 早 10 分钟,因此“GMT+1010”将比 GMT 早 10 小时 10 分钟,依此类推。

时区数据格式与位置无关,数字必须取自 Unicode 标准的基本拉丁块。请注意,不能使用自定义时区 ID 来指定自定义夏令时转换计划,这一点很重要。如果您指定的字符串值与所需的语法不匹配,那么将使用“GMT”字符串值。

当您创建 TimeZone 对象时,它将创建您的自定义时区 ID 属性。这个 NormalizedCustomID 将使用以下语法进行配置:GMT Sign TwoDigitHours : Minutes。

符号可以是加号或减号,一位数小时的范围是从 1 到 9,两位数小时的范围是从 10 到 23。分钟也使用两位数字,范围从 00 到 59。例如, TimeZone.getTimeZone("GMT-8 ")。getID( ) 将返回一个 GMT-08:00 数据值。

TimeZone 类有 21 个方法,我不会在这里详述,但是您将使用。getID()和。Watch Faces 应用代码中的 getDefault()方法。这将允许您获得 Android 用户使用的当前时区及其 id,以便您可以在用户的主机设备(手机或平板电脑)随时切换时区时,在 Watch Faces 应用中切换时区。表盘设计也将反映这一时区的变化。

如果你想更深入地了解所有这些强大的时区方法,你可以访问 docs.oracle.com 网站上的时区 URL:

[`docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html`](http://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html)

接下来,让我们通过在 ProWatchFaceService 类中使用一些与时间相关的 Java 7 代码,开始实现您已经学习过的一些类。之后,您将了解 Android BroadcastReceiver,以及它如何允许您从主机手机广播(和接收)与时间相关的数据,这些数据可用于设置表盘上的时区(实时)。

保持 Watch Face Time:Watch _ Face _ UPDATE 不变

让我们从添加与计时相关的 Java 代码开始,方法是添加定义表盘更新速率的常数。将使用一个时间单位来完成这项工作,您将为表盘使用一千(毫秒)的计时分辨率,因为您将使用秒针 。

如您所知,TimeUnit 类方法使用 long 数据类型,因为这是一个只在类内部使用的常量,所以让它成为一个私有的静态 final long 变量。您可以将它命名为 WATCH_FACE_UPDATE_RATE,因为这是它将在这个应用的上下文中表示的内容。

您将设置此常量等于时间单位值 1000,这里您将使用时间单位。SECONDS.toMillis(1)构造来设置该值。TimeUnit 类中的 SECONDS 常量表示您将秒作为时间单位来处理,而。toMillis(1)方法调用(和参数)将一秒转换为毫秒值,正如我已经提到的,它是 1000。

这个常量声明语句的 Java 代码应该放在 ProWatchFaceService.java 类的顶部,如图 8-9 所示,应该如下所示:

private static final long WATCH_FACE_UPDATE_RATE = TimeUnit.SECONDS.toMillis(1);

9781430265504_Fig08-09.jpg

图 8-9 。添加一个私有的静态 final long WATCH _ FACE _ UPDATE _ RATE 变量,并将其转换为毫秒

Android 类:时间、处理器和广播接收器

除了 WatchFaceService 和 WatchFaceService。引擎类,你已经在第七章中对其进行了子类化,我将在第九章的中对其方法进行更详细的探讨,还有一些其他重要的 Android 类也在手表表面设计中使用。我想在本节中详细介绍这些,并在公共 ProWatchFaceService 和私有 Engine 类中实现它们的一些关键方法,以向您展示如何在 Watch Faces 应用设计中使用这些类。

首先从 Android Time 类开始,这个类用来保存当前时间值,精确到秒。接下来,我将讨论 Androidbroadcast receiver类,它用于接收时区变化,最后,我将讨论处理程序类,它用于发送包含更新时区值的消息。

Android 时间类:使用秒的时间处理

Android Time 类是一个公共类,它扩展了 java.lang.Object 类。Time 类的类层次结构如下所示:

java.lang.Object
  > android.text.format.Time

这个时间类是 Android 对 java.util.Calendar 和 java.util.GregorianCalendar 类的替代。这就是为什么您要在 Watch Faces API 中使用它。Time 对象(Time 类的一个实例)用于表示时间中的单个时刻,并使用来指定时间,就时间精度而言,这对于 Watch Face 应用的使用非常有用。需要注意的是,Time 类不是线程安全的,并且不考虑闰秒。然而,这些特征限制中没有一个给手表表面应用实现带来任何问题。

Time 类有许多日历(特定于日期和日期)问题,如果您将它用于以日历为中心的用途,建议您将 GregorianCalendar 类用于与日历相关的应用。

当使用 time 类执行时间计算时,算法目前使用 32 位整数数字表示。这限制了您从 1902 年到 2037 年的可靠时间范围表示,这意味着如果您将该类用于以日历为中心的表盘设计,您将需要在大约二十年后重写代码。时间对象的许多格式和解析都使用 ASCII 文本,因此,该类也不适合用于非 ASCII 时间处理,这不适用于 WatchFace 应用。

Time 类有三个重载的构造函数方法。其中一个允许您为时间对象指定时区,如下所示:

Time(String timezoneId)

您将使用的构造函数方法,允许您在当前或默认时区中构造一个时间对象,如下所示:

Time( )

第三个构造函数允许您通过将现有时间对象作为参数传递来制作现有时间对象的副本。例如,这应该用于“非破坏性编辑”目的,它看起来像这样:

Time(Time other)

Time 类包含 26 个方法,我不打算在这里详细介绍。因为你只会使用**。。使用 Watch Faces 应用进行方法调用,我将在这里详细介绍这些方法。如果您有兴趣了解 Time 类中的其他 24 种方法,您可以在 Android 开发者网站上找到这些信息:**

[`developer.android.com/reference/android/text/format/Time.html`](http://developer.android.com/reference/android/text/format/Time.html)

**public void clear(String time zoneid)**方法被添加到 Android 的 API Level 3 中,当被调用时,它重置(清除)所有当前时间值,并将时区设置为 time zone 值,该值是在方法调用参数区域内使用 String 对象及其数据值指定的。

那个。clear()方法调用还会将时间对象的 isDst (是夏令时)属性(或特性)设置为具有一个值,这表示夏令时是否有效(或无效)是“未知”的。

这个。clear()方法调用通常在。使用 setToNow()或其他 set()方法调用。这将确保在加载时间值之前清除时间对象并将其设置为当前时区。

公共虚空 。setToNow( ) 方法没有参数,当它被调用时,它将时间对象设置为当前时间,使用用户的 Android 操作系统和硬件设备中使用的设置。这个方法也被添加到 API Level 3 中,您将在 clear()方法调用之后使用它,使用下面的 Java 编程语句:

watchFaceTime.clear(intent.getStringExtra("time-zone"));
watchFaceTime.setToNow( );

接下来,让我们在 Watch Face 应用中创建一个时间对象。之后,我将使用 Android BroadcastReceiver 类研究如何在主机(智能手机或平板电脑)和智能手表之间广播时区数据值。然后,我将使用 Handler 类和您将在 updateTimeHandler 对象中创建的自定义消息传递,深入了解系统时间本身是如何发送到智能手表的。

添加手表时间对象:手表时间

添加一个 Time 对象作为引擎类的第一行代码。选择图 8-10 中助手对话框中看到的 android.text.format 时间版本。

9781430265504_Fig08-10.jpg

图 8-10 。在引擎类的顶部添加一个时间对象声明;选择 android.text.format 版本

将您声明使用的时间对象命名为 watchFaceTime 。一旦 Java 语句就绪,使用 Alt+Enter 快捷键和importTime 类。选择一个 android.text.format.Time 版本,如图图 8-11 所示。

9781430265504_Fig08-11.jpg

图 8-11 。命名一个时间对象 watchFaceTime,用 Alt+Enter 导入时间类;选择 android.text.format.Time

既然您已经声明了 watchFaceTime 时间对象,那么是时候使用 Java new 关键字来构造对象了。您将在 onCreate()方法中完成这项工作,因为这是创建供 watch face 应用使用的内容的最佳位置。

构建时间对象的 Java 编程语句很简单,如图 8-12 中突出显示的,它应该如下所示:

watchFaceTime = new Time( );

9781430265504_Fig08-12.jpg

图 8-12 。在 Engine 类中,使用 Java new 关键字构造 watchFaceTime 时间对象

既然保存您的时间值的 Time 对象已经就绪,让我们看看如何广播当前时区设置。这样,如果用户正在旅行,或者手动更改时区,它将立即反映在表盘时间显示中。

Android 的 BroadcastReceiver 类:广播时间消息

Android 的公共抽象 BroadcastReceiver 类扩展了 java.lang.Object,有四个已知的直接子类:WakefulBroadcastReceiver、AppWidgetProvider、DeviceAdminReceiver 和 RestrictionsReceiver。这个 BroadcastReceiver 类的 Java 类层次结构如下所示:

java.lang.Object
  > android.content.BroadcastReceiver

BroadcastReceiver 类提供意图广播(传递)方法。这个类提供了一个基础设施,允许你的手表在时区改变时接收 Android 操作系统发送的意图。

您将通过实现一个**private void****registerTimeZoneReceiver()**方法在您的 watch face 应用中动态注册该类的一个实例。每当观察面进入睡眠(不可见)然后醒来时,将调用此方法,设置 updateTimeZoneReceiver 标志并发送一个加载有 ACTION_TIMEZONE_CHANGED 常量的 Intent 对象。

然后,这个 TimeZoneChange 意图将确定用户的时区是否已经更改(自从他们的手表进入睡眠和醒来),然后通过使用。registerReceiver()方法。在本章的下一节中,您将创建这个时区接收器广播接收器。TimeZone receiver broadcast receiver 对象会将您的时间对象设置为 Time zone 对象。这将确保您的表盘使用的时区始终是最新的。

这个 BroadcastReceiver 类有一个基本的 BroadcastReceiver( ) 构造函数方法调用,它有 18 个方法。我不会在这里详细介绍所有这些,但是,我会介绍一下**。onReceive( )** 方法,您将在 Watch Face 应用中使用该方法。如果您想详细研究这个类,可以访问 Android 开发者网站,网址是:

[`developer.android.com/reference/android/content/BroadcastReceiver.html`](http://developer.android.com/reference/android/content/BroadcastReceiver.html)

这个公共抽象 void****on receive(Context Context,Intent intent) 方法带两个参数。第一个是名为 Context 的上下文对象,它包含运行 BroadcastReceiver 的(私有)引擎类的上下文对象。第二个是名为 intent 的 Intent 对象,这是您的 timeZoneReceiver 对象将接收的 Intent 对象。这个方法是源自 API 级别 1 的原始 Android 方法,因为 BroadcastReceiver 对象是 Android 的核心构件。

调用此的方法。onReceive()方法,当你希望你的 BroadcastReceiver 对象接收意图广播时,registerReceiver。**。register receiver(broadcast receiver,IntentFilter)方法也有。unregister receiver(Broadcast receiver)**的对应物,这两者都将在本章中实现,以创建一个时区广播系统。

同样重要的是要注意,你不能在实现中启动任何弹出对话框。onReceive()方法,以及。onReceive()方法应该只响应已知的 操作系统动作 ,比如您将要使用的 ACTION_TIMEZONE_CHANGED 。实现应该忽略任何“意外的”(操作系统未知的)或自定义的意图。onReceive()方法实现可能会收到。

现在让我们实现 time zone receiver broadcast receiver 及其 public void。onReceive( ) 方法放在私有引擎类的内部。

添加时区 BroadcastReceiver 对象:timeZoneReceiver

让我们在名为 watchFaceTime 的时间对象之后添加一个名为 timeZoneReceiver 的最终 BroadcastReceiver 对象,并使用 Java new 关键字,键入 BroadcastReceiver 的前几个字母以显示方法助手对话框。双击 broadcast receiver(Android . content)选项,将该构造方法插入到您的 Java 代码中,如图图 8-13 所示。

9781430265504_Fig08-13.jpg

图 8-13 。在 watchFaceTime 对象之后添加最后一个名为 timeZoneReceiver 的 BroadcastReceiver 对象

单击 Java 7 代码行左侧红色错误灯泡旁边的下拉箭头指示器。选择实现方法选项,如图 8-14 左侧所示,在选择方法实现对话框中,如图 8-14 右侧所示,选择 onReceive(context:Context,intent:Intent):void ,点击 OK 按钮。

9781430265504_Fig08-14.jpg

图 8-14 。添加 Java new 关键字和 BroadcastReceiver()构造函数方法;选择实施方法

单击 OK 后,IntelliJ IDEA 将为这个public void on receive(Context Context,Intent intent) 方法实现创建一个空的引导方法结构。现在,您需要做的就是清除 watchFaceTime 时间对象,并在名为 intent 的 Intent 对象中加载新的时区信息,该对象被传递到方法中。使用访问时区字符串。getStringExtra( ) 方法使用名为“time-zone”的额外数据字段访问该时区数据,从而被意外调用。

完成之后,您所要做的就是使用将您的 watchFaceTime 对象设置为当前系统时间。setToNow( ) 方法调用,您的 Time 对象现在被设置为正确的时区和时间信息。您的 Java 代码。onReceive()方法实现应该类似于下面的 Java 7 方法结构:

@Override
public void onReceive(Context context, Intent intent) {
    watchFaceTime.clear(intent.getStringExtra("time-zone"));
    watchFaceTime.setToNow( );
}

正如你在图 8-15 中看到的,代码是无错的,你可以添加一个布尔标志变量,它会告诉你什么时候你的表盘进入睡眠状态,当它再次醒来时需要检查你的操作系统时区设置!接下来我们就这么做,然后我会解释更多关于 Android Handler 类的内容。

9781430265504_Fig08-15.jpg

图 8-15 。在 onReceive()方法中添加。清除( )和。setToNow()方法调用 watchFaceTime 对象

关于这个 timeZoneReceiver,您需要做的最后一件事是创建一个布尔“flag”变量,它会告诉您何时需要更新这个 timeZoneReceiver。

让我们将标志命名为 updateTimeZoneReceiver 并将其设置为初始值 false ,如图图 8-16 所示,因为假设手表表面最初没有进入睡眠状态,并且。不需要调用 timeZoneReceiver 对象中的 onReceive()方法(如果将其设置为 true,则需要调用)。这个布尔标志需要以假开始。后来,在**。registerReceiver( )** ,这个布尔标志将被设置为 true,并且在中。unRegisterReceiver( ) ,这个布尔标志将被设置回 false 以取消注册 BroadcastReceiver。

boolean updateTimeZoneReceiver = false;

9781430265504_Fig08-16.jpg

图 8-16 。在 timeZoneReceiver 之后添加一个布尔值 updateTimeZoneReceiver 变量,该变量的值设置为 false

这个布尔 updateTimeZoneReceiver 最初被设置为假值,指示 BroadcastReceiver 没有被注册使用。在下一节中,您将创建**。registerTimeZoneReceiver( )** 方法,该方法将该值设置为 true ,表示正在使用 BroadcastReceiver。

然后,您的 TimeZone receiver broadcast receiver 对象将更新 time zone 对象值,然后更新您的**。unregisterTimeZoneReceiver()**方法,也将在本章中创建,将把这个布尔值设置回 false。

让我们开始创建 register 和 unregister、TimeZoneReceiver 方法,这样我就可以进入 Handler 类和更多与时间相关的代码了!

调用您的时区接收器对象 : registerTimeZoneReceiver()

在布尔型 updateTimeZoneReceiver 变量下,创建**private void registerTimeZoneReceiver()**方法结构,并在其中创建 if()条件语句。在该 if()语句的求值区域内,键入 updateTimeZoneReceiver 对象的前几个字母,然后通过双击它从弹出的帮助器对话框中选择它。该选项在图 8-17 的底部以蓝色突出显示。

9781430265504_Fig08-17.jpg

图 8-17 。创建一个私有的 void registerTimeZoneReceiver()方法,并通过键入 update 的前几个字母来添加一个 if 条件

完成该方法的初始条件 if()结构,如果您的 updateTimeZoneReceiver 布尔标志已经设置为 true 值,则提供该方法的出口。**if(updateTimeZoneReceiver)**构造等同于 true,而 if(!updateTimeZoneReceiver) 构造等同于 false,因为您将在。unregisterTimeZoneReceiver()方法。

因此,如果 updateTimeZomeReceiver 布尔标志已经设置为 true 值,下面的条件 if()语句结构将“脱离”此方法,也就是说,不做任何其他事情就退出此方法:

if( updateTimeZoneReceiver ) { return; }

所以这里的逻辑是,如果接收者已经注册了,就不需要这个方法,所以让我们通过使用 Java return 关键字来退出。

一旦您确定您的 updateTimeZoneReceiver 布尔标志被设置为 false 值,由于您在上一节中创建的变量和初始化语句,它将出现在应用的开始处,您将通过使用下面的 Java 语句将布尔标志设置为一个 true 值,这可以在图 8-18 中看到:

updateTimeZoneReceiver = true;

9781430265504_Fig08-18.jpg

图 8-18 。如果 updateTimeZoneReceiver 为真,则返回(退出方法);然后将 updateTimeZoneReceiver 设置为 true

你需要做的下一件事是创建你的 Intent 对象,它装载了ACTION _ time zone _ CHANGEDIntent,这将告诉 Android OS 你希望从用户的系统设置区域确定什么。

这可以通过使用 Android IntentFilter 类来完成,因此您将需要创建一个复杂的 IntentFilter Java 语句来声明该类的用法,将 IntentFilter 对象命名为 intentFilter ,使用 IntentFilter( ) 构造函数方法来构造该对象,最后通过使用 Intent 来加载它,并更改时区意图。ACTION_TIMEZONE_CHANGED 引用意图类(对象)常数值。

键入如图图 8-19 所示的 Java 语句,使用弹出的帮助器对话框查找正确的 Android 操作系统意图常量。当你键入单词 ACTION_ 时,这些填充助手对话框列表的意图常量就是我之前提到的已知操作系统动作,因为它们是使用意图类在 Android 操作系统中“硬编码”的。这就是常量的“引用路径”使用格式意图的原因。ACTION_TIMEZONE_CHANGED,因为常量在该类中。

图 8-19 底部显示的 Java 代码应该是这样的:

IntentFilter intentFilter = new IntentFilter( Intent.ACTION_TIMEZONE_CHANGED );

9781430265504_Fig08-19.jpg

图 8-19 。添加一个名为 IntentFilter 的 intentFilter,使用新关键字构造它,然后键入 Intent。动作 _T

最后,你需要使用**。registerReceiver( )** 方法调用来注册您的时区接收器 BroadcastReceiver 对象。这将一次性启用 timeZoneReceiver 对象,并将它发送到您刚刚在 IntentFilter 类中创建的 Intent 对象上。

这是通过使用下面的 Java 7 语句完成的,可以在图 8-20 中看到,它是使用 IntelliJ 弹出助手对话框创建的:

ProWatchFaceService.this.registerReceiver( timeZoneReceiver, intentFilter );

9781430265504_Fig08-20.jpg

图 8-20 。从 ProWatchFaceService 类的上下文(此关键字)中调用. registerReceiver()方法

这就叫**。registerReceiver** 方法,为您的 ProWatchFaceService 类关闭上下文对象,使用 Java this 关键字和点符号引用一个上下文对象,在编程语句的第一部分使用 ProWatchFaceService.this 。在您的方法调用参数区域中,您将传递一个time zone receiverbroadcast receiver 对象,以及您在前面的代码行中使用ACTION _ time zone _ CHANGEDIntent 对象创建和加载的 intentFilter 对象。

要创建它,键入 ProWatchFaceService.this 和一个周期,并选择**。注册 receiver(broadcast receiver receiver,IntentFilter filter)** 选项,将方法调用插入到你的编程语句中,如图图 8-20 底部所示。

现在,您所要做的就是将 timeZoneReceiver 对象指定为该方法的 BroadcastReceiver,并指定您创建的包含 ACTION_TIMEZONE_CHANGED Intent 对象的 intentFilter 对象。在你完成这些之后,你的 Java 代码将是没有错误的,如图 8-21 所示。registerTimeZoneReceiver()方法已准备就绪!

9781430265504_Fig08-21.jpg

图 8-21 。在。registerReceiver()参数区域,调用带有 intentFilter 意图的 timeZoneReceiver 对象

接下来,您需要创建**。unregisterTimeZoneReceiver()**方法,该方法将注销 BroadcastReceiver 对象并将 updateTimeZone 布尔标志变量设置回其原始的 false 值。

调用您的时区接收器对象:registerTimeZoneReceiver()

在你个人的空虚之下。registerTimeZoneReceiver()方法,创建一个空的**private void unregisterTimeZoneReceiver()**方法结构,用下面的 Java 关键字和代码,如图 8-22 底部所示:

private void unregisterTimeZoneReceiver( ) { your custom method code will go inside here }

9781430265504_Fig08-22.jpg

图 8-22 。在 registerTimeZoneReceiver 方法后添加一个私有的 void unregisterTimeZoneReceiver()方法

在这个方法内部,创建另一个条件 if()结构 。在 if()语句的求值区域内,为 updateTimeZoneReceiver 对象键入 false 值,如果(!idspninfopath _ NV),该值将是**。updateTimeZoneReceiver)** ,然后在大括号内的 return 语句为条件 if()语句的主体。如果您的布尔标志已经为假,这将退出该方法。否则,下一行代码将该标志设置为 false,这样当您注销接收方时,它将被设置回 false。代码结构应该如下所示,如图 8-23 中的所示:

private void unregisterTimeZoneReceiver( ) {
    if(!updateTimeZoneReceiver) { return; }
    updateTimeZoneReceiver = false;
}

9781430265504_Fig08-23.jpg

图 8-23 。添加一个条件 if(!updateTimeZoneReceiver)语句与 return 语句一起使用

接下来键入 ProWatchFaceService.this 上下文对象和一个周期,并选择**unregister receiver(broadcast receiver)**对话框选项,如图 8-24 中所示。

9781430265504_Fig08-24.jpg

图 8-24 。将方法调用添加到。unregisterReceiver()关闭你的 ProWatchFaceService.this 上下文对象

在里面。unregisterReceiver()方法调用,传递 time zone receiver broadcast receiver 对象。这将取消注册 BroadcastReceiver 对象,从而允许再次使用该 BroadcastReceiver 对象来轮询您的手表界面用户使用的当前时区,如图 8-25 所示。

9781430265504_Fig08-25.jpg

图 8-25 。将 time zone receiver broadcast receiver 对象作为。unregisterReceiver( )

您将在下一章实现这两个方法,那时我将介绍用于 Watch Faces API 的核心 WatchFaceService 类和方法。

摘要

在本章中,您学习了一些用于 Watch Faces 应用开发的 Java 工具类和 Android 类。它们包括 SurfaceHolder 类,你用来为你的手表表面创建表面,以及手表表面风格。Builder 类,您用它来为手表表面设计所需的 Android UI 元素。

接下来,我深入研究了 Android 时间相关的类,包括时间时间单位时区。我还解释了 BroadcastReceiver 类以及 Intent 对象,并且您学习了如何使用。 registerReceiver( ) 和**。unregisterReceiver( )** 控制 BroadcastReceiver 对象的方法。在下一章中,您将使用这个时区广播系统,在每次表盘进入睡眠状态时检查用户的时区。

在下一章中,你将了解核心的 WatchFaceService。发动机方法。

九、实现表盘引擎:核心表盘 API 方法

既然已经为 WatchFace 应用准备好了 Canvas SurfaceHolder、WatchFaceStyle、TimeZone、TimeUnit、Time 和 BroadcastReceiver 对象,就可以开始学习实现 WatchFaces API 的高级核心方法。

本章将深入探讨 CanvasWatchFacesService 和 CanvasWatchFacesService 的超类。ProWatchFaceService 类和 Engine 类所基于的引擎。了解一下 WatchFaceServiceWatchFaceService 很重要。Engine 类,因为它们包含正确实现 WatchFaces API 所需的方法、属性和常量。

WatchFaceService 类包含您将在 Watch Face 应用中使用的常量,以及 WatchFaceService。引擎嵌套类包含 14 个方法,其中一半以上将在私有引擎类内的 ProWatchFaceService 公共类中实现。

本章的大部分讨论将集中在这些方法上,这些方法需要在核心 Watch Faces API 引擎功能的私有引擎类中实现。首先,我将解释如何使用 Android 处理程序类在交互模式下每秒更新一次手表表面时间。然后我来解释安卓消息系统。在本章中,你将会学到大量的 Android 类和方法,所以让我们开始实现核心的 WatchFaces API 方法。

WatchFace 秒时间引擎:使用处理程序

在我进入核心的 WatchFaceService 之前。引擎方法,还有一些与秒针时间动画相关的对象和方法需要你去实现。

在 Android 中,每秒更新一次秒针的方法是使用一个 Handler 对象。出于这个原因,我将给你一个 Android Handler 类的概述,然后你将创建一个updateTimeHandlerHandler 对象和一个**。handleMessage( )** 实现表盘秒针时间更新编程逻辑的方法结构。

Android 的 Handler 类:处理时间更新消息

Android Handler 类是一个扩展 java.lang.Object 的公共类,这意味着它是临时编写的,用于实现 Android 操作系统的线程和消息处理。处理程序类的类层次结构如下所示:

java.lang.Object
  > android.os.Handler

这个处理程序类有四个已知的直接子类:HttpAuthHandler、AsyncQueryHandler、AsyncQueryHandler。WorkerHandler 和 SslErrorHandler。

Handler 类(object)将允许您发送和处理与应用线程及其消息队列相关联的消息可运行对象。您将使用一个消息对象来实现 updateTimeHandler 处理程序对象及其**。handleMessage( )** 方法。

每个处理程序对象实例将与一个单独的线程和该线程对象的 MessageQueue 对象相关联。当您创建新的 Handler 对象时,它被“绑定”到一个 Thread 对象和 MessageQueue 对象,这两个对象属于创建 Handler 对象的 Thread 对象。一旦构造了处理程序,该处理程序对象将为 MessageQueue 对象传递消息(和 runnables ),并在它们离开 MessageQueue 时对它们进行处理。

Handler 对象有两个主要用途:调度要处理的消息或要在未来某个时间执行的 runnables,以及将需要在应用主线程之外的线程上执行的操作排队。在本章的下一节,您将在 Watch Faces API 实现中使用前者。调度消息是使用. post(Runnable)方法完成的。postAtTime(Runnable,long)方法。postDelayed(Runnable,long)方法。senemptymessage(int)方法,。sendMessage(消息),。sendMessageAtTime(Message,long)方法或 sendMessageDelayed(Message,long)方法。

这里您将使用**。sendmentymessage()方法在中调用。updateTimer( )** 方法,您将在实现 Handler 对象后编写该方法。

这些方法的 post 版本将允许您在 MessageQueue 对象收到可运行对象后对其进行排队,而这些方法的 sendMessage 版本允许开发人员对包含数据绑定对象的消息进行排队,这些数据绑定对象可以由处理程序对象的 handleMessage(Message) 方法进行处理,您需要在处理程序对象中实现这些方法。你将在这一章的下一节做那件事。

当向一个处理程序对象提交或发送时,您可以允许在 MessageQueue 对象准备就绪时立即处理该项,或者您可以指定一个延迟(在它被处理之前)或它被处理的绝对时间。这里您将使用前者,但是后两者将允许您实现超时、滴答或其他基于时间的行为。

当为 WatchFaces 应用创建一个进程时,主线程专用于托管这个 MessageQueue 对象。该对象管理“顶级”应用对象(Activity、BroadcastReceiver、Service)以及它们可能需要创建的任何显示窗口。

您可以创建自己的线程,并使用处理程序与主应用线程进行通信。在本章的例子中,您将创建一个 updateTimeHandler 。这是通过调用与以前相同的 post 或 sendMessage 方法来完成的,但是是从一个新的线程中。然后,消息(或 Runnable)将被调度到您的处理程序对象的 MessageQueue 对象中,并在使用消息对象请求时进行处理,至少在本章示例的实现中是这样。

Handler 类有一个嵌套类,名为 Handler 的公共静态接口。android.os 包中的回调。这个嵌套类提供了处理程序回调接口,您将在实例化处理程序时使用该接口,以避免必须实现您自己的自定义处理程序子类。这个嵌套类有一个方法,。handle Message(Message Message),您将在 Java 代码中使用它来编写 updateTimeHandler 对象。

Handler 类有四个重载的公共构造函数方法,您将在本章的 Watch Faces API 实现中使用它们。您将使用的最简单的方法如下所示,包括 Java new 关键字:

final Handler updateTimeHandler = new Handler( ) { Message Handling code will go in here }

如果你构造了处理程序。回调对象,可以使用以下构造函数方法格式:

Handler(Handler.Callback callback)

您还可以使用以下处理程序构造函数方法格式提供您自己的自定义消息 Looper 对象:

Handler(Looper looper)

Watch Faces API 实现不需要自定义 Looper 对象,它允许您为自定义线程创建 MessageQueue,因此我不会在本书中深入讨论这一点。也就是说,为了完整起见,我将在这里详细介绍处理程序构造函数方法。

第四个也是最后一个构造函数方法提供消息 Looper 对象并接受一个处理程序。回调对象中的同一个构造函数方法调用,如下面的代码所示:

Handler(Looper looper, Handler.Callback callback)

Handler 类支持 30 种方法,我显然不能在这里详细介绍,所以我将在本章下一节的代码中介绍三种方法。如果您想详细研究所有 30 个,可以访问下面的开发人员网站 URL:

http://developer.android.com/reference/android/os/Handler.html

您将在 updateTimeHandler 对象中实现的处理消息的主要方法是,您猜对了,。handleMessage( ) 方法。这个**公共 void handle Message(Message msg)**方法将采用。handle Message(Message yourmessagename here)通用格式。该方法被添加到 API Level 1 中,因为它是 Android 操作系统的核心特性和功能。

您将使用另外两个 Handler 类方法从 updateTimeHandler 对象的“外部”与 Handler 对象对话,并在不再需要消息对象时将其从 MessageQueue 对象中删除。

**public final boolean senemptymessage(int what)**发送一个带有 what 数据值的空消息对象。这个方法也被添加到 API Level 1 中,如果一个消息对象被成功地放置到 MessageQueue 对象中,它将返回一个 true 值。正如您可能已经猜到的那样,如果失败,它将返回假值。

另一方面,**public final void remove messages(int what)**方法将删除任何和所有对消息对象的 what 属性使用整数代码的消息对象的未决调度。在 watch faces 应用的情况下,这将是零,它当前位于 MessageQueue 对象内部。接下来让我们看一下 Android Message 类,这样您将对将要使用的所有对象类型有一个总体的了解。

Android 的消息类:创建时间更新消息

Android Message 是一个 public final 类,它扩展了 java.lang.Object master 类,这意味着它是临时编写的,用于保存 Android 操作系统使用的消息对象。类的层次结构如下所示:

java.lang.Object
  > android.os.Message

Message 类允许您创建、定义和实例化 Message 对象,该对象将包含您的消息描述以及任何需要发送到 Handler 对象的任意数据。在本例中,这将是您的 updateTimeHandler。

消息对象包含两个额外的 int 字段和一个额外的对象字段,允许您在大多数使用中避免使用任何额外的内存分配。鉴于消息类的构造函数方法是公共的,正如 Java 编程语言所要求的,创建消息对象的最佳代码是调用 Message.obtain(),或者在这里的示例中,调用**handler . sendemptymessage()**方法。这将从“回收的”消息对象的“池”中提取消息对象。

消息对象有许多数据字段。它们提供了属性、特性或数据字段(选择一个最适合您使用的术语),您可以用它们来描述消息对象的内容,包括 arg1、arg2、obj、replyTo、sendingUid 和 what。这里您将使用 what 属性来发送消息对象中的更新时间消息代码

如果只需要存储几个整数值, public int arg1 属性是使用 Message.setData( ) 方法的“低开销”替代方法。一个 public int arg2 也用于提供两个整数数据值。

公共对象 obj 给你一个任意的对象作为消息对象的一部分,这样你就可以发送一个对象给消息对象的接收者。公共信使 replyTo 为你提供了一个(可选的)信使对象。Messenger 对象是另一种类型的对象,它允许实现基于消息的进程间通信。这允许在一个进程中创建指向(引用)处理程序的信使对象,并允许该处理程序跨另一个进程处理信使对象。

public int sendingUid 为您提供了一个可选字段,指示首先发送消息对象的 Uid(用户标识)。 public int what 用于提供开发者定义的消息代码。您将在代码中使用它来允许接收者(Handler 对象)能够识别消息是关于什么的,在本例中,观察面部计时。

Android Message 类有一个公共构造函数方法, Message( ) 构造函数;但是,获取消息对象的首选方式是调用 Message.obtain()或 Message.sendMessage()方法调用之一,这将为您构造消息对象。

这个 Message 类有将近 24 个公共方法,在这里的 watch faces 开发中,您不会用到其中的任何一个,但是如果您有兴趣了解关于这些方法的更多信息,可以在开发人员的网站上找到详细信息:

http://developer.android.com/reference/android/os/Message.html

创建秒针计时器:更新时间处理器

我们通过键入 Java 关键字 final 和类名(对象名) Handler 并在弹出的帮助器对话框中选择 Handler (android.os) 选项来创建最终的updateTimeHandlerHandler 对象,如图图 9-1 所示。注意还有一个 java.util.logging.Handler 类,这就是我使用这个工作流程的原因,所以一定要选择正确的处理程序。

9781430265504_Fig09-01.jpg

图 9-1 。使用弹出的帮助器对话框添加最终的处理程序对象声明,以找到 android.os 处理程序

一旦双击 android.os.Handler 类将其插入,您就已经将 Handler 对象声明为 final。这确保了它只被分配(到内存)一次,因此在应用运行期间,它被固定在系统内存的相同内存位置。

接下来,您将使用 Java new 关键字和 Handler()构造函数方法调用来完成名为 **updateTimeHandler、**的 Handler 对象。产生的空处理程序对象结构应该类似于下面的 Java 代码:

final Handler updateTimeHandler = new Handler( ) { // An Empty Handler Object Structure };

正如你在图 9-2 中看到的,updateTimeHandler 对象的代码是无错的,你已经准备好编写公共 void 了。handleMessage( ) 方法。此消息处理方法是在最终的 updateTimeHandler 处理程序对象的大括号内创建的,该对象实现了 updateTimeHandler 处理程序对象的消息对象处理功能。如前所述,这是实现所需的方法。一旦这样做了,处理程序对象就是“可行的”,其他方法可以从它调用。

9781430265504_Fig09-02.jpg

图 9-2 。使用 Java new 关键字和 Handler()构造函数方法创建一个空的 updateTimeHandler

在 updateTimeHandler 对象内部,您将添加一个**@ Override public void****handle Message(Message)**方法结构,在弹出窗口中选择 android.os.Message 选项,如图图 9-3 所示,使用下面的 Java 代码:

@Override
public void handleMethod(Message updateTimeMessage) { // Message object evaluation goes in here }

9781430265504_Fig09-03.jpg

图 9-3 。编写一个公共的 void handleMessage(Message)方法,使用弹出菜单选择 android.os 消息

一旦你创造了空的。handleMessage()方法结构,如图图 9-4 所示,您将需要创建一些 Java 代码来评估名为 updateTimeMessage 的消息对象。这个消息对象将被自动传递到。每当 handleMessage()方法引用处理程序对象时。sendEmptyMessage()方法调用。这种引用的一个例子可以在下面这行 Java 代码中看到:

updateTimeHandler.sendEmptyMessage(UPDATE_TIME_MESSAGE); // Send TimeZone Update Message to Handler

9781430265504_Fig09-04.jpg

图 9-4 。带有 handleMessage()处理结构的(空)updateTimeHandler 处理程序对象现在已经就绪

实际上,在本章的后面,您将会创建这一行代码。

这个过程的下一步是评估传递到。handleMessage()方法使用一个 Java 开关评估结构。这个结构将为每个消息对象的什么属性提供 case 语句。

消息对象使用 Java 点标记法可以引用什么属性或特性。对于 updateTimeMessage 消息对象,这将类似于 updateTimeMessage.what ,并且稍后将在 switch()语句的评估参数区域中引用,使用以下 Java 结构:

switch(updateTimeMessage.what) { your case statements evaluating what attributes will go in here }

正如您在图 9-5 中所看到的,一旦您在评估参数区域中键入 switch()语句和 updateTimeMessage 消息对象,并按下句点键,您将看到一个弹出的帮助器对话框,其中包含您在本章上一节中了解到的属性。双击 what 属性,将其添加到交换机中。

9781430265504_Fig09-05.jpg

图 9-5 。添加一个 Java switch 语句来计算 updateTimeMessage 对象的 what 属性

接下来您需要做的事情是在私有引擎类的顶部创建一个更新时间消息的 what 属性整数数据常量,这样您就可以在添加 case 评估语句之前准备好它。用于定义常数的 Java 关键字是 staticfinal ,因此您的 Java 编程语句声明了一个设置为初始数据值并命名为 UPDATE_TIME_MESSAGE整数常数,如下所示:

static final int UPDATE_TIME_MESSAGE = 0;

创建一个评估 what 属性的 Java switch 语句,从 updateTimeMessage 对象中调用,并在 UPDATE_TIME_MESSAGE 常量事例评估器中调用 invalidate()方法,然后中断 switch 语句。

switch(updateTimeMessage.what) {
    case UPDATE_TIME_MESSAGE:
        invalidate( );
        break;
}

正如你在图 9-6 中看到的,你的 Java 代码现在没有错误,你已经准备好实现更多高级功能,当智能手表进入环境模式时,这些功能就会发挥作用。

9781430265504_Fig09-06.jpg

图 9-6 。声明一个 UPDATE_TIME_MESSAGE 整数有价值,并为其添加 switch case 语句

现在是时候在 onDraw()方法体中使用 watchface Canvas 对象了。你会叫一个**。drawColor(int color)** 方法关闭此对象将画布的颜色设置为黑色。当然,这优化了表盘设计的功耗!你在第六章学得不错!

观看面孔时间计算:使用系统时间

您将制作的下一个 Java 代码构造将查看表盘模式(可见或开或关,以及环境与交互),然后您将确定如果表盘可见并处于交互模式,是否需要 updateTimeHandler 来间接更新表盘设计。如果需要 updateTimeHandler,将以毫秒为单位的系统时间将用于计算下一整秒(相当于 1,000 毫秒的精确值),并以。onDraw()方法,你将在第十章中编写这个方法。

Java 系统类:以毫秒为单位的访问时间

Javapublic final Systemclass 扩展了 java.lang.Object master class,并提供对系统相关信息和资源的访问,包括您将使用的系统时间函数,以及标准系统 IO(输入和输出)。系统类层次结构如下所示:

java.lang.Object
  > java.lang.System

该类中的所有方法都是以静态方式访问的,并且该类本身不能被实例化。所以不能有任何系统对象,但是会通过使用 System.methodCallName()方法引用系统函数。因此,要使用毫秒来引用系统时间,您的方法调用结构应该类似于下面的 Java 代码,您将在本章的下一节中使用它:

System.currentTimeMillis( )

接下来,让我们使用自定义的 isTimerEnabled( ) 方法编写代码,确定何时需要为您的表盘计算秒针或秒值。这样做之后,您可以实现这个方法来告诉您何时需要在 Handler 对象的 handleMessage( ) switch 语句的 Java 编程结构中计算秒针计时。

手表表面秒:计算秒针运动

您需要做的第一件事是创建一个方法,如果手表表面当前可见并且没有使用环境模式(这意味着它处于交互模式并且被用户查看),该方法将返回一个 true 值。在 updateTimeHandler 对象下添加一行代码,声明一个私有布尔 isTimerEnabled( ) 方法,如图图 9-7 所示。在方法体内,键入 Java return 关键字,然后单词就是,然后双击 isVisible( ) method 选项,当它出现在 IntelliJ 弹出的方法选择器帮助器对话框中时,将其插入到您的代码中。

9781430265504_Fig09-07.jpg

图 9-7 。通过键入 is,然后选择 isVisible(),创建带有 return 语句的私有布尔 isTimerEnabled()方法

接下来,输入 Java 逻辑 AND 运算符(&&)和 Java 逻辑 NOT(!)运算符,然后键入单词 is。这将弹出一个 IntelliJ 弹出方法帮助器对话框,您可以在其中选择一个**isinembientmode()**方法 选项,如图 9-8 中蓝色部分所示。接下来,双击该方法将其插入到代码中,并添加分号以完成 return 语句。你的代码应该没有错误,如图 9-9 所示。

9781430265504_Fig09-08.jpg

图 9-8 。键入 Java 的&和&逻辑运算符 AND,and the not!符号,并且是,则选择 isInAmbientMode()

现在,您可以在条件 if()结构中调用这个 isTimerEnabled()方法,以确定智能手表是否在交互模式下运行。如果此方法返回一个 true 值,您可以将系统时间计算到最接近的整秒(精确到 1,000 毫秒)值,当秒正好出现在系统时钟中时,移动秒针。

在 UPDATE_TIME_MESSAGE 的 case 语句的 switch 构造中,在 invalidate()方法调用之后和 Java break 关键字之前添加一行代码。您需要首先将空的条件 if()语句放置到位,然后创建完整的第二个计时逻辑。

评估是否需要计算第二个计时的空条件 if()语句应类似于以下 Java 代码:

If( isTimerEnabled( ) ) { Your Whole Second Timer Programming Logic Will Go In Here }

正如你在图 9-9 中看到的,你的代码是没有错误的,你已经准备好添加条件 if()语句来计算系统时间到最接近的整秒值,这将触发秒针在用户的表盘设计上再滴答一次。

9781430265504_Fig09-09.jpg

图 9-9 。在 invalidate()之后和 break 关键字之前添加 if(isTimerEnabled( ))求值构造

对于这个最接近的整秒时间计算,您需要做的第一件事是使用 System.currentTimeMillis()方法调用获得系统时间,并将其赋给一个 long 变量,您将把它命名为 msTime

在条件 if()语句体中,声明一个 long msTime 变量,然后键入等于 (=)运算符和系统类名。然后按句号键,弹出 IntelliJ 弹出方法帮助器对话框,如图图 9-10 所示。双击**current time millis()**method 选项,并将其插入到 Java 代码中,以完成声明、命名和设置该 msTime 变量的值的语句。

9781430265504_Fig09-10.jpg

图 9-10 。通过键入 equals、System 和 period 声明一个名为 msTime 的长变量。然后选择 currentTimeMillis()

接下来,您需要计算一个延迟值,它代表当前系统时间和下一个 1000 毫秒值之间的毫秒数。这将代表下一个偶数(精确)秒,也就是你希望表盘设计中的秒针前进到表盘上的下一个刻度线的精确时间值。

如图 9-11 中的所示,您需要添加另一个名为 msDelaylong 变量,它将保存计算出的延迟值。这将表示直到下一个偶数毫秒发生之前的毫秒数。

9781430265504_Fig09-11.jpg

图 9-11 。声明一个名为 msDelay 的长整型变量,然后键入= WATCH,select WATCH_FACE_UPDATE_RATE

键入 Java long 变量类型和 msDelay 变量名称,以及 equals 运算符和 WATCH_FACE_UPDATE_RATE 的前几个字母,这是在图 9-11 顶部看到的变量,它保存了每秒 1000 毫秒的时间分辨率值。

找到 WATCH_FACE_UPDATE_RATE 选项,IntelliJ 很可能将它放在列表的顶部,双击它将其插入到 Java 代码中。现在你所要做的就是找到当前时间和下一个完整秒之间的“delta”或毫秒差,你就完成了。

找到 delta 或时间延迟值的关键在于 Java 操作符。为了计算在调用下一个完整的秒针前进之前等待的 msDelay 时间,取 1,000 毫秒的 WATCH_FACE_UPDATE_RATE,并减去当前毫秒时间值和 1,000 毫秒时间分辨率的模(计算剩余或增量时间值),这将给出下一个完整秒之前的毫秒数。这可以使用下面的 Java 代码来完成,如图图 9-12 所示:

long msDelay = WATCH_FACE_UPDATE_RATE - ( msTime % WATCH_FACE_UPDATE_RATE );

9781430265504_Fig09-12.jpg

图 9-12 。使用 ms time modulo % WATCH _ FACE _ UPDATE _ RATE 计算下一整秒的偏移量

如您所见,代码现在已经完成并且没有错误,您已经准备好开始实现 WatchFaceService 类和 WatchFaceService 的核心方法了。发动机级。我将概述这个类及其嵌套的 Engine 类,然后您将实现其中的一些方法。

WatchFaces API:要实现的核心方法

这一节将介绍 WatchFaceService 和 WatchFaceService 中的核心 WatchFaces API 方法。引擎类。

Android WatchFaceService 类:核心常量

一个公共抽象 WatchFaceService 类扩展了一个 WallpaperService 类,后者扩展了一个 Service 类,后者扩展了一个 ContextWrapper 类,后者扩展了一个 Context 类,后者扩展了一个 java.lang.Object 类。两个已知的直接子类包括您将使用的 CanvasWatchFaceService 和 Gles2WatchFaceService。此类的层次结构如下所示:

java.lang.Object
  > android.content.Context
    > android.content.ContextWrapper
      > android.app.Service
        > android.service.wallpaper.WallpaperService
          > android.support.wearable.watchface.WatchFaceService

WatchFaceService 和 WatchFaceService。我接下来将介绍的 Engine 是 WallpaperService 和 WallpaperService 的子类。分别是发动机。如果你想为可穿戴设备创建手表脸,你可以使用它来代替更普通的壁纸服务,你可以将它用于智能手机、平板电脑、电子阅读器和 iTV 电视机的壁纸应用。

WatchFaceService 对象,就像一个 WallpaperService,必须实现一个重要的方法 onCreateEngine(),你在第七章中实现了这个方法。正如你在第七章中所做的,这里你还必须创建(内部类)私有类引擎,扩展任一 WatchFaceService。Engine 或其已知的直接子类之一。

这个类还提供了“唤醒锁”,它将被保持,这样设备不会进入睡眠状态,直到一个表盘完成绘制。这是为了在智能手表处于环境模式时更新表盘。

这个类有一个名为 WatchFaceService 的嵌套类。引擎,这是 Watch Faces API 的实际实现。我将在接下来讨论这个问题。

这个类有五个常量,用于控制 watchface 设计中断(显示哪些通知)和硬件功能,如屏幕老化保护和低位(省电)环境模式。其中两个常量是字符串值,三个是整数值:

  • INTERRUPTION_FILTER_ALL 整数常量通过调用。getInterruptionFilter()方法。它也可以作为参数在。onInterruptionFilterChanged(int)方法,您将实现该方法。
  • 中断 _ 过滤 _ 无整数常量通过调用。getInterruptionFilter()方法。它也可以作为参数在。onInterruptionFilterChanged(int)方法,您将实现该方法。
  • 通过调用. getInterruptionFilter()方法返回整数常量**。它也可以作为参数在。onInterruptionFilterChanged(int)方法,您将实现该方法。**
  • PROPERTY _ BURN _ IN _ PROTECTION字符串常量会将一个 Bundle 对象传递给。onPropertiesChanged(Bundle)表示智能手表硬件是否支持老化保护。
  • PROPERTY_LOW_BIT_AMBIENT 字符串常量会将一个 Bundle 对象传递给。onPropertiesChanged(Bundle)指示是否有硬件设备支持低位环境模式的实现。

接下来,让我们在您的 Watch Face 应用中实现可以保存这些常量的变量。之后,我将介绍 WatchFaceService。引擎嵌套类,并且可以实现基于 Watch Faces API 的应用所需的核心方法。之后就可以重点在表盘表面绘制你的表盘设计了,这个我在第十章中有涉及,以及如何绘制表盘元素 ShapeDrawable 对象。

添加 WatchFaceService 常量:老化和低位

因为你想支持老化保护和低位环境模式,而且由于 WatchFaces API 目前没有这方面的方法(比如。isInAmbientMode()方法),您已经在自定义的 isTimerEnabled()方法中使用了它,让我们添加布尔变量来保存这些标志。

您可以通过使用一个复合声明语句来声明这两个布尔标志变量,因为您没有特别设置任何默认值。需要注意的是,如果不声明任何布尔值,Java 对任何布尔变量的默认初始化值都将是 false

让我们为这些布尔标志提供逻辑名称,以表示它们做什么:让我们将它们命名为 lowBitAmbientModeFlagburinprotectmodeflag。您的 Java 复合语句应该如下所示,没有错误,并在图 9-13 的下半部分高亮显示:

boolean lowBitAmbientModeFlag, burnInProtectModeFlag;

9781430265504_Fig09-13.jpg

图 9-13 。在引擎类中为 lowBitAmbientModeFlag 和 burnInProtectModeFlag 添加布尔变量

是时候从 WatchFaceService 实现几个最重要的 Watch Faces API 方法了。引擎嵌套类。您将添加**。onDestroy( )** ,它在 CanvasWatchFaceService 中。引擎类,因为它需要“破坏”(从内存中删除)您的画布和任何相关的。onDraw()组件也在系统内存中。

来自 WatchFaceService。引擎类,我们来实现四个方法:。onTimeTick( )。onVisibilityChanged( )。onAmbientModeChanged( ) 和**。getInterruptionFilter( )** 。记住,你已经实现了**。onCreate( )** ,尽管稍后在学习图形和。你的表盘设计的 onDraw()部分,我将在第十章中详细介绍。

Android WatchFaceService。引擎类:核心方法

Android 的 WatchFaceService 服务。Engine 类是一个公共抽象类,它扩展了 WallpaperService。引擎类,这意味着谷歌 Android 开发者主要通过使用 Wallpaper API 创建了一个 Watch Faces API。

这个类表示 Watch Faces API 的核心 Java 代码实现。您需要实现。onCreateEngine()方法,正如你在第七章中所做的那样(参见图 7-9 ),来返回表脸引擎实现。该类的 Java 类层次结构如下所示:

java.lang.Object
  > android.service.wallpaper.WallpaperService.Engine
    > android.support.wearable.watchface.WatchFaceService.Engine

Android WatchFaceService。Engine 类有两个已知的直接子类:一个 CanvasWatchFaceService。引擎,您当前正在为 ProWatchFaceService 类使用它,以及 Gles2WatchFaceService。引擎,用于实现OpenGL ES 2.0WatchFaceService。发动机功能。

该类有一个公共构造函数方法, WatchFaceService。Engine( ) 和 14 个公共方法,我将在这里与来自 CanvasWatchFaceService 的一个方法一起讨论。Engine (onDestroy()),这样您就知道所有核心的 Watch Faces API 方法能够提供什么了。

你已经创建了虚空 。onCreate(surface holder)方法,用于在手表上创建变量和内存中的对象面服务引擎创建。

虚空 。onDestroy( ) 方法 实际上是在 CanvasWatchFaceService 的文档中描述的。Engine 类,在 Android 开发者网站上,但我将把它包含在这里,因为它将在本章中实现。此方法将删除 WatchFaceService。引擎,它还可以从 MessageQueue 对象中移除消息对象。请确保在调用 super.onDestroy( ) 语句之前完成此操作。三种无效方法的用途是:

  • 虚空 。当表盘处于环境模式时,每分钟调用一次 onTimeTick( ) 方法 来更新表盘的时间(时针和分针)。
  • 虚空 **。确定硬件设备的属性时,调用 onPropertiesChanged(Bundle properties)**方法 。此方法将用于设置您在上一节中创建的低位和老化标志。
  • 虚空 **。onVisibilityChanged(boolean visible)**方法 被调用来通知你表盘变得可见或隐藏。您将使用它来发送一个消息对象,以确保 TimeZone 对象与 Time 对象同步。该逻辑所做的是将 Time 对象设置为 TimeZone 对象值。这确保了时区总是设置正确。

此类中的两个方法与设置影响中的逻辑的属性有关。onDraw()方法。因为我在第十章中介绍了绘制逻辑,所以你将在那一章中实现接下来的两个方法:

  • 一个虚空 **。onAmbientModeChanged(boolean inAmbientMode)**方法 将在用户的硬件设备进入或退出其环境模式时被调用。
  • 虚空 **。当用户改变中断过滤器设置(常量)时,将调用 onInterruptionFilterChanged(int interruption filter)**方法 。

您已经在该类中实现了其中的一些方法,例如。isInAmbientMode(),还有一个方法可以用来修改已经创建好的手表表面样式对象。为了彻底起见,我接下来将介绍这两种方法:

  • 最后一个布尔 **。isinembientmode()**方法会返回你的布尔值,这个布尔值会告诉应用手表的表盘硬件是否处于环境模式。
  • 虚空 **。setWatchFaceStyle(watchface style)watchface style)**方法 将设置 watchface style 属性,以便您可以动态更改 UI 设置。

这个类还有三个 getter 方法,允许开发人员确定中断过滤器、Peek 卡定位和未读 Peek 卡计数的当前状态(设置):

  • 最终 int 。getInterruptionFilter( ) 方法 返回用户选择的当前生效的中断过滤器常量。
  • 期末考试 。getPeekCardPosition( ) 方法 使用 Rect 对象返回第一张正在看的卡片的屏幕位置 X 和 Y 坐标。
  • 一个最终 int 。getUnreadCount( ) 方法

还有另外三个。on()方法 ,这些方法不会被实现,但是为了完整起见,我将在这里提到它们:

  • 捆绑**。onCommand(String action,int x,int y,int z,Bundle extras,boolean resultRequested)** 实现 Android 的动态壁纸功能,用于拦截来自表盘设计表面的触摸事件。
  • **虚空。onPeekCardPositionUpdate(Rect Rect)**每当第一张 Peek 卡在表盘屏幕上定位时,调用方法,使用 Rect 对象给出其位置。
  • **虚空。onUnreadCountChanged(int count)**方法在“待读队列”中未读通知卡的数量发生变化时调用。

我将在这一章的剩余部分讨论尚未添加的核心 watch face 方法,这样您就可以在。onDraw()方法。

正在添加 WatchFaceService。引擎方法:核心函数

在本章的其余部分,我将讨论与时间和内存优化有关的其他核心表面方法。它们允许您检测表盘是否可见,以及用户当前使用的智能手表设备硬件配置可用的省电和屏幕老化保护模式。

一旦所有这些核心的表盘创建、样式、定时和广播接收器对象和方法都在您的代码中就位,您就可以专注于如何绘制您的表盘设计,这将在第十章中介绍。

在环境模式下显示时间。onTimeTick()方法

实现最简单的核心方法是void??。onTimeTick( ) 方法,所以让我们从它开始,并将其添加到私有引擎类的顶部。

添加 Java @Override 关键字,表示将要覆盖超类方法,并键入 public void onTimeTick( ) 。在方法体内部,使用一个 Java 超级关键字来传递**。对 CanvasWatchFaceService 的 onTimeTick( )** 方法调用。引擎超类。如图 9-14 所示,基本方法体的代码应该如下所示:

@Override
public void onTimeTick( ) {
    super.onTimeTick( );
}

9781430265504_Fig09-14.jpg

图 9-14 。添加公共 void onTimeTick()方法;在其中,使用 Java super 关键字调用父方法

使用弹出的帮助器对话框,如图 9-14 所示,选择方法调用。

您还需要在这个方法中使用。invalidate()方法调用,让 Android 知道更新表盘上的时间。确保将其放在 super.onTimeTick()超类方法调用之后。这就是超类需要为。onTimeTick()方法是在调用此方法之前处理的。invalidate()方法调用。那个。onTimeTick()方法结构应该类似于下面的代码,可以在图 9-15 和图 9-16 中看到:

@Override
public void onTimeTick( ) {
    super.onTimeTick( );
    invalidate( );
}

9781430265504_Fig09-15.jpg

图 9-15 。添加一个。调用 super.onTimeTick()后,invalidate()方法调用来更新观察面

如果你想让 IntelliJ 为你写代码,你可以简单地输入“I”并从弹出的帮助器方法对话框中选择 invalidate()方法或者你可以双击你想让 IntelliJ 为你写的方法,如图图 9-15 所示。

接下来让我们编写另一个相对简单的方法。onDestroy()方法。

从内存中移除表盘。onDestroy()方法

在。onTimeTick()方法,再次表明您将要覆盖超类方法,并键入 public void onDestroy( ) 。在方法体内部,使用一个 Java 超级关键字来传递这个**。onDestroy( )** 到 CanvasWatchFaceService。引擎超类。如果您还没有在 watchface 应用中实现 updateTimeZoneReceiver 函数,那么这就是您需要做的全部工作。

基本的代码。因此,onDestroy()方法体将类似于下面的 Java 方法结构:

@Override
public void onDestroy( ) {
    super.onDestroy( );
}

因为您使用的是 BroadcastReceiver 对象,所以需要使用从 MessageQueue 对象中移除 Message 对象。removeMessages( ) 方法调用。这需要在调用 super.onDestroy()方法之前完成,否则这将永远不会发生,因为 WatchFaceService。引擎对象将不再存在,消息对象仍将在队列中。

所以添加**@ Override public void on destroy()**空方法结构,然后添加对 updateTimeHandler 对象的引用。接下来,按句点键,启动弹出的帮助器对话框,选择 removeMessages(int what) 选项,如图图 9-16 所示。

9781430265504_Fig09-16.jpg

图 9-16 。添加公共 void onDestroy()方法;在其中,使用 updatetimehandler . remove messages()方法

在得到的UPDATE TIME handler . remove messages()方法调用的参数区内,键入 UPDATE_TIME_MESSAGE 消息对象常量,或者键入“U”,从弹出的帮助器对话框列表中选择。然后可以添加super . on destroy(); Java 代码语句,你就完事了!

那个。onDestroy()方法结构,在图 9-17 中显示为无错误,在您完成后应该看起来像下面的 Java 代码结构:

@Override
public void onDestroy( ) {
    updateTimeHandler.removeMessages(UPDATE_TIME_MESSAGE);
    super.onDestroy( );
}

9781430265504_Fig09-17.jpg

图 9-17 。使用 removeMessages()从 MessageQueue 中删除消息对象后,调用 super.onDestroy()

接下来,您将实现**。onPropertiesChanged( )** 方法,它只有 6 行代码,比前两个方法稍微复杂一些。

确定低位和老化模式:。onPropertiesChanged()

你需要做的下一件事是实现一个**。onPropertiesChanged( )** 方法 ,使用 Java @Override 关键字。这可以在图 9-18 的底部看到,它产生了下面的空 Java 方法结构:

@Override
public void onPropertiesChanged(Bundle properties){ // The method implementation will go in here }

9781430265504_Fig09-18.jpg

图 9-18 。创建一个空的@ Override public void on properties changed(Bundle properties)方法结构

该方法使用名为属性的 Android Bundle 对象,将一组智能手表属性传递给该方法进行处理。如你所见,在图 9-18 的底部,Bundle 类很可能需要被导入,所以在空方法中点击鼠标,使用 Alt+Enter 工作进程,让 IntelliJ 为你编写import Android . OS . Bundleimport 语句。现在,您已经准备好编写方法体了。

在里面。在 PropertiesChanged()方法上,键入 Java super 关键字和一个句点,然后在弹出的帮助器对话框中选择**onproperties changed(Bundle properties)**选项,双击将其插入到 Java 语句中,如图 9-19 底部所示。

9781430265504_Fig09-19.jpg

图 9-19 。键入 Java super 关键字,并选择 onPropertiesChanged(Bundle properties)选项

这将把名为 properties 的 Bundle 对象传递给超类,并且您将准备好在您在私有(内部)引擎类的顶部声明的低位和老化布尔变量中设置包含在 Bundle 对象中的属性。

super.onPropertiesChanged(properties); Java 编程语句下,键入 lowBitAmbientModeFlag 布尔变量名、等号,然后是 properties Bundle 对象,您将使用它来调用方法调用。在 properties 之后,按下句点键以弹出方法帮助器对话框。

当弹出的方法帮助器对话框出现时,选择 getBoolean(String key,boolean defaultValue) 选项,或者双击它,这将插入。getBoolean()方法调用结构到你正在编码的 Java 语句中,如图图 9-20 所示。一旦使用完 IntelliJ 弹出助手对话框,Java 语句将如下所示:

lowBitAmbientModeFlag = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT);

9781430265504_Fig09-20.jpg

图 9-20 。键入 lowBitAmbientModeFlag 变量、等号、单词 properties 和句点

在里面。getBoolean()方法参数区,键入 a " P ,选择 PROPERTY_LOW_BIT_AMBIENT 常量,如图图 9-21 所示,我在本章前面已经讨论过。双击此选项可将 lowBitAmbientModeFlag 变量设置为此 LOW_BIT 常量的值。

9781430265504_Fig09-21.jpg

图 9-21 。在。参数区获取 Boolean()方法,并选择 PROPERTY_LOW_BIT_AMBIENT

现在,您已经从 properties Bundle 对象中提取了低位环境模式标志设置,并将其设置为 lowBitAmbientModeFlag 变量,您需要对 burnInProtectModeFlag 变量执行完全相同的操作。

在 lowBitAmbientModeFlag Java 编程语句下面,键入burinprotectmodeflag布尔变量名、等号,然后是 properties Bundle 对象,您将使用它来调用方法调用。在 properties 之后,按句点键启动弹出的方法助手对话框。

当弹出的方法帮助器对话框出现时,选择 getBoolean(String key,boolean defaultValue) 选项,或者双击它,这将插入。getBoolean()方法调用结构到你正在编码的 Java 语句中,如图图 9-20 所示。一旦您使用完 IntelliJ 弹出助手对话框,您的 Java 语句将如下所示:

burnInProtectModeFlag = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION);

在里面。getBoolean()方法参数区,键入 a " P ,选择PROPERTY _ BURN _ IN _ PROTECTION常量,如图图 9-22 所示。双击常量,这会将 burnInProtectModeFlag 变量设置为这个 BURN_IN 常量的值,这样你的应用就知道用户的智能手表是否支持(实际上需要)老化保护。

9781430265504_Fig09-22.jpg

图 9-22 。键入一个 burnInProtectModeFlag,将其设置为等于 properties . get boolean(BURN _ IN _ PROTECTION)

现在你已经知道用户的智能手表硬件支持什么样的特殊屏幕(显示)模式,接下来你需要实现的最复杂(也是最重要的)方法就是**。onVisibilityChanged( )** 方法。这个方法需要 10 行代码来实现,所以,现在就开始吧!

确定表盘是否可见:。onVisibilityChanged( )

除了。onDestroy(),您将首先使用 Java super 关键字和方法名调用一个超级方法调用,然后实现任何定制的编程逻辑,就像您对前面的方法结构所做的那样。

让我们通过添加 Java @Override 关键字和**public void onvisibility changed(boolean visible)**空方法结构来实现这一点。在里面输入 Java super 关键字和句点字符,然后选择 onVisibilityChanged(布尔可见)选项,如图图 9-23 所示。基本方法结构应该类似于下面的 Java 代码:

@Override
public void onVisibilityChanged(boolean visible) {
    super.onVisibilityChanged(visible);
}

9781430265504_Fig09-23.jpg

图 9-23 。创建 onVisibilityChanged()方法,键入 Java super 关键字,并调用 onVisibilityChanged()

这个方法的其余部分将由 if-else 条件结构组成,它将告诉手表表面应用在手表表面可见或不可见的情况下做什么。空的 if-else 条件语句如下所示,在图 9-24 中显示为无错误:

public void onVisibilityChanged(boolean visible) {
    super.onVisibilityChanged(visible);
    if(visible) { visible=true statements here } else { visible=false statement here }
}

9781430265504_Fig09-24.jpg

图 9-24 。在 super.onVisibilityChanged()方法后添加一个空的 if(visible) else 条件结构

如果手表表面活跃起来(visible = true ),要做的第一件事是调用**registerTimeZoneReceiver()**方法来检查自手表进入睡眠状态以来时区是否发生了变化。在 if-else 条件构造的一个 if(visible) 部分,键入“r”字符并从帮助器对话框中选择一个 registerTimeZoneReceiver()选项,如图图 9-25 所示,并将此方法调用插入到条件 if-else 语句中。

9781430265504_Fig09-25.jpg

图 9-25 。在 if(visible)结构中,键入“r”并选择 registerTimeZoneReceiver()方法调用

接下来您要做的是通过调用来确保时区在 time 对象中设置正确。clear( ) 方法关闭了 watchFaceTime 对象。在参数区内,键入“Ti”,然后从弹出的帮助器对话框中选择一个时区(java.util) 类,如图图 9-26 所示。

9781430265504_Fig09-26.jpg

图 9-26 。调用 watchFaceTime 时间对象的. clear()方法 并选择 TimeZone (java.util)选项

时区后,按下周期键,选择 getDefault( ) 方法 ,如图图 9-27 所示。

9781430265504_Fig09-27.jpg

图 9-27 。在 TimeZone 对象后键入一个句点,并从弹出帮助器中选择 getDefault( ) TimeZone 方法

双击后。getDefault( ) TimeZone 选项在弹出的帮助器对话框中,键入一个句点,这将启动另一个弹出的帮助器对话框,在这里可以选择一个 getID( ) 方法。双击它,这将给出最终的 Java 语句,将时区重新加载到 Time 对象中。最终的 Java 语句,如图 9-28 所示,应该如下所示:

watchFaceTime.clear( TimeZone.getDefault( ).getID( ) );

既然已经将当前时区对象加载到时间对象中,接下来需要做的就是使用**。setToNow( )** 方法调用将时间对象设置为当前时间,使用新加载的时区作为时间值应该设置的指导。在图 9-28 中显示没有错误的 Java 代码应该如下所示:

watchFaceTime.setToNow( );

9781430265504_Fig09-28.jpg

图 9-28 。键入 unregisterTimeZoneReceiver()方法的前几个字母,并在弹出窗口中双击它

现在,您只需构建 if-else 条件语句的 else部分。这就是当用户的智能手表处于睡眠状态(表盘不可见)时,您希望表盘应用执行的操作。

就像当手表表面可见(true)时调用. registerTimeZoneReceiver()方法一样,您将类似地调用一个**。unregisterTimeZoneReceiver()**方法当表盘不可见(false)时,如图图 9-28 所示。

在 else结构中键入“u ”,当出现方法帮助器弹出窗口时,选择您在第八章中创建的 unregisterTimeZoneReceiver()方法。双击它,让 IntelliJ 为您编写 Java 语句。你的无错方法现在完成了,如图图 9-29 所示。

9781430265504_Fig09-29.jpg

图 9-29 。对 if(visibility)和 else部分进行编码且无错误的 onVisibilityChanged()方法

恭喜你!现在,您已经实现了 WatchFaces API 应用的所有非图形部分!你现在已经准备好进入**。onDraw( )** 法!

摘要

在本章中,您学习了如何为您的 WatchFace 应用创建核心 Watch Face 服务引擎类方法。还有一些与。onDraw()相关的代码,我将留到下一章,那时你将开始学习矢量图形界面设计。

首先,我讨论了如何使用 Handler 对象和 Message 对象在手表表面实现秒针。您创建了一个 updateTimeHandler,它允许您每秒更新手表上的秒针。

接下来,我讨论了核心的 WatchFaces API 类:WatchFaceService 和 WatchFaceService.Engine。

在下一章中,您将开始向表盘应用添加矢量图形 2D 设计元素,并且您将学习如何仅使用代码创建表盘,因为矢量图形仅使用 SVG 或 Shape 类创建,即仅使用数学或代码。在第十一章中,你将学习如何使用位图图形来设计你的表盘。

十、表盘矢量设计:为表盘使用矢量图形

现在,您已经准备好了大部分 WatchFaces API 基础设施,这些基础设施与您将使用矢量图形在其上“绘画”的画布对象没有直接关系,您已经准备好开始实现**。onDraw( )** 方法,学习矢量图形以及实现它们的 Android 类。

本章将深入探讨与画布对象一起使用的 Android 类,主要是 Android Paint 类和嵌套类,你可能已经猜到了,因为人们是在画布上绘画的!

您还将了解 Android Canvas 类和**。** drawLine( ) 它包含的方法,你将使用它来绘制你的表盘设计的所有组件。

矢量图形绘制方法,如。drawLine()方法,使用 Paint 类(和 object)将 vector (Shape)对象绘制到画布上。到目前为止,在您的 watch face 实现中,这是保存画布表面对象的 SurfaceHolder 对象。

我将用本章的大部分时间讨论 onDraw()方法以及需要实现的方法,以便支持您在第六章中了解到的所有不同模式,如低位环境模式或老化保护模式。一旦这些都准备好了,你就可以完成最后几个 WatchFaceService 了。引擎方法实现。

WatchFace 绘画引擎:使用绘画对象

在我开始编写在画布(屏幕)上绘制基于矢量图形的手表表面设计所需的对象和方法的代码之前,让我们深入了解一下 Android Paint 类。

你对手表表面设计组件进行描边、颜色、样式和反锯齿的方式都是使用这个 Android Paint 类及其六个嵌套的“助手”中的常量和方法调用来完成的。

出于这个原因,让我们仔细看看 Paint 类,然后,您将创建用向量绘制手表表面的 Java 代码。

Android 的 Paint 类:在画布上绘制矢量形状

publicAndroidPaint类扩展了 java.lang.Object master 类,并被临时编码以提供 Android 操作系统的所有数字绘画功能。Paint 类的 Java 类层次结构代码如下所示:

java.lang.Object
  > android.graphics.Paint

Paint 类有一个已知的直接子类——text Paint 类。一个 Paint 类创建一个 Paint 对象,它包含关于样式颜色的信息。这些信息指导 Android 图形引擎绘制矢量形状对象,也称为“几何图形”,以及文本对象(和字体对象),它甚至指导如何渲染任何 BitmapDrawable 对象,我将在第十一章中讨论。

Paint 类有六个嵌套类。在本章中,我将只详细介绍其中的两个,因为您将在示例的 Watch Faces 设计中以及在 Watch Faces 应用的 Java 代码中使用它们。

  • 枚举油漆。Align 嵌套类允许开发者指定如何。drawText( ) 方法应该将一个文本对象相对于 X,Y 坐标对齐。
  • 枚举油漆。Cap 嵌套类允许开发者指定描边线条和路径的开始和结束的处理方式(方端或圆端)。这将允许您定义手表的秒针、分针和时针以及刻度线的外观和感觉。
  • 级油漆。FontMetrics 嵌套类描述在文本对象的任何给定浮点大小属性下字体对象实现的度量
  • 级油漆。FontMetricsInt 嵌套类为希望将 FontMetrics 值定义为整数的开发人员提供了方便的方法
  • 枚举油漆。Join 嵌套类允许开发者指定线条或曲线段如何在描边路径上“连接”在一起(直线或曲线连接)。这和油漆差不多。Cap 功能,除了它应用于两条或多条线或路径的连接交叉点,而不是线或路径的开放(未连接)端。
  • 枚举油漆。样式嵌套类指定了如何绘制矢量形状对象,也称为图元。这可以用一个填充、一个冲程填充和冲程来指定。您将在代码中使用填充笔画

Paint 类指定了十几个常量值,尽管 Android 操作系统不再使用其中的一个。我将在这里提到它们,以便您对这个 Paint 类可以实现的功能有一个大致的了解,因为图形设计是 watch faces 应用设计过程的关键组件之一。

  • int ANTI_ALIAS_FLAG
  • int DEV_KERN_TEXT_FLAG 现在已经不用了。
  • int DITHER_FLAG 是在渲染时启用抖动的绘画标志。打开抖动可以在 hicolor (15 位 32,767 色,或 16 位 65,536 色)或索引色** (8 位或 256 色)智能手表设备硬件显示屏上模拟真彩色图形(低颜色支持)。**
  • int EMBEDDED _ BITMAP _ TEXT _ FLAG是一个绘制标志,当您在表盘上绘制文本对象时,它使您能够使用位图字体对象。
  • int FAKE _ BOLD _ TEXT _ FLAG是一个 Paint flag,允许开发者将合成加粗效果应用到文本对象。这用于不支持(附带)字体加粗组件的自定义字体定义。
  • int FILTER_BITMAP_FLAG 如果你想要最高质量的结果,这应该在你的表盘应用中使用。(这将在第十一章中讲述,届时你将学会如何使用 BitmapDrawable。)
  • int HINTING_OFFint HINTING _ ONPaint flag 常量分别禁用或启用字体对象提示选项。字体提示相当于抗锯齿。因此,如果你在一个 Watch Face 应用中渲染字体对象,那么在低位环境模式下关闭 HINTING_OFF!
  • int LINEAR_TEXT_FLAG 是一个绘画标志,能够平滑、线性地缩放文本对象及其字体对象。这类似于 BitmapDrawable 对象的过滤,因此您可能想要打开它。
  • int STRIKE _ THRU _ TEXT _ FLAG是一个绘画标志,如果文本对象正在使用的字体对象不包括字体-删除线字体定义组件,则该绘画标志将删除线装饰应用于文本对象。
  • int SUBPIXEL_TEXT_FLAG
  • int UNDERLINE _ TEXT _ FLAG是一个绘画标志,当被启用时,如果文本对象正在使用的字体对象不包括字体-下划线定义组件,则允许将下划线装饰应用于呈现的文本对象。

Paint 类有三个公共构造函数方法,包括默认的 Paint( ) 构造函数,它使用默认设置创建一个新的 Paint 对象。还有一个“识别标志”的构造函数,它创建新的 Paint 对象,同时设置我刚才提到的那些标志常量。这个构造函数使用格式 Paint(int flags) 来创建启用了特定标志常量(特性)的新 Paint。

还有一个“copycat”Paint object 构造函数方法,它使用一种形式的 Paint(Paint paint) 来创建一个新的 Paint 对象,并通过使用构造函数方法调用的参数列表区域内指定的 Paint 对象,使用另一个 Paint 对象的属性来初始化它。

Paint 类支持 92 种方法,我显然不能在这里详细介绍,所以我将只介绍那些您将在本章的代码中使用的方法。如果您想详细研究所有 92 种方法,可以访问下面的开发者网站 URL:

http://developer.android.com/reference/android/graphics/Paint.html
  • public void setARGB(int a,int r,int g,int b) 方法是。setColor()方法。这个。setARGB()方法采用四个色板(或颜色平面)并使用这些数据配置 32 位颜色对象。A 或 alpha 分量支持 256 级(8 位)透明度(不透明度),R 或红色分量支持 256 级(8 位)红色值,G 或绿色分量支持 256 级(8 位)绿色值,B 或蓝色分量支持 256 级(8 位)蓝色值。
  • public void setAlpha(int a) 方法也是一个. setColor( ) helper 方法,但是,它只分配颜色对象的 Alpha 值,RGB 值不变。那个。setAlpha( ) 8 位(整数)值的范围必须从 0 到 255。
  • **public void setAntiAlias(boolean aa)**方法是。setFlags()方法。这个方法允许您设置或清除一个名为 ANTI_ALIAS_FLAG 的 Paint 常量,我前面提到过。true 值会打开抗锯齿,false 值会关闭抗锯齿。
  • **public void set color(int color)**方法设置一个绘制对象的颜色。integer 参数保存一个数值,其中包含 Alpha 和 RGB 数据值。这个 32 位值不是预乘的,这意味着 alpha 可以通过独立设置为任意值。不管 RGB 数据值如何。你可以研究 Android 的颜色类来获得更多的细节。
  • public void setStrokeCap(油漆。Cap cap) 方法设置绘制对象的帽样式常量。这可以是对接(默认值)、
  • public void setStrokeWidth(float width)方法设置线条或路径形状对象的像素宽度。在发际线模式 中通过零点来描边。发际线总是独立于画布矩阵绘制单个像素。宽度参数设置绘制对象的笔画宽度。每当绘制对象的样式设置为 STROKESTROKE_AND_FILL 时,都会使用该选项。
  • **public void setStyle(Paint。**方法设置绘制对象的样式。这用于控制如何解释矢量形状原始几何图形。drawBitmap 是一个例外,它总是假设填充。

现在您已经了解了基本的 Paint 类方法及其常量,您可以开始在 watch face design 中实现这个类的核心方法和常量,只需要 Java 代码、vector (Shape)对象和 Paint 对象。

表盘绘制:创建表盘绘制对象

您需要做的第一件事是声明那些 Paint 对象,它们将定义表盘组件在屏幕上的外观。基本的表盘部件是刻度和表针。

声明多个绘制对象:使用复合 Java 声明

现在让我们在低位和老化布尔标志变量声明后添加一个画图对象声明,并将四个画图对象命名为视觉标记,如图 10-1 底部所示(高亮显示)。单击红色油漆错误消息内的任意位置,并使用 Alt+Enter 让 IntelliJ 为您编写import android.graphics.Paint;声明。

9781430265504_Fig10-01.jpg

图 10-1 。为 pHourHand、pMinuteHand、pSecondHand 和 pTickMarks 添加复合 Paint 对象声明

创建 WatchFace 组件方法:配置绘画

接下来要做的是创建四个逻辑方法体来保存 Paint 对象构造(实例化)、配置和编程语句。我以这种方式构建 Java 代码,以防您想要返回并使每个 Paint 对象更加复杂。随着应用变得越来越复杂,更有组织的代码结构将会带来好处,这将允许您使用展开和折叠功能。

您将从顶部(小时)到底部(秒和刻度)工作,并从创建一个开始。 createHourHand( ) 方法。Java 代码将如下所示:

public void createHourHand() {  pHourHand = new Paint();  }

在复合 Paint 声明下添加一行代码,并键入 public void create hourhand() empty 方法结构。然后,在其中添加一个pHourHand = new Paint(); Java 对象实例化语句来创建第一个 Paint 对象,您将配置它来绘制时针。正如你在图 10-2 中看到的,你可以从弹出的助手对话框中选择画图(android.graphics) 选项,并在它下面看到嵌套的助手类。双击 Paint 类将其插入到代码中。

9781430265504_Fig10-02.jpg

图 10-2 。创建一个公共的 void createHourHand()方法;实例化一个名为 pHourHand 的新绘制对象

既然已经实例化了名为 pHourHand 的 Paint 对象,就可以开始使用颜色和绘制样式等属性来配置它了。最重要的属性是颜色,所以我们再加上**。** setARGB( ) 方法调用,将配置时针绘制为完全不透明,并使用全蓝色作为颜色。Java 语句如图 10-3 所示,应该是这样的:

pHourHand.setARGB(255, 0, 0, 255);

9781430265504_Fig10-03.jpg

图 10-3 。使用 IntelliJ 弹出帮助器对话框调用 pHourHand Paint 对象的. setARGB()方法

键入手绘对象,然后键入一个句号。当弹出的助手对话框出现时,选择**。setARGB(int a,int r,int g,int b)** 选项,如图图 10-3 ,将此方法调用插入到 Java 语句中,然后输入 255,0,0,255

时针的下一个最重要的特征是它有多厚,这是使用**配置的。****【setStrokeWidth()**方法,你将其中的像素设置为 6(6/320 就是屏幕的. 01875 或者 1.875% )。如图图 10-4 所示,Java 语句在代码库中应该如下所示:

pHourHand.setStrokeWidth(6.f);

9781430265504_Fig10-04.jpg

图 10-4 。使用 IntelliJ 弹出帮助器对话框调用 pHourHand Paint 对象的. setStrokeWidth()方法

您将 Paint 对象配置为交互式表盘模式的默认对象,然后根据用户的手表型号及其在任何给定时间的行为,检测环境温度、低位和老化。因此,为了获得最佳视觉质量,您将在默认模式下打开的抗锯齿功能,并使时针的尖端变圆**。**

完成此任务的 Java 语句如图 10-5 所示,看起来像下面的代码:

pHourHand.setAntiAlias(true);
pHourHand.setStrokeCap(Cap.ROUND);

9781430265504_Fig10-05.jpg

图 10-5 。使用 Cap 调用 pHourHand Paint 对象的. setAntiAlias()方法和. setStrokeCap()方法。一轮

正如你在图 10-5 中看到的,如果你在。调用 setStrokeCap()方法,你将得到该颜料。Cap 此对话框中的嵌套(辅助)类常量。

双击**帽。在弹出的帮助器对话框中选择 ROUND(Android . graphics . paint)**选项,如图图 10-5 所示,完成一个公共 void 的配置代码。createHourhand()方法结构配置一个画图对象。

完了。createHourHand()方法结构如图图 10-6 所示,您将在下面复制并粘贴它来创建其他三个方法,它应该看起来像下面的 Java 方法结构:

public void createHourHand() {
    pHourHand = new Paint();
    pHourHand.setARGB(255, 0, 0, 255);
    pHourHand.setStrokeWidth(6.f);
    pHourHand.setAntiAlias(true);
    pHourHand.setStrokeCap(Cap.ROUND);
}

9781430265504_Fig10-06.jpg

图 10-6 。复制 createHourHand()方法结构;将其粘贴到它的下面,以创建 createMinuteHand()方法

创建分、秒和刻度表盘组件方法

正如你在图 10-6 中看到的,你的 Java 代码现在已经没有错误了,你可以在它下面复制并粘贴一个. createHourHand()方法来创建一个**。createMinuteHand( )** 方法。我在这段之前的 Java 代码清单中用粗体显示了您将要更改的参数。

因为您也将使用圆形的分针,所以您只需要更改六行代码中两行的参数。但是,不要忘记将方法名和绘制对象名从手动改为手动。改变。setARGB()方法调用,通过将绿色数据参数完全打开(255),红色和蓝色参数完全关闭(0),将分针涂成绿色,然后使分针 4 像素宽而不是 6 像素宽( 4/320 将代表显示屏的 1.25% )。. createMinuteHand()方法结构 的代码应该如下所示:

public void createMinuteHand() {
    pMinuteHand = new Paint();
    pMinuteHand.setARGB(255, 0, 255, 0);
    pMinuteHand.setStrokeWidth(4.f);
    pMinuteHand.setAntiAlias(true);
    pMinuteHand.setStrokeCap(Cap.ROUND);
}

因为您将使用方形秒针,所以您需要在这六行代码中的三行中更改三个参数(在上面的代码中以粗体显示)。不要忘记将方法名和绘制对象名从 pMinuteHand 改为PSE second hand

改变。setARGB()方法调用,通过完全打开红色数据参数(255),关闭绿色和蓝色参数(0),将秒针涂成红色。让我们把秒针做成 2 像素宽而不是 4 像素宽( 2/320 将代表显示屏的 0.625% )。的 Java 代码。 createSecondHand( ) 方法 结构应该看起来像下面的代码:

public void createSecondHand() {
    pSecondHand = new Paint();
    pSecondHand.setARGB(255, 255, 0, 0);
    pSecondHand.setStrokeWidth(2.f);
    pSecondHand.setAntiAlias(true);
    pSecondHand.setStrokeCap(Cap.SQUARE);
}

正如你在图 10-7 中看到的,代码没有错误,你已经准备好创建最终的方法,该方法将创建并配置一个 Paint 对象,用于在表盘周围绘制刻度线。

9781430265504_Fig10-07.jpg

图 10-7 。复制 createMinuteHand()方法结构;粘贴到它的下面,创建 createSecondHand()方法

复制并粘贴 CreateSecondHand()方法到它下面,创建一个 createTickMarks( ) 方法。您需要编辑的唯一方法调用是您的**。setARGB( )** 方法调用,将所有值设置为 255 ,使刻度线为白色。方法结构的 Java 代码应该是这样的:

public void createTickMarks() {
    pTickMarks = new Paint();
    pTickMarks.setARGB(255, 255, 255, 255);
    pTickMarks.setStrokeWidth(2.f);
    pTickMarks.setAntiAlias(true);
    pTickMarks.setStrokeCap(Cap.SQUARE);
}

正如你在图 10-8 中看到的,代码是没有错误的,你已经准备好调用你在。需要由 WatchFaces API 实现的 onCreate()方法。

9781430265504_Fig10-08.jpg

图 10-8 。Copy createSecondHand()方法结构;将它粘贴到自身下面以创建 createTickMarks()方法

您已经实现了该方法的一部分,包括一个 super.onCreate()超类方法调用,并使用 WatchFaceStyle 创建了一个 WatchFaceStyle 对象。构建器类,你在第八章中学到的。

从一个调用这些方法之后。onCreate( ) 方法来设置画图对象的小时、分钟、秒针和刻度,就可以开始在上工作了。onDraw( ) 法。通过使用**,您可以创建使用这些 paint 对象在画布上绘制表盘设计组件的逻辑。来自 Android Canvas 类的 drawLine( )** 方法,我将在本章的下一节详细讨论。

调用 WatchFace 组件 Paint 方法。onCreate()

打开。onCreate()方法实现,并在 WatchFaceStyle 对象的构造和配置之前或之后调用. createHourHand()方法。正如你在图 10-9 中看到的,如果你键入单词 create ,你刚刚创建的所有方法现在都是 IntelliJ 弹出助手对话框的一部分。双击每一个,插入所有四个。

9781430265504_Fig10-09.jpg

图 10-9 。在公共 void onCreate()方法中添加 createHourHand()方法调用,以创建第一个 Paint

一旦你输入单词“create”四次并双击前四个方法调用,你的代码将看起来如图 10-10 所示。

9781430265504_Fig10-10.jpg

图 10-10 。在公共 void onCreate()方法的顶部添加所有四个绘制对象创建方法调用

接下来,让我们快速看一下 Android Canvas 类,它是托管(包含)的类。onDraw()方法。之后,您将编写。onDraw()方法来绘制表盘组件。

WatchFace 绘图引擎:onDraw()方法

Android WatchFaces API 使用 Android Canvas 类作为绘图表面,而。onDraw()方法,该方法将 Paint 对象应用于 Canvas 对象。在本节中,您将仔细查看这些类对象,并了解它们是如何相互协作的。

Android Canvas 类:你的画布绘制方法

Android Canvas 公共类扩展了 Java . lang . object 主类,这意味着它是作为绘图画布使用的临时代码。这个类在 android.graphics 包里。Android Canvas 类的 Java 类层次结构如下所示:

java.lang.Object
  > android.graphics.Canvas

Canvas 类旨在创建一个画布绘制表面对象来保存。onDraw()方法调用。为了能够在画布对象上绘图,您需要有四个基本组件。第一个是一个位图对象,它保存代表画布表面的实际像素。

第二个是一个 Canvas 对象本身,它为。onDraw()方法调用,将数据值写入该位图对象。第三个是“绘图原语”,如 vector ( Shape )子类对象,如 Rect、Path、Text 或 Line,或一个光栅 BitmapDrawable 对象。第四个是 Paint 对象,我在本章的前一节已经介绍过了,它用于描述绘制组件的颜色和样式。

这个 Canvas 类有两个嵌套的类,也称为 helper 类。一个是枚举画布。EdgeType ,定义边缘常量 AA(抗锯齿),或者 BW(黑白),不抗锯齿。另一个嵌套类是枚举画布。VertexMode ,它处理 3D OpenGL ES,我不打算在这本书里讨论。

Canvas 类有 90 个方法,几乎和 Paint 类一样多。您将使用public void drawLine(float startX,float startY,float stopX,float stopY,Paint paint) 方法,使用指定的起点和终点 X,Y 坐标绘制线段。将使用您在本章的上一节中创建的指定绘制对象来绘制线条。

绘制你的表盘:使用。drawLine()方法

接下来,让我们折叠。onCreate()方法,方法是单击该方法左侧的号,然后展开。通过点击方法左边的符号,onDraw()方法。你将首先添加基本的整数和浮点变量来设置表盘设计的中心。然后,您将从 time 对象获取系统时间,然后计算所有表盘设计组件的旋转。

寻找表盘设计的中心:centex 和 centey

让我们首先创建一个名为宽度整数变量,并使用一个. width()方法调用 Rect 对象来获得画布的宽度,该对象定义了画布的边界。要确定垂直中心,您需要将这个数字除以 2,如果智能手表使用整个屏幕,这个数字将是 320 像素。您将使用下面的 Java 代码对高度 (Y)值进行同样的操作,这也显示在图 10-11 中:

int width = rect.width();
float centerX = width / 2.f;
int height = rect.height();
float centerY = height / 2.f;

9781430265504_Fig10-11.jpg

图 10-11 。获取 onDraw( ) Rect 对象的宽度和高度,并使用它们来计算 centerX 和 centerY

在本章的这一节中,您将使用这些 centerXcenterY 值。这些值用于提供表盘指针的原点坐标和刻度排列的中心点。接下来您需要做的是创建整数变量,其中包含小时、分钟和秒的时间值。这样做是因为时间对象使用整数值。

查找当前时间:小时、分钟和秒整数

最重要的事情之一。onDraw( ) 方法每秒需要做的是旋转所有这些指针(时、分、秒)指向正确的方向,基于当前的系统时间值。这保存在 watchFaceTime 时间对象中,您已经在代码中将其放置到位。您所要做的就是创建整数变量来保存当前系统时间的小时、分钟和秒部分,这样您就可以使用圆周率、正弦和余弦对它们进行计算,以找出您的每个表盘指针应该指向的方向。

这可以使用下面的 Java 代码来完成,在使用 IntelliJ 构建这三个语句中的第一个时,也显示在图 10-12 中:

int hours = watchFaceTime.hour();
int minutes = watchFaceTime.hour();
int seconds = watchFaceTime.hour();

9781430265504_Fig10-12.jpg

图 10-12 。创建一个名为 hours 的整数变量,并将其设置为 watchFaceTime 对象的 hour 属性

如果您输入 int hours = ,以及 watchFaceTime 时间对象的名称,然后输入 period ,您将访问 IntelliJ 弹出帮助器对话框,在这里您可以看到小时、分钟和秒方法调用。

正如你所看到的,变量声明和初始化代码到目前为止是没有错误的,并且只需要 7 行代码,如图图 10-13 所示。我在 Java 代码中单击了一个 watchFaceTime 对象引用来显示它的用法,IntelliJ 使用紫色来跟踪它。我单击的 watchFaceTime 由 IntelliJ 用淡黄色高亮显示。

9781430265504_Fig10-13.jpg

图 10-13 。为分钟和秒钟创建整数变量,并将其设置为 watchFaceTime 对象属性

我将在本节中一直这样做,以帮助您了解时间对象的用法。现在,您已经准备好通过正弦、余弦和圆周率方法计算旋转。

现在,您已经有了保存表盘中心点坐标的变量以及 Time 对象的当前小时、分钟和秒,是时候(没有双关语)实现 Java 代码了,它将每秒钟将所有基本表盘组件旋转到位。

旋转时针:使用数学类圆周率、正弦和余弦

声明一个名为hourRotfloat 变量来保存时针旋转。时针将显示来自时间对象的小时值,并且还将显示每个小时之间的分数部分。您可以从 Time 对象中取出分钟值,并除以 60,然后使用下面的公式格式将它加回小时值:(小时+(分钟/60))

因为您使用的是 Java Math 类的正弦波函数,而 PI 定义了正弦波的一个完整周期(波朝上,波朝下),所以您需要将一个完整周期中的小时数(12)除以二,然后将精确的小时数除以六,使用以下公式格式:((小时+(分钟/60))/6) 。现在你所要做的就是将旋转角度乘以圆周率,圆周率可以通过数学方法得到。圆周率法。这将是使用(浮点)数学将转换为浮点值。PI ,所以你不会得到一个编译错误。时针旋转的 Java 代码如图图 10-14 所示,应该如下所示:

float hourRot = ((hours + (minutes / 60.f)) / 6.f) * (float) Math.PI;

9781430265504_Fig10-14.jpg

图 10-14 。创建一个 hourRot 来保存时针旋转计算,并创建一个 hourLength 来保存长度

时针长度的代码要简单得多,它包含一个名为 hourLengthfloat 值的声明,该值被设置为 centerX(表盘半径)值减去 80 像素,以使时针更短。

请记住,centerX 值是表盘表面区域的半径,因为它是 Rect.width()除以 2。图 10-14 中的所示的时针长度代码应该类似于下面的 Java 编程语句:

float hourLength = centerX - 80;

接下来,您需要计算线条形状对象的端点,您将从线条的 centerX 端点绘制到 hourX 坐标,以便时针线将准确地指向正确的赤纬(方向)。正弦函数获取 hourRot 值,并将其转换为 X 坐标的旋转矢量值。您需要在下一行代码中对 Y 坐标做同样的事情。Java 语句应该类似于下面的 Java 代码:

float hourX = (float) Math.sin(hourRot) * hourLength;

当你在 IntelliJ 中键入代码行时,你应该使用弹出的帮助器对话框,如图 10-14 所示,选择一个数学类 sin(double d) 选项,并将其插入到代码中,指定 hourRot 为 double 参数。

要找到 hourY 值,只需使用余弦波反转正弦波,然后用负号将该值绕另一个轴翻转,将该值变为负值。您的 Java 语句应该看起来像下面的代码,可以在图 10-15 中看到:

float hourY = (float) -Math.cos(hourRot) * hourLength;

9781430265504_Fig10-15.jpg

图 10-15 。键入 watchface Canvas 对象,并从弹出帮助器中选择 drawLine()方法调用

现在,时针偏移确定了时针线外端的 X 和 Y 坐标。您还拥有调用。drawLine()方法绘制时针线。

的。从名为 watchfaceCanvas 对象中调用 drawLine()方法,该对象在您的public void on draw(Canvas watch face,Rect rect) 中声明,如图图 10-15 所示。如您所见,如果您键入 watchface Canvas 对象,然后键入句点键,您的 IntelliJ 方法弹出助手对话框应该会出现。双击 drawLine(float startX,float startY,float stopX,float stopY,Paint paint) void 方法选项,将其插入到代码中,看起来应该像下面的 Java 语句,如图图 10-16 :

watchface.drawLine( centerX, centerY, centerX+hourX, centerY+hourY, pHourHand );

9781430265504_Fig10-16.jpg

图 10-16 。调用 watchface Canvas 对象的. drawLine()方法;传入小时绘制对象和坐标

此的方法调用结构。drawLine()方法应该包括表盘中心线的起点,用 centerXcenterY 变量表示,线的终点将使用 centerX+hourXcenterY+hourY 数据值,这些数据值在这个方法调用中作为快捷方式进行计算。最后一个参数是您在本章第一节中创建和配置的 pHourHand Paint 对象,它定义了如何绘制时针线。

旋转分针:使用数学类圆周率、正弦和余弦

分针画线逻辑与时针画线逻辑非常相似,只是它不需要调整部分分钟,而时针需要调整小时精度。分针比时针长,所以你只能从半屏值中减去 40 像素,而不是 80 像素。用于绘制分针线的 Java 代码在图 10-17 中显示无误,应该如下所示:

float minuteRot = minutes / 30f * (float) Math.PI
float minuteLength = centerX - 40;
float minuteX = (float) Math.sin(minuteRot) * minuteLength;
float minuteY = (float) -Math.cos(minuteRot) * minuteLength;
watchface.drawLine( centerX, centerY, centerX + minuteX, centerY + minuteY, pMinuteHand );

9781430265504_Fig10-17.jpg

图 10-17 。调用 watchface Canvas 对象的. drawLine()方法;传入微小的绘画对象和坐标

接下来,您将编写二手绘图逻辑。这与时针和分针逻辑非常相似,只是它仅在交互模式下绘制。

旋转秒针:使用。isInAmbientMode()方法

秒针线逻辑与分针线逻辑非常相似,但有两个例外。绘制的线更长,计算为centex-20、,只有智能手表处于环境模式时才会调用 Math.sin、Math.cos 和 Canvas.drawLine()方法。您的 Java 代码为二手行,包括一个条件 **if(!isineambientmode())**语句隔离了 Math 和 Canvas 方法调用的处理,看起来应该像下面的 Java 代码结构,如图 10-18 所示:

float secondRot = seconds / 30f * (float) Math.PI
float secondLength = centerX - 20;
if ( !isInAmbientMode() ) {
     float secondX = (float) Math.sin(secondRot) * secondLength;
     float secondY = (float) -Math.cos(secondRot) * secondLength;
     watchface.drawLine( centerX, centerY, centerX + secondX, centerY + secondY, pSecondHand );
}

9781430265504_Fig10-18.jpg

图 10-18 。使用条件 if(!idspnonenote)实现二手画线逻辑。isInAmbientMode())结构

根据 Android Watch Face API 规则,如果手表表面处于环境模式,秒针画线代码根本不会画线,如果手表处于环境模式,也不会调用任何数学函数,因此该逻辑还用于优化涉及秒针计算的处理器使用。

创建表盘刻度线:使用 Java for 循环结构

就数学而言,在屏幕周围绘制每小时刻度线的代码类似于手绘代码。您仍将使用 PI 常量(3.14159)和 Math.sin-Math.cos 来定位表盘周边的刻度,使用 for 循环来定位所有 12 个刻度。图 10-19 底部显示的代码如下:

float innerTicksRadius = centerX - 10;
for (int ticksIndex = 0; ticksIndex < 12; ticksIndex++) {
   float ticksRot = (float) (ticksIndex * Math.PI * 2 / 12);
   float innerX = (float) Math.sin(ticksRot) * innerTicksRadius;
   float innerY = (float) -Math.cos(ticksRot) * innerTicksRadius;
   float outerX = (float) Math.sin(ticksRot) * centerX;
   float outerY = (float) -Math.cos(ticksRot) * centerX;
   watchface.drawLine(centerX+innerX, centerY+innerY, centerX+outerX, centerY+outerY, pTickMarks);
}

9781430265504_Fig10-19.jpg

图 10-19 。创建一个 for 循环,使用内刻度半径和 centerX 作为外半径来绘制刻度线

现在,您已经准备好实现一些代码,这些代码控制当不同的智能手表硬件模式投入使用时,将如何修改 Paint 对象。这将实现你在第六章中了解到的所需的特殊绘图模式。本章的下一节将向您展示如何仅使用绘制常量设置代码来支持这些模式。

高级模式支持:动态绘制方法

本节将解释如何创建几个方法,这些方法可以根据用户智能手表硬件支持的不同模式动态设置绘制对象特性**。**

**这些模式包括环境模式、低位环境模式和老化保护模式,它们提供不同的图形设计特征,如第六章中的所述。互动模式使用全(亮)色,环境模式使用暗淡的颜色或灰度,具体取决于硬件支持,低位模式使用黑白,老化保护仅使用“边缘像素”来定义表盘设计,以便尽可能少的像素开启(老化)。

控制抗锯齿:创建 setAntiAlias()方法

现在在。onCreate()方法并创建一个**私有的 void setAntiAlias(boolean antiAliasFlag)方法。需要注意的是,Java 足够聪明,允许你在 watchface 应用 ProWatchFaceService.java 类中使用这个方法名,因为它通过在包、类和方法之间使用点符号来引用事物,所以它知道这个 ProWatchFaceService。您将要创建的 Engine.setAntiAlias( )Android . graphics . canvas . setantialias()**完全不同。

这个事实允许您调用这个方法 setAntiAlias(),即使 android.graphics 包中已经有一个 Android Canvas.setAntiAlias()方法。有趣的是,在这里您将实际使用这个(画布)。中的 setAntiAlias()方法调用。setAntiAlias()方法代码。这就是我这样设置它的原因,以表明它是可以做到的,并且您的 IntelliJ (Java + Android)编译器可以很好地处理它!

如果这影响了您的感受力,您可以将该方法命名为不同的名称,例如。setAntiAliasingMode(),如果你喜欢的话。在方法内部,调用 Canvas 类的。setAntiAlias()方法关闭 pHourHand Paint 对象,如图图 10-20 所示。您的 Java 代码应该如下所示:

private void setAntiAlias(boolean antiAliasFlag){
    pHourHand.setAntiAlias(antiAliasFlag);
}

如果要使用 IntelliJ 弹出的帮助器对话框,输入 pHourHand.set,如图图 10-20 所示,双击 setAntiAlias(boolean aa) 选项将此方法调用插入 Java 语句中。

9781430265504_Fig10-20.jpg

图 10-20 。使用传入该方法的布尔 antiAliasFlag 参数创建一个私有 void setAntiAlias

加上这个**。setAntiAlias(antiAliasFlag)** 方法调用所有的 Paint 对象,这将在所有的 Paint 对象上设置 on/off (true/false)设置,允许您打开或关闭手表表面的抗锯齿功能。如图图 10-21 所示的 Java 代码应该是这样的:

private void setAntiAlias(boolean antiAliasFlag){
    pHourHand.setAntiAlias(antiAliasFlag);
    pMinuteHand.setAntiAlias(antiAliasFlag);
    pSecondHand.setAntiAlias(antiAliasFlag);
    pTickMarks.setAntiAlias(antiAliasFlag);
}

9781430265504_Fig10-21.jpg

图 10-21 。调用每个绘制对象的. setAntiAlias()方法,并传入布尔抗锯齿标志

现在,您已经创建了允许您为表盘设计打开和关闭抗锯齿的方法,您可以创建另一种方法,只绘制老化保护模式的时针和分针的边缘。最后,让我们创建第三种方法来实现低位环境模式(仅黑白像素),然后您将与您在第六章中学到的所有手表面孔 API 保持一致。

控制老化:创建 setBurnInProtect()方法

您需要创建的下一个方法是处理需要屏幕老化保护的智能手表硬件,这是使用边缘渲染代码实现的,该代码仅渲染(使用自定义绘画选项绘制到画布上)手表表面组件的边缘。

创建private void setBurnInProtect(boolean enabled)方法结构,并声明一个画图。样式对象命名为 paintStyle ,并将其设置为等于默认值 Paint。Style.FILL 因为这是表盘矢量形状在除了老化保护模式之外的所有其他模式中需要使用的。

当您输入这条语句时,将出现一个 IntelliJ 弹出方法助手对话框,您可以选择画图。Style.FILL 选项,或者双击它,会将其插入到 Java 语句中。

一旦您使用完 IntelliJ 弹出助手对话框,如图 10-22 所示的 Java 方法结构应该是这样的:

private void setBurnInProtect(boolean enabled) {
    Paint.Style paintStyle = Paint.Style.FILL;
}

9781430265504_Fig10-22.jpg

图 10-22 。使用传递到方法中的布尔启用参数创建私有 void setBurnInProtect

接下来您需要添加的是一个条件 if-else 结构,它将处理老化保护是否需要打开(启用)或关闭(!启用)如果不需要的话。if 结构的 if(enabled) 部分应该设置画图。将名为 paintStyle 的样式对象添加到 STROKE 常量,如图 10-23 中弹出的帮助器对话框所示。if()结构的第一部分应该类似于下面的 Java 代码:

private void setBurnInProtect(boolean enabled) {
    Paint.Style paintStyle = Paint.Style.FILL;
    if(enabled) {
        paintStyle = Paint.Style.STROKE;
    } else {
        // an empty else structure for now
    }
}

9781430265504_Fig10-23.jpg

图 10-23 。添加一个 if(enabled) else 空条件结构,并在其中将 paintStyle 设置为 STROKE

if-else 条件语句的 else 部分将设置默认绘画。 FILL 的样式值,你也可以在方法的头部(开始)设置,这样如果不需要这个烧屏模式(false),矢量组件将被填充而不是描边,使它们用颜色填充而不是用黑色填充。这确保了只有在使能时才会使用 STROKE。到目前为止,该方法的 Java 代码应该是这样的,如图 10-24 所示:

private void setBurnInProtect(boolean enabled) {
    Paint.Style paintStyle = Paint.Style.FILL;
    if(enabled) {
        paintStyle = Paint.Style.STROKE;
    } else {
        paintStyle = Paint.Style.FILL;
    }
}

9781430265504_Fig10-24.jpg

图 10-24 。添加 paintStyle 对象集以填充 enabled = false 的条件 if 语句的 else 部分

该方法需要做的最后一件事是为表盘的时针和秒针组件设置 Paint 对象,以使用该 paintStyle。这是通过调用. setStyle()方法 并传递已经在 if(enabled)条件结构中设置为默认填充或更改为 STROKE 的 paintStyle 来完成的。

完成后,Java 方法结构应该类似于下面的代码:

private void setBurnInProtect(boolean enabled) {
    Paint.Style paintStyle = Paint.Style.FILL;
    if(enabled) {
        paintStyle = Paint.Style.STROKE;
    } else {
        paintStyle = Paint.Style.FILL;
    }
    pHourHand.setStyle(paintStyle);
    pMinuteHand.setStyle(paintStyle);
}

最终方法在图 10-25 中显示无误。我选择了 paintStyle 属性,这样你就可以通过这个方法体看到它的用法。

9781430265504_Fig10-25.jpg

图 10-25 。在 if-else 之后,使用。setStyle()

需要注意的是,如果**,这个方法将只改变绘制对象的值。****on properties changed()**方法检测用户的智能手表硬件是否设置了屏幕老化保护标志。此方法将在。onAmbientModeChanged()方法,您将在本章的后面创建它(这就是为什么您现在在这里创建它,以便在以后需要时可以使用它)。

下一件你想做的事情是确保画图对象被正确配置为低位环境和环境模式,所以让我们创建**。ensureModeSupport( )** 方法 next,该方法控制颜色和 Alpha 值,进而控制表盘组件在黑色背景色下在屏幕上的显示亮度。

确保模式支持:一个 ensureModeSupport()方法

在下面添加一行代码。setBurnInProtect()方法并创建一个私有 void ensureModeSupport( ) 方法。声明名为enablelowtambientmode的布尔标志变量,并将其设置为等于lowtambientmode flag布尔标志变量的逻辑以及从is ambientmode()方法调用返回的值。其代码如图 10-26 中的所示。

9781430265504_Fig10-26.jpg

图 10-26 。创建一个私有 void ensureModeSupport,并添加一个布尔 enableLowBitAmbientMode 参数

一旦创建了这个 enableLowBitAmbientMode 布尔标志变量,通过使用下面的 Java 代码结构创建一个空的 ifelse-ifelse 结构,它也显示在图 10-27 中:

private void ensureModeSupport() {
    boolean enableLowBitAmbientMode = isInAmbientMode() && lowBitAmbientModeFlag
    if( enableLowBitAmbientMode )  {   // Low-Bit Ambient Mode Java Statements
    } else if( isInAmbientMode() ) {   // Ambient Mode Java Statements
    } else {                           // Interactive Mode Java Statements
    }
}

9781430265504_Fig10-27.jpg

图 10-27 。添加一个 if(enableLowBitAmbientMode) else if(is ambientmode) else 条件结构

低位环境模式的特点是每像素只使用一位的数据,也就是全黑上全白。因此,你将不得不设置阿尔法为 255 (这样白色就没有黑色,从背景色)和颜色值为颜色。白色也一样。

setAlpha( ) 方法调用 Paint 对象,如图图 10-28 所示,看起来应该像下面的 Java 语句:

private void ensureModeSupport() {
    boolean enableLowBitAmbientMode = isInAmbientMode() && lowBitAmbientModeFlag
    if( enableLowBitAmbientMode )  {
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
    } else if( isInAmbientMode() ) {  // Ambient Mode Java Statements
    } else {                         // Interactive Mode Java Statements
    }
}

9781430265504_Fig10-28.jpg

图 10-28 。添加。setAlpha(255)方法调用,将所有绘制对象设置为使用其 100%的颜色值进行绘制

接下来你需要做的是用**将所有表盘设计元素的颜色设置为白色。setColor(颜色。**白色)调用掉绘制对象。如图图 10-29 所示的 Java 代码是这样的:

pHourHand.setAlpha(255);
pMinuteHand.setAlpha(255);
pSecondHand.setAlpha(255);
pTickMarks.setAlpha(255);
pHourHand.setColor(Color.WHITE);
pMinuteHand.setColor(Color.WHITE);
pSecondHand.setColor(Color.WHITE);
pTickMarks.setColor(Color.WHITE);

9781430265504_Fig10-29.jpg

图 10-29 。添加。setColor(颜色。WHITE)方法调用将所有绘制对象设置为使用白色值进行绘制

现在,您已经确保在低位环境模式下,表盘屏幕上仅使用黑白值,您需要实现条件逻辑的 else-if 部分,将表盘组成部分中使用的颜色调暗 50%。这是使用 127 的阿尔法值完成的,由于黑色(零值)背景色,这与变暗颜色值 50% 具有相同的效果。如图图 10-30 所示,Java 代码如下所示:

private void ensureModeSupport() {
    boolean enableLowBitAmbientMode = isInAmbientMode() && lowBitAmbientModeFlag
    if( enableLowBitAmbientMode )  {
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
        pHourHand.setColor(Color.WHITE);
        pMinuteHand.setColor(Color.WHITE);
        pSecondHand.setColor(Color.WHITE);
        pTickMarks.setColor(Color.WHITE);
    } else if( isInAmbientMode() ) {
        pHourHand.setAlpha(127);
        pMinuteHand.setAlpha(127);
        pSecondHand.setAlpha(127);
        pTickMarks.setAlpha(127);
    } else {
        // Interactive Mode Java Statements
    }
}

9781430265504_Fig10-30.jpg

图 10-30 。添加。setAlpha(127)调用 else-if(),将所有绘制对象设置为使用其颜色值的 50%进行绘制

使用 Alpha 值作为调光器(由于黑色背景色)将允许您支持在环境模式下支持颜色的智能手表并且仍然允许您在代码中为环境模式支持将屏幕调暗 50%。

条件 if-else 语句的最后一个 else 部分是如果智能手表根本不处于环境模式(不处于低位环境模式,甚至不处于环境模式)时将执行的代码,这意味着它处于交互模式

在交互模式下,您要做的是重新调高 Alpha 值,以便使用最大亮度(因为没有与黑色背景颜色混合,这会使颜色值变暗并降低亮度)。

为此,您将使用在条件语句的 if(enableLowBitAmbientMode)部分开头使用的四个语句,在这里您调用了。setAlpha()方法,全开值为 255。

您完成的 ensureModeSupport()方法结构,如图 10-31 所示,应该类似于下面的 Java 代码:

private void ensureModeSupport() {
    boolean enableLowBitAmbientMode = isInAmbientMode() && lowBitAmbientModeFlag
    if(enableLowBitAmbientMode)  {
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
        pHourHand.setColor(Color.WHITE);
        pMinuteHand.setColor(Color.WHITE);
        pSecondHand.setColor(Color.WHITE);
        pTickMarks.setColor(Color.WHITE);
    } else if(isInAmbientMode()) {
        pHourHand.setAlpha(127);
        pMinuteHand.setAlpha(127);
        pSecondHand.setAlpha(127);
        pTickMarks.setAlpha(127);
    } else {
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
    }
}

9781430265504_Fig10-31.jpg

图 10-31 。添加。setAlpha(255)为所有绘制对象调用 else 设置,以使用它们的 100%颜色值进行绘制

正如您所看到的,在大多数情况下,此方法控制 alpha 通道数据,您可以使用它在环境模式下充当调光器,并分别确保低位环境和交互模式下的全白(或全彩色)像素值。您正在利用手表的黑色背景颜色来使用 Alpha 值使像素变暗或变亮,因此您可以保留颜色值(低位环境模式除外)。

现在,您已经有了所需的方法(并从中调用)。来自 WatchFaceService 的 onAmbientModeChanged()方法。Engine 类,您接下来将实现该类,以确保智能手表硬件进入环境模式时支持所有这些不同的模式。

调用模式方法:onAmbientModeChanged( )

在。onVisibilityModeChanged()方法并添加了**@ Override public void on ambientmodechanged(boolean ambientModeFlag)**方法结构,这样就可以实现表盘编程逻辑,它涵盖了当用户的智能手表硬件从交互模式切换到环境模式(以节省电池电量)时你希望发生的情况。

在空方法结构中,使用 Java super 关键字调用超类的。onAmbientModeChanged()方法,传递一个 ambientModeFlag 布尔变量,该变量将被传递到您正在编码的方法中。到目前为止的方法结构如图图 10-32 所示,代码如下:

@Override
public void onAmbientModeChanged(boolean ambientModeFlag) {
    super.onAmbientModeChanged(ambientModeFlag);
}

9781430265504_Fig10-32.jpg

图 10-32 。创建一个 public void onAmbientModeChanged()方法,并调用 Java super 关键字

接下来,添加一个条件**if(lowbitAmbientModeFlag)**结构。如果您愿意,让 IntelliJ 为您编写,方法是在 If()中键入“l”,然后选择 lowBitAmbientModeFlag 布尔选项,如图图 10-33 所示,双击它让 IntelliJ 为您编写条件语句。您的 Java 条件 if()结构应该如下所示:

if(lowBitAmbientModeFlag) {
    // Java statements to be processed for low-bit ambient mode will go in here
}

9781430265504_Fig10-33.jpg

图 10-33 。添加一个 if(lowBitAmbientModeFlag)条件结构,并使用帮助器对话框编写代码

在条件 if 结构中,调用 setAntiAlias()方法,如图图 10-34 所示,使用传入该方法的 ambientModeFlag 值的相反值。这使用了 setAntiAlias(!ambientModeFlag) 方法调用格式。到目前为止,Java 代码应该是这样的:

if(lowBitAmbientModeFlag) {
    setAntiAlias(!ambientModeFlag);
}

9781430265504_Fig10-34.jpg

图 10-34 。在条件结构中,键入 set 并双击 setAntiAlias(antiAliasFlag)方法

创建另一个if(burnInProtectModeFlag)条件结构,该结构调用您刚刚编写的 setBurnInProtect( ) 方法,使用与 ambientModeFlag 相同的值,该方法将打开老化保护,如果该标志被设置为告诉手表表面智能手表硬件需要该功能。

到目前为止,onAmbientModeChanged()方法确保了如果智能手表支持环境模式下的低位,抗锯齿被关闭,如果用户的智能手表需要烧屏保护,手表表面的组件被描边(仅显示边缘)而不是填充。到目前为止的 Java 代码,如图图 10-35 所示,应该看起来像下面的方法结构:

@Override
public void onAmbientModeChanged(boolean ambientModeFlag) {
    super.onAmbientModeChanged(ambientModeFlag);
    if(lowBitAmbientModeFlag) {
        setAntiAlias(!ambientModeFlag);
    }
    if(burnInProtectModeFlag) {
        setBurnInProtect(ambientModeFlag);
    }
}

9781430265504_Fig10-35.jpg

图 10-35 。创建两个条件 if 结构,它们将调用 setAntiAlias()和 setBurnInProtect()方法

接下来的事情。onAmbientModeChanged()方法需要做的是调用 ensureModeSupport( ) 方法,如图图 10-36 所示,这是您刚刚创建的。在所有的绘画属性被重新配置(重置)后,你可以通过调用重绘表盘设计。 invalidate( ) 方法。

9781430265504_Fig10-36.jpg

图 10-36 。调用 ensureModeSupport()方法,然后通过调用 invalidate()方法刷新观察图面

您需要做的最后一件事是创建一个**。** checkTimer( ) 方法。这将重置 updateTimeHandler 并重置计时器逻辑,以便它知道要等待多长时间才能读取下一个偶数(1,000 毫秒)秒值来设置表盘秒针,因为环境模式可能已变回交互模式!

返回交互模式:checkTimer()方法

还有最后一件适用于。onVisibilityChanged()和。onAmbientModeChanged()方法。你需要确保使用 isTimerEnabled()方法重置秒针计时器逻辑,以防可见性或环境模式变回交互模式。

在 onAmbientModeChanged()方法下添加一行代码,并创建一个 public void checkTimer( ) 方法结构。在这个结构里面,键入 updateTimehandler 对象,然后按下周期键。然后在弹出的帮助器对话框中双击remove messages(int what)void选项,如图 10-37 所示。

9781430265504_Fig10-37.jpg

图 10-37 。创建一个公共的 void checkTimer()方法,键入 updateTimeHandler,选择 removeMessages(int what)

添加一个 UPDATE_TIME_MESSAGE 常量作为参数,以删除当观察面进入环境模式或不可见(不可见)模式时可能留在 MessageQueue 中的任何消息对象。

到目前为止,Java 方法结构应该类似于下面的代码:

public void checkTimer() {
    updateTimeHandler.removeMessages(UPDATE_TIME_MESSAGE);
}

该方法的下一部分需要检查。具有条件 if 结构的 isTimerEnabled()布尔返回值。如果值等于 true,它需要调用**。sendEmptyMessage( )** 方法关闭 updateTimeHandler 对象,使用下面的 Java 代码结构,如图图 10-38 所示:

If( isTimerEnabled ) { updateTimehandler.sendEmptyMessage(UPDATE_TIME_MESSAGE); }

9781430265504_Fig10-38.jpg

图 10-38 。添加一个 if(isTimerEnabled)条件并调用。updateTimeHandler 的 sendEmptyMessage()

现在你需要做的就是在 onAmbientModeChanged()方法的末尾添加一个 checkTimer()方法调用,如图 10-39 中突出显示的。

9781430265504_Fig10-39.jpg

图 10-39 。打开 onAmbientModeChanged()方法,并在末尾添加 checkTimer()方法调用

最后,你还需要在 onVisibilityChanged()方法的末尾添加一个 checkTimer()方法调用,如图 10-40 中突出显示的。当手表重新开机时,将需要此呼叫。要获取该值,计时器必须等到下一个偶数秒(甚至 1000 毫秒)。

9781430265504_Fig10-40.jpg

图 10-40 。打开 onVisibilityChanged()方法,并在最后添加 checkTimer()方法调用

现在你已经有了渲染表盘的基本方法!

摘要

在这一章中,你学习了如何使用 Android Paint 和 Canvas 类中的方法来创建“矢量”表盘设计。首先,我深入介绍了 Paint 类、嵌套类、构造函数以及您将在 watch face design Java 代码中使用的方法。之后,您为时针、分针、秒针和刻度线创建了绘制对象。然后我讨论了画布类和。drawLine()方法,用于绘制所有的表盘组件。

接下来,您创建了。onDraw()方法逻辑将表盘组件部分绘制到画布上,然后您开发了高级模式支持,使用 Paint 方法实现特殊的表盘 API 模式,使用基于布尔标志值动态调整绘制特征的方法。之后,您在 onAmbientModeChanged()和 onVisibilityChanged()方法中实现了这些新方法,它们是核心的 WatchFaceService 方法。

在下一章中,您将开始向手表表面应用添加位图图形和 2D 设计元素,并且您将学习如何创建使用新媒体资源和矢量代码的手表表面。您还将开始使用 AVD 仿真器测试您的代码。**

十一、表盘位图设计:为表盘使用光栅图形

现在,您已经有了足够的 WatchFaces API 代码来测试您的 Java 代码,这意味着这将是一个繁忙的章节。您将学习如何让模拟器工作,测试代码,并进行任何添加,在您有了一个可工作的 vector watch face 应用后,您将了解如何合并 BitmapDrawable 素材来为 watch face 应用创建背景图像。大多数表盘设计将利用位图资源和矢量绘图代码的组合来创建设计。

在让 AVD 模拟器工作并测试了目前已经准备好的代码库之后,您将确保让 watch face 工作所需的每个 Java 语句都准备好了。高级警告,我漏掉了一两个,所以您可以看到如何使用 AVDs 来测试 WatchFaces API Java 代码!

完成基本的表盘代码后,您将添加一个方法来检测表盘是圆的还是方的,然后您将进入不同的位图图像相关的类,这些类是实现矢量表盘设计背后的背景图像所需要的。

我将讨论用于访问插图信息的 Android WindowInsets 类,以及获取数字图像素材所需的 Android Bitmap 和 Resources 类。我还将讨论 Android Drawable 和 BitmapDrawable 类,它们需要将您的数字图像资源和原始位图数据包装成一种格式,onDraw()方法可以利用这种格式将位图图像素材写入您的手表表面背景。

测试表盘设计:使用圆形 AVD

既然现在已经有足够的代码来测试 watch face 应用,那么打开 Android Studio ProAndroidWearable 项目,让我们通过 Watch Face Round 2 (ARM)仿真器运行代码。我已经折叠了所有的代码,以显示大约 60 行顶级代码,如图 11-1 左侧所示(标有 1)。使用 IDE 顶部的运行菜单访问运行...子菜单(标有 2),这将打开运行浮动菜单(标有 3)。如果您是第一次选择此项,您的编辑配置对话框将会打开,这正是您想要的,因为您将需要选择显示的不启动活动选项(标有 4)。

9781430265504_Fig11-01.jpg

图 11-1 。使用运行image穿菜单序列;在编辑配置对话框中,选择不启动活动

一旦您选择了“不启动活动”选项,因为 WatchFace 应用不使用活动对象(因为它是一个壁纸对象),请单击“应用”按钮(位于对话框的右下角),然后单击“运行”按钮。

这将启动一个等待 adb 进度条对话框,如图 11-2 顶部的所示。最终会出现你的 Android_Wear_Round AVD 模拟器。

9781430265504_Fig11-02.jpg

图 11-2 。启动 Android Wear Round AVD,尝试测试应用。AVD 崩溃,单击确定关闭

在图 11-2 左侧可以看到,模拟器会将其组件加载优化到内存中,告诉你 Android 正在启动,然后你的 Android OS 就会出现。在我的情况下,Android 似乎知道我在写这本书,AVD 在我身上崩溃了,所以我借此机会向你展示,在图 11-2 的右侧,Android 开发环境并不比操作系统更防弹!这在新版本的软件中尤其如此,如 Android 5 (Android Studio 1.x),因为它们尚未完善。你应该预料到打嗝,像 AVDs 不能正常工作,不要让他们阻止你!

如果你曾经得到这个“Android Wear 没有响应”的屏幕,如右边的图 11-2 所示,只需点击 OK 按钮,进行任何更改,然后再试一次!我在使用方形和圆形 AVD 仿真器时都遇到了一些问题,所以我将在本章的第一部分向您展示我为纠正这些问题所做的一些事情。

当我使用运行image运行磨损菜单序列再次尝试时,我得到了在图 11-3 左下方看到的磨损选项卡。这个想法的这一部分向您展示了 AVD 正在发生的事情,包括位置和任何问题。

9781430265504_Fig11-03.jpg

图 11-3 。尝试使用运行image佩戴菜单序列再次启动 AVD 安卓 DDMS 面板显示流程

这次当我运行 AVD 模拟器时, Android DDMS 也弹出一个面板,在左侧窗格显示进程信息,在右侧窗格显示 logcat ,是错误日志目录的简称,如图图 11-3 所示。logcat 窗格当前是空的,因此没有记录任何错误,至少在包含代码启动的时间段内没有。一旦您开始使用 Android 应用,该窗格中可能会出现错误,通知您代码中的任何问题。

一旦你的 AVD 启动,如图 11-4 所示,找到设置选项,点击它,并找到 改变手表表面选项,然后点击它并横向滚动手表表面,直到你找到你的专业手表表面选项。一旦你点击了你的预览图片,Android 就会启动 Pro 表盘设计,它显示在图 11-4 的最右侧。似乎 onDraw()方法正在正确绘制表盘设计,但是秒针没有前进,因此您需要检查计时代码。

9781430265504_Fig11-04.jpg

图 11-4 。找到设置image改变表盘顺序,选择专业表盘,运行你的表盘

将整个秒延迟发送到您的处理程序对象

因为秒针固定在原位,所以开始查看代码的逻辑位置是名为 updateTimeHandler 的处理程序对象,因为这段代码是表盘应用每秒启动计时器逻辑的位置。

里面。 handleMessage( ) 方法,会注意到,在条件 if(isTimerEnabled( )) 结构中你已经计算出了 msDelay (直到下一个整秒偏移值的时间)。但是,您没有将那个 msDelay 数据值发送给 Handler 对象,因此它可以触发下一个与时间相关的消息对象。

这是通过使用**完成的。****sendEmptyMessageDelayed()**方法 调用,完成向 handler 对象发送 msDelay 值的 Java 编程语句将在 msDelay 计算后立即执行。Java 代码可以在图 11-5 的中看到,看起来应该像下面的语句:

updateTimeHandler.sendEmptyMessageDelayed(UPDATE_TIME_MESSAGE, msDelay);

9781430265504_Fig11-05.jpg

图 11-5 。添加对 updateTimeHandler 对象的. sendEmptyMessageDelayed()方法的调用

它所做的是使用方法调用将 UPDATE_TIME_MESSAGE 值和以毫秒为单位的延迟值发送到 updateTimeHandler,该方法调用(通过其名称)指定它在确切的 msDelay 值(表示下一个完整的(1,000 毫秒)秒发送一条空消息(触发器)。

使用 Run image Run Wear 菜单序列启动 AVD 测试表盘。秒针还冻着,肯定还少了什么!

在绘制逻辑中将时间对象设置为当前时间

既然 Handler 对象正在广播正确的整秒时间,那么检查基于时序的逻辑的下一个逻辑位置是在中。onDraw( ) 法。请注意,您使用了 watchFaceTime 时间对象来计算小时、分钟和秒针角度位置。在执行这些计算之前,您需要确保时间对象设置准确。因此,你需要打电话给**。** setToNow( ) 方法,关闭 watchFaceTime 时间对象,在顶部。onDraw()方法。

我把它放在 super.onDraw()方法调用和。将背景色设置为黑色的 drawColor()方法调用。Java 编程语句应该如下所示:

watchFaceTime.setToNow( );

正如你在图 11-6 中看到的,Java 代码是没有错误的,你现在已经准备好使用 Round AVD 再次测试代码了。我在 Java 代码中单击了 watchFaceTime 对象引用,以跟踪它在 onDraw()方法中的使用。

9781430265504_Fig11-06.jpg

图 11-6 。向添加一个调用。方法后关闭 watchFaceTime 对象。drawColor()调用

将 Java 代码的 watchFaceTime 时间对象更新行添加到代码中后,在回合 AVD 中再次测试手表表面。现在秒针应该在滴答走了,您已经准备好在 Square AVD 模拟器中测试代码,以获得一些使用 Android_Wear_Square AVD 的经验。

测试表盘设计:使用方形 AVD

使用运行image编辑配置菜单序列(如图图 11-1 所示)将仿真器设置为 Android Wear Square AVD,如图图 11-7 所示。

9781430265504_Fig11-07.jpg

图 11-7 。使用编辑配置对话框选择方形 AVD

我进入设置image换表面找了一个方形的表面预览,没有收录!我检查了我的 AndroidManifest.xml 文件,以确保引用了正确的图像素材,然后我使用谷歌查看是否有其他人在使用 square watch face 模拟器时遇到了这个问题。我看到的一个建议与 AVD 使用主机 GPU 选项有关。有人建议取消这个选项,有人建议选择它!

所以我两个都试过了;两种设置都不起作用!所以我尝试了 横向,这在模拟器中将我的内容转向侧面,但它没有显示方形的表盘预览,所以我尝试了将内存增加到 1GB 以及将内存增加到 500MB ,所有这些的结果都可以在图 11-8 中看到。

9781430265504_Fig11-08.jpg

图 11-8 。尝试改变使用主机 GPU 和方向设置,并增加 RAM 和内部存储

这些设置都不起作用,我花了几天时间试图让 square 预览出现在模拟器中,以便测试代码。我想确保这些 AVD 模拟器工作正常,因为不是所有人都有智能手表硬件来测试!我不断尝试不同的东西,以找出为什么 square Pro 表盘预览没有在 Square AVD 模拟器中显示出来。

因为这可能在某个时候发生在你身上(不仅仅是模拟器),我将告诉你一些我尝试过的事情和最终成功的事情!

在建议使用主机 GPU 不起作用并且给 AVD 更多系统资源也不起作用之后,我想知道 320 像素的正方形预览对于标准的 280 度倾斜正方形表盘来说是否太大了,因为 Android 的文档建议 280 度倾斜尺寸用于正方形表盘设计。

当我在所有这些不同的 AVD 设置和图像素材尺寸之间迭代时,我遇到了几次 Android 方形臂 AVD 的崩溃,这给了我如图图 11-9 所示的对话框。我并没有因此而气馁,因为 Android Studio 和 Android 5 都是新平台,肯定会有很多漏洞,至少在一段时间内是这样。我只是关闭了 AVD(如果它没有因为 emulator-arm.exe 的错误对话框而消失的话)并继续尝试。

9781430265504_Fig11-09.jpg

图 11-9 。在显示此对话框的过程中,方形 AVD 崩溃

我在运行/调试(编辑)配置对话框中的仿真器选项卡下找到了一个解决方案,至少对于我的硬件设置和安装是这样的。该选项卡在图 11-10 中显示为选中状态,在该选项卡下面是一个在我的安装中被默认选中的选项,名为附加命令行选项。我觉得奇怪的是这个选项被选中了,而且是空的,所以我取消选择了它。我还选择了擦除用户数据选项,以确保每次启动时,我都将这个 ARM AVD“干净”地加载到我的系统内存中。

9781430265504_Fig11-10.jpg

图 11-10 。取消选择其他命令行选项并选择擦除用户数据

我在这里的想法是,有什么东西被加载到系统内存中的 AVD 仿真器代码中,这阻止了 square watch face 预览加载到 Android Square ARM AVD 仿真器中,或在 Android Square ARM AVD 仿真器的 UI 中显示。事实证明,这解决了问题,这很好,因为我需要这个 AVD 仿真器为那些没有物理智能手表硬件但想了解 Android Wear 的人工作。

高级模拟器选项显示在图 11-10 中间,包括擦除用户数据选项,我现在已经选中了(如图)。

还有一个禁用引导动画你可以选择,如果你愿意,以加快 AVD 加载序列,也显示在图 11-10 。

现在,当我启动方形 AVD 并选择设置image更改手表表面选项时,我可以滚动并找到 Pro 手表表面方形手表表面预览图像,可以在图 11-11 中从左数第三个窗格中看到。需要注意的是,这个工作过程如果你碰巧遇到,也可以解决 Round AVD 仿真器中同样的问题。

9781430265504_Fig11-11.jpg

图 11-11 。选择设置image改变手表表面对话框,找到(方形)专业手表表面,运行并测试它

当我点击 Pro 手表表面预览图像时,我现在获得了手表表面设计,秒针正在滴答作响,因此基本的手表表面代码有效!

现在,您可以测试已经在程序逻辑中实现的各种特殊模式(低位、老化),这些模式在表盘进入环境模式时启动。在开始测试自定义硬件模式之前,我想了解一下如何使用 AVD 仿真器,因为它们相对较新,所以容易崩溃,以及如何使用 F7 键,该键在 AVD 仿真器中打开和关闭环境模式。

AVD 崩溃:无法连接和没有响应的面板

当我在写这一章的时候测试 watch faces 代码的时候,我遇到了大量的崩溃和上一节提到的问题,我甚至不能选择一个正方形的 watch faces 预览来测试我的 Java 代码。一旦我想通了这一点,我仍然有很多关于“悬挂”AVD 仿真器软件的问题,所以让我们看看我在这一部分遇到的两个场景,这样你就知道如何解决这种情况,如果你碰巧遇到它。希望在这本书出版时这些问题会被解决,但是你永远不知道,所以我在这里把它包括进来只是为了彻底。

这些崩溃的形式要么是“无法连接到手机上的 Android Wear 应用”屏幕,要么是“Android Wear 没有响应”屏幕。“无法连接”错误,可在左侧的图 11-12 中看到,通常允许您“重试”或“重置设备”并继续测试。要进入这两个屏幕,使用“滑动获取一些提示”选项,如图 11-12 中的第一个屏幕(左侧)所示。这将显示您可以单击的按钮;确保在使用重置(红色)按钮作为最后手段之前,先尝试重试(绿色)按钮。

9781430265504_Fig11-12.jpg

图 11-12 。如果你得到一个不能连接到 Android Wear 应用的屏幕,向左滑动几次以获得重试和重置窗格

我已经成功地使用了这两个按钮来“保存”AVD 会话,这样我就不必退出(在对话窗口的右上角使用一个红色的方形 X)AVD 仿真器软件并重新开始。

如果重试或重置设备按钮起作用,您将看到图 11-13 的中间窗格中显示的启动屏幕,然后您可以继续测试您的表盘应用。

9781430265504_Fig11-13.jpg

图 11-13 。如果你看到一个 Android Wear 没有响应的窗格,点击等待,你会看到开始屏幕。按 F7 进入环境模式

你可能会遇到的另一个显示停止错误屏幕是“ Android Wear 没有响应”屏幕,它可以在图 11-13 的最左侧看到。如果您点击等待按钮,您可能最终会得到图 11-13 中间的所示的启动屏幕。如果你点击 OK 按钮,AVD 模拟器将关闭,就像你使用了对话框左上角的红色方块 X 一样。

图 11-13 的右侧窗格中还显示了 AVD 环境模式,您可以通过按下 F7 键(键盘顶部的功能键 7)来调用该模式。正如你所看到的,代码通过使用将颜色调暗到亮度的 50%** 。setAlpha(127)** 技术,运转良好。如果想在环境模式下用灰度代替颜色,就要加上**。setColor(颜色。【WHITE)方法调用里面那段 Java 代码。**

现在,您已经知道如何使用 F7 键将 AVD 仿真器置于环境模式,您可以继续测试低位环境模式代码。

特殊屏幕模式:测试低位环境模式

有多种方法可以测试低位环境模式代码,方法是通过在应用编程逻辑内部的某个位置添加执行此操作的代码行,强制打开 lowBitAmbientModeFlag。

在逻辑上,lowBitAmbientModeFlag=true; toggle 语句将进入 onAmbientModeChanged( ) 方法或 onPropertiesChanged( ) 方法;在这一章中你将会用到这两个工具,所以你可以看到它们是如何工作的。

在超类方法调用后的 onAmbientModeChanged()方法中添加一个lowBitAmbientModeFlag=true; Java 语句,如图图 11-14 所示。

9781430265504_Fig11-14.jpg

图 11-14 。将 lowBitAmbientModeFlag=true 设置添加到 onAmbientModeChanged()方法中,以测试低位环境模式

一旦您人工将低位标志的值设置为真,您将使用 Run image Run Wear 菜单序列来启动 AVD 仿真器。接下来,使用设置image更改手表表面image专业手表表面序列,并启动你的手表表面应用。在图 11-15 的最右边可以看到处于低位环境模式的表盘,它使用白色,任何线条都没有抗锯齿(如锯齿状边缘所示)。

9781430265504_Fig11-15.jpg

图 11-15 。使用设置image改变手表表面image Pro 手表表面系列,和 F7 测试低比特环境模式

因为老化通常与低位模式一起使用,所以我们接下来添加老化标志设置,您可以在它们的“开”设置中测试这两个标志。

特殊屏幕模式:测试低位和老化模式

在评估该标志的 if()语句之前添加一个burnInProtectModeFlag=true;语句,如图 11-16 中的所示。IntelliJ 中弹出了一个灯泡图标,所以我下拉建议菜单,双击“从‘if’语句中删除大括号”建议。这使得 IntelliJ IDEA 稍微简化了 Java 代码。如果 IntelliJ 给你一个合理的代码优化建议,往往明智的做法是采纳它,看看效果如何!

9781430265504_Fig11-16.jpg

图 11-16 。添加一个 burnInProtectModeFlag=true,并采用 IntelliJ 建议来简化 if()构造

在这种情况下,它将该方法的代码行从 12 行减少到 6 行,或者说减少了 50%的代码,一旦您删除了强制代码行的标志,您将把它放入**。onPropertiesChanged( )** 方法下一步。

修改后的 onAmbientModeChanged()方法的 Java 代码(没有 true 标志设置)可以在图 11-17 的中看到没有错误,看起来像下面的 Java 方法结构:

@Override
public void onAmbientModeChanged(boolean ambientModeFlag) {
    super.onAmbientModeChanged(ambientModeFlag);
           if(lowBitAmbientModeFlag) setAntiAlias(!ambientModeFlag);
           if(burnInProtectModeFlag) setBurnInProtect(ambientModeFlag);
    ensureModeSupport;
    invalidate;
    checkTimer;
}

9781430265504_Fig11-17.jpg

图 11-17 。一旦实现了 IntelliJ if()优化建议,该方法就会减少到 6 行代码

接下来,让我们将这些特殊模式标志“强制真”设置放入 onPropertiesChanged()方法中,在方法的最后,如图图 11-18 所示。这个方法结构的新 Java 代码应该如下所示:

@Override
public void onPropertiesChanged(Bundle properties) {
    super.onPropertiesChanged(properties);
    lowBitAmbientModeFlag = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
    burnInProtectModeFlag = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
    lowBitAmbientModeFlag = true;
    burnInProtectModeFlag = true;
}

9781430265504_Fig11-18.jpg

图 11-18 。将设置为 true 值的特殊模式标志移到 onPropertiesChanged()方法的底部

现在,让我们使用设置image更改表盘image Pro 表盘序列再次测试表盘。正如你在图 11-19 中从左边第三个窗格看到的,当你按下 F7 键将表盘切换到环境模式时,低位标志逻辑正在工作,你已经知道了。

9781430265504_Fig11-19.jpg

图 11-19 。通过使用 Android_Wear_Square_API_21 AVD 模拟器测试您的 watch face Java 代码

然而,您的老化编程逻辑并没有像您预期的那样将轮廓放置在表盘的指针周围。再次按下 F7 键切换回交互模式,如图 11-19 最右边的第四个窗格所示。如您所见,表盘指针的 RBG 颜色值没有被恢复,因此您还需要了解一下 ensureModeSupport( ) 方法以及 setBurnInProtect( ) 方法。让我们先将开关恢复到交互式 RGB 颜色模式。

因为您在中配置了表盘指针颜色。onCreate()方法,方法是调用自定义的。createHand()方法,您需要在 ensureModeSupport()方法的最终 else 部分中设置颜色。

您已经使用将 alpha 值设置回 255(完全不透明)。setAlpha()方法调用,所以,需要使用**。setColor( )** 方法调用来重新配置 Paint 对象以使用蓝色、绿色、红色和白色的颜色类常量。我以这种方式编写代码,使用不同的方法调用,向您展示设置颜色值的不同方式。请记住,您使用了。setARGB()方法设置。createHand()方法。在图 11-20 中显示没有错误的 Java 代码应该如下所示:

} else {
    pHourHand.setAlpha(255);
    pMinuteHand.setAlpha(255);
    pSecondHand.setAlpha(255);
    pTickMarks.setAlpha(255);
    pHourHand.setColor(Color.BLUE);
    pMinuteHand.setColor(Color.GREEN);
    pSecondHand.setColor(Color.RED);
    pTickMarks.setColor(Color.WHITE);
}

9781430265504_Fig11-20.jpg

图 11-20 。添加。setColor()方法调用 ensureModeSupport()方法中 if-else 循环的 else 部分

现在我们来看看为什么?setStyle(画图。Style.STROKE)方法不是在时针和分针表盘部件周围描绘单个像素轮廓。从图 11-19 中可以看到。setBurnInProtect()方法显然对屏幕上显示的内容没有任何影响。

正如你在图 10-25 中看到的,你正在尝试使用笔画绘画。样式常量在时针和分针矢量对象周围创建轮廓效果,并使用填充绘制。样式来创建实心或填充效果。虽然这种方法可以很好地处理 Android 中的任何 2D ShapeDrawable 对象,包括文本、圆形、矩形和其他“封闭”的线条或曲线形状,但它不能处理 1D “开放”的线条或曲线形状的对象,因为它们没有内部**!因此,在 1D 矢量“射线”对象的情况下,描边和填充将产生完全相同的效果!**

你需要做的就是使用来完成这项工作。setStrokeWidth( )** 方法调用线条形状对象,这将允许您通过不必声明画图来优化老化方法。名为 paintStyle 的样式对象,并简化 if-else 条件结构,对时针使用三个像素的**,它需要比分针粗,对分针使用两个像素的**,这与您对刻度元素使用的值相同,因此它们可以很容易地在表盘显示上看到。****

****使用单个像素绘制表盘元素将无法让最终用户轻松读取表盘时间,即使这是屏幕老化保护的最佳设置。如果你想看看它看起来怎么样,你可以在你的表盘应用中使用 1.f 笔画宽度数据值进行实验!在这种情况下,时针需要 2.f 才能区分,分针和刻度线将使用 1.f 设置。在图 11-21 中可以看到一个新的方法结构,它为绘制对象设置了 StrokeWidth 属性,应该是这样的:

private void setBurnInProtect(boolean enabled) {
    if(enabled) {
        pHourHand.setStrokeWidth(3.f);
        pMinuteHand.setStrokeWidth(2.f);
    } else {
        pHourHand.setStrokeWidth(6.f);
        pMinuteHand.setStrokeWidth(4.f);
    }
}

9781430265504_Fig11-21.jpg

图 11-21 。去除油漆。请使用 setBurnInProtection 方法中的样式逻辑,而不是使用。setStrokeWidth()

现在,当您使用 Run image Run Wear 并启动 AVD 仿真器并使用设置imageChange Watch FaceimagePro Watch FaceimageF7 键时,您将获得启用低位和老化模式的环境模式,如图 11-22 中最右侧窗格所示。

9781430265504_Fig11-22.jpg

图 11-22 。使用设置image改变手表表面image Pro 手表表面系列,和 F7 测试低比特环境模式

正如你所看到的,手表表面仍然是有吸引力的,可读的,完全可用的,即使使用零抗锯齿。表盘设计使用非常少的像素来绘制表盘设计元素,这有助于提供屏幕老化保护,这是该模式的目标。

接下来你需要做的是学习如何检测用户的手表表面是方形还是圆形,使用 Android 的 WindowInsets 类。在您将表盘形状检测代码放置到位后,您将了解 Android 位图DrawableBitmapDrawable 类,以及如何使用这些来实现矢量表盘组件的位图背景图像,将您的 Android 表盘提升到全新的水平。

Android WindowInsets 类:轮询屏幕形状

Android WindowInsets 类是一个公共 final 类,它扩展了 java.lang.Object 类。阶级阶层看起来像这样:

java.lang.Object
  > android.view.WindowInsets

这个类是 android.view 包的一部分,因为它与视图对象一起使用。WindowInsets 对象可用于描述应用窗口内容的一组插入。在这种情况下,这个对象保存了表盘的特征,例如表盘的形状以及它是否有一个架子,就像摩托罗拉 MOTO 360 目前所做的那样。

这些 WindowInsets 对象是“不可变的”(固定的或不可变的)。他们可能会被扩展(由谷歌的 Android 团队),在未来包括其他插入类型。这里您将结合使用 WindowInsets 对象和**。onapplywindowsets(window insets)**方法,这是您接下来要编写的代码。

这个 WindowInsets 类有一个公共构造函数方法,它采用格式WindowInsets(WindowInsets insets)并构造一个(新的)WindowInsets 对象。它通过从源 WindowInsets 定义中复制数据值来实现这一点,在本例中,这将来自每个智能手表制造商。

这个 WindowInsets 类有 18 个方法,对于 WatchFaces API 开发,您可能想了解其中的两个。 isRound( ) 方法会告诉你表盘是圆的(或者不是圆的,会是方的),还有**。getSystemWindowInsetBottom()**方法会告诉你 MOTO 360 用来连接其手表表面屏幕的“架子”的大小(整数)。

接下来,让我们创建**onapplywindowsets()**方法,这是 WatchFaceService 的一个方法。引擎类的实现我一直保存到现在,当你学习一些实际上可以“利用”它所提供的东西的时候!

检测表盘形状:使用窗口镶嵌

在引擎类中 onTimerTick()方法之后添加一个**public void on applywindowsets(window insets insets)**空方法结构。正如你在图 11-23 中看到的,你必须使用 Alt+Enter 并让 IntelliJ 在你的类的顶部为你编写import android.view.WindowInsets;语句。

9781430265504_Fig11-23.jpg

图 11-23 。添加 public void on applywindowsets(window insets insets) empty 方法结构

下一步是使用下面的 Java 变量声明语句将 roundFlag 布尔变量添加到复合布尔语句的末尾(在私有引擎类的顶部),如图 11-24 顶部所示(高亮显示):

boolean lowBitAmbientModeFlag, burnInProtectModeFlag, roundFlag;

9781430265504_Fig11-24.jpg

图 11-24 。将名为 roundFlag 的布尔变量添加到 Engine 类中,并在 onApplyWindowInsets()方法中将其设置为等于 insets.isRound()

在空的 onApplyWindowInsets()方法中,使用 Java super 关键字调用超类,并将名为 insets 的 WindowInsets 对象向上传递给父类。接下来,将 roundFlag 布尔变量设置为等于的布尔值。 isRound( ) 方法调用掉 insets WindowInsets 对象。

如 Android Studio 底部的图 11-24 所示,Java 方法应该类似于下面的 Java 方法结构:

@Override
private void onApplyWindowInsets(WindowInsets insets) {
    super.onApplyWindowInsets(insets);
    roundFlag = insets.isRound( );
}

现在你有办法知道用户的智能手表使用的是圆形还是方形显示硬件。在本章的下一部分中,当您学习如何将位图图像放置在矢量观察面组件的后面时,将会用到这个布尔值。然而,首先让我们看看 Android 的位图和资源类,您将使用它们来加载您的图像数据。

Android 位图类:使用数字图像资源

Android Bitmap 类是一个 public final 类,实现了 Java Parcelable 接口,扩展了 java.lang.Object master 类。位图类层次结构如下所示:

java.lang.Object
  > android.graphics.Bitmap

Android Bitmap 类有两个嵌套的(helper)类。第一个是枚举位图。CompressFormat 类,它指定了这些位图对象可以使用编解码器压缩成的已知图像文件格式,编解码器是 Android 操作系统的一部分。枚举值包括 JPEGWEBPPNG

第二个是枚举位图。Config 类,它指定可能的位图配置。其中包括 alpha_8 ,这是 8 位 256 透明度级别 ALPHA 通道专用格式,以及 ARGB_8888 ,这是 32 位格式,每色彩平面(和 ALPHA 通道)使用 8 位数据。

还有一种叫做 RGB_565 的 16 位位图格式,很有意思,因为 Android 目前还没有 16 位编解码支持(BMP、TIF、TGA 都支持 16 位颜色)。还有被弃用(意味着不再支持)的 ARGB_4444 格式,你不应该使用,因为它在 Android API Level 13 中被弃用了。

Bitmap 类有 50 多个方法,所以很明显,我不能在这里详细介绍这些方法,但我会在本章以及下一章中介绍那些您将在 WatchFaces API 设计中用来实现位图素材的方法,在下一章中,我将讨论用于创建其他 watch face 模式位图素材的数字图像技术。

getWidth( ) 方法调用将返回位图对象的宽度属性,同样的,也是如此。 getHeight( ) 方法调用,将返回位图对象的高度属性。

Android API Level 1 中添加的public static Bitmap createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter) 方法,通过缩放源位图对象的图像数据来创建新的位图对象。方法参数包括一个 src ,源位图对象,一个 dstWidth ,或目标位图对象的目标宽度,一个 dstHeight ,目标位图对象的目标高度,以及一个布尔过滤器数据值,如果您希望 Android 将双线性插值应用于图像缩放算法,该值将被设置为 true 。您将使用此选项来获得最高质量的图像缩放结果。如果要进行上采样(从较低的分辨率到较高的分辨率),这一点尤其重要。

该方法返回一个缩放的位图对象,或者如果没有执行缩放,则逻辑上返回源位图对象。如果源位图对象的宽度小于(或等于)零,或者如果位图对象的高度小于或等于零,该方法将引发 IllegalArgumentException。您将在后面的章节中使用这个方法。onDraw()方法来确保您的位图对象适合您的表盘显示。

接下来,让我们看看 Android Resources 类,因为您将不得不使用它来将数字图像资源加载到 Bitmap 对象中。

Android 资源类:使用你的 Res 文件夹

公共 Android Resources 类扩展了 java.lang.Object,是 android.content.res 包的一部分。它有一个已知的直接子类,称为 MockResources。Java 类的层次结构如下所示:

java.lang.Object
  > android.content.res.Resources

Resources 类用于创建对象,这些对象允许您访问存储在 /res 文件夹中的应用资源。这个 Android“R”资源系统(例如,存储在 /res/drawable-hdpi 中并被引用为 R.drawable.imagename 的图像)跟踪与您的 Android 应用相关联的所有非代码素材。您可以使用这个类来访问这些应用资源,就像您将在下一节中所做的那样。

通过使用**,您可以获得一个 Resources 对象,该对象加载了对您的应用中的外部项目素材(资源)的所有引用。getResources( )** 方法调用应用的主上下文,使用 Java this 关键字访问该上下文。在这个应用场景中,它看起来像下面的 Java 语句:

Resources watchFaceResources = ProWatchFaceService.this.getResources( );

IntelliJ IDEA 中安装的 Android SDK 工具在构建时将应用资源编译成应用二进制文件,它们从/res/drawable 引用路径进入 R.drawable 引用路径。这就是在 Java 方法调用中使用 R.drawable.image_asset_name 而不是/res 的原因。

为了能够将外部资源用作资源,您必须将它定位到项目 res/目录中正确的源子文件夹中,例如,图像形状将进入**/RES/drawable**(r . drawable)矢量动画将进入 /res/anim (R.anim)。

作为应用构建(编译)过程的一部分,Android SDK 工具将为每个素材资源生成 r .符号。然后,您可以在应用的 Java 代码中使用这些 R. references 来访问资源。确保在运行时使用 Java 代码中的 R. references 来访问素材。

使用外部应用资源使开发人员能够在不修改 Java 代码或 XML 标记的情况下更改应用的可视特征。此外,提供这些替代资源将允许开发者优化他们的应用,跨越异常广泛(并且快速增长)的消费电子设备硬件配置集合。动态访问不同新媒体资源的能力将允许开发人员适应不同的场景,如不同的最终用户语言、不同的屏幕尺寸、形状和密度,以及在这种情况下,圆形表盘与方形表盘!

能够在运行时动态访问各种资源是开发兼容各种不同类型硬件设备的 Android Watch Faces(和其他)应用的一个重要方面。

Resources 类有两个嵌套的(也称为 helper)类。这里有资源。NotFoundException 类,该类处理在找不到请求的资源(或其路径)时引发资源 API 异常的情况。还有一个资源。主题类,保存应用中实现的特定操作系统主题的当前属性值。

创建资源对象有一个公共的构造函数方法,采用这个参数格式:Resources(asset manager assets,DisplayMetrics metrics,Configuration config) 。请注意,如果您使用。getResources(),这个资源对象将会为您创建,您不必显式地使用这个构造函数方法调用和格式化。

这是一个实现的情况,您将在本章后面的 Java 语句中使用它,这在本节前面已有概述。现在让我们为 watch face 应用创建位图和资源对象。

访问图像:使用位图和资源

在私有引擎类的顶部(声明区域) 您需要做的第一件事是创建声明和命名两个位图对象的复合语句。如果由于某种原因,智能手表硬件使用的分辨率不是 320 x 320 像素,则这些位图对象中的一个将保存图像资源(原始图像数据),另一个将保存图像数据的缩放版本。

这种方法将允许您使用更高分辨率的数字图像素材,如果更高分辨率的表盘是在以后几年开发的。复合语句的 Java 代码可以在图 11-25 中看到,应该如下所示:

Bitmap watchFaceBitmap, scaleWatchFaceBitmap;

9781430265504_Fig11-25.jpg

图 11-25 。添加一个名为 watchFaceBitmap 和 scaleWatchFaceBitmap 的复合位图对象声明

点击代码行的任意位置,如图 11-25 中突出显示的,使用 Alt+Enter 击键序列告诉 IntelliJ 为您编写导入语句。

接下来要做的事情是创建资源对象。最合理的方法是 onCreate()方法,因为它只需要在应用启动时执行一次。你可以用其他声明在引擎类的顶部声明资源 watchFaceResources,或者你可以在 onCreate()中声明、命名并本地加载这个对象**,使用下面的 Java 语句,这也可以在图 11-26 中看到:**

Resources watchFaceResources = ProWatchFaceService.this.getResources( );

9781430265504_Fig11-26.jpg

图 11-26 。创建一个名为 watchFaceResources 的资源对象;使用。getResources()方法

点击代码的资源行,使用 Alt+Enter 工作流程,指导 IntelliJ 编写导入语句,如图图 11-26 所示。

在您继续编码之前,让我们花点时间了解一下 Android Drawable 类,因为您将在代码中实现 Drawable 对象来保存这些位图对象中的一个。

Android 可绘制类:创建可绘制对象

Android 公共抽象 Drawable 类是为在 Android 中创建 Drawable 对象而临时编写的,因此它直接扩展了 java.lang.Object,如以下代码所示:

java.lang.Object
  > android.graphics.drawable.Drawable

Drawable 是一个 Android 术语,指可以在屏幕上绘制的东西。Drawable 类提供了处理底层可视资源的通用 API,这些可视资源可能是大量与图形相关的绘图元素类型中的任何一种,如九色、矢量、颜色、形状、渐变、插入、图层、剪辑或位图,这是您将在本章的下一节中使用的内容。

Drawable 对象没有任何接收或处理事件或直接与用户交互的能力,因此您必须用某种类型的视图对象(小部件)来“包装”Drawable 对象才能做到这一点。

drawable 有 15 个已知的直接子类。您将使用 BitmapDrawable 子类,但您也可以在应用开发中使用其他子类,包括:VectorDrawable、GradientDrawable、NinePatchDrawable、AnimatedVectorDrawable、PictureDrawable、LayerDrawable、ClipDrawable、ColorDrawable、ScaleDrawable、RotateDrawable、ShapeDrawable、InsetDrawable、RoundedBitmapDrawable,还有一个 DrawableContainer。

有七个已知的间接亚类;这些是直接子类的子类,包括:AnimatedStateListDrawable、RippleDrawable、AnimationDrawable、PaintDrawable、LevelListDrawable、StateListDrawable 和 TransitionDrawable。

Drawables 在被包装到视图中之前,对 Android 应用来说是不可见的。Drawables 采用多种图形元素格式:

  • 您将在这里使用的可绘制对象是BitmapDrawable;它是“位”或像素的映射,使用 GIF、PNG、WEBP 或 JPEG 数字图像“编解码器”将数字图像数据素材压缩和解压缩到系统内存中。
  • NinePatchDrawable 是对 PNG 数据格式的扩展,允许图像指定如何拉伸和缩放周界区域。
  • ShapeDrawable 包含简单的矢量绘图命令,而不是原始位图对象,允许矢量作品“渲染”到任何屏幕大小。
  • LayerDrawable 允许开发者创建一个图像合成的 Drawable。这种类型的 drawable 就像 Android 中的 mini-GIMP,你可以使用 z-order 层将多个位图 drawable 堆叠在另一个之上。
  • statedravable是另一种类型的复合 Drawable,它根据 statedravable 的状态设置从一组给定的 drawable 中选择一个。多状态 Android ImageButton 小部件就是一个很好的例子。
  • LevelDrawable 是另一种类型的复合 Drawable,它根据 LevelDrawable 对象的级别设置,从一组给定的 drawable 中选择一个。状态栏上的信号电平图标就是一个很好的例子。

Android 中有许多其他可绘制的对象类型,所以如果你有兴趣更详细地了解所有这些,请查看 ApressPro Android Graphics(2013)标题。

现在是时候创建手表表面 Drawable 对象,并使用它来加载一个位图对象,这样您就可以开始将背景图像添加到手表表面设计中。您还将合并 roundFlag 布尔值,以便如果您对方形和圆形表盘有不同的设计,您将知道如何设置表盘代码以使用正确的版本。

加载 Drawable:使用 roundFlag 布尔值

你需要做的第一件事,在私有的引擎类的顶部,是声明并命名你的可绘制对象。让我们为这个 Drawable 对象使用逻辑名 watchFaceDrawable ,这样当你在代码中使用它时,你就能确切地知道它是什么了。可绘制对象声明可以在图 11-27 的中看到,看起来应该像下面的 Java 对象声明:

Drawable watchFaceDrawable;

9781430265504_Fig11-27.jpg

图 11-27 。在 Engine 类中添加一个 Drawable 对象声明,并将该对象命名为 watchFaceDrawable

接下来您要做的是在 onCreate()方法的顶部,在 Resources 对象声明和实例化代码行之后添加一个空的条件 if-else 结构。

条件结构将评估 roundFlag 布尔变量,并用正确的数字图像资源加载 watchFaceDrawable 对象。在图 11-28 中可以看到正在构建的空语句,它应该看起来像下面的 Java 条件 if-else(空)结构:

if(roundFlag) {
         // Round Watch Face Java Statements
} else {
         // Square Watch Face Java Statements
}

9781430265504_Fig11-28.jpg

图 11-28 。在 onCreate()方法中添加 if(roundFlag)条件结构;从弹出菜单中选择圆形标志

在条件语句的 if 部分,设置watchfacedravable对象等于getDrawable(r . drawable . preview _ pro _ circular)方法调用watchfacesource对象。在图 11-38 中,可以看到预览 _pro_circular PNG 以蓝色突出显示。一旦你完成了整个条件 if-else 结构的编码,如图 11-29 所示的 Java 代码应该如下所示:

if(roundFlag) {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.preview_pro_circular);
} else {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.preview_pro_square);
}

9781430265504_Fig11-29.jpg

图 11-29 。在 if 中添加一个 watchFaceDrawable 对象,用 watchFaceResources.getDrawable()加载它

正如你在图 11-29 中看到的,当你编码。getDrawable()方法调用时,IntelliJ 会为你列出你的 Resources (R.) object assets,就像你键入的每个句点字符一样。键入 R 和句点,然后选择可绘制类型(文件夹)。接下来输入另一个句号,然后选择 preview_pro_circular PNG 图片引用,完成 Java 编程语句。现在你要做的就是在 else 结构中重复这个。

一旦你完成创建这个 if(roundFlag)-else 结构,用正确的资源对象引用加载你的 watchFaceDrawable 对象,你的代码应该是没有错误的,如图图 11-30 所示,正方形手表表面数字图像素材引用在适当的位置(并突出显示)。

9781430265504_Fig11-30.jpg

图 11-30 。在 if-else 语句的 else 部分添加不同的 watchFaceResources.getDrawable()方法调用

接下来,让我们快速查看一下 Android BitmapDrawable 类,您将在下一行 Java 代码中使用该类来“转换”Drawable 对象,该对象现在已经根据 roundFlag 布尔变量的设置加载了正确的图像资源引用。isRound()方法调用。咻!

我想给你一个这个类的概述,因为 BitmapDrawables 是 Android 应用开发中最强大和最常用的可绘制对象类型之一,既用于 UI 设计,也用于应用的图形设计。如果您正在寻找更多关于 2D Android UI 设计的高级资料,有机会的话,请查看 Apress titlePro Android UI(2014)。

Android 的 BitmapDrawable 类:图像绘制

Android 的公共 BitmapDrawable 类扩展了 Drawable 类,包含在Android . graphics . Drawable包中。Java 类的层次结构如下所示:

java.lang.Object
  > android.graphics.drawable.Drawable
    > android.graphics.drawable.BitmapDrawable

BitmapDrawable 是一个包含 Bitmap 对象的 Drawable 对象,该对象可以平铺、拉伸、旋转、着色、褪色或对齐。您可以使用 Android API Level 1 中引入的八种原始构造函数方法中的三种重载构造函数方法之一来创建 BitmapDrawable:

BitmapDrawable( )                                // This constructor was deprecated in API 4 and can be ignored

BitmapDrawable(Resources res)                   // This constructor was deprecated in API 18 and can be ignored

BitmapDrawable(Bitmap bitmap)                   // This constructor was deprecated in API level 4 and can be ignored

BitmapDrawable(Resources res, Bitmap bitmap)    // Creates Drawable using an external bitmap resource

BitmapDrawable(String filepath)                 // This constructor was deprecated in API level 5 and can be ignored

BitmapDrawable(Resources res, String filepath)  // Create a Drawable by decoding from a file path

BitmapDrawable(InputStream is)                  // This constructor was deprecated in API level 5 and can be ignored

BitmapDrawable(Resources res, InputStream is)  // Create Drawable decoding bitmap from input stream

因为您将强制转换 BitmapDrawable 对象,所以我不会在这里涵盖所有这些构造函数方法;不过,可以说,您可以通过使用图像文件路径、使用输入流、使用另一个位图对象、使用 XML 定义膨胀、使用另一个位图对象或使用资源对象来创建 BitmapDrawable 对象,就像您将要做的那样。

如果你想使用一个 XML 定义文件定义一个 BitmapDrawable,使用一个< bitmap > XML 标签来定义这个元素。BitmapDrawable 将与 Bitmap 对象一起使用,该对象处理原始位图图形的管理和转换,并且最终将是在绘制到 Canvas 对象时使用的对象,您将在本章使用的代码中注意到这一点。

有许多 XML 属性、参数或特性可用于位图对象,在表 11-1 中有所概述。

表 11-1 。使用 XML 标记参数可访问的 BitmapDrawable 属性

|

位图属性

|

位图属性功能描述

反别名 启用或禁用抗锯齿(边缘平滑算法)
发抖 针对颜色深度不匹配启用位图抖动(ARGB8888 至 RGB565)
过滤器 为高质量缩放启用或禁用位图双线性过滤
重力 定义用于位图对象的重力常数设置
米帕普 启用或禁用 mipMap 提示功能
科学研究委员会 位图资源文件标识符资源路径
平铺模式 定义整体位图对象平铺模式
平铺模式 明确定义水平平铺模式
平铺模式 明确定义垂直平铺模式

使用 BitmapDrawable 对象:提取和缩放

接下来,让我们实现一个 BitmapDrawable 对象,从包含它的 Drawable 对象中获取所需的位图对象数据。Java 语句用下面的代码将watchFaceDrawable Drawable 转换为 (BitmapDrawable) ,在图 11-31 : 中可以看到没有错误

watchFaceBitmap = ( (BitmapDrawable) watchFaceDrawable ).getBitmap( ); // Cast to a (BitmapDrawable)

9781430265504_Fig11-31.jpg

图 11-31 。添加一个 watchFaceBitmap 对象,并将其设置为等于 watchFaceDrawable 对象中的位图资源

这一行简洁的 Java 代码包含了 watchFaceBitmap Bitmap 对象、watchFaceDrawableDrawable 对象(包含您的图像资源)、一个未声明的 BitmapDrawable 对象,该对象充当 APK 文件中的 Drawable 素材和需要驻留在最终用户的 Android 硬件设备系统内存中的原始位图之间的桥梁。

该语句所做的是将 watchFaceBitmap 位图对象设置为等于一个的结果。getBitmap( ) 方法调用,该方法调用自转换结构,其中使用**(BitmapDrawable)watchFaceDrawable**转换结构将 watchFaceDrawable 对象转换为 BitmapDrawable,该转换结构神奇地将 Drawable 对象转换为 BitmapDrawable 对象。

一旦 Drawable 被转换成 BitmapDrawable,这个。getBitmap()方法调用将工作,也就是说,它将是一个有效的方法调用,不会抛出异常。

现在,您已经准备好了方法,这些方法是。来自 WatchFaceService 的 onAmbientModeChanged()方法。引擎类,当智能手表硬件进入环境模式时,你需要确保所有这些不同的模式都得到支持。

缩放位图:使用。createScaledBitmap()方法

下一个需要放置代码的地方是 onDraw()方法内部,在这里需要将一个位图对象插入到绘制管道中。在计算宽度和高度值之后,创建一个空的条件 if 结构,因为您将使用这些值来确定是否需要缩放。在图 11-32 的中显示的正在构建中的代码应该是这样的:

if(scaleWatchFaceBitmap) {
    // an empty conditional if statement thus far
}

9781430265504_Fig11-32.jpg

图 11-32 。添加一个 if(scaleWatchFaceBitmap)条件结构,以确定是否需要缩放位图

在条件 if 结构中,评估 scaledWatchFaceBitmap 是否未使用(空的或 null ),或者它是否与源位图具有不同的维度。如图图 11-33 所示,Java 代码应该如下所示:

if ( scaleWatchFaceBitmap == null ||
     scaleWatchFaceBitmap.getWidth( ) != width ||
     scaleWatchFaceBitmap.getWidth( ) != height  ) { // Java code to be processed will go in here }

9781430265504_Fig11-33.jpg

图 11-33 。添加用于确定 scaleWatchFaceBitmap 对象是空的还是需要设置的布尔 OR 逻辑

在条件 if 结构中,如果位图需要缩放,调用**。createScaledBitmap( )** 方法,关闭 Bitmap 类,如图图 11-34 所示,用这个结果加载 scaleWatchFaceBitmap 对象。到目前为止,Java 代码应该是这样的:

if ( scaleWatchFaceBitmap == null ||
     scaleWatchFaceBitmap.getWidth( ) != width ||
     scaleWatchFaceBitmap.getWidth( ) != height  ) {
     scaleWatchFaceBitmap = Bitmap.createScaledBitmap(watchFaceBitmap, width, height, true);
}

9781430265504_Fig11-34.jpg

图 11-34 。在条件 if 中,调用 scaleWatchFaceBitmap 的 Bitmap.createScaledBitmap()方法

正如你在图 11-34 中看到的,如果你输入位图类名并按下句号键,IntelliJ 会弹出一个帮助器对话框,里面有所有适用的方法。您可以选择适用于您想要做的事情的选项,在本例中为.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)。到目前为止,Java 代码是没有错误的,如图 11-35 所示。

9781430265504_Fig11-35.jpg

图 11-35 。将 watchFaceBitmap 对象和 watchface 宽度和高度传递到。createScaledBitmap()

请注意,如果 scaleWatchFaceBitmap 尚未使用(空),则此。createScaledBitmap()方法调用会将 watchFaceBitmap 对象转移到 scaleWatchFaceBitmap 对象中,即使宽度和高度相同!正如您在上一节中了解到的,这就是该方法的工作方式,因为如果不需要缩放,它将返回原始位图对象。

接下来你需要做的是绘制位图作为背景,在手表表面屏幕的左上角(0,0)。这是使用。drawBitmap()方法调用。如图图 11-36 所示,Java 语句应该如下所示:

watchface.drawBitmap(scaleWatchFaceBitmap, 0, 0, null);

9781430265504_Fig11-36.jpg

图 11-36 。打电话给。drawBitmap()方法,使用 scaleWatchFaceBitmap

如果您想知道,方法调用参数区域末尾的 null 引用了一个 Paint 对象。如果没有定义画图对象,要将更多的屏幕绘制选项应用于位图,那么它将为空,如 empty 或 undefined。如果您想定义更多关于如何在屏幕上绘制位图对象的选项,您可以创建这个 Paint 对象,命名它,加载它(配置它),并在方法调用的最后一个参数槽中使用它的名称。

测试背景位图:圆形与方形

让我们使用两个 AVD 模拟器来测试应用,以确保您的位图资源现在呈现在它们应该在表盘设计后面的地方。正如你在图 11-37 的右侧看到的,你的圆形表盘的位图背景似乎使用了方形表盘位图资源,这意味着圆形标志没有被设置为

9781430265504_Fig11-37.jpg

图 11-37 。在两个 AVD 仿真器中测试位图代码

当然,位图对象缩放也可能有一些问题,因为这个圆形的表盘 AVD 似乎被放大了不少。让我们确保并创建两个测试图像,其中的文本为圆形和方形。我将在 PNG32 图像资源中使用 alpha 通道透明度,这样您将能够看到画布对象的背景(黑色)颜色,因为那行代码被留在了。onDraw()方法,并且当前正被位图对象覆盖。这将向你展示另一个层次的灵活性;也就是说,无缝结合位图和矢量的方法来设计手表的脸。我将在圆形手表表面图像中包含一个圆形周边环。

round_face_text.pngsquare_face_test.png 图片素材复制到**/AndroidStudioProjects/ProAndroidWearable/wear/src/main/RES/drawable-hdpi/**文件夹中,结果可以在图 11-38 右侧看到。

9781430265504_Fig11-38.jpg

图 11-38 。使用操作系统文件管理工具将测试位图添加到 wear/src/main/RES/drawable-hdpi 文件夹中

接下来,使用以下 Java 代码,将用于测试 if(roundFlag)代码的手表表面预览图像的引用更改为 watch_face_test 图像,如图图 11-39 所示(突出显示):

if(roundFlag) {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.round_face_test);
} else {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable. square_face_test);
}

9781430265504_Fig11-39.jpg

图 11-39 。添加 if-else 条件结构,该结构评估是否正在使用圆形观察面并获取正确的资源

让我们在两个 AVD 仿真器中运行这个新配置,以确认发生了什么。正如你在图 11-40 中看到的,缩放正在正确地执行,因为 320 像素源在圆形 AVD 像素中,方形 AVD 正在向下采样到 Android 称方形表盘使用的 280 像素。你可以通过查看文本(字体)大小来判断。

9781430265504_Fig11-40.jpg

图 11-40 。迄今为止,在方形和圆形 AVD 模拟器中测试您的 Java 代码,并确保代码工作正常

我谷歌了一下这个。isRound()设置不正确”的问题,并发现许多开发人员在 AVDs 和实际智能手表硬件上都遇到了这个问题。其中一个解决方案涉及使用 XML 的 UI 布局设计工作区,但是这些不适用于您在这里所做的事情,即直接写入 Canvas 对象(公认的高级方法)。因此,我要试着自己解决这个问题!

解决 roundFlag 问题:onCreate()到 onDraw()

我将假设 onApplyWindowInsets()方法 正在工作,并且正在正确地设置 roundFlag 变量,并且我遇到的真正问题是这个方法的执行时间。如果首先调用 onCreate()方法,则 roundFlag 不设置为默认值(false)以外的任何值。所以,我要做的第一件事是,在宽度和高度变量计算之后,将 onCreate()方法中的代码放入 onDraw()方法中。

正如你在图 11-41 中看到的,我在我移动的代码周围留了一些空间,这样如果需要的话,我可以把它移回来或者进一步优化它。

9781430265504_Fig11-41.jpg

图 11-41 。将位图和 Drawable 相关代码从 onCreate()方法移动到 onDraw()方法 ?? 内

我在两个 avd 中测试了应用,看看这是否是问题所在,确实是问题所在,手表表面现在使用了正确的位图素材,如图图 11-42 所示。

9781430265504_Fig11-42.jpg

图 11-42 。测试位于。圆形和方形 AVD 仿真器中的 onDraw()方法

这是一个好消息,因为您将希望使用 Android Canvas 和 Java 7 代码来完成所有的手表表面设计,但这也导致了一些新的优化问题,因为 onDraw()方法被频繁调用,并且您只想在第一次绘制之前做一次这些事情,这就是为什么我最优地尝试将这些代码语句放在 onCreate()方法中。

希望 Android OS 开发人员将来会改变这种方法调用顺序,以便在 onCreate()之前调用 onapplywindowsets()。我将向您展示如何确保这些位图设置语句只执行一次,以便您的应用不会多次执行所有这些操作,这不是非常理想的,因为您只想加载一个 Resources 对象,确定要使用的图像资源(圆形或方形),然后在应用启动时一次性缩放该资源。

让我们优化的位图部分。onDraw()方法来实现 firstDraw 布尔变量,这样这些操作只在第一次 onDraw()方法调用时执行。

优化您的 onDraw():第一次抽牌与每次抽牌

在引擎类的顶部,创建一个名为 firstDraw 的布尔变量,并将其设置为等于一个 true 值,因为如果不显式设置这个布尔变量,它将默认为 false 值。在图 11-43 中显示(突出显示)的 Java 代码应该如下所示:

boolean firstDraw = true;

9781430265504_Fig11-43.jpg

图 11-43 。在引擎类的顶部创建一个布尔 firstDraw 变量,并将其设置为 true 值

接下来您需要做的是将 if(firstDraw) 条件语句放在位图相关的代码周围,这些代码是从。onCreate()方法添加到 onDraw()方法中。这将确保这些语句只执行一次,这样就不会浪费内存或 CPU 周期。

因为这在你第一次运行应用时是正确的,所以你所要做的就是把你想要执行的代码放在这个结构中,然后在条件语句结束时,在退出语句之前,把 firstDraw 布尔值设置为 false。这将锁定该语句以供将来使用,从而有效地允许您模拟。onCreate()函数。onDraw()方法结构,如图图 11-44 所示。

9781430265504_Fig11-44.jpg

图 11-44 。在位图代码周围添加一个 if(firstDraw)结构,以便它只在第一次绘制时执行

因为我是一个优化狂,我立即开始想知道这里面是否有其他语句。onDraw()方法,该方法应锁定在此“仅处理一次”框中。

我决定将 if(firstDraw)语句之后的 if(scaleWatchFaceBitmap)条件结构也放在该语句中,这样所有与位图对象相关的图像处理都只进行一次,因为硬件特性(屏幕分辨率和形状)在运行时不会改变,所以您可以在第一个 onDraw()周期完成所有这些处理。

将 if(scaleWatchFaceBitmap)条件结构从 if(firstDraw)条件结构的外部复制到 if(roundFlag) in-else 结构的内部。确保将它粘贴在将 firstDraw 布尔变量设置为 false 的 Java 语句之前,在结构的末尾。一定要缩进 if(scalewatchFaceBitmap)结构,如图图 11-45 所示。如果你在 AVD 中测试这个,你会看到你的秒针在滴答走,这意味着这个代码在工作。

9781430265504_Fig11-45.jpg

图 11-45 。将 if(scaleWatchFaceBitmap)结构放在 firstDraw=false 之前的 if(firstDraw)结构中

现在你所要做的就是为其他的表盘模式开发位图资源,你将会在第十二章中了解到,你将会从矢量和位图的角度掌握表盘设计!

摘要

在本章中,您使用 AVD 模拟器测试了到目前为止开发的 Java 代码。您了解了 avd 的来龙去脉,然后开始实现 onApplyWindowInsets()方法,以便能够检测圆形表盘类型。

您已经了解了实现位图图像作为表盘设计的装饰背景所需的 Android 类。其中包括位图类、资源类和可绘制类及其子类位图可绘制类。

之后,您学习了如何在 onDraw()方法中实现这些类以在手表表面设计的背景中加载和显示位图素材,使用条件 if 结构来优化智能手表和智能手机设备硬件上的处理负载。

在下一章中,您将使用 GIMP 图像编辑软件来创建不同的数字图像背景,以支持 WatchFaces API 要求您支持的不同模式。********

十二、表盘数字图像:开发多模式素材

现在,您已经安装了一个位图素材作为手表表面设计的背景,是时候进入数字图像和创建位图的不同模式兼容版本的工作过程了。在本章中你将使用 GIMP (GNU 图像处理程序),因为每个人都可以免费使用它,但是你也可以使用 Photoshop CS。

首先,我将讨论 GIMP 为我们提供了哪些数字图像处理算法,用于将交互模式素材(我将它用作 PNG 索引颜色(256 色)素材)转换为灰度和黑白模式,以便与环境模式、低位环境模式和老化保护模式一起使用。

一旦您为交互式(PNG8)、环境(PNG3)、环境低位(PNG1)和环境老化(PNG1)模式创建了位图资源,您将回到 Java 编码。您将修改自定义方法以合并这些位图资源之间的切换,以便矢量资源和位图资源都符合模式要求(或接近),并且矢量和光栅设计组件无缝地一起渲染(工作)。

你将返工。onDraw()方法结构,以便每当模式发生变化时,位图对象将被重新缩放,因为您现在将跨所有模式实现大范围的位图图像素材。

您还将检查 ensureModeSupport()方法以优化处理,添加位图对象(背景)支持,添加老化保护模式支持,并使用另一个 else-if 部分扩展 if-else-if 结构。

位图模式环境 : GIMP 灰度图像模式

如果你还没有,在gimp.org下载并安装最新版本的 GIMP,然后启动它。使用文件image打开菜单序列,进入打开图像对话框,如图图 12-1 所示,打开prowatchfaceint.png文件,该文件应该在本书的文件库中。

9781430265504_Fig12-01.jpg

图 12-1 。使用文件image打开菜单序列来访问打开图像对话框

正如您在对话框中看到的,GIMP 将允许您在对话框的左侧位置窗格中导航磁盘驱动器文件夹层次结构,并使用对话框右侧的预览窗格显示您选择的数字图像文件的信息。我选择的文件显示在对话框的中间部分。点击对话框右下角的打开按钮,打开您选择的文件。

请注意,我已经使用 PNG8 文件格式、优化了这个交互模式源图像,它使用 256 色加抖动。使用抖动可以让你模拟超过 256 种颜色。这样,当您也使用 256 级灰度创建灰度环境模式图像时,这也将有适当的抖动,使其看起来好像在环境模式图像中有超过 256 级的灰度。通过使用这种数字图像优化方法,您将能够为环境模式以及其他所需模式“预优化”图像素材。

通过使用抖动预优化 8 位图像,您将能够在环境模式下模拟超过 256 级的灰度,使其看起来像超过 256 种颜色(或对于环境模式,灰度级)。这是优化 8 位索引彩色抖动图像素材的优势之一;当您对它们应用灰度算法(对于环境模式)时,抖动也会出现,为环境模式资源提供与抖动为交互模式资源提供的相同的视觉升级效果,并具有完全相同的数据足迹优化(甚至可能更好)。

为了创建这个预优化的环境模式灰度图像,在 GIMP 的图像菜单下找到模式子菜单。单击模式菜单选项右侧向右箭头,并下拉子菜单。选择灰度选项,如图 12-2 顶部所示。

9781430265504_Fig12-02.jpg

图 12-2 。使用图像image模式image灰度菜单序列将彩色图像转换为灰度图像

正如您所看到的,GIMP 已经确定所选择的图像是一个索引的 8 位图像。如果图像是 24 位(PNG24)或 32 位(PNG32),那么 RGB 选项将带有项目符号。一旦您选择了这个灰度选项,它将被加上项目符号,并将应用 GIMP 算法从图像中移除色调(颜色),仅保留光度(亮度)值。

现在你所要做的就是使用文件image导出为菜单序列,如图 12-3 左侧所示,进入 GIMP 导出图像对话框。

9781430265504_Fig12-03.jpg

图 12-3 。使用 GIMP 文件image导出为菜单序列访问导出图像对话框(像在 Photoshop 中另存为)

一旦你进入导出图像对话框,如图 12-4 所示,你可以选择中间区域的 prowatchint.png,这样你就不必再输入一遍,然后将“int”改为“amb”一旦你点击导出按钮,你会得到导出图像为 PNG 的选项对话框。取消选中所有选项以产生最小的文件大小。

9781430265504_Fig12-04.jpg

图 12-4 。要使用灰度环境模式,将文件命名为 prowatchfaceamb.png,并选择最大 PNG 压缩

这将为您提供 8 位灰度环境模式图像,,它使用了您的灰度环境模式图像所能支持的最大数量的灰色值(256)。当然,一些智能手表可以支持 8 位灰度,但是,一些智能手表会将图像减少到更少的灰度,因此,我将向您展示如何优化更低的位级灰度图像,以防您的目标智能手表在环境模式下仅使用 16 种灰度。我还将向您展示如何创建仅使用八种灰度但仍然看起来很棒的位图图像(我甚至将向您展示如何使四种灰度看起来可以接受)。我还将介绍华硕 ZenWatch 使用的 1 位(两种灰度或黑白)低位环境模式。

低位模式位图:GIMP 的色调分离算法

你需要做的下一件事是创建低位环境模式图像。一些智能手表在不活跃使用(被观看)时会切换到“环境”低功耗使用模式。例如,索尼智能手表 3 (SW3)使用了透反射屏幕(一种可以在阳光下轻松阅读的技术),在互动模式下使用 16 位(RGB 565)颜色(以及背光),在环境模式下关闭背光以节省电能。所以你可以在索尼 SW3 环境模式下使用彩色,尽管谷歌 WatchFaces API 文档建议使用低位灰度。

这就是为什么我在本章的这一节详细介绍这种低位灰度优化的工作过程。一些智能手表制造商最终将使用比我之前提到的 256 级更少的灰度级。有些可能使用 16 ( PNG4 4 位)灰度,但也可能更少。请咨询您的智能手表制造商,了解他们的环境模式到底有多低!

掌握了目标智能手表制造商在环境模式下支持多少灰度(或颜色)的知识,以及您将在本章的这一部分学习的工作流程,您甚至可以优化八个灰度级( PNG3 ),甚至一个微薄的四个灰度级( PNG2 ),并且仍然使生成的位图图像素材看起来很好,特别是在较小的智能手表表面上,它使用相对精细的点间距(即小像素大小)。

要减少 8 位灰度图像中的灰度等级,您需要访问 GIMP 颜色菜单,该菜单位于图 12-5 的顶部。找到色调分离选项并选择它进入一个对话框,允许你应用颜色(或灰度)减少算法。

9781430265504_Fig12-05.jpg

图 12-5 。使用 GIMP Colors image色调分离菜单序列访问色调分离对话框,以减少灰色的数量

您要选择颜色的偶数位数 : 二色1 位四色2 位八色3 位16 色4 位灰度。

是的,即使是灰色的阴影,或者黑色和白色,也被认为是颜色!接下来让我们启动色调分离对话框,创建一个 PNG4 4 位灰度图像。

正如你在图 12-6 中看到的,如果你选择了预览复选框,你将能够实时看到该算法的滑块设置对灰度图像的影响。

9781430265504_Fig12-06.jpg

图 12-6 。将色调分离设置为 16 级灰色,以适应 4 位灰度环境模式显示

如果您将滑块值设置为 16 (4 位)颜色值,您将看到视觉效果几乎与 256 色版本一样好,使用的灰度值数据少 16 倍!然而,存在一些可见的“条带”,这并不令人满意。稍后,在我解释了色调分离算法之后,我将讨论如何使用抖动技术来减轻索引色带化。我将解释这两个工作过程,这样你就会知道使用 GIMP 降低色彩值的所有主要方法。

目前,很难从智能手表制造商那里获得关于环境模式下灰度(甚至颜色)所用的位级的物理规格。希望制造商在未来为 Android 可穿戴设备开发者发布一份涵盖这一内容的技术信息白皮书。

这种环境模式能达到的最“低位”是 1 位低位环境模式。在这里,您将使用 1 位图像来创建老化图像。

接下来,让我们看一个不同的工作过程,它允许你通过使用索引颜色转换对话框来访问 GIMP 2 抖动算法。

抖动低位图像:索引模式转换

因为你不能在色调分离对话框中访问 Floyd-Steinberg 抖动算法 ,我觉得这是 GIMP 2 开发者的疏忽,让我们看看另一个工作过程(和产生的对话框),它在颜色(这里是灰度)减少方面提供了更多的选择。

要访问弗洛伊德-斯坦伯格抖动算法,你需要使用索引颜色转换对话框。使用图像image模式image索引菜单序列进入,如图图 12-7 所示。这将改变一个图像回到索引颜色模式,你可能记得这是它开始的地方。进入灰度模式去除了色调值,只留下亮度值。

9781430265504_Fig12-07.jpg

图 12-7 。使用图像image模式image索引菜单序列,并选择 Floyd-Steinberg 抖动算法

返回到索引图像模式(在这种情况下,由于源图像数据,它是灰度的)将触发索引颜色转换对话框。这是你可以找到弗洛伊德-斯坦伯格抖动算法选项的地方,你可以在图 12-7 的右下角看到它被选中。

选择生成最佳调色板单选按钮,设置 16842最大色数值,点击转换按钮。正如你在图 12-8 中所看到的,将要应用的抖动可以在生成的低位灰度环境图像中产生显著的质量差异。右下角的 1 位环境模式(无失真)图像已经过反转(我将在“老化模式”部分讨论这一工作过程)和算法处理,以便最大限度地减少使用的白色像素数量。

9781430265504_Fig12-08.jpg

图 12-8 。正如你所看到的,抖动将改善低比特环境模式图像质量一个数量级

一旦您确定了目标智能手表制造商支持的低位灰度级别,您就可以将文件导出为文件名prowatchfacelow.png。如果你对支持的灰度值的数量有任何疑问,使用 1 位方法,反转它(如果需要),然后导出你的文件。您希望屏幕的大部分区域使用黑色(关闭)像素值。我将在老化模式部分讲述反转

在您拥有最适合目标智能手表低位环境模式(或环境模式)的低位灰度环境模式图像后,使用 GIMP 文件image导出为菜单序列,如图图 12-3 所示,并将文件命名为prowatchfacelow.png,如图图 12-9 所示,在对话框顶部的名称字段中。点击导出按钮,导出索引色 PNG 文件(不选择选项),16 灰度 PNG4 ,8 灰度 PNG3 ,4 灰度 PNG2 ,黑白 PNG1

9781430265504_Fig12-09.jpg

图 12-9 。使用文件image导出为菜单序列使用导出图像对话框保存 prowatchfacelow.png

创建老化模式位图:使用反转算法

接下来你需要创建的是黑白(1 位)低位环境模式图形,也将用于老化模式。

这个过程的第一步是撤销你最后做的任何事情,无论是色调分离对话框还是索引颜色转换对话框。

GIMP Edit image Undo 菜单根据你的最后一次操作进行定制,所以如果你一直在使用色调分离对话框,那么这个菜单序列就是EditimageUndo Posterization。这可以在 GIMP 的左上角看到,如图图 12-10 所示。使用 Edit image Undo 的原因是,这样你就可以让完整的 8 位 256 级灰度图像进入任何算法。

9781430265504_Fig12-10.jpg

图 12-10 。使用编辑image撤销工作过程返回到 8 位(256 色)原始灰度图像数据

您可能希望在将图像“重新存储”为两种颜色(1 位或 PNG1)之前执行此操作,因为您希望为该色调分离算法提供尽可能多的原始灰度,因为该算法需要处理的数据越多,产生的结果就越好。

使用 GIMP **颜色image ** 色调分离菜单序列(如图图 12-5 所示)再次进入色调分离对话框。这一次,你需要选择 1 位颜色(两种颜色),这是最低可能的色调分离设置。

使用色调分离算法的原因是因为你特别不想要 1 位颜色抖动效果,如图 12-8 右上角所示。这是因为你试图得到一个线条绘制效果 ,一旦你阿尔法混合表盘黑色背景色,这将使白色像素变暗为中灰色,这将提供所需的结果。这是为了确保在老化保护模式下不会发生老化。

设置及其结果可以在图 12-11 中看到。正如你所看到的,结果看起来不错,在智能手表显示屏上看起来会更好,因为像素间距点大小屏幕密度更精细。如果你想看看这是什么样子?setAlpha(127),看一下图 12-15 中最右边的面板。

9781430265504_Fig12-11.jpg

图 12-11 。使用色调分离对话框,将色调分离级别设置为两种颜色,然后单击确定按钮

如果你想要更细(更细)的线条,你可以使用缩放工具 (它是一个放大镜)和橡皮擦工具(看起来像你以前学校的橡皮擦)手动擦除线条较粗部分的一些像素。

正如你可能已经注意到的,即使图形现在只使用黑白颜色值,没有防走样,正如老化模式所要求的那样,现在的问题是你的表盘是打开的(白色),而不是关闭的(黑色)。你最终需要做的是与你现在在屏幕上看到的完全相反的结果。幸运的是,GIMP 有一种算法可以“翻转”或“反转”像素值,幸运的是,正如你可能想象的那样,它最适合黑白图像。

你将在算法上处理翻转白色背景颜色值以使背景变成黑色,同时使黑色线条变成白色的方法是使用 GIMP Invert 算法。这也位于颜色菜单下,在菜单上比色调分离选项更靠下一点,你可以在图 12-12 的中间看到。

9781430265504_Fig12-12.jpg

图 12-12 。要将白色背景上的黑线反转为黑色背景,使用颜色image反转菜单

当您选择此选项时,没有对话框,因为 GIMP 将简单地为您反转像素颜色值,并且您的图像将立即变成黑背景上的白线,这是老化保护模式所需的结果。

正如你在图 12-13 中看到的,你非常接近你需要的屏幕老化保护的结果,即只有在你绝对需要的地方是白色像素,其他地方都是黑色像素。重要的是要注意,如果您希望在老化保护模式下打开较少的像素,您可以在这些反转模式下编辑线条,使线条变细。

9781430265504_Fig12-13.jpg

图 12-13 。GIMP 2.8.14 右预览区看到的颜色image反转菜单序列的结果

事实上,这可能是获得一些 GIMP 2 数字图像编辑实践的好方法。要编辑这些线条,请使用放大镜工具放大图像,然后使用 GIMP 橡皮擦工具删除像素,直到图像中的所有线条都是一到两个像素宽。

为了谨慎起见,你也可以去掉拖鞋内部的颜色(白色);我会把这项工作留给你自己去做,这样你就可以得到一些使用 GIMP 的练习,因为这是一个重要的 Android watch face 开发工具,需要你去掌握。

需要指出的是,这些 BagelToons LLC 图片属于我的客户 Ira H. Harrison-Rubin,它们应该仅用于在本书的背景下学习和实践,因为 BagelToons 将发布所有 BagelToons 作品,包括这一张,作为 Watch Faces 应用。

细化完成后,使用文件image导出为菜单序列进入导出图像对话框,如图图 12-14 所示,然后将此数字图像命名为prowatchfacelow.png。点击导出按钮(不选择选项),将该烧屏保护图像素材以 PNG1 图像文件格式导出。

9781430265504_Fig12-14.jpg

图 12-14 。使用文件【导出为】菜单序列使用导出图像对话框保存 prowatchfacelow.png

如果您打算将 1 位灰度用于环境低位和老化模式,那么您将把文件保存为【prowatchfacelow.png】的,并且仅使用三个位图来覆盖四种模式。我已经展示了一幅图像的四个位图——交互(8 位颜色)、环境(3 位、八级灰度)、低位环境(黑白)和老化保护(黑白),如图图 12-15 所示,这样你就可以直观地比较所有这些。****

**9781430265504_Fig12-15.jpg

图 12-15 。四种位图资源—8 位彩色、3 位灰度、黑白、黑白和灰色—涵盖了您的所有模式

现在让我们换个话题,开始一些 Java 编码,这样您就可以实现前三个位图文件了。稍后,您将实现老化模式,这可以通过位图资源或代码中的 alpha 混合来实现。

多模式位图:使用 Java 更改位图

要使 watch face 应用位图素材与您的模式检测代码兼容,您需要做的下一件事是更改 onDraw()和 ensureModeSupport()方法,以添加更改位图素材的代码。

首先,将 onDraw()方法中的Resources watchFaceResources = ProWatchFaceService.this.getResources(); Java 语句移动到私有引擎类声明区域的顶部,如图 12-16 中的所示。因为不止一个方法将使用这个资源对象,所以您必须使它更加“可见”

9781430265504_Fig12-16.jpg

图 12-16 。将资源对象声明、命名和加载 Java 语句移到引擎顶部

因为你将不止一次地缩放多个位图对象,即从交互模式到环境模式再回到交互模式,你需要将 if(scaleWatchFaceBitmap)结构移回到if(first draw)条件 if()结构之外,如图图 12-17 所示。

9781430265504_Fig12-17.jpg

图 12-17 。将 if(scaleWatchFaceBitmap)结构移到 if(firstDraw)结构之外(在每次绘制时评估)

请注意,现在 if(scaleWatchFaceBitmap)是它自己的结构,您可以通过将 scaleWatchFaceBitmap 对象设置为 null (清除或清空它的值)来触发它进行位图评估和缩放。

这是因为 if()条件赋值器的逻辑或结构的一部分是scaleWatchFaceBitmap = null,所以如果您想要调用这个构造中的内容,只需将对象设置为 null,就可以为您创建的任何位图资源调用这个重新缩放逻辑。

在开始重写。ensureModeSupport()方法,您需要将您在前面使用 GIMP 一章中创建的素材安装到正确的 Android Studio 项目 HDPI 可绘制资源文件夹中。

将 prowatchfaceamb.png、prowatchfaceint.png 和 prowatchfacelow.png 位图素材从保存它们的文件夹中复制到**/ProAndroidWearable/wear/src/main/RES/drawable-hdpi/**文件夹中,如图图 12-18 所示。

9781430265504_Fig12-18.jpg

图 12-18 。将 8 位交互式、3 位环境和 1 位低位环境位图复制到项目的/drawable-hdpi 文件夹中

如您所见,我使用了三种优化程度最高的位图素材,以便使用最少的颜色、灰度级别和黑白级别,并以智能手表显示图像所用的最少功率实现最小的文件大小。在图 12-18 中可以看到,视觉质量不错,这里我只使用 256 色(交互模式)八灰度(环境模式)或黑白(低位或烧屏模式)。

现在,您已经准备好修改环境模式和低位环境模式的 Java 代码,以便使用正确的位图资源。在黑色背景下工作良好的白色表盘元素将被重置为使用黑色,以获得与白色位图资源的最大对比度,但老化保护位图资源除外,在该资源中,您将使用白色表盘设计元素。让我们从灰度位图开始,然后实现索引颜色和预烧保护模式位图素材的 Java 代码。

将位图对象安装到低位环境模式

将带有 prowatchfacelow 素材引用的watchfacedrable配置语句和来自 onDraw()方法的 watchFaceBitmap 语句复制到 if(enableLowBitAmbientMode)构造的顶部。颜色值保持白色,因为背景是黑色的。在图 12-19 中可以看到 if 结构的 Java 代码,没有错误,应该如下所示:

if( enableLowBitAmbientMode ) {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfacelow);
    watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
    scaleWatchFaceBitmap = null;
    pHourHand.setAlpha(255);
    pMinuteHand.setAlpha(255);
    pSecondHand.setAlpha(255);
    pTickMarks.setAlpha(255);
    pHourHand.setColor(Color.WHITE);
    pMinuteHand.setColor(Color.WHITE);
    pSecondHand.setColor(Color.WHITE);
    pTickMarks.setColor(Color.WHITE);

9781430265504_Fig12-19.jpg

图 12-19 。复制 watchFaceDrawable 和 watchFaceBitmap 代码,并添加 scaleWatchFaceBitmap = null

如你所见,第三行代码是一个scaleWatchFaceBitmap = null;语句,它将触发if(scaleWatchFaceBitmap)条件语句。触发此方法的模式更改也将触发图像重新缩放代码(如果需要),因为此语句在前两个语句之后出现。

因为背景图像现在是灰度的,主要是白色的,所以您需要更改。setColor()方法调用来引用颜色。黑色常量,以便刻度线和表盘指针相对于背景位图对象(灰度数字图像素材)具有最大对比度。

接下来,您将对。setAlpha()方法调用,添加。setColor()方法调用,并将与位图对象相关的语句添加到第二个else-if(is ambientmode())结构中。由于在环境模式中使用了大量白色背景图像,您将再次需要使用黑色颜色值常量,并且您还将想要使表盘设计元素全黑,以获得最大的可读性,以及关闭这些表盘设计元素的屏幕像素。出于这个原因,你也可以将当前的 127 Alpha 值设置为 255 的完全不透明值。我把这个方法结构中被改动过的 Java 语句都加粗了。如图 12-20 所示,Java 代码现在应该是这样的:

} else if( isInAmbientMode() ) {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfaceamb);
    watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
    scaleWatchFaceBitmap = null;
    pHourHand.setAlpha(255);
    pMinuteHand.setAlpha(255);
    pSecondHand.setAlpha(255);
    pTickMarks.setAlpha(255);
    pHourHand.setColor(Color.BLACK);
    pMinuteHand.setColor(Color.BLACK);
    pSecondHand.setColor(Color.BLACK);
    pTickMarks.setColor(Color.BLACK);

9781430265504_Fig12-20.jpg

图 12-20 。添加与位图相关的代码,将 Alpha 值更改为 255,将颜色值更改为 color。黑色

接下来,让我们修改 if-else-if-else 结构的 else 部分,以添加与位图对象相关的语句,这些语句将设置当手表表面处于交互模式时,您将为其使用的索引彩色图像素材。

优化交互模式:将刻度线颜色设置为黑色

条件 if-else 结构的最后一个 else 部分构成了 ensureModeSupport()方法的大部分,它设置交互模式特征(如果没有设置其他模式标志的话)。这段代码改动最少,只把光学标记对象改成了颜色。黑色并添加您在其他部分添加的位图相关的 Java 语句。你的代码,如图图 12-21 所示,应该是这样的:

} else {
    watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfaceint);
    watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
    scaleWatchFaceBitmap = null;
    pHourHand.setAlpha(255);
    pMinuteHand.setAlpha(255);
    pSecondHand.setAlpha(255);
    pTickMarks.setAlpha(255);
    pHourHand.setColor(Color.BLUE);
    pMinuteHand.setColor(Color.GREEN);
    pSecondHand.setColor(Color.RED);
    pTickMarks.setColor(Color.BLACK);
}

9781430265504_Fig12-21.jpg

图 12-21 。在 else 结构的顶部添加与位图对象相关的代码,并将光学标记更改为颜色。黑色

现在,您已经在 ensureModeSupport()方法中添加了与位图相关的内容并更改了颜色,从而最大限度地提高了现有 Java 代码的可读性,接下来让我们在 Square AVD 模拟器中测试交互式、环境和低位环境模式。

在 Square AVD 中测试交互和环境模式

确保您已经删除了私有引擎类中所有出现的强制标志设置,换句话说,删除lowBitAmbientModeFlag=true; and burnInProtectModeFlag=true;代码片段。接下来,使用运行image运行磨损菜单序列并启动 Square AVD 仿真器。如果您想确保您的 Square AVD 仿真器是当前选择的仿真器,您可以使用运行image编辑配置菜单序列。

一旦模拟器启动并加载 Pro 手表表面,您将看到方形手表表面测试模式,因为您将该代码留在了。onDraw()方法。如果你想知道我为什么这样做,这是为了向你展示如何在 watch face 启动时显示不同的位图(比如法律免责声明,在手表表面后面,第一次启动时)。

在第十三章介绍表盘配置对话框中添加圆形表盘装饰支持时,知道如何实现是非常有用的。最理想的情况是,您可以完全删除这些代码,只使用正方形位图资源作为表盘背景。

但是,我想向您展示,通过在。onDraw()方法结构。在任何情况下,一旦你看到正方形测试屏幕,它仍然是从 if(firstDraw)代码调用的,按下 F7 键,你将看到环境模式位图素材,它显示在图 12-22 的左侧。

9781430265504_Fig12-22.jpg

图 12-22 。使用设置image改变手表表面image专业手表表面系列,和 F7 测试环境模式

如果您再次切换 F7 键,您将触发。ensureModeSupport()方法。颜色结果可以在图 12-22 的右侧看到,正如你所看到的,状态栏图标和 Peek 卡与背景艺术作品的定位是完美的。

为了测试低位(在此优化素材的情况下为 1 位颜色)模式,您需要在 onAmbientModeChanged()方法中,紧接在**super . onAmbientModeChanged()**方法调用语句之后,再次安装一个lowBitAmbientModeFlag=true;强制模式开关,如图图 12-23 所示。

9781430265504_Fig12-23.jpg

图 12-23 。在 onAmbientModeChanged()方法中将 lowBitAmbientModeFlag 布尔值设置为 true 值

强制将这个低位环境模式标志设置为“开”状态,如果(lowBitAmbientModeFlag) 关闭代码中的抗锯齿,这应该在之前完成,正如你在图 12-24 中看到的,也调用 ensureModeSupport()方法,该方法安装低位(1 位颜色)图形。

9781430265504_Fig12-24.jpg

图 12-24 。使用设置image改变手表表面image Pro 手表表面系列,和 F7 测试低比特环境模式

现在,当你使用 Run image Run Wear 并启动 AVD 仿真器,然后使用SettingsimageChange Watch FaceimagePro Watch FaceimageF7 键时,你将得到启用低位模式的环境模式,如图 12-24 左侧所示。

你需要做的下一件事是创建低比特环境模式位图的老化模式版本,它将使用灰色颜色值而不是白色值。您将使用 GIMP 将白色像素更改为 50%灰色颜色值,这将匹配 Android 颜色。灰色恒定完美,提供了一个灰色“老化”版本的低位环境模式。

之后,您将把**if(enableBurnInAmbientMode)**构造添加到 enableModeSupport()方法中,该方法设置正确的位图和颜色值。

Android Wear 老化模式:位图和 Java 代码

为了彻底了解表盘设计,让我们实现一个老化模式图像和 Java 代码,通过这种低位设计创建老化保护!

创建老化模式位图 : GIMP 亮度-对比度

如果 GIMP 没有打开,打开它,使用文件image打开菜单序列打开你的prowatchfacelow.png文件。你要将位图的白光强度“调暗”50%,以匹配 Android 的颜色。灰色常数。

正如你在图 12-25 中看到的,你在 GIMP 中实现这一点的方法是使用颜色image亮度对比菜单序列,这将打开一个对话框,允许你调暗表盘设计中白色像素发出的光线(亮度)。相当酷!

9781430265504_Fig12-25.jpg

图 12-25 。打开 prowatchfacelow.png 文件并调用颜色image亮度-对比度菜单序列

在图 12-26 的右侧显示的亮度-对比度对话框将允许您在不同的亮度(或对比度)设置下设置预置。当您设置好想要保存的设置后,点击加号( + )图标即可。例如,你现在可以尝试将设置保存为“Android 50%灰度老化保护模式预设”。

9781430265504_Fig12-26.jpg

图 12-26 。将亮度滑块调整到最左侧,将亮度降低 50%(或 127/255)

亮度滑块向左拖动到 -127 值,实际上是 128,因为从零开始计数,正好是 8 位灰度范围内 256 个值的一半。确保您的预览复选框被选中,这样您可以实时看到修改,然后单击确定按钮,这将完成操作并应用算法。

接下来,您需要使用 GIMP 文件image导出为工作流程保存老化模式数字图像素材,如图图 12-27 所示,并使用相同的 15 字符格式将文件命名为 prowatchfacebur.png

9781430265504_Fig12-27.jpg

图 12-27 。使用文件【导出为】菜单序列使用导出图像对话框保存 prowatchfacebur.png

现在,您已经准备好返回 Java 编码,并通过插入一个新的 if-else 部分,在 enableModeSupport( ) 方法中实现老化保护模式。

Java 中的老化保护:if(enableburniambientmode)

在 ensureModeSupport()方法结构中,您需要做的第一件事是在方法结构的顶部添加一个布尔 enableBurnInAmbientMode 变量。您将把它设置为一个逻辑与条件,如果**is ambientmode()**返回 true 并且 burnInProtectModeFlag 被设置为 true 值,那么它将返回一个 true 值。否则,这将评估为假,因为老化保护要求环境模式开启,并且老化制造商支持常数到位并指定。

在私有方法中创建了这个新的布尔标志之后,您将需要在低位环境部分之后和仅环境模式部分之前添加一个 if-else 条件部分。方法的结尾是最后的 else 部分,它涵盖了交互模式设置。

这个if(enableBurnInAmbientMode)构造应该使用您刚刚创建的prowatchfacebur.png图像资源加载一个 watchFaceDrawable 对象,然后使用从 Drawable 中提取新的老化模式位图对象。getBitmap( ) 方法,将它放在 watchFaceBitmap 对象中。一旦完成,您可以将 scaleWatchFaceBitmap 对象设置为 null 。这将触发 onDraw()方法中的重缩放计算,因为您已经为新模式更改了位图资源,这可能是必要的。

作为一种优化技术,我将使用 255(完全打开)的 alpha 值和 Android OS 颜色,而不是使用 127 alpha 通道值来创建 50%的灰度值,就像您之前所做的那样。设定表盘指针和刻度线颜色值的灰色常数。

如果你想知道为什么使用这种安卓颜色。灰色常数是一种优化,如果你使用的 alpha 通道值为 255,这是因为 Android 不会调用其混合算法,这可能是处理密集型的。此外,您不希望通过将白色与您创建的烧屏保护图像中使用的灰色混合来创建亮像素。

您会注意到,在 ensureModeSupport()方法的 Java 代码清单中,我将完整地包括它,因为您现在已经实现了所有这些模式及其位图对象,您没有对这些位图图像素材使用任何 alpha 混合,因此您已经在整个方法中应用了这种优化技术。如果您打算使用背景位图,您可以决定删除这些调用!

完了。ensureModeSupport()方法可以在图 12-28 中看到,Java 代码应该是这样的方法结构:

private void ensureModeSupport(){
    boolean enableLowBitAmbientMode = isInAmbientMode() && lowBitAmbientModeFlag;
    boolean enableBurnInAmbientMode = isInAmbientMode() && burnInProtectModeFlag;
    if (enableLowBitAmbientMode) {
        watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfacelow);
        watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
        scaleWatchFaceBitmap = null;
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
        pHourHand.setColor(Color.WHITE);
        pMinuteHand.setColor(Color.WHITE);
        pSecondHand.setColor(Color.WHITE);
        pTickMarks.setColor(Color.WHITE);
    } else if (enableBurnInAmbientMode) {
        watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfacebur);
        watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
        scaleWatchFaceBitmap = null;
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
        pHourHand.setColor(Color.GRAY);
        pMinuteHand.setColor(Color.GRAY);
        pSecondHand.setColor(Color.GRAY);
        pTickMarks.setColor(Color.GRAY);
    } else if ( isInAmbientMode() ) {
        watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfaceamb);
        watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
        scaleWatchFaceBitmap = null;
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
        pHourHand.setColor(Color.BLACK);
        pMinuteHand.setColor(Color.BLACK);
        pSecondHand.setColor(Color.BLACK);
        pTickMarks.setColor(Color.BLACK);
    } else {
        watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.prowatchfaceint);
        watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
        scaleWatchFaceBitmap = null;
        pHourHand.setAlpha(255);
        pMinuteHand.setAlpha(255);
        pSecondHand.setAlpha(255);
        pTickMarks.setAlpha(255);
        pHourHand.setColor(Color.BLUE);
        pMinuteHand.setColor(Color.GREEN);
        pSecondHand.setColor(Color.RED);
        pTickMarks.setColor(Color.BLACK);
    }
}

9781430265504_Fig12-28.jpg

图 12-28 。添加带有颜色的 else if(enableBurnInAmbientMode)构造。灰度和 prowatchfacebur 图像

现在是时候测试预烧保护模式的 Java 代码,以及位图数字资源,这是您在本节前面使用 GIMP 亮度-对比度对话框创建的。

测试老化保护模式位图和 Java 代码

打开。onAmbientModeChanged()方法构造并移除强制lowBitAmbientModeFlag=true;布尔标志语句,该语句用于测试低位环境模式。相反,这里您将添加强制burnInProtectModeFlag=true;布尔标志语句,现在您将利用它来测试老化保护模式。

那个。onAmbientModeChanged()方法结构,可以在图 12-29 中看到,应该看起来像下面的 Java 方法结构:

@Override
public void onAmbientModeChanged(boolean ambientModeFlag) {
    super.onAmbientModeChanged(ambientModeFlag);
    if(lowBitAmbientModeFlag) setAntiAlias(!ambientModeFlag);
    burnInProtectModeFlag = true;
    if(burnInProtectModeFlag) setBurnInProtect(ambientModeFlag);
    ensureModeSupport();
    invalidate();
    checkTimer();
}

9781430265504_Fig12-29.jpg

图 12-29 。将 burnInProtectModeFlag 设置为 true,强制老化保护模式测试位图

使用 IntelliJ 中的 Run image Run Wear 工作流程,并使用 Android Wear Square AVD 仿真器测试代码和位图对象。正如你在图 12-30 中看到的,你已经实现了 50%暗烧模式版本的低位模式。

9781430265504_Fig12-30.jpg

图 12-30 。使用设置image改变表盘image Pro 表盘系列,和 F7 键测试环境老化模式

值得注意的是,我将在下一章中通过添加额外的 Java 代码来介绍圆形 AVD 仿真器和圆形表盘设计原则和技术,这将涉及如何允许用户定制表盘设计。其原因主要是优化驱动的决策。为什么要为表盘设计提供圆形和方形图像资源,而方形资源可以用于两者?圆形表盘装饰,例如装饰性的轮圈,最终用户可以添加用于他们自己定制的装饰。这样,如果用户希望,即使是方形表盘也可以具有圆形表盘设计装饰。这通过用户界面增加了表盘设计的价值。

摘要

在本章中,您学习了如何使用 GIMP 创建,以及如何使用 enableModeSupport()和 onDraw()方法实现大量不同的表盘设计模式。智能手表制造商将需要其中一些低功耗模式,具体取决于显示技术。

首先,我讨论了如何将优化的交互模式位图图像素材转换为灰度图像,这是环境模式通常需要的,也是 Android 推荐的。

之后,您学习了如何减少图像资源使用的灰色或灰度数量,因此您可以针对某些制造商智能手表产品要求的某些较低位模式进行优化。例如,索尼智能手表 3 有一个 3 位或 8 级灰度的环境模式,现在你知道如何使用弗洛伊德-斯坦伯格抖动算法来优化你的位图。

在创建了几个数字图像素材之后,您还学习了如何将 Java 代码添加到。enableModeSupport()方法结构,以及如何修改 onDraw()方法结构,以便每次模式改变时,如果位图资源的分辨率与图像资源不同,位图资源将被重新评估和重新缩放,以适应智能手表屏幕。

在下一章,你将学习如何使用 Android Wear 应用的另一面(或组件),即 Android Studio (IntelliJ)中所称的“移动”面。对于这个主题,Java 编码和 XML 标记变得更加复杂。因此,下一章将起到将本书的这一手表表面设计部分与本书的其他 Android(非 WatchFaces API)穿戴主题章节(第十七章)进行“桥接”的作用。**

十三、表盘配置伴侣活动:谷歌移动服务

现在,您已经拥有了可在所有显示模式下工作的基本位图和矢量表盘设计,是时候切换到移动(智能手机)设备并创建表盘“功能选择器”应用了,这将允许您的用户定制自己的表盘设计。这将允许您为您的手表面孔应用收费,因为它们将是定制的手表面孔生成器应用,而不仅仅是简单的手表面孔。

这和表盘设计本身一样复杂,只是方式不同。在本章中,你将了解到 Google API 客户端 Java 接口,它用于将 Google Play 服务集成到你的手表表面可穿戴应用中。

这涉及到使用一系列的 com.google.android.gms 包和它们的类,这是我将在本章介绍的大部分内容,同时创建一个将在用户智能手机上运行的类和另一个将驻留在项目 wear 部分的侦听器类。谷歌移动服务(GMS)就是 Android Wear 云!

您将了解可用于创建 Wear Companion 智能手机应用的 Android GMS 类和接口。它们包括 API,比如 GoogleApiClient,它允许你创建一个 Google Play 服务的客户端。其他穿戴 Api 还包括 DataApiDataItemDataMapDataMapItemComponentNameMessageEventWatchFaceCompanion ,甚至还有 CompanionResult 。您还将了解 Android AlertDialog 类和 Android Uri 类,以及它们如何用于 WatchFace 应用开发。

创建一个 ProWatchFaceCompanionConfigActivity

在本节中,您将为表盘用户手机上的对话创建基础,该对话将允许用户配置表盘设计特征。你对用户的表盘设计控制得越多,你对表盘应用的收费就越高。您将添加一个 < activity > 标签到 AndroidManifest.xml 文件,该文件位于 Android Studio 项目的移动部分。然后你将在你的项目的移动组件中创建一个空的ProWatchFaceCompanionConfigActivity.java结构,然后在你实际编写 Java 代码实现这个类之前学习 Android GoogleApiClient 公共接口,它将为你的手表脸提供一个 UI。

移动应用:将你的活动添加到 AndroidManifest

关闭 Android Studio 项目中所有打开的与 wear 部分相关的编辑选项卡,然后打开项目的 mobile 部分,如图图 13-1 所示。在manifest文件夹中,右键单击 AndroidManifest.xml 并选择跳转到源代码选项在编辑选项卡中打开该文件。

9781430265504_Fig13-01.jpg

图 13-1 。关闭所有选项卡,打开项目的移动部分,右键单击 AndroidManifest,然后选择跳转到源代码

在父 <应用> 标签内添加一个子 <活动> 标签,并用 app_name 标签将其命名为ProWatchFaceCompanionConfigActivity。在<活动>标签内添加一个子 <意图过滤> 标签。在这个里面添加一个 <动作> 子标签,带有一个配置 _ 数字动作常量,以及两个 <类别> 子标签,带有伴侣 _ 配置默认常量。注意,我还在清单顶部添加了一个 < uses-sdk > 标签,带有一个最低 SDK 版本 API 级别 18目标 SDK 版本 API 级别 21 。<使用权限>标签增加了提供 _ 背景唤醒 _ 锁定功能。如图 13-2 所示,你的 XML 标记应该如下所示:

<manifest xmlns:android=http://schemas.android.com/apk/res/android
    package="com.pro.android.wearables.proandroidwearable">
    <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="21" />
    <uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <application android:allowBackup="true"
        android:label="@string/app_name" android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".ProWatchFaceCompanionConfigActivity"
            android:label="@string/app_name" >
            <intent-filter>
               <action android:name="com.pro.android.wearables.proandroidwearable.CONFIG_DIGITAL" />
               <category android:name=
                         "com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
               <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

9781430265504_Fig13-02.jpg

图 13-2 。为 ProWatchFaceCompanionConfigActivity 添加一个<活动>父标签和<意图过滤>子标签

您可能已经注意到,IntelliJ 在 android:name 参数值上给出了一个红色文本错误高亮显示。因为您还没有创建那个 Java 文件。你将在本章的下一节纠正这一点,所以这将很快消失!

现在,让我们打开/mobile/res/values/strings.xml 文件,并为该活动指定 Pro WatchFace Options 的 app_name,以便应用的标签描述它的功能。如图 13-3 中的所示。

9781430265504_Fig13-03.jpg

图 13-3 。编辑/res/values/strings.xml 中的 app_name 变量,并将 Activity Pro WatchFace Options 命名为

接下来,您需要创建一个 prowatchfaceconnectionconfigactivity Java 类。

Java 类:创建 WatchFace 同伴活动

让我们通过右击移动 java 文件夹,然后选择新的image Java 类上下文相关菜单序列来创建一个新的 Java 类,如图 13-4 左侧所示(编号为步骤 1)。在选择目标目录对话框中,选择主包(不是 androidTest 选项)并点击 OK 按钮。在新建类对话框中,将您的类命名为ProWatchFaceCompanionConfigActivity,将 Kind 设置为,点击 OK 按钮,创建一个表盘设计配置活动类。

9781430265504_Fig13-04.jpg

图 13-4 。创建 ProWatchFaceCompanionConfigActivity.java 文件以删除红色错误高亮显示

当我输入包名时,我得到一个红色的错误高亮,可以在图 13-5 的顶部看到。当我查看 IntelliJ 项目管理窗格时,我注意到 IntelliJ 没有遵循我的指示,如图图 13-4 中编号 2 所示,并把我的类放在 androidTest 文件夹中,在那里它随后生成了“不正确的包路径”错误。

9781430265504_Fig13-05.jpg

图 13-5 。拖动 ProWatchFaceCompanionConfigActivity 并将其放在 proandroidwearable 包中

我纠正这个错误的方法(在 IntelliJ 方面)是将 Java 类文件从 androidTest 文件夹中拖到普通(非 androidTest)包文件夹中。这导致了“移动类”对话框的出现,在这里我点击了 Refactor 按钮,将类移动到正确的文件夹中,并为所有内容正确地设置了内部“因子”(指针),这样编译器就可以“看到”所有内容是如何组合在一起的。这可以从图 13-5 中看出。

现在你可以添加 Java 扩展关键字 和 Activity 类,你需要扩展它们来使这个类成为一个 Android Activity,它在很大程度上用于保存用户界面设计。

您还需要添加 Java 实现关键字,因为您将指定三个 Java 接口。您的智能手机和智能手表硬件之间的通信将需要这些。

你可以在图 13-6 中看到这两个 Java 关键字,以及 IntelliJ 弹出助手对话框,它显示了两个接口和一个嵌套类,它们是 GoogleApiClient 接口的一部分。在创建了这个空代码基础结构之后,您将在下一节中了解 GoogleApiClient,这样您就可以看到实现一个基本的(即空的)watch face companion 配置活动类、公共接口和被覆盖的(@Override 关键字)方法基础结构需要什么。

9781430265504_Fig13-06.jpg

图 13-6 。使用弹出的助手对话框实现 GoogleApiClient 类的 ConnectionCallbacks 接口

在 implements 关键字后,输入 GoogleApiClient 接口和一个句点,会弹出 helper 对话框,在这里可以选择一个connection callbacks(com . Google . Android . GMS . common . API . Google API client)选项。如果希望 IntelliJ 为您编写代码,请双击此选项。对onconnectionfailed listener接口 也进行同样的操作,这也显示在图 13-6 中弹出的帮助器对话框中。

一旦你实现了 GoogleApiClient 接口,你会想要添加 ResultCallback 公共接口,你可以看到我已经在图 13-7 的中间添加了这个接口。你完成的(空的)Java 类声明看起来像下面的 Java 结构,这也可以在图 13-7 中看到:

public class ProWatchFaceCompanionConfigActivity extends Activity implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
        ResultCallback<DataApi.DataItemResult> {  // Your Java class code will go in here  }

9781430265504_Fig13-07.jpg

图 13-7 。添加 ResultCallback < DataApi。DataItemResult >接口;使用 Alt+Enter 导入 DataApi 类

正如你在图 13-7 中看到的,你需要使用一个 Alt+Enter 组合键来让 IntelliJ 为你编写 DataApi 导入语句。

正如你在图 13-8 和 IntelliJ 中注意到的,在左边有一个错误提示灯泡图标。下拉此 how-to-correct 错误建议菜单,选择实现方法选项,打开选择方法实现对话框,如图 13-8 右侧所示。

9781430265504_Fig13-08.jpg

图 13-8 。下拉错误建议,并选择实现方法选项;实现所有方法

选择所有需要被覆盖的方法并点击 OK 按钮,IntelliJ 将为您编写整个类结构。由此产生的空类结构可以在图 13-9 中看到。

9781430265504_Fig13-09.jpg

图 13-9 。空的 prowatchfacecompancionconfigactivity,有六个导入语句,四个必需的方法

接下来您需要做的是添加标记来支持 CONFIG_DIGITAL 常量值,该常量值由 CompanionConfigurationAction 元数据对象保存。这是在 wear 项目的 AndroidManifest XML 定义文件中完成的,位于现有的父标签内。

Wear 应用:将伴随元数据添加到清单

接下来您需要做的是将第四个标签添加到 wear 应用的父标签中,该标签已经在项目的 wear 应用部分(section)的 Android 清单文件中。元数据条目定义了您将在应用中使用的东西,例如壁纸资源、表盘预览图像资源,或者在本例中,您在 mobile AndroidManifest.xml 中定义的 CONFIG_WATCH_FACE 动作,它将启动 Configuration Companion 活动。

在图 13-10 的底部,可以看到这个标签的 XML 标记突出显示,看起来应该像下面的 XML 标签结构:

<meta-data android:name="com.google.android.wearable.watchface.companionConfigurationAction"
           android:value="com.pro.android.wearables.proandroidwearable.CONFIG_WATCH_FACE" />

9781430265504_Fig13-10.jpg

图 13-10 。在<服务>标签内的 wear/manifest/androidmanifest . XML 中添加一个<元数据>子标签

既然已经设置了 Android Manifest XML 定义文件,并且已经将空的 prowatchfacecompcompanionconfigactivity 类结构放置到位,那么在开始编写所有这些方法的代码之前,让我们快速浏览一下 GoogleApiClient 类并了解一下 Google Play 服务。

Google Play 服务:GoogleApiClient 类

要访问 Google Mobile Services 和 Google Play Services 服务器,您需要为访问这些服务器的任何类创建一个 GoogleApiClient 对象。我喜欢将我的 GoogleApiClient 对象命名为 my GoogleApiClient。

Android 的 GoogleApiClient:使用谷歌移动服务

GoogleApiClient 公共接口是 Google 移动服务 API 的一部分。它位于 com . Google . Android . GMS . common . API . Google API client 包中,是 Google Play 服务集成的主要应用“入口点”。

在执行任何操作之前,GoogleApiClient 必须使用连接到 Google Play 服务。connect( ) 方法。这通常是通过完成的。onStart( ) 方法,您将在本章稍后使用下面的 Java 结构对其进行编码:

@Override
protected void onStart( ) {
    super.onStart( );
    myGoogleApiClient.connect( );
}

在调用**public void on connected(Bundle Bundle)**回调方法之前,您的 Google Play 服务客户端不会被视为已连接。这个空的方法可以在图 13-9 中间看到。

GoogleApiClient 对象可以与许多静态方法一起使用。 这些方法中的一些将需要连接 GoogleApiClient 对象,而其他方法在 GoogleApiClient 连接到客户端活动之前将调用排队。检查每种方法的当前 API 文档,以确定您的客户端是否需要连接。

当您的 watch face 应用使用 GoogleApiClient 对象完成时,您将需要调用。disconnect()方法。 这一般通过来完成。 onStop( ) 方法,在本章稍后的部分,你也将使用下面的 Java 结构对其进行编码:

@Override
protected void onStop( ) {
    if (myGoogleApiClient != null && myGoogleApiClient.isConnected( )) {
        myGoogleApiClient.disconnect( );
    }
    super.onStop( );
}

您应该使用一个 GoogleApiClient 实例化活动onCreate(Bundle saved instancestate)方法中的 GoogleApiClient 对象。构建器嵌套(帮助器)类。你将在本章后面编写的 Java 代码看起来像这样:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pro_watch_face_config);
        myGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(Wearable.API)
                .build( );
    }

有趣的是,我在这里讨论的所有嵌套类都是在上面列出的 GoogleApiClient 实例化代码中调用的。

  • GoogleApiClient。构建器嵌套类 提供了一个构建器类,用于使用点标记链配置 GoogleApiClient 对象。您可以在上面的代码中看到这一点,尽管方法链是使用句点对齐的,而不是使用句点将方法调用“链接”在一起。
  • GoogleApiClient。ConnectionCallbacks 嵌套接口提供了回调对象 ,当客户端连接到 Google Play 服务或从 Google Play 服务断开时调用这些回调对象。
  • 一个 GoogleApiClient 客户端。OnConnectionFailedListener 嵌套接口将为 Google Play 服务访问导致尝试将 GoogleApiClient 对象连接到此 Google Play 服务失败的场景提供回调对象。

GoogleApiClient 类包含 15 个公共方法。我不能在这里涵盖所有这些,但是我将涵盖您将在 Java 代码中使用的那些,以实现配套的监视界面配置活动。

如果您想快速了解所有 15 个 GoogleApiClient 方法调用规范,可以访问 Android 开发者网站,网址为:

https://developer.android.com/reference/com/google/android/gms/common/api/GoogleApiClient.html
  • 抽象的虚空**。方法 用于将 GoogleApiClient 对象连接到 Google Play 服务服务器。您将在。prowatchfaceconnectionconfigactivity 类中的 onStart()方法。
    *** 抽象的虚空
    。** disconnect( ) 方法用于在您使用完毕后关闭与 Google Play 服务服务器的连接。您将在中使用它。onStop()方法,在本章的后面,当您开始实现您的所有方法时。* 抽象布尔**。** isConnected( ) 方法用于检查 GoogleApiClient 对象是否为当前连接到 Google Play 服务的 。如果是,那么对其他 Google Play 服务方法的请求将会成功。* 抽象布尔**。** isConnecting( ) 方法 检查 GoogleApiClient 对象是否为当前试图将与 Google Play 服务连接的**。****

**接下来,让我们为 ProWatchFaceCompanionConfigActivity 类的 onCreate()方法准备一些 XML 基础结构。创建这些 XML 素材后,您可以在 Java 代码中引用 XML 组件。之后,您将了解 GoogleApiClient。构建器嵌套类,然后构建 GoogleApiClient 对象,这样您就可以在 Pro Watch Face 应用中实现 Google Play 服务,您将在本书中继续改进该应用。有大量的代码要写,有大量的 Android 类要学,所以让我们继续吧!

创建播放客户端:编写您的。onCreate()方法

任何 Android 活动中最重要的方法是 onCreate()方法,因为它总是必须在适当的位置,它创建用户界面布局,在这种情况下,还有 GoogleApiClient 对象及其对等 id。

您需要做的第一件事是通过一个**受保护的 void onCreate(Bundle savedInstanceState)**方法结构覆盖 Activity 超类 onCreate()方法。在该方法中,您将使用 Java super 关键字将 savedInstanceState Bundle 对象向上传递给 Activity 超类,以便超类。onCreate()方法可以处理它。在图 13-11 中间用黄色突出显示的 Java 方法结构应该类似于下面的 Java 代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

9781430265504_Fig13-11.jpg

图 13-11 。向该类添加一个受保护的 void onCreate()方法和 super.onCreate()超类方法调用

才能调用标准活动**。** setContentView( ) 方法,您需要创建布局 XML 目录,并将素材放置在项目的这个移动部分中。右击 /mobile/res 文件夹,选择新建image Android 资源目录菜单序列。使用资源类型下拉框选择布局;这也将设置文件夹名称,如图图 13-12 所示。

9781430265504_Fig13-12.jpg

图 13-12 。右击/res 文件夹,选择新建image Android 资源目录菜单选项和对话框

点击 OK 按钮创建 /mobile/res/layout 文件夹,然后在项目导航窗格中右键点击这个新文件夹,选择新建image Android 资源文件菜单序列。

这个菜单序列也可以在图 13-12 中看到,在新建子菜单的顶部,所以我这里只显示新建资源文件对话框,你可以在图 13-13 中看到。您将使用 Android Activity 用户界面布局文件命名约定来命名 XML 资源文件(Activity 优先并使用下划线字符),因此使用名称 activity_pro_watch_face_config 并选择一个 LinearLayout 根元素,保留其他两个字段的默认设置(main 和 layout)。点击 OK 按钮,创建一个新的activity _ pro _ watch _ face _ config . XML用户界面布局定义文件。这将保存配置 UI 的用户界面定义。

9781430265504_Fig13-13.jpg

图 13-13 。右键点击新建/res/layout 文件夹,选择新建image Android 资源文件选项和对话框

bootstrap**父标签将使用默认的垂直**方向进行配置,这是您想要的,以及默认的布局常量 match_parent 。这告诉用户界面布局容器填充屏幕尺寸,因为显示屏是 LinearLayout 用户界面布局容器 XML 定义的父级。

父容器中,添加子 < TextView > UI 小部件,它将包含用于用户界面设计的标题。添加一个带有 @+id/title 值的 ID 参数和一个带有**@ string/pro watch face _ config**值的 android:text 参数。最后,添加 layout_heightlayout_width 参数,Android OS 要求为每个用户界面元素指定这些参数。将 layout_width 设置为 match_parent ,将 layout_height 设置为 wrap_content 。这些常量设置将指定 TextView 元素的布局参数,这将迫使它从一边到另一边横跨屏幕,并限制它只与它包含的文本一样高,这将是您的 UI 设计的标题(标题)。

基本用户界面布局定义 XML 标记,如图图 13-14 所示,应该看起来像下面的 XML 定义结构:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/title"
        android:text="@string/prowatchface_config"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

9781430265504_Fig13-14.jpg

图 13-14 。在父< LinearLayout >容器中添加一个< TextView >,其标题的 android:id 值为

在我开始 onCreate()方法的 Java 编码之前,您需要创建在用户界面布局定义文件中引用的 <字符串> 常量。打开**/mobile/RES/values/strings . XML文件,在父<资源>标签下添加子<字符串>标签。将这个命名为 prowatchface_config** ,数据值为 Configure Watch Face,如图图 13-15 所示,这样你就可以直观地将这些放在一起比较。

9781430265504_Fig13-15.jpg

图 13-15 。创建一个名为 prowatchface_config 的<字符串>常量,其数据值为 Configure Watch Face

现在,您已经准备好切换齿轮,进入一些 Java 编码,这样您就可以实现配套应用的其余部分。onCreate()方法结构。添加setContentView(R.layout.activity_pro_watch_face_config);语句,如图 13-16 中的所示,并在类的顶部声明一个私有字符串 watchFacePeerId。如您所见,接下来您将使用 WatchFaceCompanion 类实例化 PeerId。getIntent(),和。getStringExtra()方法调用。接下来您将学习这些类和方法调用。

9781430265504_Fig13-16.jpg

图 13-16 。添加一个名为 watchFacePeerId 的私有字符串变量;将其设置为等于。getIntent( )getStringExtra()

使用实例化 watchFacePeerId 。getIntent()。getStringExtra( ) 方法链,然后传入 WatchFaceCompanion。EXTRA_PEER_ID 常量。

如图图 13-17 所示,Java 语句应该如下所示:

watchFacePeerId = getIntent( ).getStringExtra(WatchFaceCompanion.EXTRA_PEER_ID);

9781430265504_Fig13-17.jpg

图 13-17 。下拉错误建议菜单;选择将库“wearable-1.1.0”添加到类路径选项

请注意,在 IDE 的左侧有一个红色的错误灯泡,因此下拉此菜单并选择将库“wearable-1.1.0”添加到类路径选项。通常,第一个选项是最佳的。Android Studio 将根据它认为最可行的解决方案对选项进行优先排序。

有趣的是,当我选择这一点时,Android Studio 为 WatchFaceCompanion 类编写了一个 import 语句,因此 Android Studio 需要纠正他们的弹出助手用户界面代码来纠正这一异常,因为我查看了该项目的 Gradle 文件,但没有看到任何附加内容。

让我们花点时间对 WatchFaceCompanion 和 ComponentName 类进行一个高层次的概述,之后,您将继续您的 Java 编码。

WatchFaceCompanion 类:配置常数

Androidpublic final watch face companion类扩展了 java.lang.Object 并定义了常量供表盘配置活动类使用。这就是使用 Java final 关键字声明该类的原因。

WatchFaceCompanion 类的 Java 类层次结构如下所示:

java.lang.Object
  > android.support.wearable.companion.WatchFaceCompanion

正如您在本章中已经看到的,要注册您要在配套手机上启动的配置活动,您需要将 <元数据> 条目添加到 AndroidManifest.xml 文件中的手表表面组件(项目的磨损部分)。这指定了您的 Intent ACTION 常量,它将被“触发”以启动 Activity 子类。如果你想重温你之前写的 XML 标记,你可以在图 13-10 中看到这一点。

Activity 子类还需要有一个规范,它将列出在 XML 标记的元数据块中指定的相同的 ACTION 常量,此外还有两个 WEAR 类别,DEFAULT 和 COMPANION_CONFIGURATION,它们也已就位,如图 13-2 中的所示。

对于手表外观配置对话框活动类,用类别 com . Google . Android . wearable . watch face . category . wearable _ CONFIGURATION 替换 com . Google . Android . wearable . watch face . category . companion _ CONFIGURATION。

这个 WatchFaceCompanion 类使用两个常量,这两个常量都是字符串值:

  • EXTRA_PEER_ID 常量包含一个字符串 EXTRA 的键值,该字符串在电话侧配置活动意图(启动)对象中指定当前连接的设备的 PeerId。
  • EXTRA _ WATCH _ FACE _ COMPONENT常量包含字符串 EXTRA 的键值,该字符串为使用配置活动意图(启动)对象配置的手表面板指定 ComponentName

在开始编写与 WatchFaceCompanion 相关的 Java 代码之前,让我们更深入地研究一下 Android ComponentName 类,看看它为 Android 开发人员提供了什么,尤其是在 WatchFaces API 开发方面。

ComponentName 类:指定一个组件

Androidpublic final component nameclass 是 android.content 包的一部分,它实现了**Parcelable Cloneable Comparable**接口。该类扩展了 java.lang.Object 主类;因此,ComponentName 类具有以下 Java 类层次结构:

java.lang.Object
  > android.content.ComponentName

这个 ComponentName 类为特定的应用组件提供了一个标识符。Android 中的应用组件包括一个活动服务广播接收方内容提供方(数据库或数据存储)对象。因此,这个类允许开发者指定他们的 Android 组件类型。

在本书的例子中,它是一个活动组件类型,您正试图为 Android 操作系统定义它。需要封装在 ComponentName 对象中的两条信息来标识任何给定的 Android 组件。第一个是组件所在的包(字符串值),第二个是指定包中组件类型的子类名称(也是字符串值)。

该类指定了一个属性或字段,即公共静态最终创建者<组件名>创建者属性。

有四个重载的公共构造函数方法,没有一个是您将使用的,因为您将通过使用创建一个名为 ComponentName 的 componentName 对象。getIntent()。getParcelableExtra( ) 方法链。在本章的下一节中,您将使用下面的 Java 语句来实现这一点:

componentName = getIntent( ).getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);

在这行 Java 代码中,您可以看到 ComponentName 类和 WatchFaceCompanion 类之间的链接,这是您刚刚了解到的。

ComponentName 类有 16 个公共方法,包括**。clone( )** 方法,它将克隆 ComponentName 对象,一个**。compare to(ComponentName component name)方法比较 component name 对象,一个。equals(Object object)** 方法,一个**。getClassName( )** 方法,一个**。getPackageName( )** 方法,一个**。toString( )** 方法,以及其他类似的工具方法,可用于访问 ComponentName 信息。如果您想深入了解这 16 种方法的细节,您可以访问 Android 开发者网站的 URL:

http://developer.android.com/reference/android/content/ComponentName.html

接下来,让我们完成使用 WatchFaceCompanion 类的 Java 代码的编写,IntelliJ 已经为该类编写了一个导入语句,如图 13-18 顶部突出显示的。之后,您将了解 GoogleApiClient。构建器嵌套(帮助器)类,这样您就可以编写。onCreate()方法实例化(即构建)一个 GoogleApiClient 对象。之后,您就完成了 Google Play 服务对象的创建。

设置监视面标识:ComponentName 和 PeerId

在 setContentView()方法调用后添加一行代码,并使用声明和实例化 TextView title 对象。 findViewById( ) 方法调用,引用你的 title ID。将 watchFacePeerId String 对象设置为等于 getIntent()。getStringExtra()方法链,引用来自 WatchFaceCompanion 类的 EXTRA_PEER_ID 常量。

在类的顶部声明一个名为 ComponentName 的 componentName 对象,这样就可以在 onCreate()方法中实例化这个对象。您将设置对象等于 getIntent()。getParcelableExtra()方法链。

在 getIntent()内部。getParcelableExtra()方法链,您将需要引用来自 WatchFaceCompanion 类的 EXTRA_PEER_ID 常量。

控件的 Java 代码。onCreate()方法的结构到目前为止应该看起来如下,这也可以在图 13-18 的底部看到:

ComponentName componentName;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pro_watch_face_config);
    TextView title = (TextView)findViewById(R.id.title);
    watchFacePeerId = getIntent( ).getStringExtra(WatchFaceCompanion.EXTRA_PEER_ID);
    componentName = getIntent( ).getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
}

9781430265504_Fig13-18.jpg

图 13-18 。声明 ComponentName 对象;实例化 TextView、watchFacePeerId 和 componentName

现在您唯一要做的就是声明和实例化您的 GoogleApiClient 对象,这涉及到使用您的 GoogleApiClient。构建器类,我接下来会解释。

GoogleApiClient。构建器:构建 Google API 客户端

Androidpublic static final Google API client。Builder 类扩展了 java.lang.Object 主类,创建了以下类层次结构:

java.lang.Object
  > com.google.android.gms.common.api.GoogleApiClient.Builder

这个类创建了一个构建器类 ,用于配置 GoogleApiClient。

要实例化并构建一个名为 myGoogleApiClient 的 GoogleApiClient 对象,并为 Android Wearable API 应用提供 ConnectionCallbacks 和 OnConnectionFailedListener 支持,您可以使用以下结构:

myGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .addApi(Wearable.API)
        .build( );

GoogleApiClient。Builder 类有两个公共构造函数方法。第一个,也就是上面显示的您将使用的那个,接受上下文对象,您将使用 Java this 关键字将它传递到方法中。此构造函数方法采用以下格式:

GoogleApiClient.Builder( Context context )

还有一个更复杂(重载)的构造函数方法,如果已经在 Java 代码中创建了 ConnectionCallbacks 对象和 OnConnectionFailedListener 对象,则可以在该方法中指定这些对象:

GoogleApiClient.Builder( Context context, GoogleApiClient.ConnectionCallbacks connectedListener,
                         GoogleApiClient.OnConnectionFailedListener connectionFailedListener )

这个 GoogleApiClient。Builder 类有十几个方法,我将在这里介绍它们(就像我介绍所有的 WatchFaceStyle 一样。构建器方法),因此您可以构建应用可能需要连接到 Google Play 服务服务器的任何类型的 GoogleApiClient 对象结构:

  • <O extends Api.ApiOptions.HasOptions> GoogleApiClient.Builder addApi(Api<O> api, O options)方法允许开发者指定客户端请求哪些 API。
  • GoogleApiClient.Builder addApi(Api<? extends Api.ApiOptions.NotRequiredOptions> api)方法还允许开发人员指定客户端请求哪些 API。
  • GoogleApiClient.Builder addConnectionCallbacks(GoogleApiClient.ConnectionCallbacks listener)方法允许您注册一个监听器来接收来自 GoogleApiClient 对象的连接事件。您将在我们的表盘应用中使用它。
  • 您将在 watch face 配置应用中使用的另一个方法是GoogleApiClient.Builder addOnConnectionFailedListener(GoogleApiClient.OnConnectionFailedListener listener)方法,该方法将您的侦听器添加到 register 中,以从您的 GoogleApiClient 对象接收连接失败事件。
  • GoogleApiClient.Builder addScope(Scope scope)方法允许开发人员指定 Wear 应用需要请求的任何 OAuth 2.0 范围。
  • GoogleApiClient build( )方法允许开发人员构建一个 GoogleApiClient 对象,用于与 Google Play 服务服务器 API 进行通信。
  • GoogleApiClient.Builder enableAutoManage(FragmentActivity fragmentActivity, int clientId, GoogleApiClient.OnConnectionFailedListener unresolvedConnectionFailedListener)方法允许开发人员使用连接到。onStart()方法,并在。onStop()方法。
  • GoogleApiClient.Builder setAccountName(String accountName)方法允许开发者在硬件设备上指定一个帐户名,用于连接 Google Play 服务服务器。
  • GoogleApiClient.Builder setGravityForPopups(int gravityForPopups)方法允许开发者使用 Android 重力常量在显示屏上指定游戏服务弹出窗口的一般位置。
  • GoogleApiClient.Builder setHandler(Handler handler)方法允许开发人员设置一个处理程序来指示调用回调时使用哪个线程。
  • GoogleApiClient.Builder setViewForPopups(View viewForPopups)方法允许开发人员将指定的视图设置为弹出窗口的内容视图。
  • GoogleApiClient.Builder useDefaultAccount( )方法允许开发者指定在连接到 Google Play 服务服务器时应该使用的默认帐户。

现在您对 Builder 类有了更好的了解,让我们使用 Java new 关键字和基本构造函数来构建 GoogleApiClient 对象。

构建 GoogleApiClient:使用可穿戴 API

让我们添加上一节中的构建器代码示例,它添加了 ConnectionCallbacks 和 OnConnectionFailed 侦听器对象,以及一个可穿戴的。API,并构建 myGoogleApiClient 对象。onCreate()方法的 Java 代码如图 13-19 中的所示,应该是这样的:

GoogleApiClient myGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pro_watch_face_config);
    TextView title = (TextView)findViewById(R.id.title);
    watchFacePeerId = getIntent( ).getStringExtra(WatchFaceCompanion.EXTRA_PEER_ID);
    componentName = getIntent( ).getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
    myGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(Wearable.API)
            .build( );
}

9781430265504_Fig13-19.jpg

图 13-19 。声明并实例化 myGoogleApiClient 对象,并使用 Builder 类对其进行配置

请注意,您正在通过 Java this 关键字将该类的当前上下文对象传递给 GoogleApiClient (class),以及嵌套类 ConnectionCallbacks 和 OnConnectionFailedListener,我将在本章后面详细介绍它们。这样做的原因是,上下文对象包含了关于类的所有相关(系统)信息,这些类需要引用和使用这个信息(没有双关语)才能最佳地执行它们的相关功能。

启动和停止播放客户端:onStart()和 onStop()

既然已经在 onCreate()中创建了 Google Play 服务客户端和 UI 布局容器,下一步就是创建启动客户端的代码。创建一个受保护的 void onStart( ) 方法,该方法使用 Java super 关键字调用活动超类 onStart()方法,然后调用 myGoogleApiClient 对象的. connect()方法。

这可以用下面的 Java 代码来完成,也可以在图 13-20 中看到。我单击了 myGoogleApiClient 对象来突出显示它的用法:

@Override
protected void onStart( ) {
    super.onStart( );
    myGoogleApiClient.connect( );
}

9781430265504_Fig13-20.jpg

图 13-20 。添加受保护的 void onStart()和 onStop()方法,用于连接和断开 Google Play 服务

onStop()方法有点复杂,因为它涉及一个条件 if()语句,该语句评估 myGoogleApiClient 对象以查看它是否已连接。如果它是连接的,它就断开它。之后,使用 Java super 关键字将 onStop()方法函数调用传递给 Activity 超类,后者将应用从设备的系统内存中删除。以下代码实现了这一点:

@Override
protected void onStop( ) {
    if (myGoogleApiClient != null && myGoogleApiClient.isConnected( )) {
        myGoogleApiClient.disconnect( );
    }
    super.onStop( );
}

接下来,创建 onConnected()方法,该方法将包含连接到 Google Play 服务后要做的事情的 Java 代码。

连接客户端:创建 onConnected 方法

现在让我们实现 onConnected( ) 方法。IntelliJ 使用您在类声明中指定的 Java 接口为我们创建了该方法。在空的 bootstrap 方法中,您要做的第一件事是创建一个空的 if-else 方法,该方法查看watchfacpeerid并确定它是否已被使用,也就是说,它是否包含一个非空值。如图图 13-21 所示的(仍然)空方法结构如下所示:

@Override
public void onConnected(Bundle bundle) {
    if (watchFacePeerId != null) {
       // Things to do if a connection is detected, that is, watchFacePeerId has some ID value
    } else {
       // Things to do if there has not been any connection, that is, watchFacePeerId is empty
    }
}

9781430265504_Fig13-21.jpg

图 13-21 。添加一个空的 if-else 条件结构,评估 watchFacePeerId 字符串以查看它是否被使用

现在您已经有了评估结构,我将提供一些关于 UriUri 的信息。在实现其余的 Java 代码之前,先构建类。

Android Uri 类:统一资源标识符对象

Android 公共抽象 Uri 类扩展了 java.lang.Object master 类,实现了一个 Parcelable Comparable 接口。它可以在 android.net 的包装中找到。它的类层次结构如下所示:

java.lang.Object
  > android.net.Uri

Android Uri 对象包含不可变的统一资源标识符(Uri)引用路径。URI 引用包括一个 URI 和一个片段,即跟在井号(#)后面的 URI 的组成部分。

就像java.net包中的 Uri 类一样(不要混淆这些),Android Uri 类及其 Uri。构建器嵌套类构建并解析符合 RFC 2396 标准的 URI 引用。

为了最大化性能,Uri 类不对 Uri 路径内容本身执行数据验证。这意味着对于无效的 Uri 路径数据输入,Uri 的行为是未定义的。因此,这个类有些宽容;当面对无效的输入数据时,该类返回垃圾,而不是抛出异常,除非开发人员另有规定。

Android Uri 类包含一个嵌套类 Uri。构建器“助手”类,用于构建或操作 URI 引用。在对 Uri 进行简短讨论后,您将在 onConnect()方法中使用这两个类。下一节中的构建器类。

Android Uri 类中有 39 个方法,不幸的是,我不能详细讨论所有这些方法,因为您将使用 Uri 中的方法。生成器类。但是,如果您计划经常使用 Uri 对象,您可以在闲暇时通过以下 URL 深入研究所有这些方法:

http://developer.android.com/reference/android/net/Uri.html

安卓 Uri。构建器类:构建一个 Android URI 对象

公共静态最终 Uri。Builder 类扩展了 java.lang.Object,这意味着该类是临时编写的,并且具有以下类层次:

java.lang.Object
  > android.net.Uri.Builder

Uri。构建器类是一个嵌套类或辅助类,用于构建或操作 URI 引用。值得注意的是,这个 Builder 类对于并发使用是不安全的,也就是说,没有提供跨线程的同步;仅在一个线程中使用它。

如果您想构建一个已经存在的 Uri 对象,可以使用**。** buildUpon( ) 方法从 Uri 类。

Uri。Builder 类有一个公共构造函数方法,即 Uri。Builder()方法,它将构造或实例化一个新的 Uri。生成器对象。要声明和实例化一个名为 UriBuilder 的 uriBuilder 对象,可以使用以下 Java 编程语句:

Uri.Builder uriBuilder = new Uri.Builder( );

Uri。Builder 类有 17 个公共方法,我无法在这里一一介绍。但是,我将介绍您将用来完成 onConnect()方法的那些方法,您将在本章的下一节中完成:

  • 一个 Uri。构建器类**。authority(String authority)方法用于为 Uri 对象设置权限。一个 Uri。建造者阶级。【String path】**方法用于为 Uri 对象设置路径。
  • 一个 Uri。构建器类**。scheme(String scheme)** 方法可以用来为一个 Uri 对象指定一个方案。一个乌班**。build( )** 方法用于构造一个 Uri 对象,使用已经使用 Uri 设置的其他 Uri 属性。上面列出的 Android 开发者网站 Uri URL 上列出的构建器方法。

为客户机构建 Uri:完成 onConnected()

如果 watchFacePeerId 不为空,这意味着它已经被 Google Play 服务赋值,因此,在 onConnected()方法的if(watchFacePeerId != null)部分,您将需要声明并实例化 uriBuilder Uri。通过使用下面一行 Java 代码来创建对象:

Uri.Builder uriBuilder = new Uri.Builder( );

完成后,您就可以使用 Uri 了。你刚刚学过的构建器方法,设置方案为穿,你的路径为 PATH_WITH_FEATURE ,权限为 watchFacePeerId 值。一定要加一个**。在方法链的末尾进行 build( )** 方法调用,如图 13-22 和下面的 Java 语句所示:

Uri uri = uriBuilder.scheme("wear").path(PATH_WITH_FEATURE).authority(watchFacePeerId).build( );

9781430265504_Fig13-22.jpg

图 13-22 。实例化 Uri。名为 uriBuilder 的生成器对象;将名为 uri 的 Uri 对象设置为等于构建方案、路径和授权,然后使用。构建()

正如你在图 13-22 中看到的,你需要使用 Alt+Enter 组合键,并让 IntelliJ 为你编写一个 android.net.Uri 类导入语句。这将处理红色错误代码突出显示,至少对于 Uri (父类)、 Uri 是如此。构建器(嵌套类),和**。scheme( )** (method)引用,因为所有这些都包含在 android.net.Uri 中,因此将使用 import 语句正确引用。唯一保留的红色错误文本是 PATH_WITH_FEATURE 路径常量。

为了去掉最后一个红色的错误突出显示,您需要在类的顶部添加一个常量,使用 Java private static final 关键字链来定义 Java 常量值。使用字符串变量类型,命名为 PATH_WITH_FEATURE ,用下面的 Java 编程语句设置为等于**/watch _ face _ config/pro watch face**,如图图 13-23 所示:

private static final String PATH_WITH_FEATURE = "/watch_face_config/ProWatchFace";

既然您已经了解并设置了您的 Uri 对象,接下来您需要用这个自定义 Uri 配置的是 Wearable。API ,它是您使用 GoogleApiClient 创建的。建造者阶层的**。** addApi( ) 方法调用。这可以在图 13-19 和 13-20 中看到,如果你需要可视化的话。

9781430265504_Fig13-23.jpg

图 13-23 。使用/watch _ face _ config/pro watch face 值创建私有静态最终 PATH_WITH_FEATURE

Android 的 GMS DataApi 接口:配置数据 Api

这个安卓谷歌移动服务(GMS) DataApi 公共接口com . Google . Android . GMS . wearable . data API包的一部分。该接口用于为 Android 应用组件提供 API,以用于向 GMS 读取或写入 DataItemAsset 对象,在我们的示例中,用于可穿戴应用。DataApi 对象包含 DataItem 以及 Asset (sub)对象。

一个数据项将在您的 Android Wear 网络中的所有硬件设备之间同步。当 Wear 应用当前没有连接到网络上的任何节点时,可以加载或配置这些 DataItem 对象。当任何网络节点显示为在线时,这些 DataItem 对象将被同步。

DataItem 对象对于创建它们的应用是私有的。因此,DataItem 对象只能由其他网络节点上的应用访问。开发人员通常应该优化他们的 DataItem 对象,使其文件较小。

如果您需要传输大型或持久的数据对象,如图像,您应该使用素材对象,这是可以包含在 DataApi 对象中的另一种类型的对象。

每个 DataItem 对象都使用 URI 来标识,可通过。getUri()。Uri (object)将包含 DataItem 对象创建者和路径。完全指定的 URIs 应遵循以下格式:

穿:// < node_id > / <路径>

<node_id>部分是创建 DataItem 对象的可穿戴节点的节点 ID ,而<路径>是应用定义的路径。这意味着给定一个 DataItem 对象 URI,调用。getHost()方法可以返回对象创建者的节点 ID 数据值。</node_id>

在 DataApi 类的一些方法中,包括您将要使用的方法中,。getDataItem(GoogleApiClient,Uri) 方法,可以仅使用路径从 Uri 中省略(不利用)节点 ID 值。

在这个特定的用例中,Uri 对象可以用来引用多个数据项。这是因为多个节点可以创建使用相同确切路径值的 DataItem 对象。

包含部分指定数据项 Uri 的 URI 对象将在磨损后使用一个单正斜杠 ( / ) 字符,格式为磨损:/ <路径>

不同节点中的并发数据项对象修改可能导致不一致。在我们的用例中,DataItem 对象创建者将拥有 DataItem,因此 DataItem 对象将由原始创建者节点更新。

一个更复杂的用例可能使用所谓的“生产者消费者”方法,其中一个网络节点负责产生数据项,而另一个网络节点负责消费该数据项,当然是在该数据项被处理之后。如果您使用更复杂的用例,您应该确保 DataItem 对象具有唯一的 ID 值,并确保这些 DataItem 对象在创建后不会被修改。

DataApi 接口有四个嵌套接口:一个 DataApi。DataItemResult 接口包含单个 DataItem,即 DataApi。DataListener 接口与一起使用。addListener(GoogleApiClient,DataApi。DataListener) 方法来监听和接收数据事件, DataApi。deletedataaitemsresult接口包含被删除的 DataItem 对象的数量,最后是 DataApi。GetFdForAssetResult 接口包含所请求素材对象的文件描述符

DataApi 接口包含九个公共方法。您将使用抽象挂起结果<数据 Api。onConnect()方法中的 dataitem result>get dataitem(Google API client client,Uri uri) 方法。该方法从 Android Wear GMS 网络中检索单个 DataItem 对象。

使用 DataApi 类:配置可穿戴设备。应用接口

呼叫**。getDataItem( )** 关了可穿戴。DataApi 类,并传入您的 myGoogleApiClientUri 对象。通过向 PendingResult 类的添加方法调用来创建方法链。****setResultCallback()方法,并在当前上下文中传递对象与 Java 这个关键字。这是通过下面的 Java 语句完成的,在图 13-24 的底部高亮显示:

Wearable.DataApi.getDataItem(myGoogleApiClient, uri).setResultCallback(this);

9781430265504_Fig13-24.jpg

图 13-24 。设置可穿戴。API 到 Uri 对象。setResultCallback(this),。getDataItem(myGoogleApiClient,uri)

让我们快速看一下 Android PendingResult 类,然后创建一个使用 AlertDialog 类的名为 noConnectedDeviceDialog()的方法。

Android PendingResult 类:接收结果

Android GMS PendingResult public interface是 Wear API 的部分,包含在com.google.android.gms.common.api.PendingResult<R extends com.google.android.gms.common.api.Result>包中。这个接口有一个已知的间接子类:Batch 类。

PendingResult 对象包含从 Google Play 服务服务器调用 Wear API 方法的挂起结果。PendingResult 的最终结果对象属于类型 R ,这是 Java 的原始数据类型。可以使用两种方法之一在 Java 中检索原始数据包类型。

第一种方法是使用阻塞的调用。await( ) 或者**。await(long,TimeUnit)** 方法。第二种方法是使用 ResultCallback 接口(对象),这也是您将要做的。这是通过向**传递一个实现 ResultCallback 公共接口的对象来实现的。setResultCallback(result callback)**方法调用。

在使用。await(),或者传递给结果回调,如果再次尝试检索这个结果,就会导致抛出错误。释放与返回结果相关联的任何资源是调用实体或回调接收者的责任。一些结果类型可能会实现可释放的,在这种情况下是一个**。** release( ) 方法调用应该用来释放相关联的资源。

PendingResult 公共接口有六个方法,包括抽象 R 。await( ) 方法,该方法一直阻塞到 PendingResult 任务完成;还有一个抽象的 R 。await(long time,TimeUnit units) 方法,该方法将一直阻塞,直到 PendingResult 任务完成或等待 PendingResult 的时间单位(毫秒)超时。

还有一个抽象的虚空**。** cancel( ) 方法,该方法将请求取消 PendingResult。有一个抽象布尔**。****is cancelled()方法,该方法将指示 PendingResult 是否因调用**或调用而被取消。直接在 PendingResult 对象或封闭批处理对象上取消()。

最后是抽象的虚空**。setResultCallback(result callbackcallback)**方法,如果您希望结果对象在结果就绪时通过回调传递,该方法设置回调,以及一个抽象 void 。setResultCallback(result callbackcallback,long time,TimeUnit units) 方法,如果您希望在结果就绪或等待结果超时时通过回调传递结果,该方法将设置回调。

接下来,让我们创建一个方法,在没有连接的情况下提醒用户。

创建未连接的对话框:使用 AlertDialog

在 onConnected()方法 if-else 结构的 else 部分添加一个 noConnectedDeviceDialog()方法调用,IntelliJ 会用红色错误文本高亮显示,正如你在图 13-25 底部看到的高亮显示。

9781430265504_Fig13-25.jpg

图 13-25 。在 else 部分添加一个 noConnectedDeviceDialog()方法调用,并选择 Create Method 选项

从红色灯泡下拉菜单中选择创建方法选项,并让 IntelliJ 创建方法结构,如图图 13-26 所示。首先,声明两个名为 noConnectTextokButtonLabel 的字符串变量。

9781430265504_Fig13-26.jpg

图 13-26 。在 IntelliJ 为您创建的这个方法结构中,声明并实例化两个 String 对象

接下来,在 IntelliJ 的选项卡中打开mobile/RES/values/strings . XML文件,添加 noConnectedDeviceDialog()方法中引用的两个<字符串>常量,并为它们提供描述性的 AlertDialog 消息和 UI 按钮文本。这可以在图 13-27 中看到(突出显示),应该看起来像下面的 XML 标记:

<string name="no_connected_device">Wearable Device Not Connected!</string>
<string name="ok_button_label">OK</string>

9781430265504_Fig13-27.jpg

图 13-27 。创建文本<字符串>定义,告诉用户设备没有连接,并点击 OK

在您完成 noConnectedDeviceDialog()方法的编码之前,让我们快速了解一下 Android AlertDialog 类。

Android AlertDialog:为你的应用创建一个提醒对话框

Androidpublic alert Dialog类扩展了 Android Dialog 类,实现了 DialogInterface 。它包含在 android.app 包中,有三个已知的直接子类: DatePickerDialogProgressDialogTimePickerDialog 。类的层次结构如下所示:

java.lang.Object
  > android.app.Dialog
    > android.app.AlertDialog

AlertDialog 是一种特殊类型的 Android 对话框,可以显示一个、两个或三个按钮。如果你只想在对话框中显示一个字符串,这是为了告诉用户没有 GMS 连接。需要调用 setMessage( ) 方法。如果你想设计自定义的用户界面视图对象,你应该使用一个**。addView( )** 方法调用,您可以稍后使用更改视图 UI 设计。setView( ) 方法调用。

AlertDialog 类包含一个嵌套的 helper 类,它是一个名为 AlertDialog 的生成器类。Builder 类,您将在恢复 Java 编码时使用它来构造 AlertDialog 对象。

AlertDialog 类支持五个 Android 主题常量:Theme _ DEVICE _ DEFAULT _ DARKTheme _ DEVICE _ DEFAULT _ LIGHTTHEME_HOLO_DARKTHEME_HOLO_LIGHTTHEME_TRADITIONAL 常量,用于 Android 4.x 之前的 Android 版本,Android 4 . x 引入了 HOLO 主题。Android 5.x 使用了 Material 主题(上面提到的前两个常量)。

AlertDialog 有三个受保护的构造函数方法,包括您将要使用的方法AlertDialog(Context context),以及几个更高级的构造函数:

  • AlertDialog(Context context, int theme)构造函数将构造一个 AlertDialog,它使用您可以指定的完全独特的主题。
  • 一个AlertDialog(Context context, boolean cancelable, DialogInterface.OnCancelListener cancelListener)构造函数允许您指定一个 AlertDialog 是否是可取消的,并使用构造函数方法调用给它附加一个 cancelListener。

Android AlertDialog。构建器:构建警报对话框

Android 的公共静态 AlertDialog。Builder 类扩展了 java.lang.Object,因此被临时编码以构建警告对话框。它具有以下层次:

java.lang.Object
  > android.app.AlertDialog.Builder

该类有两个公共构造函数方法。您将使用的第一个工具是 AlertDialog。Builder(Context context) 方法,它将使用一个上下文对象(在我们的例子中是 Java this 关键字)为这个 AlertDialog 构造一个 AlertDialog。生成器对象和用于创建它的 AlertDialog 对象。

第二个构造函数是 AlertDialog。Builder(Context context,int theme) ,它允许为正在创建的 AlertDialog 指定不同的主题。

警报对话框。Builder 类有 36 个公共方法,我无法在这里详细介绍所有这些方法,但是我将在下一节介绍您将在 Java 代码中使用的方法。

  • 警报对话框**。create( )** 方法使用您提供给 AlertDialog 的参数创建一个 AlertDialog。生成器类。
  • **。setCancelable(boolean cancelable)**方法设置你的对话框是否可以被取消。
  • 。setMessage(int messageId) 方法将使用给定的资源 Id 设置要显示的消息,在我们的例子中,是一个 XML <字符串>常量。
  • 一个**。setPositiveButton(int textId,DialogInterface。OnClickListener listener)** 方法将设置一个监听器,当对话框的肯定按钮被按下时,这个监听器将被调用。
  • show( ) 方法使用提供给 AlertDialog 的参数显示 AlertDialog。生成器并将 AlertDialog 对象覆盖在屏幕上。

使用警报对话框。构建器:报警对话系统编码

在 noConnectedDeviceDialog()方法中,声明并实例化一个 AlertDialog。使用 Java new 关键字命名为 alertBuilder 的构建器,并将上下文 (this)对象传递给基本的构建器构造器方法。现在,您可以调用这个对象的三个方法,使用点标记链接,使用来自定义 AlertDialog。setMessage( ) 和**。setCancelable( )** 定义文本和 UI(取消)函数和**。setpostivebutton()**实现 UI (Button)元素及其 onClick()监听器和处理。如图图 13-28 所示,基本 Java 代码应该如下所示:

AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
alertBuilder.setMessage(noConnectText)
            .setCancelable(false)
            .setPositiveButton(okButtonLabel, new DialogInterface.OnClickListener( )
{ // handler });

9781430265504_Fig13-28.jpg

图 13-28 。实例化 AlertDialog。生成器使用。setMessage(),。setCancelable()和。setPositiveButton()

正如你在图 13-28 中看到的,你有一些红色的错误代码高亮显示,这告诉你你需要使用 Alt+Enter 组合键来让 IntelliJ 为 DialogInterface 类写一个导入语句。该类的导入触发 IntelliJ 为您提供另一个错误解决方案下拉列表。选择第一种实施方式选项,如图图 13-29 所示。

9781430265504_Fig13-29.jpg

图 13-29 。选择“实现方法错误解决”选项,让 IntelliJ 编写 onClick()方法

IDE 将在 DialogInterface 内部编写一个public void onClick(dialog interface dialog,int which)** 方法。OnClickListener( )** 对象,可以在图 13-30 中看到,以及声明、实例化和显示 AlertDialog 对象的代码,如下所示:

AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
alertBuilder.setMessage(noConnectText).setCancelable(false)
            .setPositiveButton(okButtonLabel, new DialogInterface.OnClickListener( ) {
                public void onClick(DialogInterface dialog, int which) { }
            });
AlertDialog alertDialog = alertBuilder.create( );
alertDialog.show( );

9781430265504_Fig13-30.jpg

图 13-30 。声明并实例化一个名为 AlertDialog 的 alertDialog 对象;调用. show()方法来显示它

编码 onResult 方法:DataItem 和 DataMap

接下来,您需要编写 onResult( ) 方法,它将包含一个 if-else 结构,该结构评估活动是否已经接收到 DataItemResult,因此方法名为 onResult()。if()条件使用一个逻辑和来检测是否有效。getStatus()。isSuccess( ) 方法调用链返回 true 和 if**。getDataItem( )** 方法调用不返回任何东西(null),意味着 DataItemResult 对象内部有东西。

一个空的 DataItemResult 处理结构,如图图 13-31 所示,应该是这样的(空)Java 条件 if( )-else处理结构:

public void onResult(DataApi.DataItemResult dataItemResult) {
        if ( dataItemResult.getStatus( ).isSuccess( )  &&  dataItemResult.getDataItem( ) != null ) {
                   // DataItemResult Detected! Processing
        } else {
                 // No DataItemResult Detected Processing
        }
}

9781430265504_Fig13-31.jpg

图 13-31 。在计算 dataItemResult 对象的 onResult()方法中创建 if-else 结构

现在,您可以编写 DataItem、DataMap 和 DataMapItem 对象处理代码。它位于语句的 if()部分,因为只有当 DataItemResult 对象包含要处理的数据时,它才会被执行。

下一步是进行编码,此外还要更详细地查看两个 Android GMS 类和一个 Android GMS 接口,这样您就可以理解这些与数据相关的对象在您的应用中的用途。

Android 的 DataItem 接口:磨损数据的基础

Android 公共 DataItem 接口实现了freez able,是com . Google . Android . GMS . wearable . DataItem包的一部分。DataItem 表示存储在 Android Wear 网络中的数据的基础对象。DataItem 对象在网络中的所有设备间复制。

DataItem 对象包含数据的“blob”以及相关联的素材。使用包含其创建者和路径的 Uri 来标识 DataItem。一个 DataItem 类有四个公共方法:??。getAssets( ) 方法检索包含在 DataItem 中的素材的映射。getData( ) 方法检索存储在指定 Uri 的数据的数组。getUri( ) 方法返回 DataItemUri;以及**。setData(byte[ ] data)** 方法在 DataItem 内部设置一个数据 byte[ ]数组。让我们在代码中实现一个数据项!

加载 DataItem 对象:使用. getDataItem()方法

让我们创建一个名为 configDataItem 的 DataItem 对象,并将其设置为等于一个**。getDataItem( )** 方法调用dataitem resultdataitem result 对象,该对象使用以下 Java 代码被传递到 onResult()方法中,并且可以在图 13-32 中看到突出显示。如您所见,您将需要使用 Alt+Enter 工作流程,这样 IntelliJ 将导入 DataItem 类:

DataItem configDataItem = dataItemResult.getDataItem( );

9781430265504_Fig13-32.jpg

图 13-32 。声明名为 configDataItem 的 DataItem,并将其设置为等于对 dataItemResult 的 getDataItem()调用

接下来让我们看看 Android GMS DataMapItem 类,因为这是您将在 onResult()方法 if()结构中使用的下一个对象。

Android 的 DataMapItem 类:带有地图的 DataItem

一个 Android 公共 DataMapItem 类扩展了 java.lang.Object,它包含在com . Google . Android . GMS . wearable包中,其层次结构如下:

java.lang.Object
  > com.google.android.gms.wearable.DataMapItem

DataMapItem 类(对象)包装 DataItem 对象以创建称为 DataMapItem 对象的高级映射 DataItem 对象。这些对象包含更多的结构化数据,甚至是可序列化的数据。该类有三个公共方法: static。from DataItem(DataItem DataItem)方法提供了一个包装 DataItem 对象的 DataMapItem 对象;。getDataMap( ) 方法提取了一个数据映射对象,您将在接下来了解这个对象;而**。getUri( )** 方法提取一个 Uri 对象。让我们实现 DataMapItem。

使用 DataMapItem 对象。fromDataItem()方法

声明一个名为 dataMapItem 的 DataMapItem,并将其设置为等于一个**的结果。from dataitem(configDataItem)**方法使用下面的语句调用 DataMapItem 对象,该语句在图 13-33 : 中突出显示

DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);

9781430265504_Fig13-33.jpg

图 13-33 。声明名为 dataMapItem 的 DataMapItem,并将其设置为等于 fromDataItem(configDataItem)调用

接下来,让我们看看 Android GMS DataMap 类,然后您将完成 onResult()方法结构的 if()部分的编码。

安卓资料图

Android GMS public DataMap 类扩展了 java.lang.Object,是com . Google . Android . GMS . wearable包的一部分。GMS 数据映射 Java 类层次结构如下所示:

java.lang.Object
  > com.google.android.gms.wearable.DataMap

数据映射对象包含由 PutDataMapRequestDataMapItem 对象支持的数据映射。数据映射对象也可以与捆绑对象相互转换。值得注意的是,在此数据转换过程中,此转换过程将删除 DataMap 对象未明确支持的任何数据类型。

DataMap 类有一个公共构造函数方法 DataMap()和 55 个公共方法,我无法在这里一一介绍。我将介绍一些你可能想在穿戴应用开发中使用的更有用的方法:

  • clear( ) 方法将从调用该方法的数据映射对象的映射中删除所有数据元素(因此它清除了一个数据映射)。
  • 。containsKey(String key) 如果所请求的键包含在调用方法的 DataMap 对象的映射中,方法将返回 true。
  • 。fromByteArray(byte bytes) 方法将返回一个数据映射对象,给出一个字节数组,在 Java 中注释为 byte
  • 。getByte(String key) 方法可以返回一个与给定键相关联的值,如果给定键不存在所需类型的映射,则返回字节 0。
  • 。getByte(String key,byte defaultValue) 方法返回与给定键相关联的数据值,如果所提供的键不存在所需类型的映射,则返回 defaultValue。
  • **。getByteArray(String key)**方法将返回与给定键关联的值,如果给定键不存在所需类型的映射,或者如果空值与该键显式关联,则返回 null。
  • 。getDataMap(String key) 方法将返回一个与给定键相关联的值,如果给定键不存在所需类型的映射,则返回 null;如果与该键显式相关联,则返回 null 值。
  • 一个**。** 如果数据映射的映射为空,isEmpty( ) 方法返回 true。一个**。** keySet( ) 方法将返回一个集合,包含在数据映射中用作键的字符串。
  • 。putAll(DataMap dataMap) 方法可以将给定数据映射中的所有映射插入到数据映射中。。putByte(String key,byte value) 将一个字节值插入到数据映射的映射中,替换提供的键的任何现有值。
  • 。putByteArray(String key,byte values) 方法将一个字节数组值插入到该数据映射的映射中,替换所提供键的任何当前存在的值。
  • 。putDataMap(String key,DataMap value) 方法将数据映射插入到目标数据映射的映射中。这将替换所提供键值的任何现有数据映射。
  • toByteArray( ) 方法可以返回序列化的 byte数组对象,将 DataMap 的内容表示为数组。
  • 。remove(String key) 方法有选择地从目标数据映射的映射中删除任何带有所提供键值的数据条目。
  • size( ) 方法将返回数据映射中键值对的数量。
  • toBundle( ) 方法将返回(创建)一个 Bundle 对象,该对象包含目标数据映射中包含的所有元素,该方法已被调用。

创建数据映射对象:使用. getDataMap()方法

声明一个名为 configDataMap 的数据映射对象,并使用实例化它。getDataMap( ) 方法,在前面的代码行中创建的 dataMapItem 对象中调用。这是通过以下语句完成的,该语句在图 13-34 中突出显示。不要忘记使用一个 Alt+Enter 工作流程,让 IntelliJ 为您编写导入数据映射 Java 导入语句:

DataMap configDataMap = dataMapItem.getDataMap( );

9781430265504_Fig13-34.jpg

图 13-34 。声明名为 configDataMap 的数据映射,并将其设置为等于 dataMapItem.getDataMap()调用

因为在本章中您将实现核心的 Google 移动服务引擎,所以接下来您将在 ProWatchFaceConfigListenerService 类的主体中完成 onMessageReceived()方法的编码。

创建监听程序服务:。onMessageReceived()

关闭 IntelliJ 中所有打开的编辑选项卡,进入 wear/java 文件夹,使用右键跳转到源工作流程打开 ProWatchFaceConfigListenerService 类,如图图 13-35 所示。

9781430265504_Fig13-35.jpg

图 13-35 。关闭所有选项卡,进入 wear/java,右键单击并打开 ProWatchFaceConfigListenerService

正如你在图 13-36 中看到的,因为 IntelliJ 为你创建了空类,你需要下拉错误助手对话框菜单并选择一个实现方法选项。这将允许 IntelliJ 完成空类的创建,覆盖所有需要实现的方法。

有趣的是,您不会在这些方法中添加任何新的(专有的)代码,但是请注意,这并不意味着这些方法不做任何事情(超类将实现它们的默认行为代码),只是它们不为这个实现做任何其他事情(定制的或新的)。

现在您可以声明mygoogle API clientGoogle API client 并开始工作了!

9781430265504_Fig13-36.jpg

图 13-36 。单击红色错误灯泡下拉菜单,选择实施方法选项,单击确定按钮

在类的顶部声明私有的 mygoogle API clientgoogleapi client 对象,正如在图 13-37 中突出显示的那样,这样你就可以在一个**on messagereceived(MessageEvent message event)**方法结构中构建它,在简单讨论了这个 message event 类之后,你将对其进行编码。

9781430265504_Fig13-37.jpg

图 13-37 。添加一个名为 myGoogleApiClient 的私有 GoogleApiClient 对象;将@Override 方法留空

Android MessageEvent 类:处理消息

Android GMSpublic message event 接口处理关于监听器接收到的消息的信息,在本例中是可穿戴监听器服务。它包含在com . Google . Android . GMS . wearable包中。

该接口公开了四个公共方法:。getData( ) 方法,该方法返回用消息传递的数据;。getPath( ) 方法,该方法返回消息被传递到的路径;。getRequestId( ) 方法,该方法返回发送方生成的消息的请求 ID;还有那个**。getSourceNodeId( )** 方法,该方法返回发送方的节点 Id。您将使用。getData()方法提取您的设置。

实现 MessageEvent 对象:提取数据

接下来让我们覆盖 onMessageReceived()方法,因为这将是 ConfigListenerService 用来处理 MessageEvent 对象中保存的配置设置数据的主要方法。使用以下 Java 代码创建空的**public void on messagereceived(message event message event)**方法结构,如图图 13-38 所示:

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    // Message Event Processing Code goes in here
}

9781430265504_Fig13-38.jpg

图 13-38 。添加一个公共 void onMessageReceived(message event)方法;使用 Alt+Enter 导入该类

在 onMessageReceived()方法中,声明名为 rawData 的 byte[ ] 数组,然后将其设置为等于。getData()方法 使用以下 Java 代码调用 messageEvent MessageEvent 对象:

byte[ ] rawData = messageEvent.getData( );

请记住,这个 messageEvent 对象是传递给 onMessageReceived()方法的对象。下一行 Java 代码将创建 DataMap 对象,我们将其命名为 keysToOverwrite ,然后使用加载它。 fromByteArray( ) 方法调用,使用下面的 Java 代码语句:

DataMap keysToOverwrite = DataMap.fromByteArray(rawData);

正如你在图 13-39 中看到的红色错误代码,你必须使用 Alt+Enter 组合键,并让 IntelliJ 导入数据映射类以在 ProWatchFaceConfigListenerService 类中使用。

9781430265504_Fig13-39.jpg

图 13-39 。添加一个名为 rawData 的字节数组,并使用。getData()方法来加载它;添加数据映射对象

创建 onMessageReceived()方法的下一步是创建两个空的条件 if()结构来保存条件处理任务。

其中一个将在 GoogleApiClient 对象为空(null 值)时使用,另一个将在 GoogleApiClient 存在但当前未连接时使用。这两个条件 if 结构的 Java 代码可以是 见图 13-40 ,应该看起来像下面的 Java 代码:

if( myGoogleApiClient ==  null ) {
      // Code to Create and Build a GoogleApiClient Object
}
if( !myGoogleApiClient.isConnected( ) ) {
    // Code to Execute if GoogleApiClient is NOT connected
}

9781430265504_Fig13-40.jpg

图 13-40 。添加两个条件 if()语句,以查看客户端对象是否为空,或者客户端对象是否未连接

如果 myGoogleApiClient 对象中最初没有加载任何数据,也就是说,如果 myWatchFaceClient 对象具有空值,那么您可能希望使用嵌套的 builder helper 类创建一个新的 GoogleApiClient 对象。

这是同一个 GoogleApiClient。您之前为 ProWatchFaceCompanionConfigActivity.java 类编写的构建器代码,它应该类似于下面的 Java 代码块,如图 13-41 所示:

if( myGoogleApiClient == null ) {
    myGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .addApi(Wearable.API)
        .build( );
}

正如你在图 13-41 中看到的,你需要使用 Alt+Enter 组合键,并让 Intelli-J 导入 Android Wearable 类,这样你就可以在 GoogleApiClient 内部使用它的 API。构建器方法链。addApi()方法调用。

接下来,您需要编写另一个条件 if()结构来评估 GoogleApiClient 对象(如果它不为 null),以查看它是否连接到服务器。如果它没有连接到 Google Play 服务服务器,那么您将需要使用编写一些代码。 blockingConnect( ) 方法,将 Wear 应用连接到 Google Play 服务服务器。

9781430265504_Fig13-41.jpg

图 13-41 。实例化和。build()一个新的 GoogleApiClient。如果 myGoogleApiClient 对象为 null(空),则为生成器对象

在实现 if(!idspnonenote)之前。myGoogleApiClient.isConnected())结构,让我们更仔细地看看 ConnectionResult 类(和对象),您将使用它来提取 WatchFaces 配置数据。之后,您可以完成 onMessageReceived()方法的实现,然后在下一章中,您将创建一个配置工具类,将所有这些整合在一起。

ConnectionResult 类:连接到网络

Android 的public final****connection result类扩展 Java.lang.Object 并实现 Parcelable 。它的 Java 类层次结构如下所示:

java.lang.Object
  > com.google.android.gms.common.ConnectionResult

ConnectionResult 类包含当客户端无法连接到 Google Play 服务时使用的所有可能的网络连接错误代码。

GoogleApiClient 使用错误代码。onconnectionfailed listener helper 类,它们都列在表 13-1 中,以便您熟悉它们。这些是 Google Play 服务服务器在出现问题时产生的错误。与 Google Play 合作将是 Wear 开发者需要掌握的重要领域;因此,我想详细介绍一个 ConnectionResult 常量表,因为您需要知道这些标志。

ConnectionResult 类有一个创建 ConnectionResult 对象的公共构造函数方法。这个公共构造函数方法采用格式connection result(int status code,PendingIntent pendingIntent) 。这个类有十个公共方法,包括一个**。方法描述内容();。equals( )** 方法;。getErrorCode( ) 方法,指示中断连接的错误类型;。getResolution( ) 方法,包含解决失败连接的待定意图,以及**。hasResolution( )** 方法,调用将返回 true。startResolutionForResult(Activity,int) 并将启动任何可能需要用户交互的意图对象。

表 13-1 。ConnectionResult 类常量及其对网络连接状态的意义

|

连接结果常量

|

它意味着什么

API _ 不可用 您试图连接的 API 组件不可用
已取消 客户端通过调用。断开( )
开发者 _ 错误 该应用不知何故被错误配置了
内部错误 出现内部错误
中断 等待连接完成时发生中断
无效 _ 账户 客户端试图使用无效的帐户名连接服务
许可证检查失败 该应用未授权给用户
网络 _ 错误 出现网络连接或传输错误
分辨率 _ 要求 完成连接需要某种形式的解决方案
服务 _ 禁用 设备上已安装的 Google Play 服务版本被禁用
服务 _ 无效 设备上安装的 Google Play 服务不可信
服务 _ 缺失 设备中缺少 Google Play 服务应用软件
服务 _ 更新 _ 必需 安装的 Google Play 服务版本已过期
登录失败 客户端试图连接到未登录的服务用户
需要登录 客户端试图连接到未登录的服务用户
成功 您的连接已成功协商
超时 等待连接完成时超时

有一个**。isSuccess( )** 方法,如果连接成功,则返回 true。一个**。startResolutionForResult(Activity Activity,int requestCode)** 方法通过启动任何需要用户交互的 Intent 对象来解决网络错误。还有一个**。toString( )** 将结果数据转换成文本的方法和一个**。writeToParcel(Parcel out,int flags)** 方法将结果写入一个 Parcel 对象格式。现在,让我们实现一个 ConnectionResult!

实现 ConnectionResult:阻塞连接

下一个条件 if()结构将确定 GoogleApiClient 对象是否连接到 Google Play 服务服务器,方法是使用 myGoogleApiClient.isConnected()方法调用的反义词(由前面的感叹号表示的 Java NOT 操作数)。如果 GoogleApiClient 对象没有连接,您将声明一个 ConnectionResult 对象,将其命名为 myConnectionResult ,并将其设置为等于**。blockingConnect( )** 方法调用关闭 myGoogleApiClient 对象。使用带有时间单位的 30 整数值,给网络连接尝试 30 秒。秒常量重试后续连接尝试。这将通过使用以下 Java 代码来完成,如图 13-42 底部突出显示的:

if( !myGoogleApiClient.isConnected( ) ) {
    ConnectionResult myConnectionResult = myGoogleApiClient.blockingConnect(30, TimeUnit.SECONDS);
}

9781430265504_Fig13-42.jpg

图 13-42 。声明并命名 myConnectionResult 对象,并使用。blockingConnect()方法

对于本章来说,这是一个完美的停止(休息)点,这一章涵盖了 Google Play 服务和 API,这些 API 允许我们在下一章开始编写配置常量工具类之前与它连接和对话。

摘要

在本章中,您学习了 Android 中的 Google 移动服务(GMS)包,它是用于与 Google Play 服务服务器交互的 API。您在编写实现 WatchFace 配置应用所需的另外两个类的上下文中看到了这一点。

在下一章中,您将编写第三个类,一个工具类,然后您将开始在真实世界的设备上测试 watch faces 并使用真实世界的服务!

首先,您创建了ProWatchFaceCompanionConfigActivity.java类和使其运行所需的 XML 素材,然后您创建了 GoogleApiClient 对象,这样您就可以与 Google 的 Play 服务联网。您学习了如何使用 GoogleApiClient 构建客户端。构建器以及如何用 DataItem、DataMapItem 和 DataMap 对象(类)封装数据,以及这些如何与您的手表外观配置数据相关联。

然后你创建了一个ProWatchFaceConfigListenerService.java类,它将与伴随的活动类 ProWatchFaceCompanionConfigActivity 以及你将在下一章创建的工具类一起使用。

在下一章中,您将创建 ProWatchFaceUtility 类,并将它连接到您在本章中创建的类,以及您在本章之前的章节中创建的 watch face 应用。**

十四、表盘配置伴侣活动工具和可穿戴 API

现在,您已经为项目的移动部分中的 watch face companion 配置活动编写了基础活动类,是时候创建将存在于项目的穿戴部分中的 ProWatchFaceUtility 类了。该类将定义配置设置常量,并包含操作它们的函数(方法),使用数据映射、数据项目和数据映射项目对象,以及可穿戴类的 API数据 Api节点 Api消息 Api 属性(数据字段)。在这一章中,你将会学到所有关于这个可穿戴类及其 API 的知识。

在本章中,您还将使用 XML 标记来为“观察界面配置”活动创建用户界面设计。这个 UI 设计将使用四个微调器小部件,这将允许您的用户选择八种标准系统颜色中的一种,用于您的表盘设计元素,包括刻度线、时针、分针和秒针。

在这一章中,你将学习并使用 Java 代码实现 Android Wearable class 数据字段 API 的每一个属性。其中包括 API数据 Api节点 Api消息 Api 。您还将了解 Android LinearLayout 容器类以及 Android 微调器TextView 小部件。

您将实现一个复杂的 Android 工具类,它使用 DataAPI、NodeAPI 和 MessageAPI 在 GMS 网络上进行对话。您将开发键-值数据对,该数据对将为 DataMap、DataItem 和 DataMapItem 对象定义表面配置数据,并且您将在 Java 代码中实现这一点。

ProWatchFaceUtility 类:管理数据

在第一部分中,您将为表盘用户手机上的对话框创建配套配置基础的最后一个类,该类允许用户配置他们的表盘设计颜色。

Pro WatchFaces 工具类将存在于 Android Studio 项目的 wear/java 部分,以及 ProWatchFaceConfigListenerService 和呈现手表表面本身的初始 ProWatchFaceService 类。

在您为希望您的用户能够为手表界面交互模式配置的颜色值创建了 ProWatchFaceUtility.java 类结构和常量之后,我将讨论 PutDataMapRequest 类,然后您将编写代码来实现一个工具类,使用您在过去几章中学习的类。

创建 ProWatchFaceUtility 类:定义常数

右键单击/wear/java/package 文件夹,如图 14-1 所示,选择新建image Java 类菜单序列,进入新建类对话框。因为这是 utility 类,所以我们将其命名为 ProWatchFaceUtility 并在对话框的下拉中选择类的种类作为,如图图 14-1 右上角所示。单击 OK 按钮并创建该类,该类将在 IntelliJ 中的新编辑选项卡中打开。

9781430265504_Fig14-01.jpg

图 14-1 。使用新的image Java 类菜单序列和对话框来创建你的 ProWatchFaceUtility 类

在 public 关键字之后、类声明和名称之前添加 Java final 关键字,使您的类成为 final。这将锁定它,仅用作 WatchFaces 配置工具。

接下来,添加五个公共静态最终字符串常量。第一个应该是一个 PATH_WITH_FEATURE 常量,它为 watch face companion 应用提供一个唯一标识符 ,就像您在自己的 mobile 应用组件 watchfaceconnectionconfigactivity 中创建的那样。如果您创建了多个 Watch Faces API 应用,那么每个应用都应该有一个唯一的 ID,它是不同的 Watch Faces 组件通过 GMS 网络基础设施与匹配的应用组件进行对话的方式。

四个颜色常量允许用户定义表盘设计特征所用的颜色,即刻度线、时针、分针和秒针。对于 DataItem 和 DataMap 对象中使用的键值对,您将需要颜色常量 。让我们先使用设计元素类型前的 KEY_COLOR 创建键常量,这样 KEY_COLOR_TICK_MARK 将表示您的刻度线颜色配置数据值的键值。

您的最终类声明,连同五个 Java 常量声明,如图 14-2 中突出显示的那样,应该看起来像下面的代码:

public final class ProWatchFaceUtility {
    public static final String PATH_WITH_FEATURE = "/watch_face_config/ProWatchFace";
    public static final String KEY_COLOR_TICK_MARK = "COLOR_TICK_MARK";
    public static final String KEY_COLOR_HOUR_HAND = "COLOR_HOUR_HAND";
    public static final String KEY_COLOR_MINUTE_HAND = "COLOR_MINUTE_HAND";
    public static final String KEY_COLOR_SECOND_HAND = "COLOR_SECOND_HAND";
}

9781430265504_Fig14-02.jpg

图 14-2 。将最后一个修饰符添加到类声明以及路径和交互模式颜色值的常量中

现在你已经定义了你的键串(听起来像是一个国家的热门话题:“你定义了我的键串宝贝”),让我们为每一个定义 Android 颜色类常量,这将定义默认的颜色设置。到目前为止,这个应用包括白色(刻度)、蓝色(小时)、绿色(分钟)和红色(秒)。

确保使用 Android 系统颜色常量,因为这些常量已经在操作系统中为您定义了。稍后你可以使用**。** parseColor( ) 方法将这些转化为 Android OS 可以使用的整数颜色数据值。这显示在图 14-3 的中间部分,使用以下 Java 代码:

public static final String COLOR_TICK_MARK_INTERACTIVE = "White";
public static final String COLOR_HOUR_HAND_INTERACTIVE = "Blue";
public static final String COLOR_MINUTE_HAND_INTERACTIVE = "Green";
public static final String COLOR_SECOND_HAND_INTERACTIVE = "Red";

9781430265504_Fig14-03.jpg

图 14-3 。创建一个私有的静态 int parseOptionColor()方法,将颜色字符串转换为颜色类常量

让我们创建一个名为私有静态方法。parseOptionColor( )返回一个表示 Android 颜色类常量的 int (整数)数据值。

这个方法将接受一个字符串参数,您将把它命名为 optionColor ,并使用将颜色值作为一个整数返回。 toLowerCase( ) 方法调用,关闭 optionColor 参数。这是在 Color.parseColor( ) 方法调用的参数区域内完成的,因为您总是希望编写密集的 Java 代码。你的 Java 方法结构,可以在图 14-3 的底部看到突出显示,应该看起来像下面的 Java 代码:

private static int parseOptionColor(String optionColor) {
    return Color.parseColor(optionColor.toLowerCase( ));
}

确保单击红色的错误代码突出显示,并使用 Alt+Enter 工作流程让 IntelliJ 编写您需要的导入语句。

下一步是使用。您编码的 parseOptionColor()方法来创建 COLOR_VALUE 常量。这些将用于您的键-值对的第二部分,用于 DataItem 和 DataMap 对象中,并将使用与您的默认颜色常量相同的常量名称,除了您将在单词 COLOR 之后插入单词 VALUE ,以便您的常量更具描述性,例如:COLOR _ VALUE _ TICK _ MARK _ INTERACTIVE

四个方法调用的 Java 代码,为你的键-值数据对的值部分设置常量值,可以在图 14-4 的最底部看到,应该看起来像下面四个 Java 语句:

public static final int COLOR_VALUE_TICK_MARK_INTERACTIVE =
                                           parseOptionColor(COLOR_TICK_MARK_INTERACTIVE);
public static final int COLOR_VALUE_HOUR_HAND_INTERACTIVE =
                                           parseOptionColor(COLOR_HOUR_HAND_INTERACTIVE);
public static final int COLOR_VALUE_MINUTE_HAND_INTERACTIVE =
                                           parseOptionColor(COLOR_MINUTE_HAND_INTERACTIVE);
public static final int COLOR_VALUE_SECOND_HAND_INTERACTIVE =
                                           parseOptionColor(COLOR_SECOND_HAND_INTERACTIVE);

9781430265504_Fig14-04.jpg

图 14-4 。创建调用 parseOptionColor()方法来配置自身的 COLOR_VALUE 常量

现在,您已经定义了十几个常数,并编写了一个非常小但有用的实用方法,是时候进入一些更复杂的方法编码,并了解另一个与通过网络发送 DataItem 对象(和 DataMap 对象)相关的 Android GMS 类了。

接下来您将创建的方法将常量值加载到 DataMap 对象中,并将它们作为 DataItem 对象提交给 GMS。

将 DataItems 载入至 DataMap: .putConfigDataItem()

让我们创建一个**公共静态 void****putConfigDataItem()**方法,它在参数列表区域接受一个 GoogleApiClient 对象和一个 DataMap 对象。

将 GoogleApiClient 对象命名为 googleApiClient ,将 DataMap 对象命名为 newConfigData 。因为在这个 Java 类中您还没有使用这些对象(类)类型,所以您将得到红色的错误代码高亮显示,为此您将需要使用 Alt+Enter 工作进程来让 IntelliJ 为您编写这些类的导入语句。

空的 Java 方法结构在图 14-5 底部用黄色突出显示,看起来应该像下面的 Java 代码:

public static void putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfigData) { }

9781430265504_Fig14-05.jpg

图 14-5 。创建一个公共静态 void putConfigDataItem(Google API client,DataMap)空方法结构

在使用 PutDataMapRequest 类和对象(这是该方法的核心)之前,接下来让我们了解一下这个 Android GMS 类。

Android PutDataMapRequest 类:放入数据请求

Android GMSpublic****PutDataMapRequest类扩展了 java.lang.Object,包含在com . Google . Android . GMS . wearable包中。该类的 Java 层次结构如下所示:

java.lang.Object
  > com.google.android.gms.wearable.PutDataMapRequest

PutDataMapRequest 类是 PutDataRequest 类的支持数据映射的版本。该类有六个公共方法,这些方法与向 Google Play 服务服务器输入数据地图请求相关。

asPutDataRequest( ) 方法,当您写完 putConfigDataItem()方法后,您将在本章的下一节中使用它,它从 PutDataMapRequest 对象创建 PutDataRequest 对象。

您还将使用**。create(String path)** 方法,该方法将使用自定义 PATH_WITH_FEATURE(常量)路径创建 PutDataMapRequest 对象。还有一个**。使用 DataMapItem 对象从 DataMapItem 创建 PutDataMapRequest 的方法。一个**。createwitheautoappendedid(String path prefix)方法使用随机生成的 Id 创建一个自动化的 PutDataMapRequest,并以您的路径为前缀。

有一个**。** getDataMap( ) 方法,您将使用它从 PutDataMapRequest 对象中提取一个 DataMap 对象,以及一个**。** getUri( ) 方法如果要从 PutDataMapRequest 对象中提取 Uri 对象。

使用 putdatamaprequest 放置配置数据项

现在让我们实现一个 PutDataMapRequest 对象。我将向您展示如何在 ProWatchFaceUtility.java 类中使用它来将您的配置数据项放到 Google Play 中。声明并实例化 PutDataMapRequest,将其命名为 putDataMapRequest 。将此设置为调用**。** create( ) 方法关闭 PutDataMapRequest 类,如图图 14-6 所示,使用以下 Java 代码:

PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_WITH_FEATURE);

9781430265504_Fig14-06.jpg

图 14-6 。创建名为 putDataMapRequest 的 PutDataMapRequest 对象,并使用。创建( )

您将把 PATH_WITH_FEATURE 常量传递到。create()方法向 Google Play 服务标识这个 PutDatamapRequest 对象与您的特定 WatchFaces API 应用相关。想想这就像给你的应用打上独特的品牌,这样 GMS 就不会把事情搞混了。

现在,您已经创建了一个 PutDataMapRequest 对象,该对象带有 watch face 应用的唯一路径说明符,下一步是通过使用从 PutDataMapRequest 对象中提取一个“空”的 DataMap 对象,从而为这个 PutDataMapRequest 对象创建 DataMap 对象。 getDataMap( ) 方法。你完成的(空的)Java 类声明看起来像下面的 Java 结构,这也可以在图 14-7 中看到:

DataMap configurationToPut = putDataMapRequest.getDataMap( );

9781430265504_Fig14-07.jpg

图 14-7 。创建名为 configurationToPut 的数据映射对象;用 putDataMapRequest.getDataMap()加载它

正如你在图 14-7 中看到的,你需要使用一个 Alt+Enter 组合键来让 IntelliJ 为你编写数据映射类导入语句。现在可以用配置数据加载空的 DataMap 对象了。

为此,使用**。** putAll( ) 方法带有传递到 putConfigDataItem()方法中的 newConfigData DataMap 对象。这就是包含您的配置参数的数据映射将如何替换位于 PutDataMapRequest:

configurationToPut.putAll(newConfigData);

正如你在图 14-8 中注意到的,这一行代码不需要导入并且没有错误,所以你现在可以进入最复杂的 Java 语句,你将为这个 putConfigDataItem()方法编码,该方法与使用将 PutDataMapRequest 放入一个可穿戴DataApi 对象有关。putDataItem( )

9781430265504_Fig14-08.jpg

图 14-8 。使用。putAll()将 newConfigData 数据映射加载到 configurationToPut 数据映射中,并调用。putDataItem()

在您在 Java 代码的其余部分中使用 Android Wearable 类来访问它的各种 API 之前,让我们仔细看看这个类。通过这种方式,您将熟悉它的 API 类型、数据字段和嵌套类。

Android 可穿戴类:Android 的可穿戴 API

Android 公共可穿戴类扩展了 java.lang.Object,是com . Google . Android . GMS . Wearable包的一部分。需要注意的是,Wearable class 和 wearable package 使用了相同的名称,所以面向 Android 的 Wearable 既是一个包,也是一个类,smartwatch 平台也是如此。

Wearable 类(和对象)包含 Android Wear 平台的可穿戴 API。这个可穿戴类的类层次结构如下所示,表明这个可穿戴类是为 Android Wear 临时编写的:

java.lang.Object
  > com.google.android.gms.wearable.Wearable

有一个嵌套的助手类,即 Wearable。WearableOptions 类,包含 Android Wearable API 的 API 配置参数。

可穿戴类或对象包含的四个属性、特性或数据字段代表主要 API,以及网络节点 API、消息传递 API 和数据 API。

公共静态最终 Api <可穿戴。WearableOptions > API 字段包含一个要传递给的令牌对象。addApi(Api) 方法启用 Wearable Options 对象中概述的可穿戴选项(特性)。公共静态最终数据 Api 数据 Api 字段包含数据 Api,而公共静态最终消息 Api 消息 Api 字段包含消息 Api,并且公共静态最终节点 Api 节点 API 字段包含网络节点 API。

使用可穿戴类:放置一个 DataApi 数据请求

您将要编写的接下来的七行 Java 代码实际上是一个复杂的 Java 语句,它既有方法(点)链接,又有嵌套在。setResultCallback()方法!这个构造从一个可穿戴的类开始,引用它的数据 Api 字段,其中有一个**。** putDataItem( ) 方法被调用。

在里面。putDataItem()参数区域,你传递名为的 GoogleApiClient 对象 googleApiClient 以及一个的结果。asPutDataRequest( ) 方法调用 putDataMapRequest 对象,将其转化为 PutDataRequest 对象。PutDataRequest 类(和对象)用于在 Android Wear 网络中创建新的 DataItem 对象。

那个。putDataItem()方法调用需要将 GoogleApiClient 对象和 PutDataRequest 对象作为参数传入。Java 语句的这一部分将请求提交给 GMS 服务器。

语句的下一部分,连接到。putDataItem()方法使用点链接,就是**。setResultCallback( )** 方法,该方法设置应用监听来自 GMS 服务器的数据请求响应。

在里面。setResultCallback()方法的参数区,你实例化一个新的 ResultCallback < DataApi。>dataitem result对象。在该构造中,您@覆盖了**public void****on result()**方法,使其为空,以便它执行 DataItemResult 对象(包含在 ResultCallback 对象中)的默认处理。如图图 14-8 所示,Java 语句应该看起来像下面的 Java 代码结构:

Wearable.DataApi.putDataItem(googleApiClient, putDataMapRequest.asPutDataRequest( ))
                .setResultCallback(new ResultCallback<DataApi.DataItemResult>( ) {
                    @Override
                    public void onResult(DataApi.DataItemResult dataItemResult) {
                        // an empty method represents using the default onResult( ) functionality
                    }
                });

现在您已经处理了 DataApi 请求,让我们看看节点 Api。

使用 Android 的节点 API:。fetchConfigDataMap()方法

让我们创建一个**。** fetchConfigDataMap( ) 方法 将使用节点 API 来获取(检索)配置数据映射对象。声明一个公共静态 void fetchConfigDataMap( ) 方法,带有名为 clientfinal GoogleApiClient 对象和名为 callbackfinal fetchConfigDataMap callback对象。

由此产生的空类结构可以在图 14-9 的中看到,并将利用以下 Java 代码:

public static void
fetchConfigDataMap( final GoogleApiClient client, final FetchConfigDataMapCallback callback ) {
    // Empty Method
}

9781430265504_Fig14-09.jpg

图 14-9 。在常量后面创建一个空的公共静态 void fetchConfigDataMap()方法结构

在图 14-9 中可以看到,FetchConfigDataMapCallback 有红色代码错误高亮显示,因为它引用的公共接口还没有创建。如果您下拉一个错误建议选项列表,您将看到“创建公共接口 FetchConfigDataMapCallback”,现在我们就这么做吧。在类顶部的常量后添加一个公共接口 FetchConfigDataMapCallback 。在公共接口内,声明实现所需的**void onconfigdatamappled(data map config)**方法。

FetchConfigDataMapCallback 接口的 Java 结构可以在图 14-10 的底部高亮显示,看起来应该像下面的 Java 公共接口结构:

public interface FetchConfigDataMapCallback {
    void onConfigDataMapFetched(DataMap config);
}

9781430265504_Fig14-10.jpg

图 14-10 。使用 onConfigDataMapFetched()方法创建公共接口 FetchConfigDataMapCallback

在 FetchConfigDataMap()方法中,您将使用一个 NodeApi 类和对象,所以让我们先快速概述一下这个 Android GMS 类。

Android NodeApi 接口:搜索连接的节点

Androidpublic****NodeApi 接口 为你的 Wear 应用公开 Api,以利用 poll (即搜索)本地或连接的节点。这个 NodeApi 接口是com . Google . Android . GMS . wearable . NodeApi包的一部分。节点 API 事件可以传递给设备上的所有应用,您将学习如何监听和利用这些节点事件。这个 NodeApi 接口有三个嵌套的(也称为 helper)接口:

  • NodeApi。GetConnectedNodesResult 接口包含 Wear GMS 网络上已连接节点的列表。
  • NodeApi。GetLocalNodeResult 接口包含一个惟一的名称和一个 ID,它将惟一地代表用户的硬件设备。在本章的下一节,你将在你的 Java 代码中使用这个接口。
  • 一个 NodeApi。NodeListener 接口旨在与 Android 的一起使用。addListener(GoogleApiClient,NodeApi。NodeListener) 方法来接收节点 API 事件,以便它们可以被处理。

这个 NodeApi 接口有四个公共方法:

  • PendingResult **。addListener(GoogleApiClient 客户端,NodeApi。**方法注册一个能够接收(过滤)所有节点 API 事件的监听器。
  • <nodeapi.getconnectednodesresult>抽象 pending result**。getConnectedNodes(Google API client 客户端)**方法将获得用户硬件设备当前连接的节点列表。</nodeapi.getconnectednodesresult>
  • <nodeapi.getlocalnoderesult>抽象 pending result**。**方法 getLocalNode(GoogleApiClient)将获取指向用户当前硬件设备的节点。在本章的下一节,你将在你的 Java 代码中使用这个方法。</nodeapi.getlocalnoderesult>
  • 抽象 pending result**。removeListener(GoogleApiClient 客户端,NodeApi。NodeListener listener)** 方法将删除先前使用。addListener(GoogleApiClient,NodeListener)。

现在让我们完成。fetchConfigDataMap()方法,该方法使用与 NodeApi 相关的方法和类。

线束节点 Api:使用 getLocalNode()和 getLocalNodeResult()

在一个空的 fetchConfigDataMap()方法结构中,访问 Android 的 WearableNodeApi 接口,使用点符号。叫**。getLocalNode( )** 关闭该构造,并传入名为 client 的 GoogleApiClient 对象,该对象被传入该 fetchConfigDataMap()方法。

接下来,使用 Java 方法链接并添加一个**。** setResultCallback( ) 方法调用。在其中,使用 Java new 关键字实例化 ResultCallback 对象,为 **< NodeApi 创建一个 ResultCallback 对象。getlocalnodesult>**对象类型,使用 ResultCallback < NodeApi 指定。GetLocalNodeResult > Java 构造。在这个构造中,您将@Override 一个 onResult()方法,接下来您将使用 Java 代码完成该方法,它将决定当您获得 LocalNode 结果时您希望发生什么。

目前为止 fetchConfigDataMap()方法的 Java 结构,包括 Wearable。NodeApi 结构,获取localnodesult,设置一个结果回调,以及你(当前)空的 onResult( ) 方法 体,可以在图 14-11 中看到,应该看起来像下面的 Java 代码:

public static void
fetchConfigDataMap(final GoogleApiClient client, final FetchConfigDataMapCallback callback)
{
    Wearable.NodeApi.getLocalNode(client)
                    .setResultCallback(new ResultCallback<NodeApi.GetLocalNodeResult>( ) {
                        @Override
                        public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
                            // Java code to be executed when a LocalNodeResult object appears
                        }
                    });
}

9781430265504_Fig14-11.jpg

图 14-11 。调用. getLocalNode()和。setResultCallback()并覆盖 onResult()方法

在 onResult(NodeApi。getlocalnodesult getlocalnodesult)方法,您将声明名为 myLocalNode 的 String 对象,并使用的结果加载它。getNode()。getId( ) 方法链,关闭了getlocalnodesult对象,该对象已经被传递到这个 onResult()方法结构中。

一旦 myLocalNode 加载了这个 NodeApi 标识数据,您将声明一个名为 uri 的 Uri 对象,并使用一个 Uri 实例化它。构建器类结合 new 关键字的构造器方法。使用方法链接,你将设置 URI 方案为 wear ,路径为 PATH_WITH_FEATURE ,权限为 myLocalNode String 对象中的 ID 数据,如图图 14-12 所示。到目前为止,您的 Java 代码应该如下所示:

Wearable.NodeApi.getLocalNode(client)
                .setResultCallback(new ResultCallback<NodeApi.GetLocalNodeResult>( ) {
                    @Override
                    public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
                        String myLocalNode = getLocalNodeResult.getNode( ).getId( );
                        Uri uri = new Uri.Builder( )
                                         .scheme("wear")
                                         .path(ProWatchFaceUtility.PATH_WITH_FEATURE)
                                         .authority(myLocalNode)
                                         .build( );
                    }
                });

9781430265504_Fig14-12.jpg

图 14-12 。添加一个 myLocalNode 字符串来提取 LocalNodeResult 节点数据,并使用它构建一个 Uri 对象

现在您已经有了 GoogleApiClient 和 Uri 对象,您可以在 Wearable.DataApi 之外的. getDataItem()方法调用中使用它们。setResultCallback()方法,请求传递 FetchConfigDataMapCallback 对象回调的新 DataItemResultCallback()构造函数,该对象回调被传递到此 fetchConfigDataMap()方法构造中。正如你将在图 14-13 中看到的,你完成的 Java 方法结构看起来像这样:

public static void
fetchConfigDataMap(final GoogleApiClient client, final FetchConfigDataMapCallback callback)
{
  Wearable.NodeApi.getLocalNode(client)
                  .setResultCallback(new ResultCallback<NodeApi.GetLocalNodeResult>( ) {
                      @Override
                      public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
                          String myLocalNode = getLocalNodeResult.getNode( ).getId( );
                          Uri uri = new Uri.Builder( )
                                           .scheme("wear")
                                           .path(ProWatchFaceUtility.PATH_WITH_FEATURE)
                                           .authority(myLocalNode)
                                           .build( );
                          Wearable.DataApi.getDataItem(client, uri)
                                          .setResultCallback(new DataItemResultCallback(callback));
                      }
                  });
}

9781430265504_Fig14-13.jpg

图 14-13 。打电话给。【getDataItem()方法关闭可穿戴。DataApi,将其传递给客户机和新的 Uri 对象

如图图 14-13 所示,仍然有一个红色的错误代码需要突出显示,所以让我们接着做,这样你就可以让这个方法没有错误,然后继续创建其他需要的方法。

单击这一行代码,最好是突出显示红色错误代码,然后您应该会在 ide 的左侧看到一个红色的错误灯泡下拉箭头。

单击向下箭头,下拉一个菜单列表,列出 IntelliJ 认为可以解决代码连续性问题的错误修复建议。有两种选择:创建一个完全独立的类,或者在 ProWatchFaceUtility.java 类中创建一个私有或内部类。为了保持实现 watch faces 应用紧凑所需的类的数量,选择第二个“创建内部类' DataItemResultCallback '”选项,可以看到在图 14-14 的底部突出显示。

9781430265504_Fig14-14.jpg

图 14-14 。选择“创建内部类”错误下拉菜单选项和 ProWatchFaceUtility 目标类

这将弹出第二个选择器对话框,其中有两个选项列表,允许您指定一个您希望包含内部类的目标类 。您想要 ProWatchFaceUtility 类中的私有静态内部类,所以通过双击选择它,IntelliJ 将编码一个私有静态DataItemResultCallback ,实现 ResultCallback < DataApi。DataItemResult >接口接口。这将有一些红色的错误高亮显示,直到你实现了所需的方法,这将在本章的下一节中进行。

IntelliJ 将在 ProWatchFaceUtility 类的底部创建私有的 static (inner)类,正如在图 14-15 的底部可以看到的。

9781430265504_Fig14-15.jpg

图 14-15 。现在回调对象引用已经就位,私有静态内部类也创建好了

让我们编写构成该方法主体的 Java 代码,它将使用 DataMapItem 从 DataItem 中提取手表表面配置数据映射,就像您在使用。fromDataItem()方法 。

使用可穿戴数据 Api: DataItemResultCallback()类

在这个 DataItemResultCallback 类中,您要做的第一件事是声明一个私有的 final FetchConfigDataMapCallback 对象,然后将其命名为 mCallback。接下来,创建一个公共 DataItemResultCallback()方法,该方法将名为 Callback 的 FetchConfigDataMapCallback 对象作为参数。

在此方法中,将在此类顶部创建的 mCallback 对象设置为传递到此 DataItemResultCallback()方法中的回调对象。如图图 14-16 所示,代码应该如下所示:

private static class DataItemResultCallback implements ResultCallback<DataApi.DataItemResult> {
    private final FetchConfigDataMapCallback mCallback;
    public DataItemResultCallback(FetchConfigDataMapCallback callback) {
        mCallback = callback;
    }
}

9781430265504_Fig14-16.jpg

图 14-16 。添加私有 final mCallback 变量;将其设置为等于传入 DataItemResultCallback()的回调

注意在图 14-16 中,红色波浪状的错误高亮仍然存在,所以鼠标悬停在错误高亮上,显示错误消息,它说“类必须被声明为抽象的或者实现方法 onResult(R)。”

覆盖公共空间。onResult(DataApi。DataItemResult dataItemResult)方法,在 DataItemResultCallback()方法的末尾,它将添加一个空方法(暂时)并删除红色的错误突出显示。在图 14-17 中显示没有错误的 Java 方法应该如下所示:

private static class DataItemResultCallback implements ResultCallback<DataApi.DataItemResult> {
    private final FetchConfigDataMapCallback mCallback;
    public DataItemResultCallback(FetchConfigDataMapCallback callback) {
        mCallback = callback;
    }
    @Override
    public void onResult(DataApi.DataItemResult dataItemResult) { // empty method removes error }
}

9781430265504_Fig14-17.jpg

图 14-17 。添加@Override onResult()方法以移除 DataItemResultCallback 上突出显示的红色错误

接下来,您需要创建条件 if()结构,该结构确定 onResult()方法是否返回了 DataItemResult(外部 if()结构),以及第二个内部条件 if()结构,该结构将确定 DataItemResult 是否已被使用或是否为空(也就是说,它是!= null)。

第一个外部条件 if()结构使用了一个**。getStatus( )** isSuccess( ) 方法链接出dataItemResultdataItemResult 对象,您之前已经看到使用它来获得成功结果状态标志(true 值)。

然后,第二个内部条件 if()结构使用 getDataItem()方法调用 dataItemResult 对象,并将其与空值进行比较。如果 DataItemResult 对象内部有内容,则处理 If 部分。如果为空(null),则处理构造的 else 部分。

嵌套 if()结构中的第一行代码也与 DataItemResult 处理相关,所以我也将在这里包含它。这个**。** getDataItem( ) 方法再次用于获取有效的 DataItem 结果,并将其安装到一个名为 configDataItemDataItem 对象中,在一个 Java 语句中声明、命名并实例化该 DataItem,以便它可以接收一个**DataItem result . get DataItem()**方法调用对象传输。

控件的 Java 代码。onResult()方法结构,也可以在图 14-18 的底部看到突出显示,应该如下所示:

public void onResult(DataApi.DataItemResult dataItemResult) {
    if (dataItemResult.getStatus( ).isSuccess( )) {
        if (dataItemResult.getDataItem( ) != null) {
            DataItem configDataItem = dataItemResult.getDataItem( );
        } else {
        }
    }
}

9781430265504_Fig14-18.jpg

图 14-18 。在 onResult()内创建一个 if()和嵌套的 if-else 结构;使用。getDataItem()来提取结果

既然所有的 DataItemResult 对象处理都已就绪,接下来您要做的事情是将 DataItem 转换为 DataMapItem,以便稍后您可以将 DataMapItem 转换为您需要的 DataMap 对象。

用 DataItem 对象加载 DataMapItem 对象的代码如图 14-19 中的所示,应该类似于下面两行代码:

DataItem configDataItem = dataItemResult.getDataItem( );
DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);

9781430265504_Fig14-19.jpg

图 14-19 。声明一个 dataMapItem,将其命名为 DataMapItem,并将其设置为等于。fromDataItem()

现在,所有的 DataMapItem 对象处理都已就绪,接下来您要做的是将 DataMapItem 转化为 DataMap,这样以后您就可以将 DataMap 对象传递给 onConfigDataMapFetched()方法调用。在图 14-20 的中可以看到 DataItem 到 DataMapItem 再到 DataMap 的这种转换,应该使用这三行 Java 代码来完成:

DataItem configDataItem = dataItemResult.getDataItem( );
DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);
DataMap config = dataMapItem.getDataMap( );

9781430265504_Fig14-20.jpg

图 14-20 。声明一个名为 config 的数据映射,将它设置为对 dataMapItem 的. getDataMap()调用的结果

条件语句 if 部分的最后一步是通过使用配置数据映射对象传递给mcall back FetchConfigDataMapCallback对象。onConfigDataMapFetched】方法调用。

在条件 if-else 语句的 else 部分,如果 DataItem 对象确实为空(null ),那么只需通过使用 Java new 关键字和 DataMap( ) 构造函数方法调用实例化一个新的 DataMap 对象。onConfigDataMapFetched()方法,从 mcall back FetchConfigDataMapCallback 对象调用。完成的 onResult()方法结构,如图图 14-21 所示,应该如下图所示:

public void onResult(DataApi.DataItemResult dataItemResult) {
            if (dataItemResult.getStatus( ).isSuccess( )) {
                if (dataItemResult.getDataItem( ) != null) {
                    DataItem configDataItem = dataItemResult.getDataItem( );
                    DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);
                    DataMap config = dataMapItem.getDataMap( );
                    mCallback.onConfigDataMapFetched(config);
                } else {
                    mCallback.onConfigDataMapFetched(new DataMap( ));
                }
            }
}

9781430265504_Fig14-21.jpg

图 14-21 。使用将 FetchConfigDataMapCallback 对象设置为数据映射。onConfigDataMapFetched()

请注意,我单击了。代码中的 onConfigDataMapFetched()方法跟踪其实现,返回到 FetchConfigDataMapCallback Java 接口,该接口在图 14-21 的顶部以蓝色突出显示。

接下来,让我们创建为这个类编码所需的最后一个主要方法。那个。overwriteKeysInConfigDataMap()方法将替换用户指定的任何已更改的键-数据对,从而创建一个全新的 DataMap 对象。

替换更改的数据:overwriteKeysInConfigDataMap

让我们创建**公共静态 void****overwriteKeysInConfigDataMap()**方法,带有 final GoogleApiClient,您将命名为 googleApiClient ,以及 final DataMap 对象,您将命名为 configKeysToOverwrite ,如图图 14-22 所示。Java 代码应该类似于这个空方法主体声明:

public static void overwriteKeysInConfigDataMap(final GoogleApiClient googleApiClient, final DataMap configKeysToOverwrite) {  // empty method: your method body will go in here  }

9781430265504_Fig14-22.jpg

图 14-22 。创建空的公共静态 void overwriteKeysInConfigDataMap()方法

你要在这个方法体中编码的第一件事是对你在本章前面创建的方法之一的方法调用。这将被 ProWatchFaceUtility 类名、句点和方法名引用,如图图 14-23 所示。

在方法调用参数区域内,您将传递 GoogleApiClient 对象,并使用 Java new 关键字实例化一个新的fetchConfigDataMapCallback对象。空方法声明的 Java 代码应该类似于以下代码:

ProWatchFaceUtility.fetchConfigDatamap(googleApiClient, new fetchConfigDataMapCallback( ) { empty method });

9781430265504_Fig14-23.jpg

图 14-23 。使用 IntelliJ 弹出帮助器对话框实现 FetchConfigDataMapCallback 接口

fetchConfigDatamap(GoogleApiClient,FetchConfigDataMapCallback) 方法当前被 IntelliJ IDEA 指定(着色)为未使用,如图 14-22 所示。未使用的方法或变量是使用格雷码文本颜色定义的,一旦它们在其他代码中被引用,它们将变成黑色。在图 14-23 中可以看到,IntelliJ 可以帮助编码一个空的方法体;例如,如果您键入一个 Java new 关键字和 Fe 字符,将会弹出一个 IntelliJ helper 对话框,您可以在其中从下拉选项列表中选择 FetchConfigDataMapCallback 接口。

当选择 FetchConfigDataMapCallback 接口时,IntelliJ 也会实现所需的**。onconfigdatamappled(data map config)**方法,如图 14-24 中的所示,产生如下 Java 代码:

ProWatchFaceUtility.fetchConfigDatamap(googleApiClient, new fetchConfigDataMapCallback( ) {
    @Override
    public void onConfigDataMapFetched(DataMap config) { // an empty method created by IntelliJ }
});

9781430265504_Fig14-24.jpg

图 14-24 。IntelliJ 将为您实现完整的(空的)FetchConfigDataMapCallback 基础结构

现在您所要做的就是编写创建新数据映射的 Java 语句,通过覆盖当前(或者默认,如果这是第一次更新的话)数据映射来保存最新的(更新的)配置数据数组。

更新数据映射对象:onConfigDataMapFetched()

在 IntelliJ 为您创建的这个公共 void onConfigDataMapFetched()方法中,将传递到该方法中的 DataMap 参数的名称更改为 currentConfig 。这样做是为了更准确地反映传递到这个方法结构中的内容。

接下来,声明并实例化一个名为 overwriteConfig新的数据映射对象,该对象用于保存数据映射,并将包含修改后的配置参数。要使用传递给该方法的 currentConfig 数据映射加载该对象,请调用**。** putAll( ) 方法使用 currentConfig 对象作为参数,关闭 overwriteConfig DataMap 对象。

到目前为止,你可以看到在图 14-25 底部高亮显示的 Java 方法结构,看起来应该像下面的 Java 代码:

ProWatchFaceUtility.fetchConfigDatamap(googleApiClient, new fetchConfigDataMapCallback( ) {
    @Override
    public void onConfigDataMapFetched(DataMap currentConfig) {
        DataMap overwriteConfig = new DataMap( );
        overwriteConfig.putAll(currentConfig);
    }
});

9781430265504_Fig14-25.jpg

图 14-25 。声明并实例化名为 overwriteConfig 的数据映射,并使用. putAll()方法加载它

现在,您的当前设置或默认设置(如果这是您的第一次配置参数更新)位于 overwriteConfig 数据映射中,然后您将使用相同的 putAll()方法来覆盖(替换)传递到此 overwriteKeysInConfigDataMap()方法中的新配置参数。

这个工作流程确保如果在两个之后的传入(更新或更改)数据映射中有任何不完整或缺失的键-数据对。putAll()方法,它们将被处理,并且将有一个完整的数据映射对象,其中包含所有参数,包括已更改的和未更改的(或默认的)。

到目前为止,你可以看到 Java 方法结构在图 14-26 的底部突出显示,看起来应该像下面的 Java 代码:

ProWatchFaceUtility.fetchConfigDatamap(googleApiClient, new fetchConfigDataMapCallback( ) {
    @Override
    public void onConfigDataMapFetched(DataMap currentConfig) {
        DataMap overwriteConfig = new DataMap( );
        overwriteConfig.putAll(currentConfig);
        overwriteConfig.putAll(configKeysToOverwrite);
    }
});

9781430265504_Fig14-26.jpg

图 14-26 。用另一个。putAll()方法调用,用当前数据覆盖更新的配置数据

您需要做的最后一个编程“动作”是通过将更新后的 overwriteConfig DataMap 对象传递给来编写该对象。 putConfigDataItem( ) 方法,您在本章前面编写的代码。因为您在当前方法体之外调用这个方法,所以您将在这个方法名的前面加上类名,就像这样:prowatchface utility . putconfigdataitem()。在参数区域内,传递包含更新(覆盖)的观察面配置数据的 GoogleApiClient 对象和 DataMap 对象。

该方法结构的最终 Java 代码可以在图 14-27 的底部高亮显示,应该如下所示:

ProWatchFaceUtility.fetchConfigDatamap(googleApiClient, new fetchConfigDataMapCallback( ) {
    @Override
    public void onConfigDataMapFetched(DataMap currentConfig) {
        DataMap overwriteConfig = new DataMap( );
        overwriteConfig.putAll(currentConfig);
        overwriteConfig.putAll(configKeysToOverwrite);
        ProWatchFaceUtility.putConfigDataItem(googleApiClient, overwriteConfig);
    }
});

9781430265504_Fig14-27.jpg

图 14-27 。使用将更新的数据映射配置数据发送到智能手表。从 ProWatchFaceUtility 类调用了 putConfigDataItem()方法

接下来您需要做的是从 ProWatchFaceConfigListenerService 类调用 ProWatchFaceUtility 类,将两个类连接在一起。

连接地图:从监听器调用工具

单击ProWatchFaceConfigListenerService.java选项卡,或者如果它没有在 IDE 中打开,则将其打开。向 onMessageReceived( ) 方法添加最后一行代码,该方法将把 keysToOverwrite DataMap 对象发送到 ProWatchFaceUtility 类**。****overwriteKeysInConfigDataMap()**方法。如图图 14-28 所示,Java 语句应该如下所示:

ProWatchFaceUtility.overwriteKeysInConfigDataMap(myGoogleApiClient, keysToOverwrite);

9781430265504_Fig14-28.jpg

图 14-28 。打开 Listener 类并调用 ProWatchFaceUtility 类的 overwriteKeysInConfigDataMap 方法,同时传递 keysToOverwrite

正如你将在图 14-29 中看到的,工具类的 Java 代码是无错误的,并且没有任何 Java 代码(除了常量,我将在下面处理)是灰色的

9781430265504_Fig14-29.jpg

图 14-29 。现在这两个类是交叉连接的(互相使用),唯一的格雷码是常量

现在,您已经准备好返回到 prowatchfacecompcompanionconfigactivity,它是您在第十三章中开始开发所有代码时编写的。最后,您将在已创建的其他类中创建所需的键值数据对,并完成创建颜色选择用户界面设计所需的所有 UI 设计工作,供 watch face 用户用来定制他们的 watch face 应用。

完成配置助手:UI 设计

打开ProWatchFaceCompanionConfigActivity.java类(在你的项目的移动部分),添加你在 ProWatchFaceUtility.java 类顶部声明的四个相同的 KEY_COLOR 常量。

这些常量的唯一区别是它们将使用 Java private 关键字声明,因为它们只在类中使用,而不是像在 ProWatchFaceUtility 中那样使用 public 关键字。这些常量声明可以在图 14-30 中看到,看起来像下面的 Java 语句:

private static final String PATH_WITH_FEATURE = "/watch_face_config/ProWatchFace";
private static final String KEY_COLOR_TICK_MARK = "COLOR_TICK_MARK";
private static final String KEY_COLOR_HOUR_HAND = "COLOR_HOUR_HAND";
private static final String KEY_COLOR_MINUTE_HAND = "COLOR_MINUTE_HAND";
private static final String KEY_COLOR_SECOND_HAND = "COLOR_SECOND_HAND";

9781430265504_Fig14-30.jpg

图 14-30 。声明与配套活动中的工具常量相匹配的私有 KEY_COLOR 常量

现在,所有用于在所有类之间传递 DataMap 和 DataItem 对象的 Java 代码都已就绪,让我们换个方式,进入 UI 设计模式。让我们编写一些 XML 标记来定义智能手机上 watch faces Configuration Companion 活动的用户体验。

使用微调器小部件选择颜色:XML UI 布局

在编辑选项卡中打开mobile/RES/activity _ pro _ watch _ face _ config . XML文件,在< TextView >标签下添加嵌套的< LinearLayout >标签。将 orientation 参数设置为 horizontal,这样文本小部件将位于 Spinner 小部件的旁边。在这个 LinearLayout 容器中,嵌套一个 TextView 标签,并使用@string/pro_config_tick_mark 引用<字符串>常量。然后将 layout_width 参数设置为零,将 layout_weight 参数设置为一。如图图 14-31 所示,XML 标记应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView android:id="@+id/title" android:text="@string/prowatchface_config"
        android:layout_width="match_parent" android:layout_height="wrap_content" />
    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_tick_mark" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
    </LinearLayout>
</LinearLayout>

9781430265504_Fig14-31.jpg

图 14-31 。添加一个嵌套的< LinearLayout >,并在其中嵌套一个< TextView >标签来保存第一个 UI 构造

您将 layout_width 参数设置为零密度像素,或密度独立像素(DIP) 的原因是,该设置告诉 Android OS 允许 layout_weight 参数确定屏幕分配的相对布局百分比。如果您将微调器小部件 layout_weight 设置为 2,那么您的 TextView 将获得屏幕的三分之一,计算为 1/(1+2)。Spinner layout_weight 为 3,这是您接下来要实现的,它使 TextView 占屏幕的 25%,而 Spinner 占屏幕的 75%。

让我们去掉在图 14-31 中看到的红色错误代码高亮显示,打开mobile/RES/values/strings . XML文件,使用数据值 Tick Marks 添加名为 pro_config_tick_mark 的<字符串>常量。当你这样做的时候,为时针、分针和秒针添加其他的<字符串>常量。如图 14-32 所示,XML 标记应该如下所示:

<string name="pro_config_tick_mark">Tick Marks</string>
<string name="pro_config_hour_hand">Hour Hand</string>
<string name="pro_config_minute_hand">Minute Hand</string>
<string name="pro_config_second_hand">Second Hand</string>

9781430265504_Fig14-32.jpg

图 14-32 。为 UI 标签创建<字符串>常量,用于刻度线和时针、分针、秒针

在这个最初的子水平构造中,添加小部件,就在小部件的下面。使用格式 @+id/tickMarks 给它一个 tickMarks ID,并再次将 layout_width 设置为 0dp(如果您愿意,也可以设置为 0dip),将 layout_weight 设置为 3。

要用颜色值加载微调器小部件,使用格式 @array/color_array条目参数集添加到名为 color_array数组中。

到目前为止,你的 XML 标记,可以在图 14-33 中看到突出显示,应该看起来像下面的 XML 布局定义结构:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView android:id="@+id/title" android:text="@string/prowatchface_config"
        android:layout_width="match_parent" android:layout_height="wrap_content" />
    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_tick_mark" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/tickMarks" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>
</LinearLayout>

9781430265504_Fig14-33.jpg

图 14-33 。在引用 color_array 的< TextView >标签下添加一个<微调器>小部件子标签

要去掉红色的错误文本突出显示,请单击 strings.xml 选项卡并添加颜色数据值常量,在创建引用这些颜色常量值的数组对象之前,必须将这些常量放置到位。

我将使用 Android Color 类中的八个原色常量。这些是 Android 操作系统中定义的最标准化的颜色,在黑色背景色的映衬下会非常好看。

这将为您的用户提供 1,680 种不同(非唯一)的颜色组合,这意味着同一种颜色可以用于多个表盘设计元素。计算变量个数的方法是 C!(色数阶乘)除以 E!(元素个数阶乘)。

有八种不同的颜色可能性,或者说八个阶乘 8!,也就是 8765432*1=40320 。然后你用四个不同的表盘设计元素,或者四个阶乘 4 来除它!,也就是 432*1=24 。这给了你 1680 种可能的颜色组合!

定义八种颜色的 XML 标记如图图 14-34 所示,应该如下所示:

<string name="color_yellow">Yellow</string>
<string name="color_blue">Blue</string>
<string name="color_red">Red</string>
<string name="color_green">Green</string>
<string name="color_cyan">Cyan</string>
<string name="color_magenta">Magenta</string>
<string name="color_gray">Gray</string>
<string name="color_white">White</string>

9781430265504_Fig14-34.jpg

图 14-34 。添加八个<字符串>常量定义,引用最常见的 Android OS 颜色常量

在颜色常数 XML 定义就绪之后,您将添加一个对象 XML 定义,它将创建保存颜色常数字符串值的数组对象构造。

这个标签的作用是通过嵌套引用< string-array > Array 对象定义构造中的< string >常量的 < item > 标签,使用 XML 创建一个 String Array 对象。

您需要编码以实现字符串数组结构的创建的 XML 标记可以在图 14-35 中看到,应该如下所示:

<string-array name="color_array">
    <item>@string/color_yellow</item>
    <item>@string/color_blue</item>
    <item>@string/color_red</item>
    <item>@string/color_green</item>
    <item>@string/color_cyan</item>
    <item>@string/color_magenta</item>
    <item>@string/color_gray</item>
    <item>@string/color_white</item>
</string-array>

9781430265504_Fig14-35.jpg

图 14-35 。添加一个<字符串数组>结构,其中填充了引用八种颜色的八个<项目>子标签

正如您将在图 14-36 中看到的,现在您已经创建了 XML 字符串颜色常量和数组,没有红色的错误代码高亮显示,您已经准备好复制并粘贴您的第一个嵌套的< LinearLayout >结构,并为时针颜色选择微调器 UI 元素创建第二个。

9781430265504_Fig14-36.jpg

图 14-36 。复制并粘贴第一个嵌套的 LinearLayout UI 容器,为时针创建第二个容器

选择子 XML 结构及其两个子 UI 小部件,右键选择复制,或者使用 CTRL+C 组合键。接下来,单击鼠标将插入条(光标)插入父 LinearLayout 的最后一个< /LinearLayout >结束标记之前,单击鼠标右键并选择粘贴,或者使用 CTRL+V 组合键进行粘贴。结果可以在图 14-36 中看到,应该如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView android:id="@+id/title" android:text="@string/prowatchface_config"
        android:layout_width="match_parent" android:layout_height="wrap_content" />
    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_tick_mark" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/tickMarks" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>
    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_hour_hand" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/hourHand" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>
</LinearLayout>

因为您希望允许用户为四个表盘设计元素配置颜色,所以您需要再次执行这个节省时间的复制和粘贴工作流程,并选择这两个嵌套的 LinearLayout 容器 XML 标记块,然后将它们粘贴到 UI 定义的底部。

最终你会得到一个主父级垂直 LinearLayout 容器来排列嵌套的子级水平 LinearLayout 容器,其中包含 TextView (标签)、 Spinner (颜色选择器)和 UI 元素。

最终的activity _ pro _ watch _ face _ config . xml用户界面布局设计 XML 定义,可以看到,没有错误,在图 14-37 中,应该使用以下 XML 标记:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView android:id="@+id/title" android:text="@string/prowatchface_config"
        android:layout_width="match_parent" android:layout_height="wrap_content" />

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_tick_mark" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/tickMarks" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_hour_hand" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/hourHand" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_minute_hand" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/minuteHand" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView android:text="@string/pro_config_second_hand" android:layout_width="0dp"
            android:layout_height="wrap_content" android:layout_weight="1" />
        <Spinner android:id="@+id/secondHand" android:entries="@array/color_array"
            android:layout_width="0dp" android:layout_height="wrap_content"
            android:layout_weight="3" />
    </LinearLayout>

</LinearLayout>

9781430265504_Fig14-37.jpg

图 14-37 。复制并粘贴前两个嵌套的 LinearLayout UI 容器,以创建分针和秒针

既然已经构建了 XML UI 布局定义,那么让我们切换回 Java 编程模式,并编写使这些微调器起作用的方法。

设置微调器小部件:setUpColorPickerSelection()

在接下来的几节中,您将编写与后端处理相关的 Java 方法(Java 代码;XML 标记是颜色选择小部件的前端设计。这些是通过使用标签放置的 Spinner 类(对象)定义来实现的。

单击 ProWatchFaceCompanionConfigActivity.java 选项卡(或打开它)并在类的底部结束花括号(})之前添加一个私有的 void setUpColorPickerSelection()方法。您需要向该方法传递四个参数:一个是您在 XML UI 定义中创建的 Spinner ID 参数,一个保存配置数据键的字符串,一个包含键-值对的数据映射,以及一个默认颜色常量的资源 ID 整数。如图 14-38 中的所示,Java 代码应该如下所示:

private void setUpColorPickerSelection(int spinnerId, final String configKey, DataMap config,
                                       int defaultColorNameResId) {
    String defaultColorName = getString(defaultColorNameResId);
    int defaultColor = Color.parseColor(defaultColorName);
    int color;
}

9781430265504_Fig14-38.jpg

图 14-38 。在类的末尾添加私有的 void setUpColorPickerSelection()方法,并声明变量

您需要在这个方法中放置的下一个 Java 构造是一个 if-else 条件语句,它将确定数据映射是否已经被加载(使用)或者它是否是一个未使用的数据映射。

确定这一点的方法是使用 if(config!= null)条件,因为非 null 数据映射包含一个数据映射!如果数据图是“实时”的,您可以使用**。getInt(String,integer)** ,调用配置数据映射对象。这将提取与 configKey 字符串键关联的整数值。这被赋给你之前声明的颜色整数变量。

另一方面,在构造的 else 部分中,如果数据映射为空(null 值),只需将 color 整数变量设置为 defaultColor 值,该值是使用。getString()方法调用。该构造的 Java 代码,如图 14-39 所示,应该看起来像下面的方法结构:

private void setUpColorPickerSelection(int spinnerId, final String configKey, DataMap config,
                                       int defaultColorNameResId) {
    String defaultColorName = getString(defaultColorNameResId);
    int defaultColor = Color.parseColor(defaultColorName);
    int color;
    if (config != null) {
        color = config.getInt(configKey, defaultColor);
    } else {
        color = defaultColor;
    }
}

9781430265504_Fig14-39.jpg

图 14-39 。添加 if-else 条件结构,评估配置数据映射,并相应地处理它

完成该方法的最后一步是创建一个名为 spinner 的 Java Spinner 对象,使用< Spinner > XML 定义使用 findViewById( ) 方法对其进行膨胀。Android 中的膨胀过程是使用之前创建的 XML 对象定义填充 Java 对象。然后创建一个 colorNames String[ ] 数组,并使用 XML**构造和创建的数据填充它。** getStringArray( ) 方法连锁掉一个 getResources( ) 方法。对循环使用一个**,并用你的颜色常量值加载 colorNames 数组。执行此操作的 Java 代码如图 14-40 所示,应该如下所示:**

private void setUpColorPickerSelection(int spinnerId, final String configKey, DataMap config,
                                       int defaultColorNameResId) {
    String defaultColorName = getString(defaultColorNameResId);
    int defaultColor = Color.parseColor(defaultColorName);
    int color;
    if (config != null) {
        color = config.getInt(configKey, defaultColor);
    } else {
        color = defaultColor;
    }
    Spinner spinner = (Spinner) findViewById(spinnerId);
    String[] colorNames = getResources( ).getStringArray(R.array.color_array);
    for (int i = 0; i < colorNames.length; i++) {
        if (Color.parseColor(colorNames[i]) == color) {
            spinner.setSelection(i);
            break;
        }
    }
}

9781430265504_Fig14-40.jpg

图 14-40 。创建一个名为 spinner 的 Spinner 对象;用 XML 数组中定义的 String[ ]数组数据加载它

现在,您已经用颜色值加载了微调器对象,下一步我们要设置颜色选择器侦听器对象来侦听用户选择的更改。

设置微调器监听器:setUpColorPickerListener()

现在,您已经用先前创建的中的颜色常量值填充了微调器对象,您还需要设置监听器对象,它将监听用户对微调器默认(或先前)颜色常量选择所做的任何更改。

在类的末尾创建一个private void****setUpColorPickerListener()方法,该方法接受微调器 id 整数configKey KEY_COLOR 字符串颜色常量值作为其两个方法参数。

在方法内部,声明一个名为 spinnerSpinner 对象,并使用 findViewById(spinnerId) 方法调用和参数对其进行膨胀。您在刚刚创建的前一个方法中完成了此操作;但是,请注意,这两个变量都是在每个方法中使用的局部(或私有)变量,因此它们并不冲突。对于这个方法,您需要一个惟一的 Spinner 对象,因为您将基于它构造一个侦听器结构。

到目前为止的 Java 方法结构,可以在图 14-41 的底部看到突出显示,应该看起来像下面的 Java 代码:

private void setUpColorPickerListener(int spinnerId, final String configKey) {
    Spinner spinner = (Spinner) findViewById(spinnerId);
}

9781430265504_Fig14-41.jpg

图 14-41 。在类的底部创建一个公共的 void setUpColorPickerListener()方法;然后给旋转器充气

您需要做的下一件事是使用。setOnItemSelectedListener()方法来创建 AdapterView 侦听器。Spinner 类是从 AdapterView 类的子类,因此 Spinner 对象也是 AdapterView 对象。它使用一个适配器视图。【OnItemSelectedListener()【构造函数】方法调用,配合 Java new 关键字。使用下面的 Java 代码创建这个监听器,如图 14-42 底部突出显示的:

private void setUpColorPickerListener(int spinnerId, final String configKey) {
    Spinner spinner = (Spinner) findViewById(spinnerId);
    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener( ) { // empty method });
}

9781430265504_Fig14-42.jpg

图 14-42 。从微调器调用. setOnItemSelectedListener()方法,并构造一个新的 AdapterView 侦听器

正如您在图 14-42 中看到的,您将需要使用 Alt+Enter 工作流程来让 IntelliJ 为 AdapterView 类编写一个导入语句。一旦 IntelliJ 做到了这一点,它将重新评估您的代码,并在 AdapterView 下给出一个红色波浪状的错误高亮显示。OnItemSelectedListener()构造函数方法。在图 14-43 所示的错误下拉列表中,选择实现方法选项,实现两个需要的方法,选择 Insert @Override 。IntelliJ 将为您创建一个 onItemSelected( ) 构造。

9781430265504_Fig14-43.jpg

图 14-43 。使用错误建议下拉列表,选择实现方法,并实现所有必需的方法

在空的 onItemSelected()方法中,如图 14-44 中突出显示的,创建一个最终字符串 colorName 变量并调用**。** getItemAtPosition( ) 方法关闭了名为 parentAdapterView 对象,并将其传递到该方法结构中。如果您愿意,可以使用 IntelliJ 弹出助手对话框。

9781430265504_Fig14-44.jpg

图 14-44 。创建一个名为 colorName 的最终字符串变量,并使用。getItemPosition()方法来加载它

当用户在微调器中选择了颜色常量后,使用 configKey 数据映射和来自**Color . parse Color(colorName)嵌套语句的整数结果调用sendconfiupdatemessage()**方法。

正如您在图 14-45 中看到的,这个 sendConfigUpdateMessage()方法还不存在,所以使用错误建议下拉菜单并选择创建方法选项,让 IntelliJ 在ProWatchFaceCompanionConfigActivity(一个选择目标类对话框选项)中为您编码空方法结构。

9781430265504_Fig14-45.jpg

图 14-45 。使用数据映射和颜色键调用 sendConfigUpdateMessage()方法;然后选择创建方法

编辑**私有 void。sendConfigUpdateMessage(String configKey,int I)**IntelliJ 为您编写的方法结构,通过将 int i 改为 int color 来更准确地反映方法内部的情况。现在,您可以编写 sendConfigUpdateMessage()方法结构的内部代码,然后通过编写。setUpAllPickers()方法。

9781430265504_Fig14-46.jpg

。IntelliJ 将创建 sendConfigUpdateMessage(String configKey,int i)方法;然后将 I 重命名为 color

如果有 watchFacePeerId 值,则执行该方法中的所有内容。

if(watchFacePeerId!= null) 结构就是构造一个名为 newConfig 的 DataMap,然后用一个. putInt()方法加载它,并向这个方法传递 configKeyColor 常量。如图图 14-47 所示的 Java 代码应该如下所示:

If ( watchFacePeerId != null ) { DataMap newConfig = new DataMap( );
                                 newConfig.putInt(configKey, color); }

9781430265504_Fig14-47.jpg

图 14-47 。创建 if()条件以查看 watchFacePeerId 是否存在;在其中,创建一个数据映射,并用键-值对加载它

接下来的两行代码将声明一个名为 rawConfigDatabyte[ ] 数组,并使用一个**。** toByteArray( ) 方法调用从名为 newConfig 的数据映射中提取数据。重担由穿戴者来承担。MessageApi 和。sendMessage()方法调用,该方法使用下面的代码将 rawConfigData byte[ ]数组以及唯一的 watchFacePeerId、GoogleApiClient 对象和 PATH_WITH_FEATURE 常量传递给 GMS 服务器,如图 14-48 所示:

byte[] rawConfigData = newConfig.toByteArray( );
Wearable.MessageApi.sendMessage(myGoogleApiClient,watchFacePeerId,PATH_WITH_FEATURE,rawConfigData);

9781430265504_Fig14-48.jpg

图 14-48 。从数据映射创建一个 byte[]数组,并使用。sendMessage()方法提交给可穿戴者

接下来,您需要创建一个方法来设置所有四个 Spinner UI 元素,这将允许应用为所有表盘设计元素设置颜色选择。

设置所有四个微调器:A .setUpAllPickers()方法

在 public void onResult()方法后添加一行代码,声明一个private void****setUpAllPickers()方法。您要编写的最后一个方法将调用您刚刚编写完的 setUpColorPickerSelection()和 setUpColorPickerListener()方法。它接受一个保存数据映射对象的数据映射参数,并将它连同要配置的表面设计元素和为这些元素选择的默认颜色值一起传递给 setUpColorPickerSelection()方法。

正如你从编码中所知。setUpColorPickerSelection()方法,您将需要传递 Spinner 对象的 ID 引用、要设置的数据的 KEY_COLOR 字符串常量、包含键值数据对的 configData DataMap 对象以及默认颜色字符串引用。

Java 方法结构可以在图 14-49 中看到,一旦您声明了它并添加了四个方法调用来配置手表表面元素的四个微调器对象中的每一个,它应该看起来像下面的代码:

private void setUpAllPickers(DataMap configData) {
   setUpColorPickerSelection(R.id.tickMarks, KEY_COLOR_TICK_MARK, configData, R.string.color_gray);
   setUpColorPickerSelection(R.id.hourHand, KEY_COLOR_HOUR_HAND, configData, R.string.color_blue);
   setUpColorPickerSelection(R.id.minuteHand,KEY_COLOR_MINUTE_HAND,configData,R.string.color_green);
   setUpColorPickerSelection(R.id.secondHand,KEY_COLOR_SECOND_HAND,configData,R.string.color_red);
}

9781430265504_Fig14-49.jpg

图 14-49 。调用手表外观设计元素的 setUpColorPickerSelection()方法,传递 configData

现在,您已经配置了所有的 Spinner UI 小部件,它们最初将显示什么颜色(默认),接下来您需要做的是设置一个监听器,这样如果您的用户更改了这个颜色设置,您的应用代码就可以处理这个新设置,并将该颜色发送到 watch face 应用。监听器需要知道监听哪个 Spinner (ID ),以及如果被触发要处理哪个 KEY_COLOR 常量。

调用 setUpColorPickerListener()方法四次来设置每个微调器的 Java 代码在图 14-50 中高亮显示,它应该看起来像下面完成的 Java 方法结构:

private void setUpAllPickers(DataMap configData) {
   setUpColorPickerSelection(R.id.tickMarks, KEY_COLOR_TICK_MARK, configData, R.string.color_gray);
   setUpColorPickerSelection(R.id.hourHand, KEY_COLOR_HOUR_HAND, configData, R.string.color_blue);
   setUpColorPickerSelection(R.id.minuteHand,KEY_COLOR_MINUTE_HAND,configData,R.string.color_green);
   setUpColorPickerSelection(R.id.secondHand,KEY_COLOR_SECOND_HAND,configData, R.string.color_red);
   setUpColorPickerListener(R.id.tickMarks, KEY_COLOR_TICK_MARK);
   setUpColorPickerListener(R.id.hourHand, KEY_COLOR_HOUR_HAND);
   setUpColorPickerListener(R.id.minuteHand, KEY_COLOR_MINUTE_HAND);
   setUpColorPickerListener(R.id.secondHand, KEY_COLOR_SECOND_HAND);
}

9781430265504_Fig14-50.jpg

图 14-50 。为表盘设计元素调用 setUpColorPickerListener()方法;然后传递微调器 ID 和密钥

现在一切就绪,让我们尝试在 Nexus 5 AVD 模拟器中运行 watch face 应用的移动组件,看看您的 UI 设计是否有效。

测试 WatchFaceCompanion 活动:Nexus 5

让我们在 Android Studio 的默认 AVD 模拟器中运行你一直在开发的 WatchFaces 应用的移动组件,这个模拟器恰好是运行 Android 5.0 的 Google 的 Nexus 5。使用运行image编辑配置菜单序列进入图 14-51 中的运行/调试配置对话框,将 IntelliJ 模块下拉设置为移动并将首选 Android 虚拟设备下拉设置为Nexuas 5 API****21。在活动部分,将启动单选按钮设置为ProWatchFaceCompanionConfigActivity类。点击应用按钮,然后点击确定按钮,最终完成您的设置。

9781430265504_Fig14-51.jpg

图 14-51 。使用“配置”对话框设置运行选项

使用**运行image运行‘ProWatchFaceCompanionConfigActivity’**菜单序列,运行应用代码,查看它是否运行并检查 UI。

正如你在图 14-52 中看到的,我得到了一个包错误 说“包 Android . support . wearable . companion 不存在”并且一个 Gradle Build“找不到符号变量 WatchFaceCompanion”真扫兴!

9781430265504_Fig14-52.jpg

图 14-52 。在 Gradle 构建消息窗格中出现了四个关于配套包的错误

代码中引用了 WatchFaceCompanion 类,这是引发错误的地方。我知道这个类(和包)确实存在,因为我已经在 Android 开发者网站上广泛查阅了它的信息。

因为 Android . support . wearable . companion 包似乎不见了,由于 Gradle Build 已经用黄色为您突出显示了导入语句,如图 14-52 所示,您检查编译语句的逻辑位置是应用的移动组件的 Gradle 配置文件。

方法是点击项目窗格中 Gradle Scripts 部分旁边的向右箭头。这将下拉(打开)Gradle Scripts 文件夹中的内容,包括 build.gradle 脚本。

找到build . gradle(Module:mobile)配置文件,点击右键,选择跳转到源代码选项,在编辑窗格中打开。

您将寻找的是配置的依赖项部分,其中添加了编译语句 ,提供了支持库的路径。

9781430265504_Fig14-53.jpg

。打开 Gradle Scripts 下拉箭头并右键单击它,然后为移动模块选择 Jump to Source

在图 14-54 底部的依赖关系部分可以看到。我安装了 appcompat-v7play-services ,但没有安装可穿戴。添加编译“com . Google . Android . wearable:1.1。+" 语句来修复这个问题。

9781430265504_Fig14-54.jpg

图 14-54 。加一个编译“com . Google . Android . support:wearable:1.1。依赖项部分中的+"语句

下一次我测试该应用时,我注意到 IntelliJ 正在使用一个 Wear Round AVD,这很奇怪,所以我使用了一个运行image编辑配置菜单序列来检查 IntelliJ 当前使用的设置。

果然在图 14-55 中可以看到,目标设备>模拟器下拉设置为空白(未设置),所以我选择了 Nexus 5 API 21 默认 Android Studio AVD 模拟器。

9781430265504_Fig14-55.jpg

图 14-55 。在运行/调试配置中检查目标设备image模拟器

这一次编译器没有抛出错误,这意味着我已经修复了 build.gradle 配置文件的问题,但随后我得到了一个 AVD 错误,与没有英特尔硬件有关,因为我使用的是 AMD-64 8 核处理器,正如你在图 14-56 底部看到的。

9781430265504_Fig14-56.jpg

图 14-56 。再次运行应用,如果您在 AMD-64 系统上,您可能会得到这些英特尔硬件加速执行管理器(HAXM)错误

在为 Android OS 开发时,你需要勤奋并准备好一切,所以我使用了工具image Android image AVD 管理器来改变我的 AVD 模拟器。

我需要更改配置以使用一个 ARM 仿真器 版本,正如我在设置 Wear 仿真器时所做的那样,并且我确保您同时拥有 x86 和 ARM 版本,因为不是每个人都使用英特尔架构。

打开工具image安卓image AVD 管理器对话框,选择 Nexus 5 API 21,然后点击最右边的绿色铅笔图标。这将打开一个虚拟设备 配置 对话框,您可以在其中更改核心设置。

请注意,在该对话框的顶部,您可以更改您的 AVD 名称,并且您可能希望在某个时间点为您的 Wear AVD 仿真器执行此操作,例如,将 Android Wear Square and Round 2 AVDs 重命名为 Android Wear Square and Round ARM。总是花一些时间来定制你的想法,所以在你的应用开发过程中,一切对你来说都是非常清楚的。

在对话框的第三部分,您将看到该 AVD 的软件平台,即 Lollipop,该硬件(软件)仿真器当前设置为 x86 (Intel),我必须将其更改为 ARM,以便在我的 AMD-64 系统上进行测试。

找到该部分右侧的 Change 按钮,您可以使用该按钮访问一个对话框,如果需要,该对话框允许您选择 ARM 硬件仿真器。正如你将在图 14-57 中看到的,我需要更改 AVD 以使用 ARM 硬件仿真而不是 Intel 硬件仿真,所以我在测试过程中遇到的另一个错误现在已经解决了。你很快就能看到这个多旋转器的 UI 设计了!

9781430265504_Fig14-57.jpg

图 14-57 。使用虚拟设备配置对话框来重命名或重新配置硬件仿真设置

点击完成按钮,确保 CPU/ABI 栏中列出了新的设置,在我的例子中是 ARM ,如图 14-58 中的高亮显示。现在,您已经准备好尝试运行应用并对其进行测试。

9781430265504_Fig14-58.jpg

图 14-58 。请务必在 Android 虚拟设备管理器主屏幕中检查您的新模拟器设置

正如你在图 14-59 中看到的,Nexus 5 仿真器现在正在启动,你可以看到我忘记了用 _ARM 扩展名重命名这个仿真器,所以我必须返回去重新命名!左边是初始启动屏幕,中间是图标,因为一个应用图标不可用,我将再次使用 IntelliJ 中的运行命令,这会将这个应用放入仿真器,因为它已经在系统内存中启动了。

9781430265504_Fig14-59.jpg

图 14-59 。运行 Nexus 5 API 21 AVD 仿真器中的配套配置活动,检查 UI 设计

正如你在图 14-59 的右手边所看到的,填充了嵌套的< LinearLayout >容器的< LinearLayout > UI 设计正确地呈现了< TextView >和< Spinner >小部件,并且如预期的那样在其中包含了< string-array >数据。没有通过您编写的 Java 代码设置默认值的原因是因为 AVD 模拟器只能到此为止,并且与 Google Play GMS 服务器连接不是 IntelliJ 中 AVD 环境的当前功能。

出于这个原因,我将在下一章解释如何在真实世界的硬件产品上测试基于 WatchFaces API 的 Android 应用,如三星 Gear S 或索尼智能手表 3,使用真实世界的网络提供商(我使用 T-Mobile)和使用真实世界的智能手机(我使用 T-Mobile 的三星 Note4)。这是越来越令人兴奋的每一个进步的章节!

摘要

在这一章中,你学习了 Android 中的可穿戴包以及可穿戴包类,其中包含用于通过 GMS 网络进行通信的 API。这些包括 APIDataAPINodeAPIWearableAPI ,所有这些都在你在本章编写的 Java 代码中使用,以实现最终的 ProWatchFaceUtility.java 类。

在创建了工具类并定义了常量之后,您编写了 putConfigDataItems( ) 方法来将 DataItem 对象加载到 DataMap 对象中。您了解了 PutDataMapRequest 类以及如何输入数据映射请求,然后您了解了 Android Wearable 类及其四个 API 数据字段。

接下来,您创建了一个 fetchConfigDataMap( ) 方法向 GMS 网络请求 DataMapResult。这使用您编写的**fetchConfigDataMapCallback()**接口来设置 ResultCallback ,并使用 onResult( ) 方法结构来处理它。

然后,您创建了 dataItemResultCallback( ) 方法,该方法利用 fetchDataMapCallback()方法并处理 DataItem 对象,以提取用于在 onResult()方法结构内创建 DataMap 对象的 DataMapItem 对象。如果提取的 DataItem 为空(null),则创建一个空数据映射。

接下来,您创建了 overwriteKeysInDataMap( ) 方法来更新现有的配置数据映射对象。这实现了一个onconfigdatamappled()方法,该方法被定义为需要在 fetchConfigDataMapCallback()接口内实现,并使用了。putAll( ) 方法调用,用用户最新的配置首选项覆盖当前(或新)数据映射中的键。

然后,您转换思路,打开了ProWatchFaceCompanionConfigActivity类,并添加了与 ProWatchFaceUtility 类中使用的常量相匹配的常量。然后,在一个activity _ pro _ watch _ face _ config . XML定义文件中,使用 XML 标记为四个微调器小部件设计用户界面。

接下来,您添加了设置颜色选择器(微调器)小部件的 Java 方法,然后使用 AVD 模拟器测试了应用的移动端。您看到了一些可能会妨碍应用测试的问题,以及如何解决这些问题。您确保了您的 UI 设计能够正常工作,现在您已经准备好在真实的硬件和真实的网络上进行测试。

在下一章中,您将更仔细地了解如何在实际的硬件设备上测试您的 watch faces 应用,包括使用 Java Singleton 设计模式,这是 watch faces API 实现中的最后一个编码步骤,然后我将介绍如何设置硬件、逐步构建以及运行和测试您的应用。

十五、可穿戴应用测试:在 Android Studio 中使用硬件设备

现在,您已经完成了 watch faces 应用的大部分编码和设计,您需要开始在真实硬件设备 上测试您的应用,这可能是一个非常复杂的工作过程。

第一步是让电脑、智能手机和智能手表硬件协同工作,这也是本章的主要目标,在软件开发环境(Android SDK 和 IntelliJ IDE)和硬件设备环境之间架起桥梁。我已经讨论过只使用电脑(带 AVD),现在我将讨论使用电脑和带 AVD 的智能手机,最后我将讨论同时使用电脑、智能手机和智能手表。

这将涉及到使用你制造商的 USB 驱动软件,除非你使用谷歌硬件设备,这也是模拟器使用的设备。如果你的硬件没有 USB 驱动程序,你将学习如何使用谷歌的 USB 驱动程序来让你的 Android 智能手表设备与电脑接口。你还将在你的智能手机上安装谷歌穿戴应用,并学习如何为配对调试进行配置。

之后,您将了解 Android 调试桥 ,或 ADB ,以及如何使用该命令行工具在您的计算机和 USB 或蓝牙连接设备之间建立通信端口。然后,您将了解如何让 Android Studio IDE 使用 IntelliJ 及其 Gradle 构建系统 中的特性来识别和构建硬件设备。我们开始吧!

将设备与计算机连接:USB 驱动程序

除非你使用的是谷歌 Nexus 设备(在这种情况下,你可以使用 USB 驱动程序,即你安装 Android Studio 时为你安装的 SDK 管理器,否则你首先要做的就是访问 Android 开发者网站上的 OEM USB 驱动程序页面,网址如下:

http://developer.android.com/tools/extras/oem-usb.html

到达该页面后,右键单击 Android 设备的制造商驱动程序 URL。这将打开一个上下文相关菜单,在图 15-1 的右上角用红色圈出。选择第一个上下文菜单选项**,在新标签**中打开链接,并在浏览器的第二个标签中打开您的 Android 设备制造商网站 (我使用的是谷歌 Chrome 浏览器)。

9781430265504_Fig15-01.jpg

图 15-1 。去 developer.android.com/tools/extras/oem-usb.html,右键点击并打开制造商的网站

因为我目前使用的是一台三星 Galaxy Note 3 运行 Android 4.4 来自 T-Mobile 我会在接下来的几张截图中展示我的三星 Note 3 工作流程,应该和你们厂商的工作流程 差不多。

如果 Android 链接没有带你去你的制造商网站上的支持部分,就像我的情况一样。单击您的设备类型,在我的例子中,它被标记为“手机”在图 15-2 的左下方可以看到我点击的链接被红色圈出。

9781430265504_Fig15-02.jpg

图 15-2 。找到制造商网站上与您正在使用的 Android 设备类型相匹配的部分

这应该会带你到一个页面,在那里你可以找到你的智能手机产品,通常使用某种搜索应用对话框,或者可能使用一系列页面的优化过程来找到你的设备。

在三星网站 上,这是一系列下拉(微调)UI 控件元素,允许您从流派(手机、预设)到硬件设备(手机、预设)细化搜索,以选择手机运营商。

我的运营商是流行的 T-Mobile 服务,因此,我选择了 T-Mobile 作为该选项,如左下方的图 15-3 所示。T-Mobile 是一个用于 Android 开发和测试的优秀网络,因为它们支持各种各样的 Android 硬件,并具有现代、快速的 4G LTE,以及最近的宽带 4G LTE 网络。T-Mobile 现在到处都安装了 4G LTE 技术,甚至在这里的 Point Conception 半岛,旁边是一家著名的太空技术公司(Space-X)和范登堡空军基地。

9781430265504_Fig15-03.jpg

图 15-3 。找到你的产品;在三星网站上,这是在移动image手机imageT-移动手机下拉框下

正如你在图 15-4 中看到的,一旦我指定 T-Mobile 为运营商,我就可以访问第四个下拉微调 UI 元素,其中列出了 T-Mobile 支持的所有三星手机。找到了 SM-N900T,代表三星制造 Note 900 电话(我猜的)。

9781430265504_Fig15-04.jpg

图 15-4 。在你的设备上找到型号 ,找到确切的产品,并下载 USB 驱动程序

请注意,有一个 W(白色)和一个 K(黑色,对于那些不熟悉印刷和 CMYK 颜色模型的人来说)版本。因为外壳的颜色不会影响司机,我可以选择这两个选项中的任何一个,但我选择了技术上正确的黑色版本,这样我就可以在图 15-4 所示的截图中展示我的智能手机的图像。如图 15-4 右侧所示(不要让漂亮的硬件设备分散你的注意力),一旦你选择了一个硬件型号,你将会得到一个“获取下载”(或类似的)按钮。单击下载按钮,打开驱动程序软件下载页面。

寻找下载(。EXE)或类似按钮,并单击它开始下载过程。一旦你这样做了,在浏览器的底部应该有一个进度标签,显示在图 15-5 的底部。下载完成后,使用下载文件的下拉选项菜单并选择文件夹中的显示选项,这将使您在文件管理工具中拥有最大的控制权。

9781430265504_Fig15-05.jpg

图 15-5 。下载 USB 驱动(上);下载完成后,从下拉菜单中选择“在文件夹中显示”

正如你在图 15-6 中看到的,一旦你进入文件管理工具,你可以右击这个下载的文件,再次得到一个上下文相关的菜单,菜单中有很多选项,其中一个是以管理员身份运行选项,它允许你使用完整的操作系统(读写)权限运行文件。

9781430265504_Fig15-06.jpg

图 15-6 。右击驱动安装程序可执行文件,选择以管理员身份运行菜单选项 ??

完成安装过程。正如你在图 15-7 中看到的,我选择了美国英语并接受了一个默认的C:/Program Files/SAMSUNG/USB Drivers作为我的安装目录。然后我使用安装按钮,在我的 64 位 Windows 8.1 AMD-64 系统上安装驱动程序,如图图 15-7 所示。

9781430265504_Fig15-07.jpg

图 15-7 。使用下一步按钮浏览安装配置对话框;然后单击安装按钮

需要注意的是,此时您并没有真正地将 USB 驱动程序安装到您的操作系统配置中;您只需在硬盘上安装 USB 驱动程序软件,以便计算机管理对话框可以找到它,并使其成为您当前操作系统硬件配置的一部分。

正如你在图 15-8 中看到的,你访问这个对话框的方法是右击你的电脑我的电脑,或者在 Windows 8.1 的情况下,右击名为这台电脑的图标。在上下文菜单上,您会发现一个管理选项,该选项将打开计算机(硬件)管理对话框 。

9781430265504_Fig15-08.jpg

图 15-8 。右键单击这台 PC(计算机)图标;选择管理选项,打开计算机管理对话框

Windows 8.1 计算机管理对话框应该更准确地命名为计算机硬件设备管理器工具,因为这就是它的用途。如果您想了解您的计算机设置,您可以单击左窗格中的向右箭头,打开系统工具列表中的所有区域。点击设备管理器条目 ??,显示所有连接到您电脑的硬件设备,如图图 15-9 所示。

这应该包括智能手机,您应该使用 USB 充电线将智能手机连接到 USB 端口。我一直把我的电脑连接在电脑上,所以我总是充满电。你可以在中间的窗格看到,它被高亮显示,上面写着:华莱士·杰克逊(SM-N9) ,代表车主姓名和车型名称的前几个字母。如果您的智能手机没有连接,现在插入它,它应该会被检测到并出现。

9781430265504_Fig15-09.jpg

图 15-9 。在左窗格中选择设备管理器;找到 USB 驱动程序;右键单击并选择更新驱动程序软件

再次使用右键访问上下文菜单,并选择更新驱动程序软件菜单选项。这将打开你的更新驱动软件对话框,如图图 15-10 所示。点击自动搜索更新的驱动软件,确保您拥有最新版本的 USB 驱动。

9781430265504_Fig15-10.jpg

图 15-10 。选择自动搜索获取最新的驱动程序版本,以确保您拥有最新的驱动程序版本

现在你已经确保你有最新的 USB 驱动程序版本,选择另一个选项,浏览 我的电脑寻找驱动软件。然后你的操作系统会找到你下载的最新的 USB 驱动软件,并开始安装过程,如图图 15-11 所示的一系列对话框。现在,您的计算机可以将数据传输到您的智能手机,您的智能手机将对 IntelliJ IDEA 可见。

9781430265504_Fig15-11.jpg

图 15-11 。在您的计算机上浏览 USB 驱动程序软件,并在您的操作系统上安装 USB 驱动程序

现在当你查看你的文件浏览器(文件管理)工具 时,你将能够看到一个 Android 硬件设备,如图图 15-12 所示。

9781430265504_Fig15-12.jpg

图 15-12 。显示我的 Galaxy Note 3 智能手机使用 USB 驱动程序安装到我的 PC 上

安装 Wear API:将智能手表与手机链接

现在,您已经用正确的软件设置了 Wear 开发工作站,下一步是确保您将在移动设备上安装 Android Wear,以便与您的可穿戴硬件配合使用。

这包括您将用来测试各种 Pro Android 可穿戴设备应用的智能手机(或平板电脑)和智能手表。这将包括你的手表表面应用,你将在现实世界的硬件设备上测试。

因为你的智能手表已经安装了 Android 5.0 和 Wear,这相当于在你的智能手机上安装了这些 Wear APIs,然后将智能手表与智能手机连接起来。这就是你在本章这一节要做的事情,这样你就可以设置好你的移动硬件。

下载和安装 Wear API:谷歌 Play 商店

你下载和安装 Wear API 的方式是在智能手机上,通过服务提供商(就我而言,就是 T-Mobile 的 4G LTE 服务)。在智能手机上找到您的谷歌 Play 商店图标,您将使用它来测试您的应用并启动 Play Store。在你的谷歌 Play 商店搜索栏中,输入“wear”这个词,你就可以找到与 Android Wear 相关的软件。

首先出现的结果之一将是 Android Wear 应用本身。该应用将显示您可以免费下载。你的 Android 设备的下载部分应该看起来类似于图 15-13 所示,当然,统计数据反映了当前的使用情况。

9781430265504_Fig15-13.jpg

图 15-13 。安装后的 Android Wear(打开 Wear 前)

Android Wear 应用本质上是将你一直在学习的可穿戴 API 安装到任何最终用户的智能手机上,这样他们就可以运行穿戴应用。Wear 应用使智能手机能够通过使用蓝牙 4.x、NFC 或 Wi-Fi 等技术与任何兼容 Wear 的智能手表硬件“配对”,如果智能手表硬件支持的话。

因为 Apple Watch 支持 Wi-Fi,所以用不了多久 Android Wear 智能手表产品也会支持它。其实索尼智能手表 3 已经支持了。有趣的是,当我在 Note 3 上安装 Android Wear 后,谷歌应用(主要的谷歌应用,包括所有谷歌的东西/应用)也想更新自己,所以我也更新了这个应用。

现在,您已经升级了智能手机,可以使用 Android Wear 智能手表,是时候设置智能手表本身并将其与智能手机配对,以便您可以开始设置 IntelliJ 来使用它们。

设置智能手表:索尼智能手表 3

接下来,我将设置我的索尼智能手表 3 ,它是我刚刚收到的,用于这本书的佩戴开发。我按照指示插上 SW3 充电 30 分钟,然后在与 Galaxy Note 3 配对之前打开它,Galaxy Note 3 现在已经安装了 Wear 应用。

有趣的是,我得到了与 AVD 模拟器完全相同的启动动画(旋转谷歌彩色点),然后是语言菜单选择器。我选择了英语(美国),然后选择了“在您的手机上安装 Android Wear”通知。

接下来,我启动了 Android Wear 应用,出现了“让我们为您设置”屏幕,在这里,我点击了继续(蓝色圆圈中的右箭头)图标,以便我可以继续智能手表配对过程。

下一个屏幕有关于 Google 定位服务、Google Fit 服务以及接受选项的信息。一旦您选择此选项,即表明(同意)您接受隐私政策和服务条款。

我看到的下一个屏幕是“打开蓝牙”屏幕,所以我向下滑动屏幕顶部,点击蓝牙按钮,为我的 Note 3 打开该功能。我选择(检查)了我的设备(华莱士杰克逊(SM-N9)。

这个蓝牙对话框还显示智能手表 3 804D 是一个可用的设备。接下来我选择了扫描按钮,只是为了确保一切设置正确。

当我回到 Wear 应用时,我能够选择智能手表 3 804D 选项,并得到一个“配对”对话框,给我一个 224433 配对代码。

在我的智能手表 3 上,我选择(触摸)了复选标记 UI 选项,然后在两个设备配对时出现了一个下载屏幕。

这个过程花了很长时间,因为我当时正在使用 2G 网络。如果你有 3G 或 4G 连接,速度会快得多。关键是,更新过程是通过谷歌移动(GMS)网络进行的,因此速度将取决于当时的连接速度。

在我的智能手机上,我看到了一个“打开手表通知”屏幕,我被告知要前往智能手表通知设置,并选择 Android Wear 旁边的复选框,所以我点击了这个通知,它将我带到了一个屏幕,我可以在这个选项旁边打上复选标记。然后,我使用屏幕底部的后退按钮完成了设置。

需要注意的是,打开手表通知是 Wear 应用的一部分,而不是智能手表 UI 的一部分,所以请确保您没有在那里寻找它,因为您不会找到它!(我犯了这个耗时的错误。)

当我在 Wear update(下载)后第一次使用智能手表时,它有一系列的操作屏幕,与 AVD 中看到的完全一致。接下来让我们来看看如何将所有这些硬件与 Android Studio 连接起来。

使用 ADB:将智能手机与 AVD 连接起来

在本章的这一节,我将解释如何将 IntelliJ AVD 仿真器用作智能手表。你可以通过使用 USB 电缆将其连接到智能手机上,并对其进行设置,以便 AVD 充当你的智能手表。

ADBAndroid 调试桥 的主要用途是提供 Android 设备硬件的实时连接。这个桥允许您将您的设备硬件作为 Android Studio (IntelliJ)的扩展,这将允许开发系统(在本例中为 Android Studio)和用于测试的设备硬件之间的无缝交互。

这种数据连接将允许 AVD 仿真器和物理 Android 设备使用 USB(通用串行总线)端口双向“看到”彼此,以便运行和调试应用。

首先,让我们看看如何实现硬件image想法方向,这样你就可以将你的 Wear AVD 用作智能手表,然后我将开始使用 Android 设备硬件而不是 AVD 进行测试,这将等同于想法image硬件方向。

要使 Android 硬件能够“看到”USB 硬件,您需要在智能手机上启用开发者设置选项。一旦完成,您将能够选择启用 USB 调试设置,允许您的手机与您的工作站通信,并最终与您的 IDE 通信。

为此,进入智能手机的应用区(一个标有应用的 16 方形网格图标),点击标有“设置”的齿轮图标点击顶部的常规选项卡,向下滚动到关于选项,然后点击。在关于部分,您将找到一个构建编号条目,您将连续点击该条目七次。这将为智能手机解锁一个 Android 开发者模式,一个新的开发者选项部分将出现在您的常规选项卡下。在这一部分中有大量的开发人员选项。

选择 USB 调试选项,在检测到智能手机和工作站之间的 USB 连接处于活动状态(就位或插入)时启用调试(与 Android Studio 通信)。现在,您已经在计算机上安装了 USB 驱动程序,并在您的智能手机上启用了 USB,您剩下要做的就是在您的工作站上配置软件,使用称为 ADB 的东西来管理此连接。

接下来,让我们详细了解一下 Android 调试桥的功能。这一点很重要,因为 ADB 是开发工作站 Android Studio IDEA 和任何 Android 设备硬件之间的实时网络(数据连接)链接,您可能想用它来测试您的应用,以查看它的外观和功能。IntelliJ 可以访问连接硬件的许多高级功能,允许您对应用的 CPU、系统内存、屏幕空间和类似信息的使用情况进行基准测试。

Android 调试桥: 联网软硬件

ADB 是一个命令行工具 ,可以让你与 AVD 仿真器实例或连接的 Android 驱动设备进行通信。你可以通过使用你的文件管理器搜索功能在你的硬盘上找到它。

为此,请打开您的文件管理器(在 Windows 8.1 中,它被称为资源管理器),在左侧窗格中,单击最顶层的 OS 级别(这台 PC、我的电脑、计算机等)。)在您的文件管理工具中。这将向搜索工具显示您想要从哪个级别(向下)搜索。

接下来,在搜索栏中输入你要找的adb.exe可执行文件,如截图右上角的图 15-14 所示,红色包围。一旦你按下键盘上的回车键“启动”搜索,文件管理工具将搜索你的整个硬盘驱动器,这样你就可以找到文件。更重要的是,这将向您显示一个您需要使用的路径(下一步),以便使用命令提示符工具访问和运行这个 ADB 文件。在搜索结果的右侧可以看到路径(文件夹地址),如图 15-14 中用蓝色突出显示的。

9781430265504_Fig15-14.jpg

图 15-14 。使用带有 adb.exe 搜索规范的文件资源管理器的搜索结果

ADB.exe 是客户端-服务器体系结构的客户端部分,包括三个主要组件,包括在开发机器上运行的客户端和作为后台进程在开发机器上运行的服务器。该服务器将管理您的客户端和 ADB 守护进程之间的所有通信,该守护进程将运行在您的仿真器和/或设备上,这取决于您正在做的事情和您在开发(IDEA image设备或设备image AVD)过程中的发展方向。

您可以从 shell 中调用客户机,在 Windows 中,这是 DOS 命令行工具,您很快就会用到它。要发出 adb 命令,您应该调用 ADB 可执行文件,并使用命令行提示符为其提供一系列开关和命令,以及可选的数据输入选项。

其他 Android 工具,如 ADT 插件或 DDMS,也将自动创建 adb 客户端,以便 AVDs 和硬件等模块可以作为一个无缝的开发环境与之通信。这就是为什么 ADB 代表 Android 设备桥的原因,因为它允许你将东西连接或桥接到 IDE 中。

第三个组件是 ADB 守护程序,它作为后台进程在每个 AVD 仿真器上运行,并在每个硬件设备实例上活动。

你可以在**/platform-tools/文件夹中找到 adb 工具,但这通常是一个隐藏文件夹,至少在 Windows 中是这样的。正如你在图 15-14 中看到的,搜索工具在 /AppData/ 文件夹中找到了adb.exe**,这在左侧显示的文件夹层次窗格中是不可见的。

需要注意的是,如果您仍在使用 Eclipse IDE 并且安装了 ADT 插件,那么您不需要直接使用 adb 在模拟器或设备上安装应用,因为 ADT 插件会处理应用的打包和安装。然而,Eclipse 在一年多前就停止了 Android Studio 开发,所以您可能希望尽快切换到 IntelliJ IDE 版本 14 (Android Studio)和 Gradle。

在 Android Studio 内部使用 AVD:ADB****端口转发

为了在你的智能手机硬件和 AVD 仿真器之间发送数据,你需要设置一个叫做 ADB 端口转发的东西。这可以通过使用 adb -d forward 命令行条目并指定您的 TCP 端口来完成。

让我们详细回顾一下这个过程,以便您可以连接您的硬件设备。启动 Android Studio,进入运行image编辑配置并指定一个穿戴模块??,不启动活动选项, Android 穿戴轮 AVD 仿真器,如图图 15-15 所示,点击应用按钮,然后点击确定

9781430265504_Fig15-15.jpg

图 15-15 。设置磨损模块和磨损轮模拟器

接下来,确保在工具image安卓子菜单中勾选了启用 ADB 集成选项,如图 15-16 中的突出显示。

9781430265504_Fig15-16.jpg

图 15-16 。确保工具image安卓image启用亚行整合子菜单项目已被选中

接下来,确保您的智能手机安装了 Wear 应用,并通过 USB 连接到您的工作站,并且 Wear 应用已在手机上启动,然后单击 IntelliJ 顶部的 Play 图标,或使用 Run image Run Wear 启动 AVD 模拟器。您也可以使用 Run image Debug Wear 在调试模式下启动仿真器。对于这个例子来说,这两种方法都应该是可行的(我对这两种方法都进行了测试,结果都是可行的)。

等到 Wear AVD 模拟器初始化,显示一个 Android Wear 主屏幕,然后你就可以将智能手机与 AVD 模拟器“配对”了。这是通过使用以下命令序列将 AVD 通信端口转发到所连接的手持设备来完成的:

adb -d forward tcp:5601 tcp:5601

需要注意的是,每次您通过 USB 端口将智能手机连接到工作站时(或者如果您关闭工作站或拔掉智能手机),您都需要执行此步骤。

在 Windows 8.1 下这样做的方法是在右键单击开始菜单(看起来像一个窗格),然后从上下文菜单中选择**命令提示符(管理)**选项,如图 15-17 中的突出显示。

这将打开命令提示符(Windows Shell 或命令行)工具,您将使用它来运行adb.exe工具,并向它发送开关命令参数,就像上面概述的 adb -d forward tcp:5601 tcp:5601 命令一样。-d 是交换机(d 表示设备),forward 是命令,tcp:5601 是参数(tcp 是传输控制协议或互联网协议,5601 是端口位置或“地址”)。这些 TCP 端口地址有两个,网络连接“等式”的每一端(工作站和设备)各有一个,该等式是通过使用 ADB 设备转发命令行序列在内存中创建的。

9781430265504_Fig15-17.jpg

图 15-17 。右键单击开始菜单,并选择命令提示符(管理)

当您的管理员:命令提示符窗口出现时,命令提示符将显示这个cmd.exe工具“所在”的目录,因此您将知道您“所在”的计算机硬盘驱动器的位置这将是你在左上角看到的C:\ WINDOWS \ system32>??,如图图 15-18 所示。键入一个 *cd* 命令,意思是“更改目录:根目录”并点击回车**。现在,提示符将显示为 C:\ > ,您可以使用 CD (或 CD)命令将目录(文件夹)更改为您位于图 15-14 中的平台工具文件夹。更改目录 (cd)命令使用 cd 命令,然后是用户\默认。default-PC \ AppData \ Local \ Android \ SDK \ platform-tools路径,它指定了硬盘上您想要“站立”的文件夹。**

9781430265504_Fig15-18.jpg

图 15-18 。导航到平台-工具文件夹,列出连接的设备,转发 adb 端口,并列出更多连接的设备

提示符现在显示您在平台工具文件夹中,可以运行 adb.exe 文件,因为它在您现在所在的同一个文件夹中。您现在可以使用 adb devices 命令行条目列出连接到工作站的所有设备。这将输出一个连接了的设备列表,正如你在图 15-18 中看到的,我的 AVD 还没有启动,所以只显示了我的 Note 3。

在我的例子中,智能手机设备被列为 4d4f4baf ,这很可能是一个内存位置。我输入了一个 adb -d forward tcp:5601 tcp:5601 命令,然后一旦 AVD 完成加载到内存中,我再次运行 adb devices 命令,正如你在底部的图 15-18 中看到的,一个 AVD 现在在附加设备列表中可见,指定为 emulator-5554

下一步是在您的 Android 设备上启动 Android Wear 应用并连接到仿真器,这也可能很困难,因为您需要找到的菜单项在 Android Wear 设置菜单上并不直接可见,并且工作流程在互联网上的太多地方都没有概述,所以请密切关注过程中的这一步!值得注意的是,Android Wear 应用可能会在未来改变 UI 设计,以使这个“与仿真器配对”选项更容易定位。

选择 Android Wear 的功能菜单,这是通过我的 Note 3 智能手机硬件左下角的菜单按钮访问的。这将看起来像一个正方形图标,里面有两个水平条,因此看起来像一个下拉菜单。点击它,屏幕上会出现一个菜单。

在菜单的顶部有一个“与新的可穿戴设备配对”的选项。选择这个,会出现一个“选择一个设备”屏幕,其中根本没有列出 AVD,只有配对的智能手表设备,在我的例子中,这是索尼智能手表 3。一旦该屏幕可见,再次点击硬件菜单按钮,然后您将看到您需要选择的“与仿真器配对”选项。

为了测试 ADB 连接并将数据发送到 AVD,再次访问佩戴功能菜单(在我的 Note 3 上,这是使用菜单硬件键)。在这个菜单的中间,你会看到一个选择演示卡功能的选项。

您可以使用此功能来测试设置为智能手表的 AVD。只需选择一些演示卡,并确保它们作为 Wear Round AVD 模拟器主屏幕上的通知显示在您的 AVD 上。我选择了体育、酒店和股票演示卡来测试工作站到设备的接口,该接口是通过 USB 总线(端口)使用 ADB 连接协议建立的,如图图 15-19 所示。

9781430265504_Fig15-19.jpg

图 15-19 。测试智能手机到 AVD(通过 Android Studio,在工作站上)的 ADB 连接链接

接下来,这个过程将变得更加复杂,你还将连接智能手表硬件,这样你的工作站就可以通过智能手机与智能手表进行对话。

蓝牙调试 :链接到智能手表

我之所以先介绍如何从智能手机链接到 AVD,是因为需要以同样的顺序执行这些步骤,以将您的 IDE 连接到硬件设备,从另一个方向进行。你将需要能够通过 USB 电缆将你的应用从 Android Studio 发送到你的智能手机,然后再发送到智能手表。让我们来看看将智能手表与智能手机配对的步骤,然后通过 USB 建立从 Android Studio 到智能手机再到智能手表的实时链接。

值得注意的是,如果您希望将智能手表和智能手机都插入工作站上的 USB 端口,则无需执行所有这些操作。一些智能手表,如 MOTO 360,不允许 USB 连接,因为所有智能手表都有蓝牙调试,我将展示一种更复杂的方法,在没有智能手表 USB 连接的情况下让一切正常工作。

智能手表设置:配对并启用蓝牙调试

你需要做的三个主要步骤是将智能手表与智能手机配对,为智能手表启用蓝牙调试,然后在智能手机上启用蓝牙调试。第一个是通过智能手机上的 Wear 应用完成的,第二个是在智能手表上完成的,第三个是在智能手机上完成的。完成所有这些工作后,您将使用 ADB forward 并连接命令序列,以使 ADB 守护进程可以看到所有内容。

启动智能手机上的 Wear 应用,并确保它与您的智能手表配对,或者使用 Wear functions 菜单上的与新的可穿戴设备配对,这是您在上一节中使用的。第一次进行配对时,Android Wear companion 应用会建议您参加一个介绍可穿戴 UI 及其基本功能的简短教程。之后,你将能够访问一个标准的穿戴 UI,在那里你可以选择表盘,启用语音操作,并浏览谷歌 Play 商店中建议的穿戴应用。

下一步是为智能手表设备启用蓝牙调试。所有可穿戴设备都将禁用 USB 调试作为默认设置,因此您必须在智能手表上手动启用它。启用蓝牙调试的选项将被隐藏,就像它在智能手机上一样。这是为了“正常”的最终用户不会意外地启用调试模式。

您需要打开“设置”,选择“关于”,然后连续七次点按“构件编号”。这个新的设置菜单包括开发者选项,在这里你可以通过蓝牙进行调试。在我的智能手表 3(索尼)上,通过蓝牙调试选项是灰色的(禁用),直到我首先选择了 ADB 调试选项。因此,对于每个智能手表硬件设备,通过蓝牙打开调试所需的序列可能不同。

下一步是返回到您的智能手机设备,在那里您可以启动“设备调试会话”这是通过再次打开 Android Wear 应用并转到应用右上角的穿戴设置(齿轮)图标来完成的。

点击设置图标打开设置菜单,滚动到底部,找到通过蓝牙调试选项,并在其旁边放置一个复选标记以启用此功能。

在调试蓝牙功能标题下面应该有一些较小的字体文本,表明主机(您运行 ADB 连接到 Android Studio 的工作站)与断开,而目标(您的智能手表)与连接

下一步是使用命令提示符工具访问 ADB 守护程序,并将其配置为使用蓝牙链接查看智能手表。这可以通过在 DOS 命令提示符下输入两个命令来完成,一个是 forward,另一个是 connect。第一个亚行前进命令应该是这样的:

adb forward tcp:4444 localabstract:/adb-hub

第二个 adb connect 命令更简单,应该如下所示:

adb connect localhost:4444

正如你在图 15-20 中看到的,我仍然在运行 AVD 仿真器,所以我得到了一个错误。

9781430265504_Fig15-20.jpg

图 15-20 。调用 adb forward 和 adb connect 命令,连接智能手表

一旦成功,你将得到一个“允许 USB 调试?”在你的 Wear app 上留言,旁边还有一条这台电脑的 RSA 加密密钥指纹是:<密钥串在这里> 留言。点击 OK 按钮完成设置。还有一个“总是允许来自这台计算机”的选项,这是我选择的。

执行此操作后,Wear 应用设置中的“通过蓝牙调试”选项下的较小字体文本将更改为显示以下内容:

Host:   connected
Target: connected

现在,您已经将所有东西“连接”在一起,您应该能够从工作站访问您的智能手机和智能手表。现在,您已经开始测试本书中到目前为止构建的 Watch Faces API 类和功能。

在你开始在 Android Studio 中测试和调试之前,我想介绍一个额外的话题,Java Singleton 设计模式。您将向 ProWatchFaceUtility.java 类添加最后一个私有构造方法,然后您将准备好测试和调试您的新应用。

Java Singleton:prowatchface utility()构造函数

在 Java 软件开发中,单例模式是一种设计模式,它将一个类的实例化限制在一个对象上。当只需要一个对象来协调整个系统的操作时,这很有用,就像在 ProWatchFaceUtility 类中一样。这个概念通常被推广到只有一个对象存在时操作更有效的场景,例如资源有限的穿戴(穿戴或智能手表端)组件。

这样做的第一步是将类的默认构造函数方法创建为私有方法。这将防止其他类直接实例化该对象。在你的 ProWatchFaceUtility.java 类的末尾添加空的private ProWatchFaceUtility( ){ }构造函数方法,如图 15-21 中底部高亮显示的。

9781430265504_Fig15-21.jpg

图 15-21 。在 ProWatchFaceUtility 类的末尾添加一个私有的 ProWatchFaceUtility()构造函数方法

这使得这个私有方法成为一个类级别的方法,这意味着您的 ProWatchFaceUtility 可以被静态访问,而无需创建对象。正如您可能想象的那样,这是一种内存和 CPU 优化技术。

事实上,你会看到这个 IntelliJ 建议**通过创意左侧的一个黄色灯泡为' com . pro . Android . wearable . proandroidwearables . prowatchface utility '**添加按需静态导入,如图图 15-22 所示。

9781430265504_Fig15-22.jpg

图 15-22 。使用 ProWatchFaceUtility . overwritekeysinconfigdatamap()进行静态访问(prowatchface utility)

通过在这里使用单例模式,你现在将能够使用 ProWatchFaceUtility . overwritekeysinconfigdatamap()方法访问你的 prowatchface utility 类中的方法(就像你在你的 ProWatchFaceConfigListenerService.java 类中的 onMessageReceived()方法结构中所做的那样),没有双关的意思,正如在图 15-22 的底部突出显示的那样。

接下来,让我们开始测试和调试您的 Watch Faces 应用!

测试和调试:创建您的。APK 档案

为了使用外部硬件设备在 Android Studio 中测试和调试您的 wear 应用,您需要生成**。APK** 档案。您还可以使用您的 avd 测试这些调试和发布“构建”。

您使用调试构建版本来测试您连接的硬件设备,使用 AVD 仿真器,并且您使用发布构建版本来分发您的应用,一旦您“签署”(序列化)它。

Android Studio 构建系统:概述

如前所述,Android Studio 的构建系统建立在高级的 Gradle 构建系统之上。这是一个集成的工具包,你将利用它来构建、测试、运行并最终打包(发布)一个应用。

Gradle build 系统可以使用 Android Studio 菜单系统作为集成工具包运行,也可以独立于命令行运行。您可以使用这个构建系统的特性来配置、定制和扩展这个梯度构建过程,以满足您特定的应用开发需求。

使用同一项目及其模块中的不同功能,可以为您的应用创建多个 APK 版本。您将跨不同的应用源“集”重用代码素材和资源 Android 的 Gradle build 系统的灵活性使开发人员能够实现所有这些优化特权,而无需修改应用的核心源文件。

Android 构建过程涉及许多工具和过程,它们会生成大量的“中间”文件,比如。java,。类,。德克斯还有。apk,沿途产生一个签名,内存优化。你可以把 apk 放在谷歌 Play 商店。如果您正在使用 Android Studio 开发应用,那么每次您为项目及其模块运行 Gradle 构建任务时,都会执行一个完整的构建过程。理解过程的各个阶段发生了什么是有用的,因为构建过程的大部分是可配置的,甚至是可扩展的。

为此,我将详细介绍 Gradle build 系统中涉及的各种组件,然后解释如何在 Android Studio 项目中创建构建,这是一个相当复杂的工作过程。

构建系统的组件:AAPT、AIDL、德克斯、CLASS 和 APK

Gradle 构建过程的工作方式是, APPT (Android 素材打包工具 ) 将收集您的应用的所有资源文件,例如 UI 设计所需的 XML 文件、应用清单和新媒体素材。对所有这些非编程逻辑素材进行编译,并生成 Java 模块,您将在 Java 代码中将该模块引用为 r,它实际上是通过这个 Java 文件到素材的点符号路径,如以下代码所示:

watchFaceDrawable=watchFaceResources.getDrawable(R.drawable.prowatchfacelow); //ProWatchFaceService

这将从您的项目 /res 资源文件夹中的适当分辨率密度文件夹(/res/drawable-hdpi)中访问prowatchfacelow.pngPNG BitmapDrawable 素材,在那里您的 R.java 资源被组织为 AAPT 编译成可以在运行时使用的格式。

AIDL(Android 界面定义语言 ??)工具 ?? 将你的。aidl 接口,用于将 IPC(进程间通信)实现为 Java 接口。IPC 在磨损应用中不常用,也不在本书的讨论范围之内,但是如果您需要使用它,您可以从下面的开发人员网站 URL 获得更多信息:

http://developer.android.com/guide/components/aidl.html

在图 15-23 中可以看到,所有定制的 Java 代码,包括 Java 生成的 AAPT 和 AIDL 生成的。aidl 文件,被一个 Java 编译器编译成中间**。类**文件,你也在你的 AndroidManifest 中引用了它。XML 应用定义。

9781430265504_Fig15-23.jpg

图 15-23 。Android 构建过程,从 AAPT 到。爪哇,到。上课去。德克斯去。apk,致署名 APK

构建过程的下一步是在。类文件转换成优化的。dex 文件格式,使用 Dalvik VM 工具。所有第三方库和。包含在项目模块构建中的类文件也将被转换成。德克斯文件。这是为了将它们打包到最终版本中。apk 文件。

所有未编译的资源,通常放在项目的 /res/raw 文件夹中,以及所有已编译的资源(R.java)和您的。然后,dex 文件被发送到 apkbuilder 工具,打包到您的 APK 中。

一旦你。apk 建立后,必须使用调试发布密钥对其进行签名,然后才能安装在 Android 硬件设备上进行测试(调试)或使用(发布)。我将在下一章讨论等式的发布部分,在那里我将解释一个梯度构建过程的最后一行(步骤),如图 15-23 所示。

最后解释一下构建过程,如果您的应用是以发布模式签名的,您必须对齐这个。使用 zipalign 工具进行 apk。如果你想知道,调整你的期末考试。当你的应用在 Android 硬件设备上运行时,apk 会减少内存使用

Android 应用 Java 代码限制 : 64 千字节的方法

Android 应用分配给方法引用的代码限制是 64kb——64kb 代表 65,536 个字符的代码。如果您的应用达到了这个限制,Gradle 构建过程将无法通过。dex 转换。

随着开发人员可以用来创建应用的 Android API 库的规模不断膨胀,使用这些 Java 类或方法创建的 Android 应用的规模也随之膨胀。当应用和它们引用的库达到一定大小时,您将会遇到构建错误,指示应用已经达到 64 KB Android 应用构建限制。

Android 4 构建系统的早期版本报告了如下错误:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

Android 5(Android Studio)Gradle build 系统的最新版本可能会显示不同的错误,这表明存在相同的问题:

Trouble writing output:
Too many field references: 128000; max is 65536.
You may try using --multi-dex option.

这两种错误情况都显示 64 KB 的十进制等效值:65,536。这个数字表示在一个 Dalvik 可执行(DEX)字节码文件中,Java 代码可以调用的引用总数。如果您构建的 Android Wear 应用会“抛出”这一错误,那么您可能有过多的 Java 代码,并且可能想要查看代码优化。

有一种方法可以绕过这个限制,这就是本章的这一节所解释的。我把这个包括在内,以防你的应用需要复杂。Android 应用包(或 APK)文件包含可执行字节码文件。这些将采用 Dalvik 可执行文件 (DEX)文件的形式,其中包含运行应用所需的 Dalvik VM 优化 Java 字节码。

当前的 Dalvik 可执行优化规范限制了单个。dex 文件到 65,536。这是一个均匀的“2 的幂”内存边界,可以包括您的 Android API 框架方法、库方法和您为应用功能创建的自定义方法。

通过“链接”可以超过这个 64 KB 的限制。德克斯的文件放在一起。这要求您配置应用的梯度构建过程,以生成多个。dex 文件。这在 Android 中通常被称为 multidex 应用 分发配置。

在 Android 5 之前的 Android 平台版本使用 Dalvik VM 运行时来执行应用代码,而版本 5 和更高版本使用为 Android 定制的 ART (Android 运行时)。Dalvik VM 限制每个应用只能有一个 classes.dex 字节码文件。apk 文件。为了避开这个限制,你可以使用多指数支持库。该库将成为主库的一部分。dex 文件,然后将管理对您的附加。dex 文件,有效地链接到它们包含的字节码。

Android 5 及更高版本使用 Android 运行时(ART),它原生支持加载多个。dex 文件,使用单个。apk 文件。这是因为 ART 在用户安装应用时进行预编译,所以 ART 可以扫描多个 classes.dex 文件,并将它们编译成一个单独的**。由 Android 设备执行的 oat** 文件。安。oat 文件存储了为新的 Android 运行时 (ART) 设计的应用的原生 C++ 代码,该运行时在 Android 4.4 和更高版本中可用。

在将应用配置为使用超过 65,536 个方法引用之前,您应该尝试优化应用的 Java 代码调用的引用总数。这包括在应用的 Java 代码中定义的方法,或者更有可能是通过减少 API 库包导入的数量。

要做到这一点,通过确保应用中包含的任何大型库的使用方式超过添加到应用中的代码量,来优化应用的直接和可传递依赖关系。

开发人员有时会犯的一个错误是,在很少使用一些实用方法的情况下包含大型库。减少 Java 代码依赖性通常可以避免这 64 KB。dex 方法参考限制。

您也可以使用 ProGuard 删除未使用的代码。为此,请为运行 ProGuard 的应用配置 ProGuard 设置,并确保您已经为您的 APK 版本 Gradle builds 启用了收缩。启用收缩将确保 APK 中不包含任何未使用的 Java 类或方法。

使用这些优化技术可能有助于避免在应用中允许更多方法引用所需的构建配置更改。优化还可以减小 APK 的大小。这对于那些带宽成本高的市场来说尤其重要,因为这样可以降低应用的运行成本!

配置梯度构建:创建不同的 APK 类型

Gradle build 将生成一个特定的。应用/构建文件夹中每个构建变体的 apk 文件。一般来说,这将是一个调试变体,用于您的测试工作流程,以及一个发布变体,用于将您的应用发布到谷歌 Play 商店进行分发。

您可以为您的 Wear 应用发布工作创建您需要的任何其他“风格”的应用。例如,您可以在一个 /src/demo 文件夹中创建一个“有限功能演示”版本,并指示 Gradle 使用该文件夹的内容为您创建一个pro watch face-demo-release . apk文件。

生成到 app/build/outputs/apk/ 目录中的 apk 文件将包含使用以下 APK 文件命名约定命名的包:

<application name>-<flavor>-<buildtype>.apk

例如,ProWatchFace-full-release.apk 将是您的发布 apk,或者 ProWatchFace-full-debug.apk 将是您的完整 WatchFace 应用演示版本的调试版本。

接下来,在我开始讨论 ProGuard 和 Gradle build 系统之前,让我们检查一下,确保您有最新版本的 Android Studio。在你这样做之后,你实际上可以使用 Android Studio 来生成一个构建,你可以开始查看构建和测试你的 Watch Face 应用的过程。

启动 Android Studio,使用帮助image检查更新菜单序列,确保没有新的 Android 组件已经发布。希望你会看到“你已经安装了最新版本的 Android Studio”对话框,如图图 15-24 所示,以及帮助image检查更新菜单序列。如果需要,请在继续之前更新 Android Studio!

9781430265504_Fig15-24.jpg

图 15-24 。在开始构建、调试和测试阶段之前,总是使用 IDE 的检查来更新特性

接下来,让我们仔细看看移动模块(组件)的 Gradle build 配置文件,并了解 build.gradle 文件中的其他部分(除了 Android 和依赖部分,我已经详细介绍过了)。

配置 构建??:解剖一个 构建.配置

首先,让我们深入了解一下应用的移动模块的 gradle.build 文件。打开你的 app 的 Gradle Scripts 部分,右击build . Gradle(Module:mobile),选择 Jump to Source (或按 F4 功能键),在编辑页签中打开该 Gradle script,如图图 15-25 所示。

9781430265504_Fig15-25.jpg

图 15-25 。打开移动模块 build.gradle 文件,并使用“立即同步”功能确保它是最新的

如果在 build.gradle 编辑窗格的顶部出现黄色的“Gradle 文件已更改”消息,请单击右侧的立即同步链接选项。这将同步您的项目,您总是需要在构建之前这样做。

第一个 apply 插件语句允许构建配置的 android 部分引用 Google 的 Android Maven 存储库中的 com.android.application 定义。这允许文件的 android {…}部分存在(也就是说,有意义),并因此被正确解析。

正如你已经知道的那样, android 部分定义了你的版本和包命名约定,而你的依赖项部分引用了外部 API 库,Gradle 需要将这些 API 库引入到你的应用编译过程中。请注意,有一个 wearApp 项目条目,它连接应用的 wear 模块,还有一个编译文件树条目,它将包括您可能放在项目的/ExternalJarFiles 目录中的任何第三方 JAR 文件(即物理或 3D 渲染库)。

在构建时,buildTypes部分是主要关注的内容。图 15-25 中的 buildTypes部分显示了 build types 部分的嵌套发布 子部分。如果您想知道 build.gradle 文件使用的是哪种编码格式,它是一种叫做 Groovy 的编程语言。

您可能想知道为什么没有 debug子部分,因为总是会生成两种主要的构建类型。这是因为调试构建规范是自动生成的(默认),而不必实现显式的构建配置规范。也就是说,如果您愿意,可以使用 debug子部分。

例如,您可以添加“.”的应用 id 后缀。debug”来进一步确定您的调试文件版本,如果您愿意,可以使用下面的 Groovy 代码:

debug {
       applicationIdSuffix ".debug"
      }

buildTypes部分的 release部分将优化您的。APK 文件发布版,使用了 ProGuard 工具和 minification 标志,通过使用下面的 Groovy 代码,其中没有使用分号字符来终止其语句!这可以在图 15-25 的中看到:

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

接下来,让我们来详细了解一下 ProGuard 工具和 minification 标志,这样您就可以确切地知道它们是什么,以及为什么要在您的发布 APK 文件构建中使用它们。

使用 程序 :压缩、优化、混淆代码

ProGuard 工具优化了您的代码,使其更加紧凑,同时也使代码更难逆向工程,这一过程称为“混淆 ”它通过移除未使用的代码并使用晦涩的名称重命名类、字段和方法来实现这一点,从而使代码难以逆向工程。

结果是一个更紧凑的版本。apk 文件,逆向工程难度更大。因为 ProGuard 使您的应用更难被逆向工程,所以当应用利用对安全性敏感的特性时,例如当您正在许可一个应用或当它包含专有或有价值的 IP 时,实现 ProGuard 是很重要的。

ProGuard 被集成到 Android build 系统中,如图图 15-25 所示,这样你就不用手动编码调用来实现它,使用 Groovy。只有在发布模式下构建应用时,才应该调用 ProGuard。这样,当您使用调试模式构建应用时,就不必使用混乱的代码进行调试,至少可以说这是令人困惑的!

运行 ProGuard 进程是可选的,但是强烈建议这样做。要打开 ProGuard,将 minifyEnabled 标志设置为等于 true 值,要禁用(关闭)ProGuard,将 minifyEnabled 标志设置为 false。此 minifyEnabled 属性是 release子部分中 buildTypes部分的一部分,它控制应用于发布版本的设置。当您准备好压缩(压缩)和混淆 Java 代码时,将 minifyEnabled 属性设置为 true 以启用 ProGuard。

需要注意的是,尽管 Android 希望您使用 ProGuard,这也是 Android Studio 为您安装这两个 Groovy 语句的原因,但 Android 通过将 minifyEnabled 设置为 false 值来停止为您实现这两个语句,这在一开始就关闭了 ProGuard。

getDefaultProguardFile( ) 方法 设置一个“ProGuard-Android . txt”ProGuard 配置定义,在图 15-25 中高亮显示。这是你默认的 ProGuard 配置设置,这个文件可以在 Android/tools/ProGuard/文件夹中找到。

还有一个proguard-Android-optimize . txt文件,在 Android**/tools/proguard/**文件夹中也有。该文件实现了相同的规则,但是它也支持“字节码级别”的优化。这些优化将在 Java 字节码级别执行额外的优化分析。这是在方法内部和方法之间完成的,当 Android RunTime (ART)引擎以 Java 字节码的形式运行应用时,这有助于使应用更小、运行更快。

最后,还要注意在图 15-25 中突出显示的 Groovy 代码行中,Android Studio 在 proguardFiles 条目的末尾使用了一个逗号,并在该模块的根目录添加了**‘proguard-rules . pro’**文件引用。这样,如果需要,您可以轻松地添加您自己的、定制的 ProGuard 规则,这些规则应该是特定于发布模块的,您可以将它们添加到该文件中,这样它们将确保生效。

接下来,让我们看看你的**build . grad le(Module:wear)**并确保它在你构建应用之前不需要同步。

右键单击build . gradle(Module:mobile),选择跳转到源代码(或按 F4 功能键),在编辑页签中打开该 Gradle 脚本,如图图 15-26 所示。如果在 build.gradle 编辑窗格的顶部出现黄色的“Gradle 文件已更改”消息,请单击立即同步

9781430265504_Fig15-26.jpg

图 15-26 。打开磨损模块的 build.gradle 文件,并使用“立即同步”链接同步到您的项目

现在是时候看看 Gradle 的构建过程并创建调试 APK 了,这样你就可以开始漫长的测试、调试和发布过程,这将贯穿本章的剩余部分以及下一章。

构建你的项目:使用 Gradle 构建引擎

虽然您可以使用 AVD 模拟器在内部测试项目组件,但是为了在下一个级别真正测试您的项目组件,您需要将它们“构建”到 Android 包中,或者。简称 APK,文件格式。这种格式也可以用 AVD 来测试。更重要的是,它可以在现实世界的硬件上进行测试,通常是在应用开发过程的最后。

运行 Make 项目工具 :使用 Gradle Build 控制台

在 Android Studio 中打开你的项目,使用 Build image Make Project 菜单序列,调用 Gradle build 过程,如图图 15-27 所示。

9781430265504_Fig15-27.jpg

图 15-27 。使用 Build image Make Project 为 ProAndroidWearable 项目创建 Android 包 APK 文件

正如你在图 15-27 所示的菜单中看到的,你可以只构建移动模块,但是因为这是一本穿戴书籍,你将构建项目,该项目将构建移动(电话)和穿戴(智能手表)组件。

要查看 Gradle 在项目的移动和穿戴部分的构建过程中做了什么,请单击位于 Android Studio 右下角的 Gradle 控制台选项卡。正如你在图 15-28 中看到的,我不得不使用两个屏幕(在一个图中)来显示 24 个移动构建步骤和 24 个磨损构建步骤,从而在 29 秒内成功构建了 APK。

9781430265504_Fig15-28.jpg

图 15-28 。单击右下角的 Gradle 控制台选项卡,查看 Gradle 正在执行的构建任务

既然您已经构建了项目,下一步就是运行项目模块,通过 USB 连接的 Android 硬件设备来查看它们是如何工作的。

运行您的 APK:使用一个 USB 设备目标设备设置

当您将一个模块作为 Android 应用(APK)运行时,Android Studio 会自动创建您的运行配置。默认运行配置将启动默认项目活动,并使用自动目标模式进行设备选择。自动目标设备模式将选择仿真器,选择(首选)AVDs 的(空下拉菜单区域)。

一个手动目标设备模式是你在本书中迄今为止一直使用的,在这里你选择了 Nexus 或 Wear 你想要启动的 AVDs。现在您将在运行/调试配置对话框中为常规选项卡的目标设备部分选择 USB 设备选项,如图图 15-29 所示(左侧为移动模块,右侧为磨损模块)。

9781430265504_Fig15-29.jpg

图 15-29 。使用运行/调试配置对话框设置移动和磨损模块使用 USB 设备

我必须通过 USB 重新建立硬件接口,所以我在图 15-30 中包含了我的 DOS 命令提示符窗口,以向您展示我如何使用 adb 设备来查看是否有设备连接,以及一旦我看到设备,我如何使用 forward 和 connect 命令来访问智能手机和智能手表。

9781430265504_Fig15-30.jpg

图 15-30 。使用 ADB 命令设置 USB 智能手机连接和智能手表蓝牙

设置好之后,我使用运行项目(不是运行 wear)来运行移动模块和磨损模块,这样我可以一起测试它们。我在我的设备上收到了允许谷歌检查设备病毒Wear 未连接的消息,尽管 Wear 应用正在运行。我在 LogCat 窗格中得到一些错误和警告,如图 15-31 所示。

9781430265504_Fig15-31.jpg

图 15-31 。设备和 LogCat 窗格将在 Gradle 控制台旁边打开,显示警告和错误

我首先使用谷歌搜索解决了红色的错误消息,看看是否有其他开发人员得到了这个senduractionevent()mView = = null错误消息。原来这是三星硬件的问题,因为我目前正在使用 Galaxy Note 3,所以我排除了这个问题,我需要在我的代码库中寻找解决方案。

我想解决另一个警告,即没有 epolicy 文件来找出什么是 epolicy 文件,所以我也在谷歌上搜索了一下,发现这个错误与运行多个构建会话有关,与智能手表上没有手表表面无关。

在这种情况下,接下来要尝试的是通过 USB 电缆直接连接智能手表,这样智能手机和智能手表都可以直接连接,并且可以关闭蓝牙调试(这比较慢,可能也是问题所在),从而消除一层复杂性。需要注意的是,没有 USB 端口的智能手表(如 MOTO360)必须使用蓝牙调试,这也是我详细介绍它的原因。

硬连线 USB 连接 :设置智能手表 USB 驱动

因为我找不到专用的索尼智能手表 3 USB 驱动程序,这将是一个很好的机会,向您展示如何使用谷歌 USB 驱动程序,您使用 Android SDK 管理器安装该驱动程序来完成连接。

因为你已经安装了谷歌 USB 驱动,你唯一需要做的就是在你的AppData/Local/Android/SDK文件夹中找到它。它不在/platform-tools 文件夹中,而是在一个不同的目录中,名为**/extras/Google/USB _ driver**,名为 android_winusb

对智能手机 USB 驱动程序执行与之前相同的步骤,这可以在图 15-8 到 15-10 中看到,除了这一次,指定您刚刚找到的谷歌 Windows USB 驱动程序。需要注意的是,如果你使用的是 Linux 或者 Macintosh,你不需要安装任何 USB 驱动。

现在 adb 命令序列变得更容易,因为你不必使用 adb 转发和 ADB 连接蓝牙序列;您只需使用 adb devices 命令来查看硬件设备是否已连接。您也不需要ADB-d forward TCP:5601 TCP:5601命令来启用通过智能手机到智能手表的端口转发。

正如你在图 15-32 中看到的,新的 ADB 命令行序列要短得多,因为你不需要设置一个蓝牙端口,然后连接到它。

9781430265504_Fig15-32.jpg

图 15-32 。当两个硬件设备都通过 USB 连接时,ADB 命令行序列要短得多

当你进入 Android Studio 并查看 Gradle 控制台窗格旁边的 Android 窗格时,你会看到现在有两个硬件设备,如图 15-33 中屏幕左侧突出显示的下拉菜单所示。

9781430265504_Fig15-33.jpg

图 15-33 。在 Android 选项卡中,在 IntelliJ 的底部,您会在下拉菜单中找到这两种硬件设备

当您使用 Run image项目、Run image Wear 或者 Run image Mobile sequence 时,会弹出选择 设备对话框,如图图 15-34 所示,这样您就可以选择要将该构建推送到的硬件设备。

9781430265504_Fig15-34.jpg

图 15-34 。当您选择运行image wear 或运行image mobile 时,您会看到一个选择设备对话框,其中列出了硬件

然而,尽管我的配套应用在智能手机上运行良好,但我的表盘组件却没有在智能手表上显示出来。心血来潮,查了一下更新,得知 Android 5.2 刚刚发布。

事实证明,5.2 版本是一个重要的版本,对 API 进行了重大的修改。这影响了 ProWatchFaceService 类和我正在使用的方法。所以我决定在这里结束这一章,并添加另一章关于如何处理这些重大的操作系统更新变化,这样你就可以看到整个过程,以防它发生在你身上,这总是会发生的,因为 Android 是一个“移动的目标”。

摘要

在本章中,您学习了如何让您的电脑、智能手机和智能手表硬件协同工作,在软件开发环境(Android Studio)和您的硬件设备环境之间架起桥梁

这包括获得一个制造商的 USB 驱动程序软件,并学习如何使用谷歌的 USB 驱动程序,如果你需要让你的 Android 智能手表设备与电脑接口。

你学会了如何确保在你的手机上安装一个 Google Wear 应用,以及如何配置它用于配对调试目的。

您学习了Android Debug Bridge(ADB)命令行工具,以及如何使用 DOS 命令提示符来访问它,以便在您的计算机、USB 或蓝牙连接设备之间建立通信端口。然后,您开始使用 Android Studio IDE 来识别和构建您的硬件设备,使用 IntelliJ 及其 Gradle 构建系统中的各种窗格和对话框功能。

在下一章中,你将升级到 Android 5.2,从你的 BitmapDrawable 代码中取出一个不推荐使用的方法调用,并学习在 Android 中开发如何成为一个移动的目标,以及如何最好地处理这个问题!

十六、Wear API 弃用:更新应用以使用新的类或方法

在第十五章中,你设置了硬件设备,这样它们就可以通过使用 Android 调试桥与 Android Studio 对话。然后,您尝试让 wear 组件显示在智能手表上,以便测试配置伴侣活动。我在工作过程中尝试的一件事就是检查 Android Studio 和 Android SDK 的更新。

有一个重要的操作系统更新,Android Studio 1.2(Android 5.2),已经发布了!所以我决定将这个更新和不推荐的代码更新的过程放在它自己的一章中。本章将介绍更新该项目的 wear 组件的过程,该组件目前尚未在智能手表设备硬件上按预期运行。我将介绍整个过程,从处理意外的重大 SDK 升级到重新编码任何不赞成使用的类或方法。在这种情况下,这些将涉及 ProWatchFaceService.java 类。

因此,在本章中,您将了解更多关于日历公历日历类的信息,它们将用于取代 Android 5.2 中不推荐使用的时间类。您还将实现一个不同版本的**。getDrawable( )** 方法,作为该方法调用的两个版本之一,在本次 Android 5.2 API (SDK)升级中被弃用。

当我完成代码升级时,一个Android Studio 1.2.1.1升级也发布了,这给 Gradle build 系统与 IntelliJ (Android Studio)的集成带来了一些问题。所以我会解释更多关于 Gradle 和 IntelliJ 的内容来帮助你学习如何解决这个问题。

应对突发更新:Android 5.2

当我检查更新时,我得到了一系列对话框,如图图 16-1 所示。然后我继续绕过如图图 16-1 所示的对话框来更新 Android。我点击了更新和重启按钮,我得到了下载补丁文件更新进度条对话框,最后,我选择了我想从以前版本导入我的设置单选按钮,和确定按钮来完成这个过程。几个星期后,1.2.1 出来了,一周后又是 1.2.2,所以我不得不从头再做一遍!

9781430265504_Fig16-01.jpg

图 16-1 。更新对话框,显示一个主要的 71MB 更新可用于更新到 Android Studio 1.2

正如你在图 16-2 中看到的,这次更新是一次重大更新,需要安装 28 个 API SDK 包。我点击了安装 28 包按钮,并更新。

9781430265504_Fig16-02.jpg

图 16-2 。此安装会自动选择 28 个 SDK 包、库和系统映像来安装或更新

Android SDK 管理器日志显示了 Android SDK 工具修订版 24.1.2 和 Android SDK 平台工具修订版 22 的下载和安装,如您所知,其中包括 ADB 服务器和工具。如果 ADB 服务器正在运行,安装程序可以停止它,它确实停止了,然后安装程序试图重命名当前的**/平台工具**文件夹以便备份它。这失败了,因为我打开了一个命令提示符,所以安装程序停止并产生一个错误信息,在图 16-3 中显示为红色。

9781430265504_Fig16-03.jpg

图 16-3 。为了继续安装,我必须关闭命令提示符工具

一旦我关闭命令提示符,安装就完成了,如图图 16-4 所示。

9781430265504_Fig16-04.jpg

图 16-4 。选择最新的 Android SDK 构建工具;更新英特尔 x86 仿真器,看看它是否能在 AMD 上工作

如你所见,最新版本的 Android Build Tools (22.0.1)没有被选择安装。我也选择了这一点,因为我试图解决我的项目的手表表面(佩戴)侧不在智能手表上显示的问题,解决方案可能在这个新的 Build Tools 22 代码中。

我还选择了英特尔 x86 模拟器加速器(HAXM)的更新,以查看是否添加了对 AMD64 系列处理器的支持。接下来,我单击了 Install 2 packages 按钮,安装了这些在第一次操作系统平台更新时没有安装的软件包,为了在 IDE 中安装所有需要的东西,您经常需要这样做。

现在我已经更新到了 Android 的下一个主要版本(5.2),是时候看看 ProWatchFaceService.java 文件(类)了,看看代码中是否有任何东西可能会阻止应用的手表表面(佩戴)侧在智能手表设备硬件上运行。

处理折旧:。getDrawable()和时间

正如你在图 16-5 中看到的,现在你的代码中有了在最新版本更新之前没有的亮点。被弃用的代码被排了出来(这使用了删除线字体),这可能有些令人不安,但它直观地通知开发者,他们正在使用的代码是安卓 5.x 平台不再支持的**,这就是被弃用的意思。你可以将鼠标放在突出显示的代码上,你会得到一个弹出提示,告诉你这个**。getDrawable(int)** 方法调用已被否决。这意味着你需要到开发者的网站上查看 API。**

**9781430265504_Fig16-05.jpg

图 16-5 。打开在智能手表上不工作的 ProWatchFaceService 类来查看代码

你可以在 Gradle 控制台中看到颜色突出的注释,显示在图 16-5 的底部,它建议你如果想要使用不推荐使用的代码,你需要使用名为 -Xlint:deprecation 的编译开关。我想在这本书里使用 100%干净的(无 bug 和最新的)代码,所以我将在这里采取更困难的方法,重新编写 Java 代码。

Android 5.2 (Android Studio 1.2)中有两个主要的弃用之处会影响到你一直在写的代码。一个是时间类,另一个是。getDrawable(int)方法。在本章的这一节,我将讨论可以用来代替你一直在使用的不赞成使用的类和方法的类和方法。值得注意的是,您仍然可以使用您在本书前面添加的代码,但是,随着时间的推移,不推荐使用的代码最终会变成停止使用的代码,所以最好立即升级(替换)不推荐使用的 Java 语句!

Android 的资源类:两个。getDrawable()方法

我已经解释了 Android Resources 类,所以我在这里不再重复。我将只介绍适用于必要的 Java 代码升级的内容,这将消除 IntelliJ (Android Studio 1.2)中删除的代码。

为了升级已弃用的。getDrawable(int)方法调用,你将需要在开发者的网站上查看 Android 的 Resources 类。你需要这样做来确定是否有这些的其他版本。getDrawable( ) 方法调用将允许您从/res/drawable-hdpi/文件夹中检索背景图像可绘制素材。可以在以下 URL 找到资源类技术信息页面:

http://developer.android.com/reference/android/content/res/Resources.html

注意有两个。Resources 类中列出的 getDrawable()方法,其中一个现在已被否决,所以您必须使用较新的方法,这是在线文档中建议的。关于这些的技术信息。在线的 getDrawable()方法结构应该是这样的:

Drawable       getDrawable(int id)
This method was deprecated in API level 22. Use getDrawable(int, Theme) instead.

Drawable       getDrawable(int id, Resources.Theme theme)
Return Drawable object associated with a particular resource ID and styled for the specified theme.

因此,您对此 Android 5.2 升级弃用的解决方案是使用不同版本的。getDrawable(),它使用了一个资源。主题对象名为主题。如果你想继续使用你当前指定的主题,使用一个空值**。让我们来看看参考资料。下一个主题嵌套类。**

资源。主题嵌套类:主题属性

Resources 类有一个嵌套的公共最终资源。 主题 类,该类扩展了 java.lang.Object 主类,具有以下层次结构:

java.lang.Object
  > android.content.res.Resources.Theme

这个嵌套的助手类被创建来保存特定主题对象定义的当前属性值。众所周知,主题是一组使用资源属性定义用户界面外观和感觉的值。

主题对象封装了一个保存这些主题属性的 TypedArray 对象。使用 int[ ] 数组和 AttributeSet ,可以利用 TypedArray 来解析属性的最终值。你通常会使用**。获取样式属性(AttributeSet,int[ ],int,int)** API 来检索 XML 定义的主题属性,并使用父用户界面和子用户界面属性定义标签应用样式和主题信息。Wear 应用通常不会改变主题,至少你正在编写的 Watch Face 应用不会。

当您想要保留一个默认的或者当前的主题时,您将为资源使用一个空值。. getDrawable()方法调用中的主题对象。

主题对象的属性以两种方式发挥作用。第一种是通过 styled 属性,它可以通过在 XML 标记参数中使用themeParameterName="?themeAttribute"语法显式引用主题中的任何值。

引用主题对象属性的第二种方法是在 Java 代码中使用**。获取样式属性(AttributeSet,int[ ],int,int)** API 方法调用来检索 XML 定义的主题属性。如果想保持的向后兼容性,还有一个 ResourcesCompat 类。

接下来让我们看看 ResourcesCompat 类,因为添加向后兼容特性可以防止这种情况再次发生。这个类也有一个. getDrawable()方法,允许你在一个方法调用中传递一个资源 R 引用、一个可绘制索引整数和一个主题

ResourcesCompat 类:向后兼容性

Android 的public****Resources compatclass 也扩展了 java.lang.Object,因此被临时编码以提供资源的向后兼容性。该类的 Java 类层次结构类似于下面的代码:

java.lang.Object
  > android.support.v4.content.res.ResourcesCompat

这个类是作为一个助手类创建的,开发人员可以利用它来访问 API level 4 之后引入的资源中的特性。这是以一种向后兼容的方式完成的,因此 Android 开发者可以避免在这本书的过程中发生的事情。你不需要在 wear 开发中使用它,因为 Wear 是在 4.4 中引入的,并且正在快速发展到主要利用 5.0 到 5.2(到目前为止)。

这个类有一个公共构造函数方法, ResourcesCompat( ) ,用于创建一个对象。这个类中还有两个公共方法调用。一个是**。getDrawable(Resources res,int id,Resources。Theme theme)** ,返回与引用的资源 ID 相关联的可绘制对象,并将使用指定的主题对象进行样式化。

另一个是getDrawableForDensity(Resources RES,int id,int density,Resources。主题主题)方法调用。这可用于访问不同密度的可提取素材。大多数智能手表产品的像素范围是 320 到 400 像素(我希望到 2016 年可以增加到 480 到 640 像素),所以你现在不需要使用这个版本的方法调用,它为密度(文件夹)添加了一个变量来访问可绘制的素材。

因为 Resources 类是 Wear 应用中更常用的标准类,也是您已经在使用的类,所以我将使用它。接下来我们来看看另一个主要的弃用,整整一节课!

处理废弃的类:时间类

您可能还注意到,在 ProWatchFaceService 类的顶部,Time 类(对象)引用在代码中被删除了,这意味着它也不被推荐使用。这个类仍然可以使用,至少到 2038 年,当它覆盖的日期(时间)范围用完时。这很可能是这个类被弃用的主要原因,尽管 Android 开发者的网站上没有给出具体的原因。

这是这次 Android 5.2 更新中的另一个主要反对意见,它影响了 ProWatchFaceService 类的 Java 代码,这意味着您必须重新编码来解决这个问题。

这一次,需要使用日历公历类或它们的组合来替换整个类,即 time 类。您很快就会了解到,这些类是紧密相关的,并且与 Android 的日期时区类相关。

Android Calendar 类是一个抽象类,用于创建 GregorianCalendar 具体类,这个 Calendar 抽象类和 GregorianCalendar 具体类可以访问(利用)日期和时区对象。您将直接使用 Calendar 类(对象)和。getInstance()方法调用,您将在接下来学习它。

日历类:日期和时间的抽象类

Android 公共 抽象日历类 扩展了 java.lang.Object,实现了可序列化可克隆可比<日历> Java 接口。这个日历类的 Java 类层次结构如下所示:

java.lang.Object
  > java.util.Calendar

Calendar 类有一个已知的直接子类, GregorianCalendar ,我将在下一节介绍它。Calendar 类是一个抽象类,用于创建 GregorianCalendar 类。它提供了方法调用,可用于从一个日期对象和该日期对象包含的一组整数字段中提取数据。

其中包括年、月、日、小时、秒、分钟和毫秒。Date 对象表示时间中的一个瞬间,如果需要的话,它可以使用毫秒级的精度。您很快就会在新的代码库中使用这种数据提取功能,所以我将在这里详细介绍这个类及其相关方法。

与其他 Android 5 位置敏感类类似,这个 Calendar 类提供了一个名为的方法调用。getInstance( ) 。这可用于创建此日历类的实例以供一般用途。getInstance()方法将返回一个 Calendar 对象,其位置的微调基于应用用户的系统设置。该对象的时间数据字段将使用用户的当前系统日期系统时间进行初始化。

对象声明和实例化可以在一个统一的语句中完成,这可以通过使用下面的 Java 语句来完成:

Calendar calendar = Calendar.getInstance(); // Declare a Calendar object named calendar and load it

您将在新代码中使用一个 Calendar 对象,使用下面的 Java 代码会有所不同:

Calendar watchFaceTime = Calendar.getInstance(); // Create/Load Calendar object named watchFaceTime

然后,您将使用 Calendar 访问日历对象的属性。通过属性常量。get() 方法调用,使用以下 Java 语句:

int hour   = watchFaceTime.get(Calendar.HOUR);
int minute = watchFaceTime.get(Calendar.MINUTE);
int second = watchFaceTime.get(Calendar.SECOND);

日历对象可以定义为宽松非宽松。宽松的日历对象接受的字段值范围比它生成的范围更广。例如,如果您使用宽松的 GregorianCalendar 对象,如果您使用 MONTH == APRIL,DAY_OF_MONTH == 31,它会将这一天解释为 5 月 1 日。

在这种情况下,非宽松的 GregorianCalendar 会抛出一个异常,原因是试图设置一个超出范围的数据字段值。当 Calendar 对象作为. get()方法调用的结果重新计算要返回的字段值时,您的 Calendar 对象将“规范化”它。例如,Android GregorianCalendar 将始终生成一个介于 1 和当前月份长度之间的 DAY_OF_MONTH 值,同时考虑闰年规则。Calendar 对象使用两个参数定义特定于位置的每周七天。第一个是一周的第一天,第二个是第一周的“最小天数”(1 到 7)。这些数字是在实例化日历对象时从位置资源数据中获取的。

位置特定的数据参数也可以由 Android 开发者通过利用该日历 API 中的方法来明确指定。如果您想更深入地了解日历对象(类)的所有细节,请使用以下 URL 阅读 Android 开发者页面:

http://developer.android.com/reference/java/util/Calendar.html

我想指出的最后一点是,这个 Calendar 类有两个构造函数方法: Calendar( )Calendar(时区区域设置)。如果抽象类不能实例化,为什么它会有构造函数方法?抽象类不仅可以有一个构造函数方法,而且它总是会有一个。如果你不指定一个构造函数,这个类将会有一个默认的,没有参数的对象构造函数,就像其他的类类型一样,比如嵌套类和匿名类。在匿名类的情况下,不可能指定一个构造函数,所以你总是得到默认的构造函数。构造函数方法总是使用子类来访问,所以您不能说Calendar calendar = new Calendar();,但是您可以通过使用Calendar calendar = Calendar.getInstance(); Java 语句来创建一个日历对象。

这个 Calendar 类是一个拥有构造函数方法的抽象类的优秀例子。您将通过调用 Calendar.getInstance()来创建您的 Calendar 对象,但是 Calendar 具有受保护的构造函数。

日历构造函数受到保护(不是私有的)的原因是它可以被子类扩展,比如 GregorianCalendar。因为 Calendar 的构造函数是受保护的,所以只有它们包中的其他类可以扩展 Calendar。从用于 Android 5 的 Java 7 开始,GregorianCalendar 类是唯一可以访问这两个日历构造函数的子类。

接下来,是时候仔细看看 Calendar 类的 GregorianCalendar 子类,以便您对这两个可行的 Calendar 类有完整的了解。您将在新代码中使用顶级 Calendar 类来消除 ProWatchFaceService 中不推荐使用的代码,但是,建议使用 GregorianCalendar 类(object)来代替 Time 类(object ),因此我将包含下一节以提供完整的主题范围。这样,您将对可以在应用中使用的两个 Android (Java 7)日历工具类有一个全面的了解。

GregorianCalendar 类:日期和时间的具体类

public****Gregorian Calendarclass 是 Calendar 的一个具体子类,它提供了一个在世界上大多数地方使用的标准日历表示。它包含在一个 java.util 包中,因此它的 java 类层次结构如下所示:

java.lang.Object
  > java.util.Calendar
    > java.util.GregorianCalendar

如果您想知道什么是具体类,它与抽象类有什么不同,那么将使用 Java new 关键字和构造函数方法实例化具体类,而抽象类不能使用 Java new (instance)关键字实例化,正如您现在所知道的。

我在 2014 ApressBeginning Java 8 Games Development标题中使用抽象类来创建游戏角色类,如果你想更详细地回顾这个概念的话。在 Java 中,具体的类被设计成子类,就像抽象类一样,但是它也可以被实例化,不像抽象类,它不能直接在你编译的(最终的)代码库中使用。

标准的公历支持两个“纪元”,公元前和公元。Android 实现处理单个不连续性,默认情况下,在大多数国家,该不连续性对应于公历的实施日期,即 1582 年 10 月 15 日,尽管在其他国家这可能会更晚。在 Android 中,开发者可以通过调用来更改这个“交接日期”。setGregorianChange( )** 方法。在最先采用公历的国家,1582 年 10 月 4 日将是 1582 年 10 月 15 日。因此,这个 Calendar 子类将正确地模拟这个日期异常。**

在这个公历切换日期之前,Android GregorianCalendar 类将实现儒略历日历。公历和儒略历的区别在于其闰年规则**。无论如何,儒略历每四年都会指定一个闰年。

对于那些不能被 400 整除的世纪年,公历会忽略每个偶数世纪年的闰年。

Android GregorianCalendar 类同时实现了公历和儒略历。通过使用指定的截止日期向前和向后推断当前规则来计算日期。

由于这个事实,GregorianCalendar 类可以用于所有年份,为 watch faces 应用生成有意义且一致的日期结果,还可以用于访问系统时间,精确到毫秒。

通过访问 GregorianCalendar 对象获得的日期只有从公元 4 年 3 月 1 日起才是“历史上准确的”。这是现代儒略历规则被采用的时候。在此日期之前,闰年规则是不定期适用的。请注意,在公元前 45 年之前,儒略历并不存在。在公历诞生之前,元旦是 3 月 25 日!

为了避免混淆,Calendar 子类将总是使用 1 月 1 日。如果需要,开发人员可以对公历 1 月 1 日到 3 月 24 日之间的日期进行手动调整。

GregorianCalendar 类从其 Calendar 超类中访问数据字段(属性或特性),我将在下面讨论。它们包括常量名称,如月、日、周、小时、小时、分钟、秒、毫秒、日、日、时、年、年、月、周、月。

从某种意义上说,您可以利用这种弃用,从使用 Time 类到使用 Calendar 类中的一个。通过一个日历对象访问系统时间,你将在基础设施中允许在你的表盘设计中显示一个很酷的日期,如果需要的话。

现在是时候开始在 ProWatchFaceService Java 代码中使用 Calendar 类了。

升级您的代码:日历和。getDrawable( )

要去掉删除线字体(删除代码),需要用 Calendar.getInstance()对象替换时间对象实例化,并升级。getDrawable(int)与。getDrawable(int,Theme)方法调用。

您将首先进行类替换,因为这是主要的更改,然后向。getDrawable()方法调用。这将与您之前的方法调用做同样的事情,但是它不会生成不推荐使用的代码突出显示。如您所知,null 告诉这个方法使用当前的主题对象定义。您在前面使用 XML 定义对此进行了定义。

升级时间类代码:使用日历类

在图 16-6 中可以看到,Android 时间类已经停止使用(已弃用),在 API 等级 22。首先,您需要将鼠标悬停在上面,看看是否有任何关于如何使您的 Java 代码完全更新的建议。正如你所看到的,在图 16-6 中,即使你点击了更多链接,建议工具提示中也没有太多的信息,这与弹出的黄色工具提示陈述的内容完全相同。这意味着你必须查看你的 Android 时间类文档。

9781430265504_Fig16-06.jpg

图 16-6 。您可以在私有引擎类的顶部看到一个不推荐使用的时间类声明(引用)

Time watchFaceTime; Java 对象声明语句改为使用 Calendar 类。这将使它成为一个Calendar watchFaceTime; Java 语句。

正如你所看到的,在图 16-7 中间突出显示的,你将需要使用 Alt+Enter 击键工作过程,并让 Android Studio 在你的 ProWatchFaceService 类的顶部为你编写导入语句。请确保删除时间类导入语句。

9781430265504_Fig16-07.jpg

图 16-7 。在 watchFaceTime 声明中将 Time 类引用更改为 Calendar 类引用

接下来您需要做的是在 onCreate()方法中加载这个 Calendar 对象,使用您在上一节中学到的 Calendar.getInstance()结构。使用下面的 Java 语句,可以看到它在图 16-8 的中间以淡黄色突出显示:

watchFaceTime = Calendar.getInstance();

9781430265504_Fig16-08.jpg

图 16-8 。在 onCreate()方法内将 watchFaceTime 日历对象设置为 Calendar.getInstance()

这将创建一个 watchFaceTime 日历对象,并加载一个有效的日历对象,在本例中,它将是一个 GregorianCalendar 子类。

通过这种方式,您可以设置您的代码,这样您就不必将Gregorian Calendar 转换为 Calendar,反之亦然,您可以在代码中使用 Calendar。因为 GregorianCalendar 是具体的类,所以这就是将要创建的内容(因为 Calendar 是静态类),这也是 Time 类中的一个建议用来替换 Time 的内容。

现在已经有了替换 watchFaceTime 时间对象的 watchFaceTime GregorianCalendar 对象,您需要更改。clear()方法调用和。setToNow()方法调用 Time 类,一些方法调用使用 GregorianCalendar 和 Calendar 类。其中包括一个. setTimeInMillis()方法调用、一个. get()方法调用和一个. setTimeZone()方法调用。现在让我们为时区接收器广播接收器这样做。

升级时区接收器。setTimeZone()方法

单击左边的加号(+)图标,打开您的 timeZoneReceiver 对象 结构。注释掉。清除( )和。setToNow()方法调用结构,如图图 16-9 所示,这样你就可以在以后使用不推荐使用的代码(如果你想使用的话,Time 类仍然可以工作)。

9781430265504_Fig16-09.jpg

图 16-9 。在 BroadcastReceiver onReceive()方法中,使用。setTimeZone()方法,而不是。清除()

要更换这些,请使用**。setTimeZone( )** 方法,调用时区类**。getDefault( )** 方法,来替换.clear(intent.getStringExtra("time-zone")然后调用 invalidate( ) 方法来更新表盘时间 onDraw 逻辑。这将通过使用以下更新的 Java 结构来完成:

public void onReceive(Context context, Intent intent) {
    watchFaceTime.setTimeZone( TimeZone.getDefault() );
    invalidate();
}

接下来,让我们升级 onDraw()方法中的代码,因为这是下一个最重要的代码。使用新的日历,您将在方法的顶部获得当前时间。setTimeInMillis()方法调用。

升级 onDraw()方法:使用。setTimeInMillis( )

首先,让我们注释掉super.onDraw(watchface, rect);,因为您要完全替换 onDraw()逻辑,以及您的watchFaceTime.setToNow( ); Java 语句,因为您要用一个不同的 Calendar 类方法调用来替换它。键入 watchFaceTime GregorianCalendar 对象并按下句点键,然后开始键入”。setTi”并双击.setTimeInMillis(long milliseconds)选项,如图 16-10 中的下拉方法选择帮助器所示。

9781430265504_Fig16-10.jpg

图 16-10 。在 onDraw()方法中,替换。带有. setTimeInMillis()方法调用的 setToNow()方法调用

在方法参数区内,输入系统类和句点,然后双击 currentTimeMillis( ) 选项,将其插入到您的 Java 语句中,如图图 16-11 所示。生成的 Java 语句应该如下所示:

watchFaceTime.setTimeInMillis(System.currentTimeMillis()); // Load Calendar object with current time

9781430265504_Fig16-11.jpg

图 16-11 。在里面。setTimeInMillis()方法,访问 System.currentTimeMillis()方法来设置时间

现在,您的 GregorianCalendar 对象已经使用 system . current time millis()getter 方法加载了当前时间值,您将升级 hour、minute 和 second 整数变量,以便从日历常量中提取时间值,您在介绍 Calendar 类的章节中已经了解了这些常量。

加载时间变量:使用。get()方法

要从 watchFaceTime GregorianCalendar 对象中提取系统时间值,您将使用**。get( )** 方法调用并在日历中传递。您想要的数据的值常量,在本例中为小时、**分钟、秒、**秒,这将为您提供其他 onDraw()逻辑分别定位时针、分针和秒针所需的值。

替换 watchFaceTime 时间对象的简单方法的 Java 语句调用时间对象的。小时( ),。分钟( ),以及。second()方法调用在图 16-12 的中突出显示,应该类似于以下三个 Java 编程语句:

int hours = watchFaceTime.get(Calendar.HOUR);
int minutes = watchFaceTime.get(Calendar.MINUTE);
int seconds = watchFaceTime.get(Calendar.SECOND);

9781430265504_Fig16-12.jpg

图 16-12 。转换小时、分钟和秒整数以使用. get(日历。TIME_UNIT)方法调用

这里的一个优点是您还可以使用。get()用于其他日期或时区数据!

现在,您的 onDraw()方法已经更新为最新的代码,您可以继续升级 onVisibilityChanged()方法来使用。setTimeZone()。

升级 onVisibilityChanged()方法:。setTimeZone()

单击左边的加号(+)图标打开 onVisibilityChanged()方法结构并注释掉。清除( )和。setToNow()方法调用结构,如图图 16-13 所示,这样你就可以在以后使用不推荐使用的代码,如果你愿意的话。请注意,这与您在 BroadcastReceiver 对象的 onReceive()方法中所做的修改相同。

9781430265504_Fig16-13.jpg

图 16-13 。在 onVisibilityChanged()方法中,更改。清除()和。将 ToNow()设置为。setTimeZone()

需要注意的是,您不需要包含 invalidate()方法调用,因为这已经在 onReceive()方法更新中完成了。

升级代码:使用。getDrawable(int,Theme)

让我们回到 onDraw()方法,升级 BitmapDrawable 代码以使用一个非预先声明的。getDrawable(int,Theme)方法版本通过在方法调用参数区域内添加一个逗号和一个空值。这是一个相当基本的 Java 语句升级,可以在图 16-14 中看到突出显示。新的 Java 语句应该如下所示:

if(firstDraw) {
    if (roundFlag) {
        watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.round_face_test, null);
    } else {
        watchFaceDrawable = watchFaceResources.getDrawable(R.drawable.square_face_test, null);
    }
    watchFaceBitmap = ((BitmapDrawable) watchFaceDrawable).getBitmap();
    firstDraw = false;
}

9781430265504_Fig16-14.jpg

图 16-14 。升级。getDrawable()方法调用使用空值来指示 Android 继续使用当前主题

正如您在图 16-15 中看到的,您也需要对进行完全相同的升级。ensureModeSupport()方法中的 getDrawable(int)方法调用,需要该方法将此方法转换为仍然受支持的。getDrawable(int,Theme)版本。

9781430265504_Fig16-15.jpg

图 16-15 。升级。getDrawable()方法调用 ensureModeSupport()方法也使用空值

对于用于 prowatchfacelow.png、prowatchfacebur.png、prowatchfaceamb.png 和 prowatchfaceint.png watchface resources 对象素材加载器 Java 语句的四个 BitmapDrawable 素材,也需要这样做。

这就完成了旧时间类以及旧时间类的不推荐使用的代码升级。getDrawable(int)方法调用。下一个合乎逻辑的步骤是测试这段代码,但是 Android Studio 扔给我们一个 1.2.1.1 升级曲线球!

解决由 SDK 升级引入的 IDE 问题

就在我准备开始测试的时候,我检查了更新,在我开始任何严肃的应用测试之前,我总是这样做。我发现 Android Studio 1.2.1.1 更新可用,如图图 16-16 所示。我再次经历了如图 16-1 到 16-4 所示的步骤。

9781430265504_Fig16-16.jpg

图 16-16 。另一个火速 Android Studio 从 1.2.0 升级到 1.2.1.1 需要再次升级过程

我将补丁应用到 Android Studio (IntelliJ 加上 Android SDK 插件),然后使用 Android SDK 管理器来确保我将所有需要的 API、工具、文档、系统映像等升级到位。然后我重启了 Android Studio,推出了新的 1.2.1.1 版本。

正如你在图 16-17 中看到的,我遇到了一个相当大的问题。这显然不是我的错,因为我只修改了 Java 代码,其他什么都没做。我在 IntelliJ 的右上角收到一条消息,问我是否想**将项目迁移到 Gradle?**因为这个项目一直是一个 Gradle build 系统项目,我需要找出是什么导致了这个问题。这促使我将这一部分添加到我如何发现这一点的章节中,因为这很可能在某个时候发生在你身上。因此,在这里我将深入探讨 Gradle 构建系统,IntelliJ 中集成的项目,以及如何使用 Invalidate 和 Restart,导入项目,以及最激进的解决方案,如何从头开始创建您的项目。在我解释了如何解决这个问题,并把这个项目变成 Gradle build 系统兼容性,1.2.1.1 升级似乎已经改变了。

9781430265504_Fig16-17.jpg

图 16-17 。升级后重新启动时,项目不再认为它正在使用 Gradle 构建系统

我将向您展示我尝试事情的顺序,从必要的 Gradle build 定义升级,到使用 Invalidate and Restart ,到导入项目工作流程,到完全重新创建一个 Android Wear 项目。

升级梯度文件 :添加构建定义节

我尝试修复 Migrate Project to Gradle warning 的第一件合乎逻辑的事情是升级 build.gradle (wear、mobile 和 Project)构建定义文件。

正如你在图 16-18 中看到的,我添加了一个 buildscript 定义部分,包含存储库和依赖项的子部分,显示在 wear 定义文件的顶部,以及指定 Java 7 的编译选项,和指定 Java 和资源位置的源集,显示在底部。

9781430265504_Fig16-18.jpg

图 16-18 。升级 wear gradle.build 定义以包括 buildscript、compileOptions 和 sourceSets 部分

接下来,我将这些相同的部分添加到移动 gradle.build 文件中,如图图 16-19 所示。我升级了我的编译依赖项并增加了几个。

9781430265504_Fig16-19.jpg

图 16-19 。升级 mobile gradle.build 定义以包括 buildscript、compileOptions 和 sourceSets 部分

因为我不知道 WatchFaces API 应该使用哪些编译依赖项,所以我使用了https://github.com/googlesamples/android-WatchFace作为谷歌内部开发团队发现适用于 1.2.1.1 版本的指南。这些编译依赖项将适应您决定添加到 WatchFace API 设计中的任何特性。需要注意的是,如果不使用未使用的编译依赖项,它不会导致任何问题。

GitHub WatchFace 代码不再包含根(ProAndroidWearable)Gradle . build 文件,所以我简单地使用下面的语句将我的从 Gradle 1.0 类路径升级到 1.2.3 类路径,如图 16-20 所示:

buildscript {
    repositories { jcenter() }
    dependencies { classpath 'com.android.tools.build:gradle:1.2.3' }
}

9781430265504_Fig16-20.jpg

图 16-20 。升级根 gradle.build 的类路径依赖项以使用 com.android.tools.gradle:1.2.3

新的 gradle.settings 文件引用了 Wearable 和 Application 文件夹:

include ':Application', ':Wearable'

在我重启 Android Studio 后,我打开了 AndroidManifest,正如你在截图左侧的图 16-21 中看到的,引用错误仍然存在。这些错误导致运行/调试配置对话框无法运行(构建、执行)项目,因此无法对其进行测试,正如你在图 16-21 右侧看到的。

9781430265504_Fig16-21.jpg

图 16-21 。升级 Gradle 构建系统定义没有修复 AndroidManifest 引用问题

由于升级 Gradle 配置文件没有解决这个问题,我尝试的下一件事,也是下一个最简单的解决方案,是使用 Invalidate 函数,它位于 File 菜单下。

如果这不起作用,尝试下一个最困难的解决方案,即导入项目,然后看看它是否可以构建和运行。如果这不起作用,您将不得不从头重新创建项目,这将修复任何 Android Studio (IntelliJ 14)文件引用问题。

使用无效并重启:重建项目结构

在 Android Studio 中点击文件菜单,如图图 16-22 所示。选择失效缓存/重启菜单选项,点击失效重启

9781430265504_Fig16-22.jpg

图 16-22 。使用文件image修复项目引用使缓存失效并重启工具

这个想法将会消失并重新启动。打开 AndroidManifest,您将看到红色引用错误是否已经消失。我的 AndroidManifest.xml 仍然显示红色的错误高亮,这可以在图 16-21 中看到。

下一个最困难的工作过程包括将项目移动到备份文件夹,像第一次一样启动 IntelliJ,没有新项目,并使用 Import Project 特性将代码和 XML 从一个已经损坏的项目导入到一个没有这些引用错误的新 IntelliJ 文件和项目层次结构中。

使用导入项目

我用来导入这个项目的工作流程是备份项目,使用剪切和粘贴,如图图 16-23 所示。我右键单击项目文件夹,并使用剪切将其从 AndroidStudioProjects 文件夹中删除,然后右键单击C:/Back-Up/Android studio projects文件夹,并使用粘贴将整个项目层次结构文件夹位置移动到我的 C:\驱动器上。

9781430265504_Fig16-23.jpg

图 16-23 。使用剪切和粘贴清空 AndroidStudioProjects 文件夹,将项目移动到 C:\Back-Up

这将使 Android Studio 认为当前没有项目在使用,因为 AndroidStudioProjects 文件夹中什么也没有。

这将产生一个包含导入项目选项的 IntelliJ 对话框。这就是我需要用来创建一个新的 IntelliJ 项目基础设施的东西,它将有望修复这个素材引用问题。

图 16-24 显示了备份到我的 c 盘的 ProAndroidWearable 项目。

9781430265504_Fig16-24.jpg

图 16-24 。我使用粘贴创建了一个备份\AndroidStudioProjects 文件夹,将 ProAndroidWearable 移动到那里

一旦您的项目文件夹层次结构被备份(并对 IntelliJ 隐藏),启动 Android Studio,您将获得原始菜单,允许您启动一个新项目,打开现有项目,导入代码示例,从版本控制中签出项目,并导入项目。

选择导入项目选项,如图图 16-25 所示,然后选择C:/Back-Up/androidstudio projects/ProAndroidWearable/gradle . build文件,点击确定按钮。你会看到一个构建项目进度条,新项目最终会在一个空的 Android Studio IntelliJ IDEA 中打开。

9781430265504_Fig16-25.jpg

图 16-25 。启动没有项目的 Android Studio,使用导入项目,并选择 build.gradle,重建项目

右键单击项目层次结构的磨损组件(文件夹)中的 AndroidManifest.xml 文件,并使用跳转到源代码选项来检查该文件。

正如你在图 16-26 中看到的,我的 AndroidManifest XML 文件中仍然有引用错误高亮显示。它们与我之前遇到的引用错误不同,我还在 IDE 的右上角发现了一个新的框架检测到的警告,以及一个配置链接。这将打开一个名为设置框架 的对话框,在图 16-26 中突出显示,这是最初破坏我的项目的地方。无论您选择这些下拉菜单选项中的哪一个,类型还是目录,最终结果都是一样的。在我的例子中,这等同于 XML 文件中不同的 AndroidManifest 引用错误,所以看起来我将重新创建项目!

9781430265504_Fig16-26.jpg

图 16-26 。在 Android Studio 第二次启动时,我收到了框架检测警告,这改变了项目

正如你在图 16-27 中看到的,这个新项目现在有着我导入的项目所具有的相同问题,所以我需要创建一个新的 Android Wear 项目,并将我所有的 Java 代码、XML 标记和素材复制到其中。

9781430265504_Fig16-27.jpg

图 16-27 。AndroidManifest 现在有同样的引用错误我以前有过,所以这是什么打破了这个项目

因为最初的项目是在 Android Studio 1.0 下创建的,IDE 现在在 1.2.1.1,所以可能会有一些新功能,可能与 WatchFaces API 有关,可以用来简化 Java 编码或 XML 标记任务。我将保持本书前 15 章的代码不变,因为它们向您展示了如何从头开始创建 Watch Faces 应用,因此您在学习如何修复代码方面拥有最大的学习经验。

从头开始重新创建项目:复制代码和素材

剪切并粘贴 AndroidStudioProjects 文件夹下的任何子文件夹,就像你在上一节和图 16-25 所示的启动菜单中所做的那样,然后选择开始一个新的 Android Studio 项目选项。它将给出如图图 4-16 所示的对话框,在这里您将使用相同的 ProAndroidWearablewearables.android.pro.com条目。确保该项目位于用户文件夹和 AndroidStudioProjects 子文件夹中。点击下一个按钮后,会出现一个目标 Android 设备对话框,如图图 16-28 所示。该对话框也可以在图 4-17 中看到,使用旧的磨损 API 等级。

9781430265504_Fig16-28.jpg

图 16-28 。为手机和平板电脑选择 API 等级 18,为磨损组件选择 API 等级 21

为手机和平板电脑应用选择 Android 4.3 API 18 ,为 Wear 应用组件选择 Android 5.0 API 21 。然后点击下一步按钮,进入添加活动穿对话框,如图图 16-29 所示。如果您想查看该对话框的旧版本,请参考图 4-19 。

9781430265504_Fig16-29.jpg

图 16-29 。选择 WatchFace 选项,让 Android Studio 编写您的 WatchFace API 代码基础架构

注意,这个对话框中有两个选项是 Android Studio 1.0 中没有的:显示通知和 WatchFace API。我将选择 WatchFace API 选项,向您展示 Android Studio 将为您编写 WatchFace API 代码,因为您将很快替换它。

服务名数据字段中,输入 ProWatchFaceService Java 类名,在下拉菜单中选择您的模拟 WatchFace 样式选项,如图 16-30 所示,然后点击完成创建您的项目。

9781430265504_Fig16-30.jpg

图 16-30 。将您的服务命名为 ProWatchFaceService ,选择模拟 WatchFace 样式,然后单击 Finish

构建完新项目后,IntelliJ IDEA 将会打开,使用标准的 bootstrap WatchFace API 代码,该代码远没有您在本书过程中开发的代码那么详细或复杂。我还使用了更具描述性的对象和变量名称,这样您就可以直观地看到这些对象和数据字段在整体上对 WatchFace API 代码库做了什么。

需要注意的是,这些代码都不会用到!我在这里做的是使用这个新的项目结构,主要是新的**。IML** ,或者 IntelliJ 标记语言项目结构定义,来解决我遇到的问题。所有的 Java 代码、XML 和素材都将被复制过来。

我注意到在 bootstrap 代码库中,不赞成使用的 Time 类还没有被 Calendar 类代码替换,你可以看到在图 16-31 顶部附近用黄色突出显示。我猜他们会在 Android Studio 1.3 更新中实现这一点,预计会在 2015 年晚些时候发布,希望在你看到这本书的时候。

9781430265504_Fig16-31.jpg

图 16-31 。Android Studio 为您创建了基本的模拟 WatchFace 服务类和所有的基本方法

这些快速更新真的越来越让人受不了了!。

我做的第一件事是重新启动 IDE,重新构建项目,并确保一切都在 Gradle 控制台选项卡中。我很高兴我采取了这一步,因为正如你在图 16-32 中看到的,这个引导项目结构的 Gradle 控制台中存在一个错误!让我们点击打开文件编码设置链接并立即修复它。在项目编码下拉列表中,选择 UTF-8 ,点击确定按钮,为整个 Wear 项目设置一个建议的 UTF-8 项目编码格式。

9781430265504_Fig16-32.jpg

图 16-32 。使用文件编码对话框(右)修复一个 Gradle sync 项目的编码问题(左)

正如你在图 16-33 中看到的,在我点击右上角显示的 Gradle 项目刷新图标后,我的 Gradle 控制台现在没有任何错误或警告信息。我还向您展示了如果您想再次回到图左侧的文件编码对话框,您将使用的菜单序列。这是使用文件image设置image编辑器image文件编码完成的。

9781430265504_Fig16-33.jpg

图 16-33 。打开(点击)Gradle 选项卡(右)并使用刷新 Gradle 项目图标清除 Gradle 控制台

我将按照您在本书中重建项目的顺序进行操作,因此您在第五章中做的下一件事就是为项目创建 AVD 仿真器基础设施。我查看了运行/调试配置对话框,我在第五章中创建的圆形和方形 AVD 仿真器仍然是 Android Studio 的一部分,这很有意义,因为它们独立于项目,所以让我们继续。

第七章介绍了 gradle.build 和 XML 定义文件,现在让我们把它们放在适当的位置。从 ProAndroidWearable 文件夹、wear 和 mobile 文件夹中复制 build.gradle 文件文本,并替换新引导项目的 build.gradle 文件文本。wear/RES/XML/watch _ face . XML文件已经就绪,所以将备份 wear/res/values 文件夹中的 strings.xml 文件中的值复制并粘贴到新项目wear/RES/values/strings . XML文件中,替换您的 bootstrap < string >值。对您的mobile/RES/values/strings.xml中的 strings . XML 值执行相同的操作,以便为 WatchFace Companion 应用准备好颜色数组和值。

接下来我们来转移一下活动 UI 设计。在你的 mobile/res 文件夹中创建 /layout 文件夹,然后创建一个activity _ pro _ watch _ face _ config . XML文件。复制并粘贴您的配置活动 UI 定义。在替换 AndroidManifest.xml 文件之前,您需要复制清单文件访问的 PNG 素材,否则您将得到错误高亮显示。

幸运的是,PNG 文件都在 wear/res/drawable-hdpi 文件夹中,所以接下来,您需要将这些文件复制并粘贴到您的新项目中,粘贴到 res/drawable-dpi 文件夹以进行缩放,或者复制到 res/drawable-nodpi 文件夹以供固定使用。

有趣的是,当我研究这些新的 Android 1.2.1 和 1.3 WatchFaces API 升级和更新时,我发现了一个新的推荐 WatchFaces 背景位图图像大小为 400 像素,并且建议图像放在**/wear/RES/drawable-nodpi/**文件夹中,因此我将我的位图素材粘贴到这个文件夹中。我的猜测是新的 400 像素推荐,因为新的华为智能手表采用了 400 像素的屏幕。这意味着,一旦 480 像素的智能手表问世,那将是在 2015 年或 2016 年,WatchFaces 位图的这一推荐 NODPI 分辨率将变为 480。

我注意到的另一件事是 res/drawable-dpi 文件夹已经被替换为 res/mipmap-dpi 文件夹。在 Android 4.3 中引入了 mipmap 功能用于图标,MIP map 起源于 3D。使用预先优化的 PNG32 图像预先计算这些图像以适合 Android DPI 图标分辨率,每个 png 32 图像都是从同一应用图标图像的最高分辨率版本重新采样的较低分辨率表示。我将只为你的应用图标使用这些文件夹,并为你在 Wear 应用中使用的 BitmapDrawable 素材使用/drawable 文件夹。

因为您的 AndroidManifest.xml 文件还引用了您已经创建的 Java 类,您将需要使用 IDEA New image Java class 特性来创建这些 Java 类,然后将您的 Java 逻辑复制并粘贴到这些 Java 类中。

让我们先从你的ProWatchFaceService.java文件开始,因为这个 Java 类是由新项目系列对话框创建的。将代码复制并粘贴到 ProWatchFaceService 选项卡中,替换引导代码。接下来右键单击**/wear/Java/com . pro . Android . wearables . proandroidwearable文件夹,使用新建image Java 类对话框创建 config listenerProWatchFaceConfigListenerService.java**文件。然后将你在书中写的 Java 代码复制粘贴到你的 ProWatchFaceConfigListenerService 标签页,替换空的 Java 类代码。在创建 ProWatchFaceUtility.java 类文件之前会有一个错误,所以接下来让我们开始吧。

右键点击**/wear/Java/com . pro . Android . wearables . proandroidwearable文件夹,使用新建image Java 类对话框创建你的工具类ProWatchFaceUtility.java**文件。然后将您在本书中编写的 Java 代码复制并粘贴到 ProWatchFaceUtility 选项卡中,替换空的 Java 类代码。最后,让我们添加移动组件的 Java 代码。

右键点击**/mobile/Java/com . pro . Android . wearables . proandroidwearable文件夹,使用新建image Java 类对话框创建 ConfigActivity 类ProWatchFaceCompanionConfigActivity.java**文件。然后将该类的 Java 代码复制并粘贴到 prowatchfaceconnectionconfigactivity 选项卡中,替换空的 Java 类。现在,您可以复制并粘贴 wear 和 mobile AndroidManifest.xml 文件。将您的图标引用更改为使用带有这行代码的 mipmap:

android:icon="@mipmap/ic_launcher"  // Replace the @drawable reference with an @mipmap reference

现在mobile Android manifest . XML已经被复制过来,只有一个引用更改,是时候复制更复杂的 wear AndroidManifest 了,它可能会有一些升级,因为它是在 Android Studio 1.0 下创建的。

除了 mipmap 引用更改之外, wear AndroidManifest.xml 是无错误的,我还添加了一个 < uses-sdk > 标签,在清单顶部的<清单>父标签内指定 API 级别 21 。这可以在图 16-34 的顶部看到,并使用了以下 XML 标记:

<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />

9781430265504_Fig16-34.jpg

图 16-34 。添加一个指定 API 级别 21 的< uses-sdk >和另一个用于 Google Play 服务的<元数据>标签

我还添加了一个 <元数据> 子标签,就在</应用>结束标签之前,为这个应用定义一个谷歌移动服务(GMS)版本。这可以在图 16-34 底部用黄色突出显示,并使用以下 XML 标记:

<meta-data android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />

接下来我做了一个 Run image Run Wear,项目构建再次使用 Gradle build 系统工作,所以我又有了一个工作想法,尽管这需要一个极端的工作过程才能实现!

这个故事的寓意是:Android 不仅仅是 Java 或 XML

重要的是要记住,如今的编程是一个移动的目标,涉及多个集成的组件,如 IDE (IntelliJ)、构建系统(Gradle)、SDK (Android Studio)、编程语言(Java 7)、标记语言(XML)、新媒体编解码器(MPEG4、JPEG、PNG、MP3、GIF、WebM、WebP 等)。)、技术 API(WebKit、OpenGL、SQLite)和操作系统(Windows、Linux 和 Mac)平台,而这只是等式中 Android 应用开发的一面,我在本书中对此进行了介绍。

在设备方面,你也有一个 Dalvik 虚拟机(DVM)或 Android 运行时(ART)引擎、Android 操作系统和 Linux 64 内核,因此开发和发布任何类型的 Android 应用,无论是通用的 Android 还是更专业的 Android Wear(本书)、Android TV、Android Auto 或 Android Glass(ApressPro Android IoT本书),无论如何都不会是在公园里散步!

所有的组件都由数百名程序员维护,他们在多家公司(例如 Google 和 Oracle)和开源组织(例如 MPEG LA、Khronos、Apache 和 W3C)工作。我们甚至经历过一个全新的、100%由 Android Studio 生成的项目出现 Gradle build 系统错误,必须立即修复!

摘要

在本章中,您学习了如何升级不推荐使用的代码,以使用新的、推荐的类和更新的 API 类的方法。我还向您展示了如何修复非代码相关的问题,在本例中,IntelliJ IDEA 相关的问题将会由于 OS API 升级而不可避免地出现。

这需要您了解一个日历超类,及其子类 GregorianCalendar 类,以及您需要使用的新方法来替换 Time 类及其最初用于实现 Watch Faces API 的方法。如果你不介意在你的想法中删除代码,被否决的类和方法仍然可以工作。

您还学习了如何使用更高级的 getDrawable(int,Theme) 方法调用,Android 不赞成使用更简单的 getDrawable(int)版本,在我看来,这是为了迫使开发人员将主题融入他们的代码使用中。

在下一章中,您将了解其他 Android APIs,以及一些其他很酷的东西,如 GPS 位置感知和语音识别功能,您可以将它们添加到 Wear 应用中。****

十七、Android 物联网 API 的未来:Android 电视、眼镜、汽车和穿戴

因为不可能涵盖所有的 Android,或者任何可用于 Android 的利基硬件设备 SDK,所以我想我会给你留一章,给你一些关于在你的 Android 硬件设备开发之旅中接下来要看什么的想法。

Android 正在智能地组织这些 API,将 API 功能与硬件产品流派或类型相匹配,例如运行 Android TV 的互动电视集 (iTV 集),运行 Android Glass 的智能眼镜,虽然没有发布但显示在新项目对话框中(参见图 16-28 ,或者运行 Android Auto 的汽车仪表盘,还有运行 Android Wear 的智能手表

我将在明年写一本关于 Android IoT 的书,这本书将涵盖这些其他 API,以及同时可能实现的任何其他物联网 API。随着其他消费电子类型越来越受欢迎,并被主要硬件制造商采用,Android 将发布其他定制的 API。

例如,许多制造商都有虚拟现实耳机,可以运行 Android VR API。Android 驱动的机器人,甚至无人机,越来越受欢迎,所以一个 Android 机器人 API 将允许开发者创建一个定制他们的机器人或无人机的应用。另一个增长的领域是家用电器,所以看到一个 Android Home API 不要惊讶。在这一章中,我将讨论一些其他的 Android APIs,以及一些你应该实现的 Wear 的其他特性。

高清和 UHD 安卓电视:磨损的对立面

Android TV SDK 解决了与 Android Wear SDK 相反的消费电子产品尺寸范围,Android Wear SDK 针对小方形或圆形屏幕进行了优化。Android TV 旨在为大宽屏显示器开发应用,这些显示器具有真正的高清分辨率,或 1920 x 1080 分辨率,或更新的 ?? UHD 分辨率,或 4096 x 2160 ?? 分辨率。这些显示器的对角线尺寸从 60 到 80 英寸不等,而智能手表的对角线尺寸从 1 到 2 英寸不等!

合乎逻辑的 Android 电视应用类型包括 2D 和 3D 游戏、直播内容流、电视播放、互动电视和内容搜索。

Android TV 应用需要在 res/drawable-xhdpi 项目文件夹中有一个320 x 180像素的主屏幕横幅,并且应该包括识别应用的本地化文本。这个横幅在 AndroidManifest.xml 应用定义中声明和引用,使用以下参数:

<application android:banner="@drawable/png_banner_name" >
    // Nested Tags for the TV Application will go in here
</application>

打算在 iTV set 设备上运行的 Android TV 应用将需要在 AndroidManifest 中为 Android TV API 声明一个 iTV 启动器活动,使用 <意图过滤器> 子标签中的CATEGORY _ LEANBACK _ Launcher常量。

该意图过滤器将用于识别与电视兼容的 iTV 应用。这是 Google Play 将 iTV set 应用正确分类到商店的 Android TV 应用部分所必需的。该意图还标识了当用户选择 iTV 主屏幕上的图标时要启动的活动。以下 XML 标记显示了如何在清单中编写意图过滤器:

<application android:banner="@drawable/banner" >
  <activity android:name="com.example.android.MainActivity" android:label="@string/app_name" >
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>
  <activity
    android:name="com.example.android.iTVsetActivity"
    android:label="@string/itv_app_name"
    android:theme="@style/Theme.Leanback">
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    </intent-filter>
  </activity>
</application>

Android 有一个支持 iTV 用户界面设计的库,叫做 V17 后倾库 。这为 iTV 活动提供了标准主题,称为主题。正如你在上面的 XML 标记中看到的那样。

主题。Leanback android: theme 参数可以为 iTV set 应用实现一致的视觉 UI 风格。建议大多数 iTV 应用使用这种向后倾斜主题。主题。Leanback 应与来自 V17 Leanback 库的 iTV UI 设计类一起使用,该库也支持电视媒体播放类。

其他可用于 Android TV 开发的类包括 V7 RecyclerView 库 ,它提供了用于内存高效管理和显示长列表的类。V17 向后倾斜库中的许多类依赖于 V7 RecyclerView 库中的类。还有 V7 CardView 库 ,它为开发人员提供了额外的用户界面小部件,用于显示包含媒体项目图片和媒体项目文本描述的内容信息卡。V7 向后倾斜库也依赖于 V4 支持库,所以就像 Wear 应用一样,许多旧版本的支持库可能需要包含在 Android TV 应用的 build.gradle dependencies 部分中。

您还需要声明您的应用在 Android Manifest XML 文件的 < manifest > 级别使用了一个向后倾斜的用户界面。如果您将此必需的 <用途特征> 属性设置为值,则 Android TV 应用将仅在实现 Android TV 向后倾斜 UI 的 iTV set 设备上运行。对此的 XML 标记如下所示:

<manifest>
    <uses-feature android:name="android.software.leanback" android:required="true" />
    // Other Manifest Child Tags will go in here
</manifest>

如果您正在开发运行在手机、可穿戴设备、平板电脑、电子书阅读器等设备上的应用,除了运行在 Android TV 上之外,您将使用以下标记将这个必需属性设置为一个 false 值:

<manifest>
    <uses-feature android:name="android.software.leanback" android:required="false" />
    // Other Manifest Child Tags will go in here
</manifest>

你打算在 iTV 上运行的 Android 电视应用使用遥控器,不依赖触摸屏进行输入,至少在主要制造商发布触摸屏 iTV 之前是这样,他们可能会在某个时间点发布触摸屏 iTV。

为了向您的用户阐明触摸的使用,您的 iTV 应用清单需要声明此Android . hardware . touch功能不是必需的。这是通过使用 < uses-feature > 标签内的 false 设置来完成的。下面的 XML 标记显示了如何将它包含在 Android 清单应用设置和特权声明文件中:

<manifest>
    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    // Other Manifest Child Tags will go in here
</manifest>

此设置会将您的 iTV 应用识别为能够与 Android TV 设备配合使用,并且该应用需要被视为 Android TV 应用并在 Google Play 中的正确 iTV 区域列出。

需要注意的是,目前必须以这种方式声明 iTV 应用,也就是说,在您的应用清单中不需要触摸屏支持,如本示例代码所示,否则您的应用无法出现在电视设备下的谷歌 Play 商店中。

除了互动电视节目,最受欢迎的互动电视应用之一肯定是互动电视游戏。与为纵向设计的智能手机应用或为方形和圆形屏幕设计的智能手表应用不同,Android TV 应用是横向设计的。高清电视(1920×1080)的正常纵横比是 16:9,但具有 2160×1080 分辨率的超宽 16:8 WHD 屏幕也出现了。

与大多数通过网络共享的多人游戏(使用自己的显示器、系统内存和处理器)不同,iTV 游戏不需要任何网络,从而减少了延迟,但它们也共享相同的屏幕、系统内存和多核处理器,因此它们需要精心设计和优化。共享屏幕也可能带来一些独特的多人游戏设计挑战,因为所有玩家都可以看到一切,这在设计依赖玩家专有(未公开)游戏策略信息的纸牌游戏和类似策略游戏时尤其困难。

一个可能的解决方案是一个配套应用,也称为第二屏幕显示应用。这将在蓝牙手机或平板电脑上运行。这将使玩家能够隐藏游戏策略信息,只有他们可以查看,使用第二个屏幕蓝牙连接的应用。

Android TV 主屏幕列出的游戏与常规的 iTV 应用不同。要使一个游戏出现在游戏列表中,请在 Manifest 标签中将 android:isGame 属性设置为“true ”,使用以下标记:

<application android:isGame="true" >

为了告知用户游戏需要或支持游戏控制器,您必须在清单文件中包含以下条目:

<uses-feature android:name="android.hardware.gamepad" />

如果你的 iTV 游戏使用,但不是绝对需要游戏控制器,你将额外包括一个 android:required="false" 参数在< uses-feature >标签中。这个 XML 标记看起来如下所示:

<uses-feature android:name="android.hardware.gamepad" android:required="false" />

Android Auto:汽车仪表盘的 Android 应用

Android Auto SDK (API)平台使您能够扩展您的应用,以便与使用 Android Auto 的汽车控制台系统一起工作。这些 Android 汽车控制台设备 为汽车应用提供基本的 Android 用户界面。这些汽车应用将允许用户在上班、购物、娱乐、约会或公路旅行的路上随身携带应用功能。

就像 Android Wear 一样,适用于 Android 自动控制台的应用可以在蓝牙设备上运行,比如手机或平板电脑。该应用通过特定的 API 与仪表板的 Android 控制台进行通信,该控制台为专为汽车环境内使用而设计的联网 Android Auto 应用提供 Android Auto 用户界面。

Android Auto 应用用户通过语音识别调用的操作和车辆输入控制(如仪表板内的触摸屏,甚至是物理仪表板硬件按钮)与应用和服务进行交互。

Android Auto SDK 目前支持两种类型的应用:音频应用,允许用户在汽车立体声环境中浏览、预览和播放音乐以及语音音频内容,如有声读物;或者消息应用,接收来电通知,使用文本到语音合成大声朗读消息,或者使用汽车或仪表板内置的麦克风通过汽车中的语音输入发送回复。

您可以配置当前为手机和平板电脑开发的音频或消息应用 ,使其在 Android Auto 下工作,而不必适应特定车辆的硬件差异。要使这些应用能够使用 Android Auto,应用必须使用 Android 5.0 API 级别 21 或更高的 targetSdk 设置。应用清单必须声明它使用的汽车功能,如音频播放或消息服务。这与您在 Android Wear 中为 Watch Faces API 执行此操作的方式非常相似。

在应用清单中,您应该提供对 Android Auto XML 配置文件automotive _ app _ desc . XML的引用,您将在 /res/xml 文件夹中创建该文件,就像您对 res/xml/watch_face.xml 文件所做的那样。

为此,您将在<应用>父标签中使用一个 <元数据> 子标签。这个<元数据>标签通过使用以下 xml 标记引用了设置为“com . Google . Android . GMS . car . application”的 android:name 参数和设置为“@xml/automotive_app_desc”的 android:resource 参数:

<application>
    <meta-data android:name="com.google.android.gms.car.application"
     android:resource="@xml/automotive_app_desc"/>
    // Other Application Child Tags will be nested inside of the parent <application> tag
</application>

您将使用放置在项目 XML 资源目录中的 XML 文件来指定汽车应用使用的 Android Auto 功能,您可能需要创建(res/xml)该文件,就像您为 Wear 所做的一样。

要声明一个用于 Android Auto 的音频应用,创建名为 automotive_app_desc.xml 的文件,将其保存在 project res/xml 文件夹下,并确保 xml 定义包含 < automotiveApp ><使用> 标签:

<automotiveApp>
    <uses name="media" />
</automotiveApp>

子标签声明了一个应用打算使用的 Android Auto 特性。如果您的应用需要使用多个 Android Auto SDK 功能,将会添加多个标签。name= " "属性用于指定应用将要利用的功能。

支持的常量包括媒体,用于在车内播放音乐(启用音频应用),以及通知,用于通过汽车概览屏幕显示消息通知,允许用户选择要大声朗读的消息,并允许用户通过语音输入对消息做出响应(启用 Android 自动消息应用)。

对于这个媒体常量,Android 自动控制台设备期望连接到媒体浏览器服务来浏览音轨列表。您应该在清单中声明这个 MediaBrowserService 子类。这将允许汽车的仪表板设备硬件发现服务并连接到应用。

声明 MediaBrowserService 对象以及用于启动它的 Intent 对象所需的 XML 标记看起来很像下面这样:

<application>
    <service android:name=".ProAutoMediaBrowserService" android:exported="true">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService"/>
        </intent-filter>
    </service>
  // Other Application Child Tags will be nested inside of the parent <application> tag
<application>

正如你所看到的,应用提供的用于浏览音轨的ProAutoMediaBrowserService对象必须扩展 AndroidMediaBrowserService类,这将成为 ProAutoMediaBrowserService 类的超类。

Android 汽车硬件设备也使用 Intent 对象作为通知常量,以表明用户已经阅读或回复了应用提供的消息。应用定义阅读或回复消息的意图类型,并将此信息添加到消息通知中,以便仪表板系统可以在用户采取这些操作之一时通知应用。

您需要为应用定义一个 heard 动作意图和一个 reply 动作意图。这些由 BroadcastReceiver 类使用,这些类处理应用清单定义文件中定义的意图。实现这些意图的 XML 标记示例及其关联的 BroadcastReceiver 对象应该如下所示:

<application>
    <receiver android:name=".ProAutoMessageHeardReceiver">
        <intent-filter>
          <action android:name="com.myapp.messagingservice.PRO_AUTO_ACTION_MESSAGE_HEARD"/>
        </intent-filter>
    </receiver>
    <receiver android:name=".ProAutoMessageReplyReceiver">
        <intent-filter>
          <action android:name="com.myapp.messagingservice.PRO_AUTO_ACTION_MESSAGE_REPLY"/>
        </intent-filter>
    </receiver>
</application>

在这个 XML 中,“ProAutoMessageReadReceiver”和“ProAutoMessageReplyReceiver”是您编写来处理您的意图的 BroadcastReceiver 子类的名称。您可以选择将任何您喜欢的常量定义为操作对象名称。确保所有操作名称都是完全唯一的。

构建用于 Android 自动控制台设备的通知将需要 Android V4 支持库中的类。使用 Android SDK 管理器将 Extras > Android 支持库更新到版本 9 或更高版本,并将 Extras > Android 支持库更新到版本 21.0.2 或更高版本(如果有更高版本可用)。

在更新了这些 Android 支持库之后,使用下面的 Groovy 代码,通过将这个依赖项添加到 Android Auto 应用的 build.gradle 文件中,将这些库导入到 Android Studio 开发项目中:

dependencies { compile 'com.android.support:support-v4:21.0.2' }

Android Auto SDK 使用标准的用户界面和用户交互模型,可在所有控制台上工作,因此您无需担心不同的仪表板硬件。Android 非常重视司机分心的问题,它有特定的设计要求,以使汽车应用有资格在谷歌 Play 商店销售。

当用户连接到 Android Auto 时,他们首先会遇到一个概览屏幕,该屏幕根据用户的位置和时间显示上下文卡片。用户可以使用这个屏幕来查看来自消息应用的通知,并选择消息来通过语音输入发送响应。点击活动栏中的耳机图标,查看安装在手持设备上的音频应用。在用户选择音频应用之后,显示器显示该主要应用 UI。主应用 UI 中的媒体控制卡最多支持四个主操作,以及四个辅助操作(在溢出栏上)和返回操作。

Android Auto 使用抽屉 UI 范例进行列表浏览操作,汽车控制台的显示器将使用抽屉转换显示数据列表的内容。对于媒体(音频)应用,自定义的列表 UI 显示应用中媒体服务提供的媒体容器和音频文件。您可以使用列表项目的图标来自定义抽屉中的数据条目。

标准的 Android Auto 用户界面描述了白天和夜间使用的不同配色方案。Android Auto 平台提供状态(白天或夜间)变量,并自动设置该状态。

就 Android 自动颜色主题而言,你的应用可以使用材质主题的调色板中的颜色。材质调色板包含 500 种原色和强调色,可用于插图或开发您的应用颜色方案。它们被设计成彼此和谐地工作,可以在下面的 Google URL 上看到:

http://www.google.com/design/spec/style/color.html

该调色板从 20 种原色开始,即白色、红色、绿色、蓝色、紫色、粉色、靛蓝、青色、蓝绿色、石灰、黄色、琥珀色、橙色、灰色、蓝灰色、棕色、深橙色、深紫色、浅绿色和浅蓝色。如果你认为黑色是一种颜色,实际上总共有 21 种颜色。

其他 480 种颜色填充这个光谱,为 Android 或 WebKit 创建一个完整且可用的调色板。谷歌建议使用 500 种颜色作为你的应用的主要颜色,然后使用其他自定义颜色作为你的强调色。您的 Android Auto 应用将被允许为系统调色板指定两种颜色。这些是在 Android 操作系统中使用 colorPrimaryDarkcolorcentnext数据字段常量定义的。

Android Auto 平台旨在最大限度地提高安全性,减少任何干扰,这个调色板只是这个过程的一小部分。

Android Auto 应用的用户界面需要快速且易于导航,每个 Android Auto 应用都必须通过正式审查,并满足最低安全和驾驶员注意力分散的要求和法规。Android 汽车应用需要完全可预测,并具有内在的直观性,以确保司机始终关注道路,因为生命可能受到威胁。

您的应用需要为 Android Auto 定制。如果没有针对驾驶体验对用户界面、可用性和设计进行调整,就不要在 Android 清单 XML 文件中指定 Android 自动兼容性。复杂的操作,如创建帐户、登录,甚至创建播放列表,都应该在手机或平板电脑上进行,而不是在自动显示器上进行。

Android 汽车应用设计应符合本平台在其汽车平台 UI 中实现的原则,包括简明的 UI 设计和可预测的应用功能,具有持续连接的用户体验,并为用户提供集成的应用环境。

谷歌眼镜:为智能眼镜开发应用

谷歌眼镜开发套件 ,也被称为 GDK,是 Android SDK 的附加软件,可以让你构建直接在谷歌眼镜上运行的玻璃器皿(眼镜应用)。截至本书撰写之时,谷歌眼镜 1 已经停产;然而,谷歌眼镜 2 计划在 2015 年的某个时候发布。如果谷歌聪明的话,它不会让 Glass 2 成为 Android 的外部 API,而是做同样的事情,让它成为一个 Android Glass API 和 SDK,与 Android OS 更加集成,可供所有智能眼镜制造商使用,如 Luxottica、LGE、索尼、six15、Vuzix、三星、GlassUp、PivotHead、Meta AR 和许多其他制造商。

谷歌眼镜 1 的屏幕分辨率为 640 x360。这是四分之一伪高清,因为 1280×720 的屏幕有四个 640×360 的矩形区域。像 Android TV 或 Auto 一样,Glass 有一个独特的 UI 范式,专门适合你的硬件设备。这是围绕时间轴 UI 范例设计的。

该时间线是呈现给用户的主要用户界面。它由 640 x 360 分辨率的卡组成。Glass UI 提供了一些线性功能,例如查看动画和静态卡片、调用语音识别命令以及启动 Android Glass 应用的通用方式。

Android Glass 用户可以在这个时间轴 UI 的不同部分之间线性滚动,显示来自过去、现在和未来的卡片 UI 元素,这些元素等同于屏幕上(现在)、以前查看的(过去)和尚未查看的(未来)。最近查看的(过去的)项目将保持最接近家庭卡。这是用户恢复使用 Android Glass 或者第一次使用时看到的默认卡。

除了导航时间轴卡片,这款 Android Glass UI 还能对用户输入做出响应。用户使用触摸板浏览时间轴 UI,并可以使用语音命令启动 Android Glass 应用。卡片还允许显示菜单项,因此 Android Glass 应用将能够将控制权交给用户,以便他们可以完成操作,例如回复收到的短信,甚至分享照片或视频。

Android Glass 时间轴 UI 及其卡片被组织成多个不同的功能部分,包括首页过去现在未来。一个默认的家庭卡上有一个安卓玻璃时钟。这张 Home 卡占据了时间轴用户界面的中心,每当用户开始使用 Android Glass 设备时,它就会出现。此主页卡保留在时间轴 UI 的中心区域,因为它提供了对 UI 其余部分的访问。

Android Glass 提供语音或触摸命令,允许用户启动 Android Glass 的其他操作系统组件或定制应用。语音命令允许 Android Glass 的免提操作。

Android Glass 主屏幕(时钟)右侧是历史部分,代表过去,显示静态数据卡片。这样做的原因是因为活卡总是被认为是在当下,所以它们永远不会出现在 Android Glass UI 的这个历史部分。

如果不通过重新访问来刷新静态卡,它们会在历史部分自然“衰退”。随着较新的卡片在过去的部分结束,它们将被放置在最靠近主屏幕(时钟)的位置。安卓眼镜会把最老的卡放在最右边。Glass OS 将移除任何超过七天的卡,或者达到 200 张卡的限制。

在 Android Glass 主屏幕(时钟)的左侧是现在未来部分,其中包含静态和动态卡片。实时卡片总是显示与用户相关的实时信息。活卡应该总是出现在现在和未来的版块。当一张活动卡拥有焦点并且 Android Glass 进入睡眠状态时,这张卡将成为 Android Glass 重新开机时出现的默认卡。包含未来“时间戳”或“固定”的静态卡也将出现在当前和未来部分。

例如,Google Now 天气卡可能会自动显示当前和未来部分的相关信息,即使它是一张静态卡。静态信息可以在预定义的时间动态更新。

时间线 UI 的最左侧是设置卡片。有了这些,你就可以配置 Android Glass 操作系统的设置,比如音量或者你正在连接的 Wi-Fi 网络。

Live cards 显然是 Android Glass 的核心功能,因为这些内容总是包含丰富的实时内容,或者用户要求定期更新并保留在屏幕当前区域的内容。Live cards 应该使用自定义图形经常更新,以显示用户引人注目的实时信息。对于需要根据一些用户数据或用户使用的外部数据不断更新的动画用户界面来说,该功能是必需的。

活动卡可以访问加速度计和 GPS 等低级传感器数据。这允许尖端类型的用户交互和特征,这是静态卡所不可能的,因为这样的传感器实时流出动态数据,因此需要动态卡格式。

Live cards 能够在 Android Glass timeline UI 中运行。当这些实时卡中的每一个运行时,用户可以向左或向右滚动以查看其他实时卡并与之交互。这使得用户可以进行多任务处理,并在后台无缝维护每个 live 卡的运行时状态。

静态卡只能显示文本、图像和视频内容,这些内容可以一次性加载到内存中(静态),并且包含不变的数据(非动态)。视频是动画,但它是一系列图像。因此,虽然数字视频看起来是动态的,但实际上不是,因为它不会随着时间的推移而改变,并且每次播放时都是相同的图像系列!静态卡片包含可以用 HTML、文本、图像或视频构建的信息。静态卡不经常更新数据,如果有的话,它们应该为内容显示和快速通知而设计。

静态卡片允许有一个共享菜单项,这允许你的用户与其他人,称为联系人,甚至与另一个 Android Glass 应用共享卡片。您可以将这些 Android Glass timeline UI 卡声明为“可共享”,还可以为 Android Glass 应用定义一个联系人,该联系人可以接受共享的 timeline UI 静态卡项目。

当你需要对用户体验进行全面的用户界面控制时,Android Glass 有一种叫做“immersions ”的东西这些将在典型的 Android Glass timeline 用户界面或用户体验之外运行。

沉浸可以让你呈现自己的用户界面,处理 Android Glass 应用编程逻辑内部的所有用户输入。Android Glass 应用需要沉浸感,这些应用不能在时间轴用户界面范式的严格线性约束下运行,需要自由形式、非线性或面向对象的 GUI 方法。

值得注意的是,时间线卡片和沉浸都可以包含菜单项。它们执行相关的操作,比如回复、配置、删除、共享、数据输入以及更多的用户操作。

最初的 Google Glass 1 API(称为 Mirror API 和 Glassware GDK)并不是 100%特定于 Android 的,尽管它支持 Java 1.6,因此 32 位 Android 4.x. Glass Mirror 也支持 Python 和 PHP。Android Glass API 自然会成为新 Glass 2.0 的一部分,它将是 64 位 Android 5.x 甚至 6.x,并使用 Java 1.7 (Java 7)或 Java 1.8 (Java 8)甚至 Java 1.9 (Java 9)。

因为这个 API 目前正在开发中,还不可用,所以我将在本章的这一节中介绍谷歌眼镜 1 的基础知识,并在我的下一个 Pro Android 标题中介绍 Android 眼镜(或谷歌眼镜 2.0), Pro Android IoT (Apress,2016)。

Android Wear:探索有趣的 API 元素

在本章的其余部分,我将介绍 Android Wear 的一些其他重要功能,这些功能是我在本书前面没有介绍的。你可以在你的表盘代码基础设施上使用其中的大多数,这将允许你制作同时兼容表盘的 Android Wear 应用,这将成为 Android Wear 的一种趋势,因为智能手表硬件设备首先用于数字手表。这就是为什么我确保在本书中完整地介绍了 Watch Faces API。

检测位置:来自 Google Play 服务的 GPS 数据

一些穿戴应用 将需要知道它们的位置,以便能够计算行进的距离(健身和旅行应用),甚至确定用户在地球表面的哪个位置。一些用户,比如我自己,出于隐私原因,倾向于关闭智能手机上的 GPS 功能,除非有令人信服的理由允许自己被跟踪。在考虑使用 GPS 数据的穿戴应用时,也要记住这一点。

许多可穿戴设备包括 GPS 传感器,它可以检索位置数据,而无需绑定到另一个设备,如平板电脑或智能手机。幸运的是,当你在可穿戴应用中请求位置数据时,无需担心这些位置数据来自哪里。Android 可以确定位置数据,并在智能手机或智能手表上使用节能方法更新数据。

穿戴应用应当能够处理位置数据的任何丢失,以防穿戴硬件设备失去与配对设备的连接,并且不碰巧具有 GPS 传感器硬件。

可穿戴设备上的 GPS 位置数据将通过 Google Play 服务(location API)获得,您已经在本书中学习了如何利用该服务。对于 GPS,您将利用 FusedLocationProviderApi 及其附带的类来获取位置数据。要访问 GPS 定位服务,创建您的 GoogleApiClient 实例,这是任何 Google Play 服务 API 的主要入口点,就像您在本书中为 Android Wear Watch Faces API 实现所做的一样。

为了连接到 Google Play 服务,创建一个 GoogleApiClient 的实例,并创建一个活动来指定接口 ConnectionCallbacksOnConnectionFailedListenerLocationListener 的实现。为了管理连接生命周期,在 onResume()方法中调用 connect(),在 onPause()方法中调用 disconnect(),类似于下面的 Java 代码:

public class WearableMainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener, LocationListener {
    private GoogleApiClient myGoogleApiClient;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addApi(Wearable.API)        // used for the data layer API
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
    }
    @Override
    protected void onResume() {
        super.onResume();
        mGoogleApiClient.connect();
    }
    @Override
    protected void onPause() {
        super.onPause();
        mGoogleApiClient.disconnect();
    }
}

应用连接到 Google Play 服务后,就可以接收位置更新了。当 Android 调用。在客户机的 Connected()回调中,您将通过创建 LocationRequest 对象并使用诸如之类的方法设置选项来构建位置数据请求。setPriority( ) 。您可以使用请求位置更新。onConnected()中的 requestLocationUpdates( ) 或使用删除位置更新。在 onPause()方法 中的 removeLocationUpdates( ) ,可以在下面实现的方法示例代码中看到:

@Override
public void onConnected(Bundle bundle) {
    LocationRequest locationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(UPDATE_INTERVAL_MS)
            .setFastestInterval(FASTEST_INTERVAL_MS);
    LocationServices.FusedLocationApi
            .requestLocationUpdates(myGoogleApiClient, locationRequest, this)
            .setResultCallback(new ResultCallback() {
                @Override
                public void onResult(Status status) {
                    if (status.getStatus().isSuccess()) {
                        if (Log.isLoggable(TAG, Log.DEBUG)) {
                            Log.d(TAG, "Successfully requested location updates");
                        }
                    } else { Log.e(TAG, "Failed in requesting location updates, "
                                        + "status code: " + status.getStatusCode()
                                        + ", message: " + status.getStatusMessage());
                    }
                }
            });
}
@Override
protected void onPause() {
    super.onPause();
    if (myGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(myGoogleApiClient, this);
    }
    myGoogleApiClient.disconnect();
}
@Override
public void onConnectionSuspended(int i) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "connection to location client suspended");
    }
}

一旦您启用位置更新,系统会调用一个**。onLocationChanged( )** 方法,使用通过指定的间隔值更新位置。setInterval( ) 方法调用。

并非所有智能手表硬件都配备 GPS 传感器。如果用户出去兜兜风,却把手机或平板电脑落在了身后,这款可穿戴应用就无法通过有线连接接收 GPS 定位数据。如果智能手表硬件没有 GPS 传感器,您可以检测到这种情况,并警告用户 GPS 定位功能当前不可用。这是通过使用。hasSystemFeature()方法调用。这个 Java 代码示例轮询一个设备,看它在开始活动时是否有 GPS:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
    if ( !hasGpsData() ) {
      // Fall-back functionality, for use without location data (Or warn user: No location
data!)
    }
}
private boolean hasGpsData() {
    return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS);
}

依靠有线连接获取位置数据的智能手表硬件设备也有可能突然失去连接。如果您的智能手表应用处理持续的 GPS 数据流,您将需要根据数据中断或变得不可用的位置来处理断开连接。在没有 GPS 传感器的智能手表硬件上,当智能手表失去其有线数据连接时,可能会丢失 GPS 位置数据。

如果您的应用依赖有线数据连接来获取位置数据,而穿戴设备没有 GPS 传感器,您应该检测连接丢失,警告用户,并更改应用的功能。

为了检测一个连接数据的丢失,你需要扩展一个 WearableListenerService。此服务将允许您处理数据层事件。为此,在 AndroidManifest 中声明一个,通知 Android 有关 WearableListenerService 的信息。此过滤器将允许操作系统在需要时绑定到您的 WearableListenerService。

定义 ListenerService 类的父标签和 BIND_LISTENER 子标签< intent-filter >的 XML 标记应该如下所示:

<service android:name=".MyNodeListenerService">
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
    </intent-filter>
</service>

在 WearableListenerService 的 MyNodeListenerService 子类中,您可以实现**。onPeerDisconnected( )** 方法 用于处理智能手表硬件设备是否内置 GPS 的情况。Java 代码如下所示:

public class MyNodeListenerService extends WearableListenerService {
    @Override
    public void onPeerDisconnected(Node peer) {
        if( !hasGpsData() ) {
          // Fall-back to functions that don't use location or notify user to bring their
handset
        }
    }
}

如果它丢失了 GPS 数据(信号),您应该使用。getLastLocation()方法调用。在无法获取 GPS 数据的情况下,或者智能手表没有内置 GPS 功能,并且与手机或平板电脑失去连接时,这种方法会很有帮助。下面是如何使用?getLastLocation()方法调用(记住在您的清单 XML 文件中请求 GPS 定位权限)以便检索最近已知的 GPS 定位数据(如果可用的话):

Location location = LocationServices.FusedLocationApi.getLastLocation(myGoogleApiClient);

如果您的智能手表应用使用手表上的 GPS 记录数据,您也可以将智能手表位置数据与智能手机位置数据同步。使用 LocationListener,您可以实现一个**。onLocationChanged( )** 方法

@Override
public void onLocationChanged(Location location) {
    addLocationEntry(location.getLatitude(), location.getLongitude());
}
private void addLocationEntry(double latitude, double longitude) {
    if (!mySavedGpsLocation || !myGoogleApiClient.isConnected()) { return; }
    mCalendar.setTimeInMillis(System.currentTimeMillis());
    String path = Constants.PATH + "/" + mCalendar.getTimeInMillis();
    PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path);
    putDataMapRequest.getDataMap().putDouble(Constants.KEY_LATITUDE, latitude);
    putDataMapRequest.getDataMap().putDouble(Constants.KEY_LONGITUDE, longitude);
    putDataMapRequest.getDataMap().putLong(Constants.KEY_TIME, mCalendar.getTimeInMillis());
    PutDataRequest request = putDataMapRequest.asPutDataRequest();
    Wearable.DataApi.putDataItem(myGoogleApiClient, request)
            .setResultCallback(new ResultCallback() {
                @Override
                public void onResult(DataApi.DataItemResult dataItemResult) {
                    if (!dataItemResult.getStatus().isSuccess()) {
                        Log.e(TAG, "Failed to set the data, "
                                + "status: " + dataItemResult.getStatus()
                                .getStatusCode());
                    }
                }
            });
}

另一个在 Wear 智能手表硬件中越来越受支持的主要功能是一个麦克风,接下来我们来看看语音识别!

语音动作:使用语音识别技术

我们都记得(或者我们记得吗?)先锋迪克·特雷西(什么时候会有人把这个翻拍成卖座片?)漫画系列,主人公迪克·特雷西能够对着他的智能手表说话,引发奇迹,总能化险为夷。

现在你也可以让你的用户与你的智能手表应用对话,让它为他们创造奇迹,因为语音动作已经成为 Android Wear 用户体验的一个不可或缺的部分,允许用户不用手就能相对快速地触发应用动作。

Android Wear 允许开发人员进行两种类型的语音操作。系统提供的语音动作基于操作系统任务,内置于 Wear 平台。当使用 Android 中的语音识别引擎调用特定的语音动作时,您可以使用 IntentFilter 对象为您希望启动的活动定义它们,我将在稍后讨论这一点。系统提供的语音操作的一些例子包括“记笔记”、“设置闹钟”或“叫出租车”,甚至“启动秒表”等等。

第二种语音动作是应用提供的语音动作。这些语音操作是基于应用的,您可以像启动器图标一样声明它们。为了利用这些动作,用户将声明“start”和一个动作(指定为标签),以便利用语音动作,并且您指定的活动子类将随后启动(Start)。

Android Wear 平台提供了许多基于用户动作的语音意图常量,允许用户说出他们想要做的事情,并让系统计算出要启动的最佳活动。这些包含在 android.speech 包的 RecognizerIntent 类中,如果您想研究它们,可以在下面的 android 开发人员网站 URL 中找到:

http://developer.android.com/reference/android/speech/RecognizerIntent.html

当用户调用语音操作时,您的应用可以过滤用于启动相关活动的意图,如您的清单中所定义的。

如果您想启动一个服务,将活动显示为可视提示,并在该活动中启动服务。务必打电话给**。当你想去掉活动(服务)的可视提示包装器时。**

例如,应该用来实现“Take a Note to Self”命令的 XML 标记块在将启动名为 SelfNote 的活动子类的父中声明了子,如下所示:

<activity android:name="SelfNoteActivity">
  <intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="com.google.android.voicesearch.SELF_NOTE" />
  </intent-filter>
</activity>

如果没有任何语音意图常量适用于您的应用,您可以使用自定义的“开始活动[此处输入名称]”语音操作直接启动您的应用。注册应用以使用自定义“开始”动作的方法与注册启动器图标的方法相同。不是在启动器 XML 结构中请求应用图标,而是一个应用将请求一个语音动作,使用 android:label 参数。要指定用户在“开始:”后使用的语音命令,请为您希望启动的活动指定标签属性。例如,这个<意图过滤器>构造识别“开始:语音控制应用”语音动作,启动 StartVoiceActivity:

<application>
  <activity android:name="StartVoiceActivity" android:label="VoiceControlApp">
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>
</application>

除了结合 Android Wear 中的内置语音识别常量使用语音操作来启动活动,您还可以使用 SpeechRecognizer 类来处理“自由形式”或非标准的语音输入,该类访问识别服务类和识别意图类,后者访问前面提到的预定义语音操作常量。

这些只是 android.speech 包的部分组件。这个语音识别器类和相关类将被用来从用户那里获得免提语音输入,并处理该语音输入,提供一个结果。

这方面的一个例子可能需要发送短文本消息或为搜索引擎指定搜索项。值得注意的是,语音 API 并不打算用于连续语音识别,例如您可以使用专业语音软件包,如 Nuance Communications 的 Dragon Naturally Speaking Premium 软件。微软 Windows 8.1 还内置了语音识别功能。

你不想用 Android Wear 进行连续语音识别的原因是,尽管 Android 操作系统非常强大,但它仍然只是一个嵌入式设备操作系统,目前不具备执行连续语音识别和翻译的 CPU 内核、内存和时钟速度。

试图在 Wear 应用内完成这种级别的语音识别将消耗大量的电池寿命、处理开销和网络带宽。SpeechRecognizer 方法需要使用主应用线程来调用。您使用一个 ACTION_RECOGNIZE_SPEECH 操作调用 startActivityForResult( ) ,启动一个语音识别活动。然后,您将使用下面的 Java 结构在 onActivityResult()方法中处理结果:

private static final int SPEECH_REQUEST_CODE = 0;
private void displaySpeechRecognizer() {
    Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);     // Create an Intent object
    intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                    RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
    startActivityForResult(intent, SPEECH_REQUEST_CODE);               // Start the Activity
}
@Override // The callback which is invoked when the Speech Recognizer returns
protected void onActivityResult(int requestCode, int resultCode, Intent data) {    // Process Intent    if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
        List<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
        String spokenWord = results.get(0);
        // Process Voice Recognition Result
    }
    super.onActivityResult(requestCode, resultCode, data);
}

添加位置感知和语音识别可以让您的应用感知!

摘要

恭喜你,你已经完成了这本专业的 Android 可穿戴设备书籍,并获得了成功!您正在创建创新的穿戴应用,并准备好引发这场物联网革命!

在这最后一章中,除了 Wear 之外,您还了解了 Android OS 最近引入的一些额外的物联网 API。你看了一下 Android TV,它允许你为用户现在安装在他们客厅的 64 到 96 英寸的大屏幕创建应用。

接下来,您了解了新的 Android Auto API,它允许您创建在汽车仪表板环境中无缝工作的应用,并利用内置的汽车硬件,如音频扬声器。

你还看了一下谷歌眼镜 1,即将成为 Android 眼镜 2,以及 Android Wear 的其他一些你应该考虑使用的功能。

接下来,您看了一下使用 GPS 传感器硬件数据、Google 移动服务(GMS)和 Google Play 服务实现基于位置的服务。

本章最后介绍了最新语音识别技术的使用,以及 Android 提供的内部语音动作命令。