PhoneGap-初学者指南-全-
PhoneGap 初学者指南(全)
原文:
zh.annas-archive.org/md5/0cfdb8b3b7385c6d5bb1fd49883e2110译者:飞龙
前言
PhoneGap 入门指南第三版将帮助您进入移动应用程序开发的领域。您将学习如何设置和配置您的移动开发环境,实现现代移动应用最常见的功能,并构建丰富、原生风格的应用体验。所有示例都涉及真实用例场景,涵盖各种插件的功能。
本书涵盖内容
第一章, PhoneGap 简介,介绍了如何在开发环境中设置依赖项和移动平台 SDK。
第二章, 构建您的第一个 PhoneGap 项目,涉及 PhoneGap 内部结构、项目结构和使用 CLI 工具。
第三章, 移动框架,介绍了各种移动框架以及使用 jQuery Mobile 的示例项目。
第四章, 使用插件,涵盖了有关 PhoneGap 插件及其在应用程序中使用的信息。
第五章, 使用设备存储和文件 API,涉及离线数据存储能力和文件插件。
第六章, 使用联系人及相机 API,介绍了如何实现联系人 API 以及如何通过相机 API 与设备相机进行交互。
第七章, 访问设备传感器和位置 API,涉及使用设备传感器和位置 API 及其与 PhoneGap 提供的插件相比的优缺点。
第八章, 高级 PhoneGap,涵盖了添加多语言和触摸手势支持等一些高级主题。
第九章, 准备发布,帮助您了解如何使您的应用程序在各种应用商店中准备公开发布。
第十章, 一个示例 PhoneGap 项目,涉及从头开始开发一个完整的 PhoneGap 应用程序,使用最常用的 PhoneGap 插件。
附录 A, JavaScript 快速参考表,是常用 JavaScript 方法和属性的迷你参考表。
附录 B, 发布您的应用,描述了您如何在不同的应用商店发布您的应用。
附录 C, 相关插件资源,列出了一些可以与 PhoneGap 一起使用的相关插件。
附录 D,PhoneGap 工具,描述了可以帮助您无困难地调试和测试应用程序的工具。
您需要这本书什么
一台连接到互联网的个人电脑以及与 PhoneGap 一起分发的命令行工具。OS X 和 Linux 用户只需要默认的命令行工具。
这本书面向的对象
这本书是为希望快速在移动市场提高生产力的网络开发者而写的。实际上,通过使用 PhoneGap,可以基于网络标准部署原生应用程序。本书假设对 HTML/CSS/JavaScript 和移动平台(如 Android、BlackBerry、iOS 和 Windows Phone)有非常小的了解,并逐步引导读者深入了解 PhoneGap 及其 API。
部分
在这本书中,您会发现一些频繁出现的标题(行动时间、刚刚发生了什么?、快速测验和英雄尝试)。
为了清楚地说明如何完成一个程序或任务,我们使用以下部分如下:
行动时间 – 标题
-
行动 1
-
行动 2
-
行动 3
指令通常需要一些额外的解释以确保它们有意义,因此它们后面跟着这些部分:
刚刚发生了什么?
本节解释您刚刚完成的任务或指令的工作原理。
您还会在书中找到一些其他的学习辅助工具,例如:
快速测验 – 标题
这些是旨在帮助您测试自己理解的简短多项选择题。
英雄尝试 – 标题
这些是实际挑战,为您提供实验您所学内容的想法。
惯例
您还会发现许多文本样式,它们区分了不同类型的信息。以下是一些这些样式的示例及其含义的解释。
文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 处理方式如下所示:“您可能会注意到我们使用了 Unix 命令 rm 来删除 Drush 目录,而不是 DOS 的 del 命令。”
代码块按以下方式设置:
document.addEventListener("deviceready", function() {
// Application starts here
});
当我们希望将您的注意力引到代码块的一个特定部分时,相关的行或项目将以粗体显示:
reader.onload = function(evt){
var img = document.querySelector('#firstImage');
img.src = evt.target.result;
};
任何命令行输入或输出都按以下方式编写:
C:\example1> phonegap prepare android
新 术语 和 重要 词汇 以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,在文本中如下所示:“通过点击面板右下角的 运行 按钮在控制台中查看结果。”
注意
警告或重要注意事项以如下方式出现在框中。
小贴士
小技巧和技巧如下所示。
读者反馈
我们欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢或不喜欢的地方。读者的反馈对我们非常重要,因为它帮助我们开发出您真正能从中获得最大收益的标题。
要发送一般反馈,请简单地通过电子邮件发送至mailto:feedback@packtpub.com,并在邮件主题中提及书籍的标题。
如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在您已成为 Packt 书籍的骄傲拥有者,我们有一些事情可以帮助您从购买中获得最大收益。
勘误
尽管我们已经尽一切努力确保内容的准确性,但错误仍然会发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进此书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告它们。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题的勘误部分下的现有勘误列表中。
要查看之前提交的勘误表,请访问www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将在勘误部分显示。
侵权
互联网上版权材料的侵权是一个持续存在的问题,涉及所有媒体。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现任何形式的我们作品的非法副本,请立即提供地址或网站名称,以便我们可以寻求补救措施。
请通过链接mailto:copyright@packtpub.com与我们联系,并提供涉嫌盗版材料的链接。
我们感谢您在保护我们作者和我们为您提供有价值内容的能力方面的帮助。
询问
如果您在这本书的任何方面遇到问题,您可以通过链接mailto:questions@packtpub.com与我们联系,我们将尽力解决问题。
第一章. PhoneGap 简介
PhoneGap 是一个混合应用框架,它允许您使用 HTML 和 JavaScript 构建原生应用。开发者可以编写一次代码,并将他们的应用部署到多个移动操作系统上。PhoneGap 提供了一个 JavaScript 编程接口,允许您使用纯 JavaScript 访问与操作系统相关的功能。本章涵盖了关于 PhoneGap 以及设置您的开发环境的信息。
在本章中,我们将涵盖以下主题:
-
理解 PhoneGap 是什么
-
了解 Apache Cordova/PhoneGap 的历史概述
-
PhoneGap 和 Cordova 的演变
-
学习移动网页和移动应用之间的区别
-
学习如何配置您的开发环境和所有依赖项
-
学习如何使用 JBoss、Xcode 和 Visual Studio 创建新项目
关于 PhoneGap
PhoneGap,也称为 Apache Cordova,是一个开源的混合移动应用开发框架。它使用标准且广为人知的 Web 技术,如 HTML、层叠样式表(CSS)和 JavaScript,在不使用原生开发语言的情况下创建跨平台的移动应用。
如果您想开发原生安卓应用,您应该了解与 Java 和 Android SDK 相关的 API。对于 iPhone 应用,您需要了解 Objective-C 或苹果的新 Swift 语言。同样,对于所有其他移动平台,您需要了解它们各自的编程语言。您为某个平台编写的代码在另一个平台上可能无法工作。为了克服这些困难,我们现在有一个混合移动开发框架,称为 PhoneGap。您使用 PhoneGap 开发的程序可以部署到其他几个平台上。这是一个一次编写,到处部署的框架。
以下是 PhoneGap 目前支持的移动平台列表:
-
安卓
-
iOS
-
亚马逊 FireOS
-
黑莓 10
-
Firefox OS
-
Ubuntu
-
Windows Phone 8
-
Windows 8
-
Tizen
-
Windows 7
-
浏览器
该框架使开发者能够使用简单的 JavaScript 访问所有硬件相关功能。开发者将能够通过简单的函数访问设备联系人、位置、文件、设备信息等,更多功能。请注意,现在支持一个新的平台,称为“浏览器”。听起来很有趣?这取决于您来增加您的好奇心。
PhoneGap 和 Apache Cordova
PhoneGap最初由 Nitobi 开发,该公司后来在 2011 年被 Adobe 收购。收购后,Adobe 将 PhoneGap 代码库捐赠给了Apache 软件基金会(ASF),项目名为Cordova,这是温哥华街道的名字,Nitobi 的办公室就位于那里,公司在那里创建了 PhoneGap 的第一个版本。
将代码库迁移到 ASF 的最大优势之一是任何人都可以轻松地为项目做出贡献。许多公司不仅对 Apache 风格的许可证感到舒适,而且已经与 Apache 签署了贡献者许可协议。对于有兴趣为充满活力的开源项目做出贡献的开发者来说,Apache Cordova 是一个很好的机会。
PhoneGap 是 Apache Cordova 的免费和开源分发版。将 Cordova 想象成 PhoneGap 及其相关服务(调试、模拟和构建服务)所构建的引擎。Adobe 提供了一个集成平台,只需单击一下即可为多个平台构建应用程序。如果你不想使用 Adobe 的构建功能,你必须手动为每个移动平台构建应用程序。除此之外,对于 PhoneGap 和 Cordova 开发者来说,没有任何变化。
Adobe 在该项目中继续扮演着重要角色,投资于其持续发展,并且公司决定保留 PhoneGap 这个名称来描述其 Cordova 项目的分发。Apache Cordova 项目的其他贡献者包括 Google、RIM、Microsoft、IBM、Nokia、Intel 和惠普。有关 Cordova 和 PhoneGap 的更多详细信息,请参考以下在线资源。它们提供了许多有用的信息和教程,供初学者开始开发:
注意
在本书中,我们将使用 PhoneGap 和 Cordova 这两个术语,技术上它们是相同的。你可以将 Cordova 和 PhoneGap 互换使用。
PhoneGap 和 Cordova 的发展历程
自 2012 年 1 月以来,PhoneGap 发展非常迅速,每年都有多次发布。Apache Cordova 使用 语义化版本控制 规范(更多信息请参阅 semver.org)。这是一个非常好的实践,它告诉开发者一个发布版本是否有主要或次要的改进。
自 1.4 版本以来,该项目被称为 Apache Cordova。这个版本通常被认为是框架的第一个稳定版本,拥有相当完整和最新的文档。1.5 版本修复了长长的错误列表,但社区最初的反应并不非常积极,因为文档已经过时,对主要文件的一些更改导致了使用早期版本开发的应用程序构建问题。
1.6 版本对插件架构、相机和指南针 API 以及项目模板文件进行了一些改进。正如成熟社区通常所发生的那样,这次发布并不完美,但与之前的版本相比,整体质量有了显著提升。1.7 和 1.8 版本是错误修复,并增加了对 Bada 2.0 的支持。由于发布速度较快,社区反应也较为积极。1.9 版本解决了更多的错误修复,并增加了对 iOS 和 Android 平台新特性的支持。
Apache Cordova 2.x 增加了以下功能和支持:
-
定义一个独特的 JavaScript 文件,以便在所有平台上使用,这得益于 Cordova 应用程序框架 JavaScript 层的统一。
-
通过命令行工具(CLI)的引入,可以以标准方式执行常见操作,如项目创建、调试和仿真,适用于(Android、iOS 和 BlackBerry)
-
能够使用 Cordova WebView 将 PhoneGap 应用程序嵌入到更大的原生 iOS 和 Android 应用程序中。
-
支持 Windows phone 平台。
-
将Web Inspector Remote(Weinre)移植到 Node.js,并引入了一个便于使用Node 包管理器(npm)安装的 node 模块。
-
插件文档的改进。
-
对创建 iOS 应用程序的过程进行了多项改进。
-
每个平台可用的命令标准化(即
build、run等)
随着 2.x 版本的发布,Apache Cordova 和 PhoneGap 已成为移动开发者工具箱中成熟、稳定且强大的工具。
自 2013 年 9 月引入 3.0 版本以来,PhoneGap 使用了一种新的插件架构,以保持应用程序核心小且性能快速。可以使用更新的 Cordova 命令行界面(CLI)安装和卸载插件。PhoneGap 3.0 还引入了几个新的命令行工具;例如,用户现在可以使用 npm 轻松安装 PhoneGap,而无需下载 ZIP 文件。PhoneGap 3.0 版本还发布了两个新的 API,即 InAppBrowser(之前称为 ChildBrowser)和 Globalization API。他们也开始讨论停止对 webOS、Symbian、Blackberry(BB7 及更早版本)和 Windows Phone 7 的支持。
在 PhoneGap 3.1.0 版本中,提供了对 FirefoxOS 和 Windows 8 的基本支持,以及其他错误修复和平台增强。2013 年 11 月发布的 PhoneGap 3.2.0 版本专注于稳定性和几个错误修复。2013 年 12 月,发布了 PhoneGap 3.3.0 版本,增加了对 Android KitKat(v4.4)的支持。
随着 PhoneGap 3.4.0 的发布,增加了对 FirefoxOS 的高级支持。PhoneGap 3.5.0 是最后一个支持 Windows 7 的版本。随着 PhoneGap 3.6.3 的发布,增加了 Cordova 支持的命令。PhoneGap 的每个新版本都部署了几个新功能。在撰写本书时,我们有 PhoneGap 5.1.1,它提供了对 PhoneGap 桌面应用和移动应用的高级支持。
选择开发操作系统
PhoneGap 遵循规则。如果供应商只为单个操作系统发布 SDK,那么您将不得不使用该操作系统来构建和部署您的应用。
详细来说,对于每个 PhoneGap 平台:
-
您可以在任何主要的桌面操作系统上开发Android应用——Windows、Mac OS X 或 Linux
-
您可以在任何操作系统上开发Symbian Web Runtime应用,但您只能从 Windows 运行模拟器
-
您可以在任何主要的桌面操作系统上开发BlackBerry应用——SDK 可以安装在 Windows 或 Mac OS X 上(运行模拟器时,您需要安装 SDK 附带提供的虚拟机)
-
Windows Phone 8 SDK 在 Windows 8 或 Windows 8 Pro 上运行
-
iOS SDK 需要 OS X 10.7 或更高版本(根据 OS X EULA,还需要一台 Mac 电脑)
注意
您可以使用 Ripple(目前孵化在 Apache 软件基金会incubator.apache.org/projects/ripple.html的 Chrome 扩展)或使用emulate.phonegap.com提供的在线仿真服务在桌面浏览器中模拟应用。
实际上,您在移动开发上最好的选择是获取一台 Mac,并在单独的分区上安装 Windows,或者使用Parallels或VMWare Fusion在虚拟环境中运行它。根据苹果的法律条款,您不能在非苹果硬件上运行 Mac OS X;如果您坚持使用 Windows PC,您将能够为除 iOS 以外的每个平台构建。然而,您仍然可以通过使用 VirtualBox 并在 Windows PC 上运行 OS X 来使用 OS X。
总之,有了新的 CLI 工具,为所有主要移动平台构建应用变得越来越容易。移动开发者对构建跨平台应用中涉及的问题非常清楚;不出所料,build.phonegap.com服务因其允许开发者使用他/她的首选操作系统而开始变得相当受欢迎。注册此服务后,可以从一个共同的代码库开始构建跨平台应用。您可以上传代码库或从 GitHub 仓库中拉取。本书的最后,我包括了一个关于移动应用分发过程的章节。
移动网页与移动应用
有许多讨论集中在决定应该进行哪种类型的发展:响应式网站或原生应用。一些流行的网站,例如 Gmail,既有移动友好的网站,也有原生应用。为了更好地理解这一点,让我们看看这些是什么:
| 响应式网站 | 移动应用 |
|---|---|
| 响应式网站可以在从移动设备到桌面计算机的各种设备上工作。这种设计方式通常被称为响应式网页设计(RWD)。 | 应用是为移动设备设计的。它们不是为桌面计算机设计的。 |
| 响应式设计为所有设备上的用户提供更好的观看体验。 | 移动应用提供典型的移动体验,整个界面都是针对移动设备设计的。 |
| RWD 涉及一些开发概念,如流体网格、CSS 媒体查询、响应式表格和图像。 | 使用与移动相关的布局模式。 |
| 响应式网站能够很好地适应设备的屏幕尺寸,这使得阅读内容更加容易。 | 内容通常是为移动屏幕尺寸设计的。可读性总是更好。 |
| 移动网页是一个普通的网站,它能够适应移动设备。 | 移动应用具有比简单网站更多的功能。 |
| 响应式设计是移动应用的低成本替代方案。 | 需要投入专门的开发时间和资源进行应用开发。 |
| 与任何其他网站一样,它们受限于浏览器的功能。 | 通过使用相机、加速度计等与设备相关的 API,提供丰富的用户体验。 |
关于响应式设计(RWD)及其背后的设计原则的更多信息,请阅读以下基础内容:developers.google.com/web/fundamentals/layouts/rwd-fundamentals/
总结来说,响应式设计和移动应用都有其优缺点。决定你要开发哪种类型的是你的业务或需求。如果你打算开发移动应用,你应该针对多个移动平台,而且在短时间内为所有平台编码是不太可能的。这时,使用 PhoneGap 开发的混合移动应用就显示出了其优势。
安装依赖项
为了准备好构建 PhoneGap 应用,必须从相应的官方网站下载并安装每个目标平台的最新 SDK:
-
BlackBerry 10:
developer.blackberry.com/html5/downloads/#blackberry10 -
Firefox OS:不需要特殊的 SDK
-
Windows 8 Phone:
www.microsoft.com/en-in/download/details.aspx?id=35471
每个前面的平台 SDK 都有自己的设置配置,建议参考各自的网站以获取最新信息。要使用多个 IDE,例如 NetBeans、Eclipse 或 JBoss Developer Studio,我们需要 Java 可用。
Java JDK
你需要安装最新的Java JDK(不仅仅是 JRE)。接下来,为JAVA_HOME创建一个环境变量,指向 Java JDK 安装的根文件夹。例如,如果你的安装路径是C:\Program Files\Java\jdk7,将JAVA_HOME设置为这个路径。之后,将 JDK 的bin目录(C:\Program Files\Java\jdk7\bin)也添加到PATH变量中。
Apache Ant
我们需要安装并配置Apache Ant。更新你的PATH变量以包含安装文件夹中的bin文件夹。对于高级配置细节,请访问ant.apache.org/manual/index.html。
由于 PhoneGap 框架的最新变化,始终建议使用命令行界面来创建新项目、更新框架或安装插件。要使用命令行,我们需要Node.js应用程序。首先,从nodejs.org下载并安装 Node.js。一旦完成,你应该能够在命令行中通过调用npm或node来验证安装。你可能需要将npm目录添加到系统PATH中,以便调用全局安装的 npm 模块。
在每个安装的末尾,你应该能够使用以下命令验证每个软件包的安装:
android list sdk
javac –version
ant –version
node -v
如果前面的任何命令行没有按预期工作,你可能需要重新检查你的系统PATH设置。例如,PhoneGap 和 Cordova 需要将ANDROID_HOME环境变量设置在PATH中。这应该指向[ANDROID_SDK_DIR]\android-sdk目录(例如,c:\android\android-sdk)。
接下来,更新你的PATH变量以包含tools/和platform-tools/文件夹。因此,使用ANDROID_HOME,你应该添加%ANDROID_HOME%\tools和%ANDROID_HOME%\platform-tools。
对于 OS X 用户,安装 ios-sim 也非常重要。ios-sim 工具是一个命令行实用程序,可以在模拟器上启动 iOS 应用程序。要安装此工具,你可以再次使用 npm,如下所示:
npm install –g ios-sim
根据你的权限,你可能需要以管理员身份运行npm命令(即在npm命令前添加sudo)。
一旦安装了 SDK,建议您还安装 GitHub 客户端。GitHub 是一个社交编码平台,您可以在其中找到大多数酷的开源项目(如 Apache Cordova),包括访问最新的补丁、构建和源代码。您可以使用 PhoneGap 的免费在线构建服务从公共 GitHub 仓库轻松地为多个平台构建项目。
安装 PhoneGap
从 Apache Cordova 2.0 开始,安装过程和开发环境的设置已经变得容易得多。在 2.0 时代之前,PhoneGap 的安装过程很令人困惑,因为有很多依赖项。这些依赖项是由于,为了为不同的平台编译应用程序,您不仅需要平台特定的 SDK,还需要平台特定的工具;例如,为了构建 Android,需要 Eclipse、IntelliJ 或 Android Studio;为了构建 iOS,需要 Xcode;等等。
现在,您可以使用 Cordova 随附的一系列命令行工具,这使得开发跨平台应用程序更加容易。安装 Cordova 和 PhoneGap 现在就像运行几个命令一样简单。请注意,我们可以使用与 Cordova 或 PhoneGap 相关的 CLI 命令。PhoneGap CLI 提供的功能比其 Cordova 对应版本更多,例如支持 PhoneGap 桌面应用程序和移动应用程序。
为了启动开发,让我们创建一个新的项目。我们将使用 npm 实用程序 Node.js 自动下载最新的框架代码。
在 Windows 环境中,运行以下命令:
C:\>npm install -g phonegap
在 Linux 和 OS X 上,运行以下命令:
$ sudo npm install -g phonegap
在前面的命令中使用 -g 标志,我们指示 npm 全局安装 PhoneGap;否则,它将在当前目录中安装。一旦安装过程完成,请在命令行终端中运行以下命令以验证安装。这应该返回 PhoneGap 的最新版本号。
phonegap --version
一旦我们验证了 Cordova 的安装,我们可以使用以下命令创建一个新的应用程序项目:
phonegap create hello
完成此命令可能需要一些时间。这是创建一个名为 hello 的项目的最小语法要求。在当前工作目录中将会创建一个名为 hello 的新文件夹。目录内容将如下所示:
├── hooks
├── merges
├── platforms
├── plugins
├── www
| └── css
| └── img
| └── js
| └── index.html
├── config.xml
在 www 文件夹中,您将找到运行与分发二进制文件捆绑的示例 PhoneGap 应用所需的 HTML/JS/CSS 文件。
设置您的开发环境
本节提供了设置 Android、iOS 和 Windows Phone 开发环境的详细指南。为了基于简单的文本编辑器设置开发环境,只需使用命令行工具运行几个命令即可。
行动时间 - 使用 PhoneGap 3.x 设置 Android
准备设置 Android 开发环境并使用 Android 作为目标平台创建 PhoneGap 应用。在前一节中,我们看到了如何创建一个新项目。现在,是时候将所需的平台添加到项目中了。像往常一样,在 OS X 上使用终端或在 Windows 上使用 DOS 提示符来运行这些命令。
-
启动一个命令行工具(DOS 或终端)并将目录更改为我们刚刚下载的 Cordova/PhoneGap 分发目录,如下所示:
$ cd hello -
为了创建一个针对 Android 的 PhoneGap 项目,您只需运行命令将平台添加到项目中:
$ phonegap platform add android
该命令告诉 Cordova 为 Android 平台添加支持。在命令成功执行后,您可以在平台目录内看到一个名为 android 的新目录。它包含所有平台相关的文件。
现在是时候在模拟器中运行项目了。以下命令将需要一些时间来完成:
$ phonegap emulate android
工具将检查是否已定义了一些虚拟设备,如果没有定义,将提示用户定义一个。如果已经定义了多个设备,工具将询问使用哪一个。
发生了什么?
您已创建了一个 PhoneGap 项目,并在 Android SDK 中配置的测试设备之一中模拟了该应用。
与其他平台一起工作
要与 iOS、Windows Phone 等其他平台一起工作,您必须遵循与我们为 Android 所做的相同过程。您可以添加到项目中平台列表取决于平台名称。在添加平台之前,您应该确保您的机器上已安装所需的 SDK。
在 Windows 机器上,如果您已为每个平台安装了所需的 SDK,则可以运行以下任何命令。您不会在这里看到 iOS 平台,因为我们需要 Mac 机器来处理 iOS:
C:\hello> phonegap platform add wp7
C:\hello> phonegap platform add wp8
C:\hello> phonegap platform add windows8
C:\hello> phonegap platform add amazon-fireos
C:\hello> phonegap platform add android
C:\hello> phonegap platform add blackberry10
C:\hello> phonegap platform add firefoxos
在前面的命令中,wp7 和 wp8 分别表示 Windows Phone 7 和 8。
在 Mac 机器上,安装相应的平台 SDK 后,您可以在以下平台上进行开发。您不会在这里看到 Windows Phone 平台,因为我们需要 Windows 操作系统来安装 Windows Phone SDK:
$ phonegap platform add ios
$ phonegap platform add amazon-fireos
$ phonegap platform add android
$ phonegap platform add blackberry10
$ phonegap platform add firefoxos
开始使用 Android 和 JBoss
有几个用于 Java 的 IDE,其中一些是 IntelliJ IDEA、Eclipse、NetBeans 和 JBoss Developer Studio。Android Studio 是 Android 开发最推荐和最常用的 IDE。然而,它不支持 Cordova/PhoneGap 开发。与其他 IDE 相比,JBoss Developer Studio 提供了一种非常简单方便的方式来创建和操作 PhoneGap 项目。您可以从 JBoss 网站下载最新版本,网址为 www.jboss.org/products/devstudio/download/。为了运行基于 Apache Cordova/PhoneGap 的示例应用程序,您需要安装 Android SDK,并将 JBoss Hybrid Mobile Tools 插件添加到您的 JBoss 安装中。此工具扩展了 JBoss 的功能,让您可以快速设置新的 Cordova 项目,添加 Cordova 插件,调试应用程序,甚至导出已签名(或未签名)的 APK 文件以分发应用程序。
开始行动 - 使用 JBoss Developer Studio
为了将 Hybrid Mobile Tools 安装到 JBoss 中,只需执行以下步骤:
-
启动 JBoss Developer Studio,然后导航到 帮助 | JBoss Central。
-
在 JBoss Developer Central 中点击 软件/更新 选项卡。
-
输入
JBoss Hybrid Mobile Tools或在列表中滚动以定位到 JBoss Hybrid Mobile Tools + CordovaSim。 -
选择相应的复选框,然后点击 安装。
-
安装后提示重启 IDE 时,点击 是 以重启 JBoss Developer Studio。
一旦 JBoss Hybrid Mobile Tools 安装配置正确,就可以使用相应的向导创建一个新项目。
前往 JBoss Hybrid Mobile Tools | 新建 | 其他。接下来,在 移动 部分中选择 Hybrid Mobile (Cordova) Application Project。点击 下一步:

输入 项目名称、名称 和 ID 的值。我们将使用 example 作为 名称,org.example 作为 ID,如下截图所示:

点击 下一步 以选择要使用的 Cordova 版本。我们总是选择最新版本:

再次点击 下一步 将显示安装一些插件的选项。对于本教程,我们不需要安装任何插件。因此,我们不选择任何内容。
点击 完成 以退出向导。
在向导结束时,将为您创建一个默认应用程序。现在让我们构建创建的应用程序,看看它在模拟器上的样子。点击 运行 工具栏图标,并选择 使用 CordovaSim 运行 选项以打开模拟器:

刚才发生了什么?
一旦构建过程完成,你将在屏幕上看到一个模拟器。有了这个,我们现在能够创建一个新的项目并成功模拟它:

开始使用 iOS 和 Xcode
为了开始为 iOS 设备开发应用,必须拥有一个 Mac 并下载 iOS SDK(Xcode),可在 Apple 开发者中心 developer.apple.com 获取。完成以下步骤以安装 Xcode:
-
从 App Store 安装 Xcode。
-
安装 Xcode 命令行工具(Xcode 首选项 | 下载 | 组件 | 命令行工具 | 安装)。
现在,让我们使用命令行工具创建一个 iOS 项目,如下所示:
$ phonegap create hello
$ cd hello
$ phonegap platform add ios
$ phonegap build
执行上述命令后,你将创建一个 Xcode 项目。你可以看到 hello/platforms/ios/hello.xcodeproj 文件,你可以双击它来打开它。有了这个,我们的项目已经设置好,可以调试和部署了。
为了部署应用,在工具栏上的 方案 下拉菜单中更改 目标 为 Hello(或当前项目名称),并在工具栏上的 方案 下拉菜单中更改 活动 SDK 为 iOS [版本] 模拟器。完成这些操作后,点击 运行 按钮。
注意
如果你正在寻找一个具有高级重构功能、更好的代码补全、对单元测试的强大支持以及强大的代码检查工具的 Objective-C 工具,你应该考虑购买 JetBrains 的 AppCode。更多信息可在 www.jetbrains.com/objc/ 找到。
开始使用 Windows Phone 和 Visual Studio
微软 Visual Studio 现在支持多设备混合应用的插件,包括 Cordova,这允许你在 Android/Windows Phone 上运行和调试应用。如果你想在实际设备上调试应用,你必须创建一个开发者账户在 dev.windowsphone.com/en-us/account,以便解锁调试真实设备的功能。
摘要
在本章中,你学习了如何使用 Apache Cordova 包含的 CLI 工具设置你的开发环境,以及如何与多个平台协同工作。下一章将帮助你选择一个开发环境,并展示如何在多个平台上调试你的第一个应用。
第二章:构建您的第一个 PhoneGap 项目
在第一章《PhoneGap 简介*》中,您学习了 PhoneGap 试图解决的问题之一——在多个平台上持续开发移动应用程序,以及如何设置您的构建环境。接下来,您将深入了解 PhoneGap 的内部结构。首先,您需要了解项目是如何构建的,以及 CLI 工具是如何使用的。
在本章中,您将:
-
查看 PhoneGap/Cordova 应用程序的结构
-
了解使用 Cordova 命令行工具
-
了解 Cordova 生命周期事件
-
创建一个示例
-
了解浏览器调试工具的概述
-
考虑到移动设备,回顾调试工作流程
-
学习如何构建和部署应用程序
PhoneGap 应用程序的结构
在上一章中解释了创建新项目之后,您将在项目的根目录内看到以下结构:
-
www: 这个目录包含应用程序的源文件,因此我们将在该目录中进行大量操作。它包含css、js和img子目录,您可以将相应的文件放置在这些子目录中。除了 CSS、JavaScript 和图像等资产外,我们还将有与应用程序相关的 HTML 文件。 -
merges: 在这个文件夹中,您可以添加任何特定于平台的定制,这样您就不必每次与项目一起工作时都修改源文件。例如,我们可以使用这个功能在 Android 设备上使用不同的字体样式。您想要覆盖的平台将有一个类似于www文件夹的特定文件夹,包含自己的 CSS、JS、HTML 和图像内容:merges/ |-- ios/ | '-- app.js |-- android/ | '-- app.js www/ '-- app.js在先前的目录结构中,全局应用程序包含
app.js文件,而在merges目录中,每个平台都有一个单独的app.js文件。在构建过程中,全局的app.js文件将被平台特定的app.js文件替换。 -
platforms: 这个目录将包含平台相关的构建文件。对于我们将添加到项目中的每个平台,我们都可以看到一个子目录。 -
plugins: 这是您将找到项目中使用的插件的目录。每次我们安装新的插件时,它们都将被添加到这里。 -
hooks: 这个目录可以包含可以用来自定义 Cordova/PhoneGap 命令的脚本。这主要针对需要与构建系统和版本控制系统集成的高级用户。 -
config.xml: 这个配置文件将包含所有特定于应用程序的值,例如应用程序名称、包名、版本号以及其他此类配置。
使用 PhoneGap CLI
您可以使用 Cordova 命令行界面初始化项目代码,之后您可以使用各种平台的 SDK 进一步开发它们。在上一章中,我们讨论了如何创建新项目,添加所需的平台,并使用 CLI 构建它们。
除了使用 CLI 工具创建项目外,Cordova 的 CLI 还执行其他几个功能。由于安装 CLI 的步骤已经介绍,让我们继续介绍高级 CLI 使用。一旦创建项目,使用 cd 命令进入它,您就可以执行各种项目级命令。
以下是最常用的 CLI 命令列表:
-
platform add <platform>: 这会将一个平台添加为项目的构建目标。 -
platform [rm | remove] <platform>: 这会移除之前已添加到项目中的平台。 -
platform [ls | list]: 这会列出项目将为其构建的所有平台。 -
platform [up | update] <platform>: 这会更新给定平台使用的 Cordova 版本。 -
plugin [ls | list]: 这会列出项目中包含的所有插件。 -
plugin add <path-to-plugin> [<path-to-plugin> ...]: 这会将一个(或多个)插件添加到项目中。 -
plugin [rm | remove] <plugin-name> [<plugin-name> ...]: 这会从项目中移除一个(或多个)插件。 -
plugin search [<keyword1> <keyword2> ...]: 这会在插件注册表中搜索与关键词列表匹配的插件。 -
prepare [platform...]: 这会将文件复制到指定的平台,或所有平台。然后它就准备好由 Eclipse、Xcode 等构建了。 -
compile [platform...]: 这会将应用程序编译成针对每个目标平台的二进制文件。如果没有参数,它将为所有平台构建;否则,它将为指定的平台构建。 -
build [<platform> [<platform> [...]]]: 这是对cordova prepare后跟cordova compile的别名。 -
emulate [<platform> [<platform> [...]]]: 这将启动模拟器并将应用程序部署到它们。如果没有参数,它将为项目中添加的所有平台模拟应用程序;否则,它将为指定的平台模拟应用程序。 -
serve [port]: 这将启动一个本地 Web 服务器,允许您在指定的端口(默认8000)上访问每个平台的www目录。
所有的上述 CLI 命令都需要在项目目录内执行。以下是一些示例用法:
c:\hello>phonegap platform add android
c:\hello>phonegap platform add ios
c:\hello>phonegap platform remove android
c:\hello>phonegap platform list
help 命令是一个全局命令,它显示一个包含所有可用命令及其语法的使用帮助页面。此命令可以在任何命令窗口中执行,而无需一定在项目目录内:
c:\> phonegap help
Cordova 事件
要最大化使用 Cordova 的好处,您应该了解所有可用的事件。它们被称为 生命周期事件,因为它们是您应用程序生命周期的一部分。这些事件默认对所有应用程序都可用,开发者可以使用它们来实现更好的设计。尽管有多个事件,但我们将讨论最重要的和最常用的事件。
deviceready 事件
deviceready事件是 Cordova 的重要事件,在 Cordova 的世界里,没有它你无法生存。当 Cordova 完全加载并且应用程序准备好使用时,会触发这个事件。我们应该知道应用程序何时准备好使用,因此这个事件就派上了用场。这个事件应该是所有应用程序功能的大门:
document.addEventListener("deviceready", function() {
// Application starts here
});
为了使代码易于理解,我们可以单独定义函数并将其绑定到事件,如下所示:
document.addEventListener("deviceready", onDeviceReady);
function onDeviceReady() {
// Application starts here
}
在线事件
当设备与互联网建立连接时,会触发online事件。通过这个事件,你可以确定你的应用程序当前是否处于在线状态。如果你的应用程序需要用户在线,这可能会很有帮助:
document.addEventListener("online", onOnline);
function onOnline() {
console.log('device is now online');
}
离线事件
如你所猜,offline事件是online事件的相反。当设备离线时,应用程序可以通过这个事件捕获它,并且开发者可以采取必要的行动:
document.addEventListener("offline", onOffline);
function onOffline() {
console.log('device is now offline');
}
注意
没有准确的方法来确定设备当前是处于在线还是离线状态。这些事件基于连接状态,有时可能会出错。请注意,即使设备连接到 2G、3G 或 Wi-Fi,这并不意味着设备在线。
暂停事件
当应用程序被移动到后台时,会触发pause事件,这通常发生在用户切换到另一个应用程序时。你可以使用这个事件来通知用户他们正在被带离应用程序:
document.addEventListener("pause", onPause);
恢复事件
当应用程序再次被带到前台时,会触发resume事件。这通常发生在pause事件之后,因为应用应该在移动平台的前台之前处于后台:
document.addEventListener("resume", onResume);
返回按钮事件
当用户在移动设备上按下返回按钮时,会触发backbutton事件。你可以使用这个事件来覆盖按下返回按钮时发生的默认操作:
document.addEventListener("backbutton", onBackbutton);
有几个其他事件是由外部插件支持的。有一个这样的插件详单,可以通过向项目中添加适当的插件来使用。
注意
并非所有事件都支持所有平台。例如,backbutton事件在 iOS 设备上不受支持。有关每个事件支持的完整平台列表,请参阅cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events上的文档。
动手实践时间 - Hello World 示例
PhoneGap 是一个介于移动设备和应用程序之间的中间层;应用程序位于浏览器内部,使用 PhoneGap API,你可以连接到电话功能,如联系人相机。
PhoneGap 应用程序的用户界面层是一个占据设备宽度和高度 100%的网页浏览器视图;将用户界面层视为一个浏览器。用户界面层被称为WebView。PhoneGap 使用的 WebView 与原生操作系统使用的相同。
在讨论了 PhoneGap 的基础知识和命令行工具之后,我们现在将创建一个简单的应用程序。这不是典型的 Hello World 示例。使用已经学到的命令和通过 npm 配置的环境,让我们创建一个新的项目:
C:\> phonegap create example1
C:\> cd example1
C:\example> phonegap platform add android
在完成前面的命令后,我们创建了一个名为example1的新项目,并将 Android 平台支持添加到了项目中。现在的目录结构如下:
example1
├── config.xml
├── hooks
├── merges
├── platforms
├── plugins
└── android
├── www
└── css
└── img
└── js
└── index.html
默认情况下,Cordova 的create脚本生成一个基于 Web 的应用程序骨架,其主页是项目的www/index.html文件。你可以随意编辑这个应用程序,但任何初始化都应该指定为deviceready事件处理程序的一部分,默认情况下从www/js/index.js引用。
当你打开www目录中存在的index.html文件时,你会看到 HTML 代码。body部分将与这里展示的代码类似。这是 CLI 工具为项目生成的默认body内容。它只显示一个带有图像和一些文本的页面:
<body>
<div class="app">
<h1>Apache Cordova</h1>
<div id="deviceready" class="blink">
<p class="event listening">Connecting to Device</p>
<p class="event received">Device is Ready</p>
</div>
</div>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript" src="img/index.js"></script>
<script type="text/javascript">
app.initialize();
</script>
</body>
输出将是以下内容:

对于一个复杂的应用程序,页面不会这么简单。首先,让我们修改页面以添加一些文本。修改后的代码如下,这是一个简单的静态 HTML 内容:
<body>
<h1>First Project</h1>
<h3>What we have learnt?</h3>
<ul>
<li>PhoneGap Structure</li>
<li>CLI Commands</li>
<li>Developer Tools</li>
<li>Debugging in Browsers</li>
<li>Cordova Events</li>
</ul>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener('deviceready', deviceready, false);
function deviceready() {
alert("Example Event");
}
</script>
</body>
如果在 HTML 的head部分顶部包含了任何 JavaScript 外部文件,浏览器将停止解析,直到文件下载完成。因此,建议将任何 JavaScript 文件或代码块添加到body标签的末尾,以减少等待时间。
现在,大多数现代浏览器支持脚本上的async和defer属性。这些属性告诉浏览器,在脚本下载的同时,它安全地继续解析:
<script type="text/javascript" src="img/script1.js" async></script>
<script type="text/javascript" src="img/script2.js" defer></script>
发生了什么?
我们从默认创建的项目中移除了代码,并添加了我们自己的内容。请注意,我们添加了deviceready事件监听器。当应用加载并准备好执行时,该事件将显示一个警告框。deviceready事件应该是所有与设备相关的操作的入口点。
当我们构建和模拟示例项目时,我们将在模拟器中看到以下输出:

WebKit 调试 – Chrome、Safari 和 Opera
基于 WebKit 的浏览器支持各种调试工具。例如,当遇到 JavaScript 问题时,你可以启动 Web Inspector 或开发者工具,并开始使用 JavaScript 控制台探索日志和错误。
在 Chrome 中,您可以从自定义菜单访问开发者工具(单击自定义菜单,然后转到 工具 | 开发者工具)。自定义菜单位于右上角。当使用 Safari 时,您首先需要通过打开 Safari 的 偏好设置 面板并选择菜单栏中的 显示开发 菜单复选框来启用开发者工具。然后,您可以通过选择应用程序的 开发 菜单中的 显示 Web 检查器 来访问检查器。
由于 Web 检查器是 WebKit 代码库的一部分,您可以在 Chrome 和 Safari 中使用相同的快捷键来访问调试工具。
在 Windows 和 Linux 上,按:
-
Ctrl + Shift + I 用于打开开发者工具
-
Ctrl + Shift + J 用于打开开发者工具并将焦点置于控制台
-
Ctrl + Shift + C 用于切换元素检查模式
在 OS X 上,按:
-
⌥ ⌘ I(选项 + 命令 + I)打开开发者工具
-
⌥ ⌘ J(选项 + 命令 + J)打开开发者工具并将焦点置于控制台
-
⌥ ⌘ C(选项 + 命令 + C)用于切换元素检查模式
当访问开发者工具时,您可以通过单击相应的图标在工具之间切换。
元素 面板允许您以浏览器渲染网页的方式查看网页。当使用它时,您可以看到原始的 HTML 和 CSS,并探索 文档对象模型(DOM)。通过单击 元素 面板并在页面的源代码中移动,您可以识别 HTML 块,并实时更改 CSS 选择器的值以进行实验和修复可能的渲染问题:

资源 面板允许您检查在检查的页面中加载和可用的资源。它允许您与包含框架资源的框架树(HTML、JavaScript、CSS、图像、字体等)交互,HTML5 数据库、本地存储、cookies 和 AppCache。
使用 网络 面板,您可以探索网页或应用程序从网络服务器请求的组件,这些请求需要多长时间,以及需要多少带宽。
使用 资源 面板,您可以访问页面中加载的所有资源。使用此面板访问 JavaScript,在代码中设置断点,并探索每个错误的堆栈跟踪。为了设置断点,选择您想要设置断点的脚本,然后点击您感兴趣的行号。当调试工具达到断点时,您可以通过探索调用堆栈(即执行到该断点的函数和方法链)和作用域变量来查看代码中的情况,并进入和退出函数:

您可以直接在调试器中编辑 JavaScript,并通过使用导航箭头来回切换来实时查看您的更改。如果您希望调试器在每次抛出异常时停止代码执行,请使用面板左下角的暂停所有按钮。有关每个功能的详细信息,我们建议您参考developer.chrome.com/devtools/docs/javascript-debugging的官方文档。
时间线面板让您可以分析 WebKit 背后的各种活动,例如浏览器处理 DOM 事件、渲染页面布局和处理事件所需的时间。
一旦您按下记录按钮,您就可以开始检查您当前查看的页面中发生的事情。
事件和帧图标(在 Chrome 中可用)允许您访问两种不同的时间线数据视图,第一个是基于时间,第二个是基于帧;您可以通过使用顶部的灰色垂直控件来放大每个视图。
内存图标让您可以探索特定网页的内存使用情况;为了在探索过程中更加准确,按下面板底部的垃圾箱图标强制垃圾收集是一个好习惯。垃圾收集是一种自动内存管理形式;收集器试图回收垃圾或浏览器窗口不再使用的对象占用的内存。

配置文件工具帮助您捕获和分析 JavaScript 脚本的性能。例如,您可以了解哪些函数执行时间最长,然后放大可能的瓶颈并确切了解在哪里进行优化。
审计面板就像有一个自己的网页优化顾问坐在您身边。此面板可以在页面加载时分析页面,并提供建议和优化以减少页面加载时间并提高感知响应性。
Gecko 调试 – Firefox
Firefox 基于 Mozilla 基金会和 Mozilla 公司开发的许多应用程序中使用的 Gecko 开源布局引擎。它提供了良好的调试工具,并且发展迅速,包括像 Desktop WebRT 这样的创新项目,它允许您在运行时构建桌面 Web 应用程序,该应用程序提供类似原生的外观和感觉,以及 Windows、OS X 和其他桌面平台上的平台集成 API。
如果您不为 Android 或 iOS 开发应用程序,您可以使用 Firefox 布局引擎,它提供了一些强大的开发和调试工具。让我们快速了解如何使用 Firefox/Firebug 检查和调试您的应用程序;正如您将看到的,WebKit 和 Firefox 中可用的调试工具之间存在几个相似之处。
与 Firefox 集成的 Firebug 将一套强大的开发者工具放在你的指尖,其功能与 WebKit 网页检查器相媲美。为了安装 Firebug 扩展,你必须前往 www.getfirebug.com/downloads/ 并安装最新版本。安装后,你可以通过导航到工具 | Web 开发者 | Firebug来打开扩展。
Firebug 工具栏让你可以访问 HTML 源代码和 CSS 规则,让你可以探索和调试 JavaScript 函数,等等:

一旦调试器达到断点,你可以:
-
探索你在设置断点时定义的代码块中的变量
-
探索函数/方法调用的堆栈
-
创建监视器以了解变量内容在代码执行过程中的变化情况
Firebug 中的脚本控制台非常出色。你可以在右侧窗口中输入你的代码,然后通过点击面板右下角的运行按钮来运行它,并在控制台中查看结果:

要过滤日志,请使用窗口顶部的所有、错误、警告、信息、调试信息和Cookies选择器。
如前所述,Firefox 有三个出色的原生开发工具:Scratchpad、Inspect 和响应式设计视图。你可以通过导航到工具 | Web 开发者来通过菜单栏访问这些工具。
将 Scratchpad 视为一个文本编辑器;你可以用它来输入和执行 JavaScript。Scratchpad 与控制台的区别在于它看起来像文本编辑器,你可以在执行之前编写所有你想要的代码:

响应式设计视图工具允许你更改分辨率而不需要调整浏览器的大小。你还可以用它来模拟设备旋转:

Internet Explorer
在撰写本文时,Internet Explorer 仍然拥有广泛的安装基础;它也是开发者中最不受欢迎的浏览器。几乎每个开发者都曾在为 IE 优化网页时遇到过严重问题;这是因为 IE 在许多重要领域偏离了网络标准,但情况正在改变,IE 10 的预览在各种测试中都获得了良好的评分。对于开发者来说,Internet Explorer 11 更好。
开发者工具是在 Internet Explorer 8 中引入的,并在 Internet Explorer 9 中通过新功能进行了更新。Internet Explorer 10 的开发者工具增加了 Web Worker 调试和多个脚本源的支持。
你可以通过按 F12 或通过从菜单栏导航到工具 | 开发者工具来访问开发者工具:

IE 10 开发者工具提供了与 Safari、Chrome 和 Firefox 开发者工具类似的用户界面。
调试工作流程
桌面浏览器是混合移动应用开发的强大工具。你可以在桌面浏览器中预览和调试大部分移动开发工作。因为 PhoneGap 利用了开放网络标准(HTML、CSS 和 JavaScript),你可以在桌面浏览器中开始工作,一旦功能完善,就可以转移到原生项目。这样,可以加快我们的开发周期,并有更多时间实现核心功能。你可以使用任何主流桌面浏览器的最新版本,如 Internet Explorer(IE)、Google Chrome、Firefox、Safari 或 Opera,以开始使用 PhoneGap 应用。所有这些浏览器都有用于记录和调试代码的开发者工具。
新开发者往往更喜欢基于 WebKit 的浏览器;在撰写本文时,Chrome 拥有最大的安装基础市场份额,其次是 Firefox 和 Internet Explorer。
注意
Chrome 还提供了一种名为Google Packaged Apps的技术,用于基于网络标准构建原生应用。更多关于它的信息可在developer.chrome.com/apps/about_apps找到。
正如你所见,每个浏览器都提供了不同的调试工具,每个工具都有其优缺点。然而,无论你使用哪种工具,你的调试工作流程都是相同的。
在调查特定问题时,你通常会遵循以下流程:
-
在调试器的代码视图中找到相关的代码。
-
在你认为可能发生有趣事情的地方设置断点。
-
如果是内联脚本,通过在浏览器中重新加载页面再次运行脚本,如果是事件处理器,则通过点击按钮来运行。
-
等待调试器暂停执行,以便可以逐步执行代码。
-
调查变量的值。例如,寻找本应包含值但未定义的变量,或者当你期望它们返回
true时返回false。
如果需要,你可以使用控制台来评估代码或更改变量以进行测试。你还可以在实现之前执行复杂的 JavaScript 代码并测试解决方案。
通过学习哪段代码或输入导致了错误条件,并隔离它来识别问题是一种合适的方法。然而,在移动应用中,事情并不总是这么简单。PhoneGap 的优势在于你可以在浏览器等通用环境中开发和调试,但请记住,移动应用还需要在目标设备上进行测试和调试。
虽然这不是一种整洁的调试方式,但你可以使用控制台日志消息进行调试。这些日志消息将在浏览器开发工具的控制台中打印出来。这里提供了一个 JavaScript 代码示例供参考:
console.log("Application running now");
对于 PhoneGap/Cordova,还有其他几种调试工具。虽然其中一些是免费服务,但也有一些是付费服务:
-
Ripple 模拟器:
emulate.phonegap.com/ -
GapDebug:
www.genuitec.com/products/gapdebug/ -
jsHybugger:
www.jshybugger.com/ -
Adobe Edge Inspect:
creative.adobe.com/products/inspect
构建和部署
一旦你的开发工作完成,你可能想在真实的移动设备上测试应用程序。每个移动平台都会有不同类型的二进制格式用于应用程序。例如,Android 应用程序文件将具有.apk扩展名。同样,每个平台都会有不同格式的应用程序。
要从源代码为所有平台创建二进制文件,你需要本地构建应用程序或使用云服务。
如果你是在本地构建应用程序,你需要运行命令行界面,并且所需的平台 SDK 应该安装在本机上。CLI 支持以下操作系统上的组合:
| Windows | Linux | Mac |
|---|---|---|
| Amazon Fire OSAndroidBlackBerry 10Windows Phone 7Windows Phone 8Windows 8Firefox OS | Amazon Fire OSAndroidBlackBerry 10Firefox OS | iOS(仅在 Mac 上) |
此表清楚地表明,你无法在 Windows 机器上构建 iOS 应用程序或在 Mac 机器上构建 Windows 8 应用程序。你需要一台合适的机器,并在机器上安装所需的 SDK。
假设你已经安装并配置了所有所需的 SDK,要为每个平台构建应用程序,你可以使用 CLI 的build工具。
运行以下命令以构建项目中添加的所有平台的工程。如果你使用platform add命令向项目中添加了多个平台,构建将针对所有平台进行:
C:\example1> phonegap build
如果你只想为特定平台构建应用程序,比如 Android 或 iOS,你可以使用以下命令单独针对它们:
C:\example1> phonegap build android
C:\example1> phonegap build ios
build命令是准备和编译项目的快捷方式。你还可以按以下步骤构建项目:以下命令集相当于build命令:
C:\example1> phonegap prepare android
C:\example1> phonegap compile android
一旦构建过程完成,特定平台的 app 将可在项目的platforms子目录中找到。
在了解了手动构建应用程序的方法后,现在让我们来了解一下 PhoneGap Build。如果你想要从源代码为所有平台创建二进制文件,你需要在机器上安装单个源代码的各个 SDK,这在实际操作中是不可能的。这就是我们觉得 PhoneGap Build 过程很方便的原因。PhoneGap Build 是一个基于单一源代码为所有移动平台创建二进制文件的在线服务。
它会提取源代码,并为所需的每个平台创建应用文件。这就像上传你的 Cordova/PhoneGap 项目,然后云服务完成剩余的工作。PhoneGap Build 流程的整体功能在以下图表中提供,该图表可在 build.phonegap.com 获取:

要使用 PhoneGap Build,你必须遵循以下步骤:
-
完成你的 PhoneGap/Cordova 项目,并确保你的应用中包含了设计中的所有功能。
-
PhoneGap Build 只需要你的项目
assets目录的内容(HTML、CSS、JavaScript 和图片)。 -
如果你包含了任何
phonegap.js或cordova.js文件在你的资源中,请移除该文件,因为 PhoneGap Build 流程将自动注入所需的文件。 -
你可以将项目上传到 PhoneGap Build 流程,并见证魔法发生。
你可以轻松地通过单次点击下载所有平台的应用构建版本:

关于 PhoneGap Build 流程的更多详细信息可以在 docs.build.phonegap.com/en_US/ 获取。
摘要
在概述了几个工具和一些调试技术之后,本章为你准备进入下一步,即使用 Apache Cordova 及其 PhoneGap 发行版创建和构建多平台应用。你还学习了各种命令行工具和 Cordova 事件的使用。在下一章中,你将了解各种移动框架和模板,以开始你的移动开发。
第三章. 移动框架
在第二章《创建您的第一个 PhoneGap 项目》,你学习了 PhoneGap 应用程序结构、事件、如何使用命令行工具以及如何创建示例应用程序。你还了解了可以帮助开发者的各种调试方法,并看到了如何构建/部署你的应用程序。在本章中,你将学习设计方法、行业中的移动框架以及这些框架的详细信息。我们还将看到如何使用 jQuery Mobile 创建示例应用程序。
在本章中,你将完成以下事项:
-
回顾一些关于为移动设备开发原生 UI 的概念
-
探索开发移动 UI 的最佳实践
-
了解最受欢迎的应用程序框架概述
-
学习如何使用 jQuery mobile 创建移动应用程序
为移动设备构建原生 UI
当使用 PhoneGap 时,你创建基于标准的混合应用程序。应用程序通过 WebView 呈现给用户,这意味着它是一个封装在应用程序本身中的浏览器实例。
因此,了解如何使用针对移动设备的 HTML 标签、CSS 属性和 JavaScript 方法、属性和事件非常重要。
viewport元标签
viewport元标签是由苹果公司在 iOS 1.0 中引入的,并且在所有主要的移动浏览器中得到广泛支持。当一个网页不适合浏览器的大小,移动浏览器的默认行为是缩放它。viewport元标签是你控制这种行为的所需元素。
viewport元标签看起来像以下代码片段:
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1, maximum-scale=1.5, user-scalable=1">
你实际上告诉浏览器的是,内容的默认宽度和高度是设备屏幕的宽度和高度(width=device-width和height=device-height),内容是可缩放的(user-scalable=1),以及最小和最大缩放值是多少(minimum-scale=1和maximum-scale=1.5)。
在 Apple 开发者库网站上提供了关于viewport元标签的详尽参考,网址为developer.apple.com/library/safari/#documentation/appleapplications/reference/SafariHTMLRef/Articles/MetaTags.html。在 Opera 开发者网站上也有一些有用的信息,网址为dev.opera.com/articles/view/an-introduction-to-meta-viewport-and-viewport/。
记住,PhoneGap 默认使用的 WebView 会忽略viewport元标签中定义的设置;你将在本章中学习如何在你的应用程序中启用对 viewport 设置的处理。
不想要的电话号码链接
在大多数手机上,移动浏览器点击拨号格式检测并不准确;许多数字被选中,包括地址、ISBN 号码以及各种不同类型的非电话号码的数字数据。为了避免任何可能的问题并完全控制从你的 HTML 标记中发起的通话,有必要在页面头部添加以下元标签:
<meta name="format-detection" content="telephone=no">
定义此标签后,你可以通过在href属性中使用tel或sms方案来控制如何处理数字:
<a href="tel:18005555555">Call us at 1-800-555-5555</a>
<a href="sms:18005555555?body=Text%20goes%20here">
请注意,这种电话格式检测仅适用于移动浏览器。在桌面浏览器中,它不起作用,除非你在桌面上安装了某些桌面电话软件(如 Skype)以及浏览器插件。
自动更正
使用移动设备提交数据对用户来说是一项繁琐的操作,因为有时内置的自动更正功能根本不起作用。为了禁用自动更正功能,请使用autocorrect、autocomplete和autocapitalize属性与input字段结合使用:
<input autocorrect="off" autocomplete="off" autocapitalize="off">
CSS 媒体查询和移动属性
CSS 的一个有趣特性是媒体查询。媒体查询本身其实相当古老,并不特定于移动设备,但在处理移动设备上的不同屏幕尺寸时非常实用。媒体查询可以内联使用:
@media all and (orientation: portrait) {
body { }
div { }
}
或者,媒体查询可以作为link标签的media属性使用:
<link rel="stylesheet" media="all and (orientation: portrait)" href="portrait.css" />
没有使用它们的最佳方式,因为这取决于应用程序的类型。使用内联媒体查询,CSS 文件往往会增长,并且在旧设备上解析可能会变慢。另一方面,将 CSS 规则组织在单独的文件中有助于保持代码的良好组织并加快解析速度,但这意味着更多的 HTTP 调用,通常由于移动连接的延迟,这不是移动设备上的最佳选择。
应该通过使用离线缓存策略来达到良好的平衡,你将在下一章中了解更多关于离线缓存策略的内容。
有几个 CSS 特定于移动的属性;大多数都是供应商特定的,并带有前缀。在移动开发中最常用的属性包括:
-
-webkit-tap-highlight-color: 0;(iOS):这覆盖了当用户点击链接或可点击元素时出现的半透明颜色覆盖层。这是唯一的 iOS 特定属性。 -
-webkit-user-select: none;:这阻止了用户选择文本。 -
-webkit-touch-callout: none;:这阻止了当用户触摸并保持一个元素(如anchor标签)时调用工具栏的出现。
总是记住,在 JavaScript 中使用浏览器前缀仅通过使用混合大小写或小驼峰式格式化是可能的,这意味着为了防止用户通过 JavaScript 选择文本,你必须使用以下语法:
yourElementVariable.style.webkitUserSelect = 'none';
驼峰式格式化是由于在 JavaScript 中变量名不能使用破折号符号的事实。
屏幕方向
当处理应用时,屏幕方向很重要,因为当方向改变时,屏幕的大小会显著变化。在每次旋转 90 度(纵向和横向模式)时都会触发orientationchange事件,并且可以使用addEventListener来监听它;当前的方向可以通过window.orientation获取。
设备方向
如果您想获取有关设备方向的更详细信息,可以为deviceorientation事件定义一个监听器。deviceorientation事件会非常频繁地触发,并给出有关设备方向的三维信息,以 alpha、beta 和 gamma 的值表示,如下所示:
window.addEventListener("deviceorientation", handleOrientation, true);
function handleOrientation(event) {
var alpha = event.alpha;
var beta = event.beta;
var gamma = event.gamma;
// Change device orientation based on the data
}
deviceorientation事件严格与设备上是否存在陀螺仪相关;陀螺仪测量 3D 角度方向,即使设备处于静止状态。有关使用方向数据的详细信息,请参阅developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation。
摇晃手势
手势处理是成功应用的关键。当用户摇晃或移动其设备时,会触发devicemotion事件。devicemotion事件严格与加速度计相关,当设备加速时,加速度计会触发事件。
媒体捕获 API
虽然旧版本的 iOS 仍然缺乏基本的文件输入,但 Android、iOS 6 版本及以后、Windows Phone 8 和 BlackBerry 10 正在为开发者提供对用户可以上传的内容的精细控制,并允许您访问设备摄像头和麦克风:
<!-- opens directly to the camera -->
<input type="file" accept="image/*;capture=camera"></input>
<!-- opens directly to the camera in video mode -->
<input type="file" accept="video/*;capture=camcorder"></input>
<!-- opens directly to the audio recorder -->
<input type="file" accept="audio/*;capture=microphone"></input>
数据 URI
您可以将图片表示为 Base64 字符串,这确保了更高的性能,因为没有 TCP 协商来打开新的 HTTP 连接。从实际的角度来看,这意味着与在网络上通常加载图片的方式相比,延迟更低。当将base64字符串分配给img标签的src属性时,代码看起来如下所示:
<img src='data:image/png;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub// ge8WSLf/rhf/3kdbW1mxsbP//mf/// yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0 ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFj sVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/ gAwXEQA7' width='16' height='14' >
当将图片转换为 Base64 时,会有 30-40%的重量增加;因此,在转换之前必须仔细优化图片,并在可能的情况下,在服务器上激活 GZip 压缩。
在 iOS 上实现原生外观和感觉
iOS 平台最大的问题之一是您的应用在 App Store 的发布。事实上,苹果在检查应用是否可以添加到商店时非常谨慎。
被允许进入 App Store 的最重要标准之一是应用提供 iOS 用户体验。有关应用必须满足的详细要求以进入 App Store 的信息,请访问位于developer.apple.com/app-store/review/的 Apple 开发者网站。
近年来,已经开发出几个针对 iOS 的特定移动框架,例如 Framework 7,可以为您的应用提供 iOS 原生外观。更多信息,请访问www.idangero.us/framework7/。
选择移动框架
开发者通常有自己的模板库,这些库是从零开始构建或商业的,用于快速启动他们的项目。在本节中,我们将简要概述一些有用的 HTML/CSS/JavaScript 框架,您可以将它们作为蓝图、库和框架集成到您的项目中。
注意
框架或库本质上是一组您可以调用的函数,这些天通常组织成类或文件。框架体现了一些抽象设计,并内置了更多行为。马丁·福勒在他的文章中进一步讨论了库和框架之间的区别,该文章可在martinfowler.com/bliki/InversionOfControl.html找到。
使用 HTML5 移动模板
这是一个非常干净、适合移动设备的 HTML 模板,它包括优化的 Google Analytics 片段、基于触摸的设备图标占位符、Zepto 库(一个适用于现代浏览器的极简 JavaScript 库,具有大量与 jQuery 兼容的 API),以及 Modernizr 功能检测库(一个使用对象检测技术来发现在使用之前某个功能是否可用的库,允许网页优雅降级或渐进增强)。
您可以从官方网站html5boilerplate.com/mobile/下载 HTML5 移动模板;要获取更新或参与项目,请关注 GitHub 上的项目github.com/h5bp/mobile-boilerplate。
如果您想下载一个定制的 HTML5 移动模板版本,该版本允许您选择要使用的模板、要包含的 JavaScript 库等,请访问www.initializr.com/。
使用 Zurb Foundation
您可能已经听说过响应式设计,这是一种响应观看者设备约束的网站设计。目前这是一个热门话题,Foundation 框架最重要的特性是其布局机制的响应性。
此外,Foundation 提供了一系列模板,用于您应用中最常见的部分;当您从foundation.zurb.com/download.php下载框架时,可以选择您想要的模板。
Foundation 的优势如下:
-
一个基于百分比、最大宽度任意的 12 列网格
-
忽略像素的图像样式——基础图像通过网格按不同宽度缩放
-
UI 和布局元素,包括常见的元素,如排版和表单,以及标签页、分页、N-up 网格等
-
移动可见性类——Foundation 让您能够非常快速地在桌面、平板电脑和手机上隐藏和显示元素。
要跟上 Foundation 的最新构建,请关注 GitHub 项目,该项目位于github.com/zurb/foundation。
使用 Twitter Bootstrap
Twitter Bootstrap 是一个用于创建网站和 Web 应用程序的免费工具集合。它包含基于 HTML 和 CSS 的设计模板,用于排版、表单、按钮、图表、导航以及其他界面组件,以及可选的 JavaScript 扩展。
这个项目是 GitHub 上最受欢迎的项目之一;它组织得非常好,看起来天生就是为了构建应用程序而设计的。实际上,它包括了基本的 CSS 和 HTML 来创建网格、布局、排版、表格、表单、导航、警告、弹出框等功能。
由于 Bootstrap 使用了 jQuery,因此使用 Bootstrap 开始工作非常简单。要下载 Bootstrap,您可以参考项目下载和自定义页面,该页面位于twitter.github.com/bootstrap/customize.html;如果您想下载 Bootstrap 的模板,可以参考已提到的www.initializr.com/网站。但请注意,Bootstrap 是专门为移动 Web 设计的,而不是为混合移动应用程序设计的。
Ionic 框架
Ionic 框架是一个强大的 HTML5 混合应用程序开发框架,它帮助您使用 Web 技术(如 HTML、CSS 和 JavaScript)构建具有原生感觉的移动应用程序。它是考虑到混合移动应用程序而制作的。由于 Ionic 基于 AngularJS 并构建在 PhoneGap/Cordova CLI 之上,您可以看到更多类似于它的 CLI 命令语法。它有一个较深的学习曲线,但值得学习。AngularJS 本身不是一个混合移动框架,但 Ionic 提供了混合移动框架所需的一切。该框架正在积极维护。此外,该框架根据平台处理 UI 渲染,为用户提供原生感觉。请注意,它仅对 Android 和 iOS 提供官方支持。您可以从ionicframework.com/了解更多信息。
ExtJS
ExtJS 是一个具有美观 UI 小部件的 JavaScript 框架,用于构建交互式网站,基于 ExtJS 的 Sencha Touch 可以用于构建混合移动应用程序。它具有各种功能,很难一一列举。总的来说,您将拥有开发功能丰富的应用程序所需的一切。它既有开源许可,也有企业许可,您可以在www.sencha.com/products/extjs/和www.sencha.com/products/touch了解更多信息。
AngularJS
AngularJS 是来自 Google 的 JavaScript 框架,现在是企业开发的明星。由于它基于 JavaScript,AngularJS 和 Cordova/PhoneGap 配合得非常好。AngularJS 的学习曲线陡峭,如 ExtJS,但一旦你度过那个阶段,你会感到很棒。为了构建混合应用程序,AngularJS 有一个特殊的移动版本,称为 Mobile AngularJS UI。你可以在angularjs.org和mobileangularui.com分别了解更多关于 AngularJS 和 Mobile AngularJS UI 的信息。
jQuery Mobile
jQuery Mobile 框架是一个跨所有流行移动设备平台的用户界面系统,建立在坚固的 jQuery 和 jQuery UI 基础之上。其代码库采用渐进增强构建,并具有灵活、易于主题化的设计。
jQuery Mobile 对所有主要的现代桌面、智能手机、平板电脑和电子阅读器平台都有广泛的支持。此外,由于渐进增强方法,还支持功能手机和旧浏览器(详情请参阅jquerymobile.com/gbs/)。
jQuery Mobile 的主要功能可以总结如下:
-
跨平台、跨设备和跨浏览器
-
优化触摸设备的 UI
-
可主题化和可定制的设计
-
仅使用非侵入性的语义 HTML5 代码
-
AJAX 调用自动加载动态内容
-
轻量级(压缩后 12 KB)
-
渐进增强
-
可访问性
要下载最新稳定版本并保持项目更新,请参考官方网站jquerymobile.com,在那里你可以找到有用的示例和创建你自己的主题的工具jquerymobile.com/themeroller/。
行动时间 - 使用 jQuery Mobile 进行开发
让我们看看如何在 PhoneGap 应用程序中使用 jQuery Mobile 的一个快速示例。你可以通过使用 jQuery Mobile 使任何 HTML 页面都变得移动友好。我们将创建一个简单的应用程序,以列表形式显示页面中的多个条目,并显示一些页脚菜单。
-
首先,我们需要下载 jQuery 和 jQuery Mobile 组件,并将它们放置在我们的项目 JS/CSS 目录中。jQuery 是一个单一的 JavaScript 文件,jQuery Mobile 包括一个 JavaScript 文件和一个 CSS 文件。我们需要在应用程序的 HTML 页面中包含它们。
-
在 HTML 页面的顶部包含 CSS 文件,紧位于
</head>标签之上:<link rel="stylesheet" href="css/jquery.mobile-1.4.4.min.css" /> -
在页面底部包含 JavaScript 文件,紧位于
</body>标签之上:<script src="img/jquery-1.11.1.min.js"></script> <script src="img/jquery.mobile-1.4.4.min.js"></script> -
在 jQuery Mobile 中,您通过将
data-role属性分配给page来定义页面部分。首先,让我们添加一个
div元素来标记一个页面:<div data-role="page"> </div> -
现在,让我们向页面添加一个列表。在 jQuery Mobile 中,列表是通过向
<ul>元素添加listview数据角色来识别的:<div data-role="page"> <ul data-role="listview"> </ul> </div> -
要向列表视图添加项目,我们将向
<ul>标签添加数据,如下所示:<div data-role="page"> <ul data-role="listview"> <li><a href="#">Apache</a></li> <li><a href="#">BASIC</a></li> <li><a href="#">COBOL</a></li> <li><a href="#">Delphi</a></li> <li><a href="#">FOTRON</a></li> <li><a href="#">Hadoop</a></li> <li><a href="#">Java</a></li> <li><a href="#">PHP</a></li> <li><a href="#">Perl</a></li> <li><a href="#">Python</a></li> <li><a href="#">jQuery</a></li> </ul> </div>您可以在桌面浏览器或来自 IDE 的移动模拟器中测试输出。对于前面的代码,您将在模拟器中看到以下输出:
![行动时间 - 使用 jQuery Mobile 开发]()
-
现在让我们给页面添加一个标题,使其看起来更好。要添加标题到页面,使用
data-role属性作为header,如下所示:<div data-role="header"> <h1>Tech Words</h1> </div>添加了标题后,您现在将在模拟器中看到以下输出:
![行动时间 - 使用 jQuery Mobile 开发]()
-
现在,是时候给应用程序添加一个页脚菜单了。使用以下代码将页脚菜单添加到页面中:
<div data-role="navbar"> <ul> <li><a href="#" data-icon="bars">Tools</a></li> <li><a href="#" data-icon="home">Languages</a></li> <li><a href="#" data-icon="search">Database</a></li> </ul> </div>前面的 HTML 代码在
div元素内创建了一个简单的无序列表。列表的内容将根据我们设置的data-role属性作为导航栏。现在应用程序将看起来如下截图所示:
![行动时间 - 使用 jQuery Mobile 开发]()
我们创建了一个基于 jQuery Mobile 的简单应用程序,并在此提供了完整的 index.html 代码供您参考:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" />
<link rel="stylesheet" href=" css/jquery.mobile-1.4.4.min.css" />
<title>Hello Cordova</title>
</head>
<body>
<div data-role="page">
<div data-role="header">
<h1>Tech Words</h1>
</div>
<ul data-role="listview">
<li><a href="#">Apache</a></li>
<li><a href="#">BASIC</a></li>
<li><a href="#">COBOL</a></li>
<li><a href="#">Java</a></li>
<li><a href="#">PHP</a></li>
<li><a href="#">Perl</a></li>
<li><a href="#">jQuery</a></li>
</ul>
<div data-role="navbar">
<ul>
<li><a href="#" data-icon="bars">Tools</a></li>
<li><a href="#" data-icon="home">Languages</a></li>
<li><a href="#" data-icon="search">Database</a></li>
</ul>
</div>
</div>
<script src="img/jquery-1.11.1.min.js"></script>
<script src="img/jquery.mobile-1.4.4.min.js"></script>
</body>
</html>
选择框架
很难说哪个框架是最好的,因为每个框架都有自己的优缺点。大多数时候,我不得不说这取决于您必须实现的功能,以及您的应用程序的本质。例如,如果应用程序仅针对手机,那么您可以选择使用更轻量级的 HTML5 Mobile Boilerplate;另一方面,如果应用程序旨在用于 Web 和手机,那么一个更复杂的库可能是正确的选择。
jQuery Mobile 非常容易学习和实现。然而,当谈到良好的性能时,jQuery Mobile 并不是领导者。另一方面,某些框架,如 ExtJS 和 Ionic,需要更多的学习才能在企业级应用程序中使用。它们有几个现成的组件,有助于快速开发。
一些框架仅提供 CSS,一些提供 JavaScript,还有一些两者都有混合。例如,jQuery Mobile 提供了 CSS 主题功能,还提供了 JavaScript 插件以添加更多功能。始终记住,您的目标是找到内置功能和性能之间的平衡,因为移动设备远不如桌面设备强大。
摘要
在本章中,您学习了如何构建一个在所有平台上看起来都像原生的 PhoneGap 应用程序,并概述了各种移动开发框架。逐步解释了一个 jQuery Mobile 应用程序代码示例。在下一章中,您将学习如何在您的 Cordova/PhoneGap 项目中处理插件。您将了解如何使用命令行工具安装、集成和删除插件。
第四章:使用插件
使用 Web 标准和 JavaScript 构建原生应用可能有其局限性,因为使用原生代码开发的应用可以深度与操作系统交互。但处理 PhoneGap 应用时,这仅部分正确,因为其架构允许开发者通过自定义插件扩展框架功能。
在本章中,你将:
-
了解 PhoneGap 插件是什么以及如何在项目中安装插件
-
了解如何列出所有已安装的插件并移除不需要的插件
-
了解如何使用 Plugman 管理项目插件及其依赖关系
-
发现如何在项目中实现设备 API
插件简介
当你开发和部署一个新的简单混合应用时,该应用可能不会执行任何高级操作。我们知道混合应用是使用标准 Web 技术(如 HTML、CSS 和 JavaScript)开发的。我们需要某种机制让应用与各种设备级功能交互。为了实现这一点,我们使用 PhoneGap/Cordova API 的插件。自 3.0 版本以来,所有 PhoneGap API 都已转换为插件,这意味着对插件有深入理解现在对 PhoneGap 开发者来说更为重要。
PhoneGap 插件是 WebView 和应用程序运行的本地平台之间的桥梁。
为了能够快速有效地使用 PhoneGap 插件,了解框架的工作方式非常重要。一个 PhoneGap 应用由三个主要层组成。具体如下:
-
用户界面,使用 HTML、CSS 和 JavaScript 开发
-
业务逻辑,使用 JavaScript 开发
-
PhoneGap 框架,通过 JavaScript API 暴露给业务逻辑的本地代码
用户界面和业务逻辑是应用的主要源代码,以及大多数开发者集中开发努力的部分。插件严格集成在框架中,并通过 JavaScript 暴露给用户。你可以想象插件是框架的附加组件,其工作方式类似于所有 PhoneGap API。
插件入门
使用插件,您可以扩展 PhoneGap 框架以满足您应用程序的需求。这意味着没有令人信服的限制,但也意味着您应用程序的源代码必须为不同的平台维护。在plugins.cordova.io列出了所有平台的几个插件。为了确保现有的插件符合您的需求,您必须检查与您项目中使用的 PhoneGap 版本的兼容性,并最终更新源代码以符合规范。PhoneGap 的一个优势是其持续发布模型,因为这加快了新功能和错误修复的发布。但这也意味着插件应该得到维护,以满足框架的弃用策略。有关即将弃用的更新信息,请参考在线 Wiki,链接为wiki.apache.org/cordova/DeprecationPolicy。
为了使用自定义插件中实现的功能,您必须在项目中安装它。在 PhoneGap 项目中手动管理插件并不是一件容易的事情;幸运的是,有一个命令行工具可以使您的生活更加轻松。
安装插件
您可以使用 Cordova 的 CLI 工具将插件安装到项目中。以下命令将插件添加到项目中,并在必要时对清单文件进行必要的更改:
$ cordova plugin add PLUGIN.ID
在这里,PLUGIN.ID是插件在插件存储库中注册的 ID。
以下列表总结了目前可用的 API 以及将它们添加到 PhoneGap 项目的命令。如果您想删除插件,请使用$ cordova plugin remove命令代替。
-
基本设备信息:
-
设备 API:这获取与设备相关的基本信息
$ cordova plugin add cordova-plugin-device -
状态栏 API:这自定义状态栏背景
$ cordova plugin add cordova-plugin-statusbar
-
-
网络和电池状态:
-
网络 API:这获取蜂窝网络信息
$ cordova plugin add cordova-plugin-network-information -
电池 API:这监控设备电池的状态
$ cordova plugin add cordova-plugin-battery-status
-
-
加速度计、指南针和地理位置:
-
设备运动(加速度计)API:这处理设备的运动传感器
$ cordova plugin add cordova-plugin-device-motion -
设备方向 API:这获取设备方向
$ cordova plugin add cordova-plugin-device-orientation -
地理位置 API:这使得应用程序具有位置感知能力
$ cordova plugin add cordova-plugin-geolocation
-
-
相机、媒体捕获和媒体播放:
-
相机 API:这使用设备的相机捕获照片
$ cordova plugin add cordova-plugin-camera -
捕获 API:这使用设备捕获所有媒体文件
$ cordova plugin add cordova-plugin-media-capture -
媒体 API:这记录和播放音频文件
$ cordova plugin add cordova-plugin-media
-
-
访问设备或网络上的文件:
-
文件 API:这访问设备的文件系统
$ cordova plugin add cordova-plugin-file -
文件传输 API:这使用 API 上传或下载文件
$ cordova plugin add cordova-plugin-file-transfer
-
-
通过对话框或振动发送通知:
-
对话框 API:使用此 API,通知和警报变得简单
$ cordova plugin add cordova-plugin-dialogs -
振动 API:这会使设备振动
$ cordova plugin add cordova-plugin-vibration
-
-
联系人:
-
联系人 API:这提供了对设备联系人列表的完全访问权限
$ cordova plugin add cordova-plugin-contacts
-
-
全球化:
-
全球化 API:这为应用程序添加了地区支持
$ cordova plugin add cordova-plugin-globalization
-
-
启动画面:
-
启动画面 API:此功能显示和隐藏启动画面
$ cordova plugin add cordova-plugin-splashscreen
-
-
应用程序内浏览器:
-
InApp Browser API:此功能将在应用程序浏览器中打开任何 URL
$ cordova plugin add cordova-plugin-inappbrowser
-
虽然使用插件 ID 添加插件是最简单的方法,但还有其他高级选项。这些选项帮助我们自定义插件安装。我们不仅可以使用插件 ID,还可以使用此处所示的 GitHub 仓库 URL:
$ cordova plugin add URL_TO_THE_GITHUB_REPO
在这里,URL_TO_THE_GITHUB_REPO 是插件(即 API)仓库的路径。
例如,你可以用以下方式安装 Device API 插件:
$ cordova plugin add https://github.com/apache/cordova-plugin-device.git
或者,你也可以用这种方式安装 Device API 插件:
$ cordova plugin add cordova-plugin-device
如果你使用的是 PhoneGap CLI 而不是 Cordova CLI,你可以使用这里提供的以下两个命令中的任何一个。以下示例命令将 Camera 插件安装到你的项目中。
$ phonegap plugin add https://github.com/apache/cordova-plugin-camera.git
或者,你可以使用以下命令:
$ phonegap plugin add cordova-plugin-camera
如果你想要安装特定版本的插件,你可以指定它并附带插件 ID:
$ cordova plugin add cordova-plugin-camera@0.3.6
如果你想要从本地目录源安装插件,你可以使用以下格式,其中你可以指定源路径而不是 GIT 仓库 URL:
cordova plugin add /path/to/directory
PhoneGap 将会在这个目录及其所有子目录中查找插件。
列出已安装的插件
要列出应用程序项目中安装的所有插件,你可以使用 list 命令。它有三个变体,所有这些都会输出相同的内容:
$ cordova plugin
$ cordova plugin list
$ cordova plugin ls
前面的三个命令都会列出安装的插件,包括插件 ID、版本和完整名称。
移除插件
有时候你可能需要从你的应用程序中移除不再需要的插件。与添加插件一样,移除插件也很简单。要移除插件,请通过列出中出现的相同插件 ID 来引用它。例如,以下是如何从项目中移除对 Camera API 的支持的示例:
$ cordova plugin rm cordova-plugin-camera
或者,你可以使用这个命令来做同样的事情:
$ cordova plugin remove cordova-plugin-camera
如果你想要一次性移除多个插件,你可以指定多个参数:
$ cordova plugin rm cordova-plugin-console cordova-plugin-camera
使用 Plugman 使用插件
插件是通过一个名为 Plugman 的工具安装和移除的;从开发者的角度来看,这只是在 cordova-cli 工具中可用的一个命令。Apache Cordova Plugman 项目 github.com/apache/cordova-plugman 是一个开源的命令行工具,作为 npm 模块分发,以简化插件的安装和卸载。Plugman 支持 Android、Amazon FireOS、BlackBerry 10、Windows Phone 8 和 iOS 平台。安装过程与其他任何 npm 模块相同;记住,如果你全局安装它(使用 -g 选项),你必须以 root 身份运行命令:
$ npm install plugman -g
安装完成后,你可以使用以下命令从命令行工具获取插件源代码,安装和卸载插件,以及将插件打包以与你的应用程序一起分发(Plugman 是 cordova-cli 工具的一部分;如果你已经在使用它,则无需安装):
-
--fetch选项可以从目录、Git 仓库或按名称检索插件,并将其放入指定的插件目录:$ plugman --fetch https://github.com/phonegap-build/GAPlugin.git --plugins_dir PATH_TO_YOUR_PLUGINS_DIR -
--install选项在 PhoneGap 项目中为特定目标平台安装插件。插件可以通过名称或 URL 安装:$ plugman --platform PLATFORM --project PLATFORM_PROJECT_PATH --plugin https://github.com/phonegap-build/GAPlugin.git小贴士
–-plugin参数可以是插件的名称或 Git 仓库的路径。默认情况下,Plugman 会启动install命令,并在plugins目录中不存在插件时获取插件。因此,--install参数是可选的。 -
--uninstall选项通过名称卸载之前安装的插件:$ plugman --uninstall --platform PLATFORM --project PLATFORM_PROJECT_PATH --plugin PLUGIN_NAME -
--list选项列出之前使用 Plugman 获取的所有插件。 -
--prepare选项设置插件,正确注入所需的 JavaScript 文件并定义适当的权限。当你安装或卸载插件时,--prepare命令会隐式调用。
关键字 PLATFORM 可以有这些值之一:ios、android、amazon-fireos、blackberry10 或 wp8。此外,请注意,Plugman 是一个低级 CLI,因此你只能一次添加一个平台的插件。如果有多个目标平台,你需要使用 Plugman CLI 为每个平台安装插件。然而,如果你使用 Cordova CLI 安装插件,它将一次性为所有目标平台安装插件。
为了将插件添加到目标平台,你可以运行 compile 命令或 prepare 命令。
当使用 Plugman 作为独立工具时,你可以在安装时使用 --variable 参数指定变量。这些变量对于需要 API 密钥或其他自定义、用户定义参数的插件是必要的。
要获取 Plugman 命令及其语法的完整列表,你可以使用全局的 help 命令,如下所示:
plugman - help
自从 PhoneGap 3.0 版本发布以来,所有 API 都可以作为外部插件使用;你将在本书的后面部分了解更多关于插件的内容。目前,你需要知道的是,将每个单独的 API 作为插件处理,可以使你根据项目需求组合出适合的 PhoneGap 版本。
探索 Device API
由于我们已经介绍了在 Cordova/PhoneGap 项目中使用插件的基本知识,我们现在将看看如何使用 Device 插件的示例。
设备 API 是 PhoneGap/Cordova 中最简单的 API 之一,它为你提供了有关其运行的设备的详细信息。这是当你刚开始学习插件时可以尝试的一个很好的 API。使用该 API,你可以获取以下设备属性:
-
设备平台
-
设备唯一 ID
-
设备版本号
-
设备型号名称
你可以使用这些属性为用户提供设备特定的功能。
行动时间 - 访问 Device API
你已经在 第二章 中使用了 deviceready 事件来处理我们应用的引导。使用设备 API 获取事件触发后你正在运行的设备类型信息。
-
打开命令行工具,使用你之前安装的 Cordova 命令行工具创建一个新的 Cordova 项目:
$ cordova create DeviceApi这将在你的当前工作目录中创建一个名为
DeviceApi的新目录。 -
移动到刚刚创建的目录:
$ cd DeviceAPI -
在设备 API 上添加你想要测试的平台。例如,我们添加了 Android 平台:
$ cordova platform add android -
安装设备 API 插件。你也可以使用这里所示的最短方式安装插件,而不是使用整个 GIT 仓库 URL:
$ cordova plugin add cordova-plugin-device -
在你刚刚创建的项目文件夹中的
www文件夹内,打开index.html文件,并添加一个带有id的div元素以渲染使用 PhoneGap API 收集的设备信息:<div id='deviceInfo'>Loading device properties...</div> -
定义一个监听
deviceready事件的监听器,以便访问所有支持的 API 属性:document.addEventListener("deviceready", onDeviceReady, false); -
在 HTML 页面上显示设备信息:
function onDeviceReady() { var element = document.getElementById('deviceInfo'); element.innerHTML = 'Device Model: ' + device.model + '<br />' + 'Device Cordova: ' + device.cordova + '<br />' + 'Device Platform: ' + device.platform + '<br />' + 'Device UUID: ' + device.uuid + '<br />' + 'Device Version: ' + device.version + '<br />'; } -
使用 Cordova 命令行工具构建项目:
$ cordova build -
使用以下命令在模拟器中查看应用程序。启动模拟器可能需要一些时间:
$ cordova run
整个代码已经在这里提供供你参考:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" />
<title>Hello Cordova</title>
</head>
<body>
<div id='deviceInfo'>Loading device properties...</div>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
var element = document.getElementById('deviceInfo');
element.innerHTML = 'Device Model: ' + device.model +
'<br />' + 'Device Cordova: ' + device.cordova +
'<br />' + 'Device Platform: ' + device.platform +
'<br />' + 'Device UUID: ' + device.uuid +
'<br />' + 'Device Version: ' + device.version + '<br />';
}
</script>
</body>
</html>
这就是实际设备上的输出将看起来是什么样子。你实际设备上看到的值可能会根据你的设备而有所不同:

发生了什么?
你处理了 deviceready 事件,使用 PhoneGap API 访问了相关的设备信息。你刚刚使用的设备对象描述了设备的硬件和软件:
-
device.platform: 这将获取操作系统名称 -
device.uuid: 这将获取通用唯一标识符 -
device.version: 这将获取操作系统版本 -
device.cordova: 这将获取设备上运行的 Cordova 版本 -
device.model: 这将获取型号名称
注意
由于 PhoneGap 的滚动发布模型,强烈建议你始终参考 GitHub 仓库 github.com/apache/cordova-plugin-device,以检查特定的设备 API 是否将要被弃用。
自定义插件
我们看到了可用于 Cordova/PhoneGap 平台的基本插件列表以及如何在我们的应用程序中使用它们。开发者不仅限于使用这些小插件集。有数百个插件可用,并且每天都有许多新插件被添加。以下是一些最常用的第三方插件。在选择插件时,你应该注意它们对各种平台的支持情况。
-
Social Sharing: 这为您的应用程序添加社交分享功能(
plugreg.com/plugin/EddyVerbruggen/SocialSharing-PhoneGap-Plugin) -
Push Notifications: 这可以向您的用户发送定制的推送通知(
plugreg.com/plugin/phonegap-build/PushPlugin) -
Facebook Login: 这允许用户使用 Facebook 登录到您的应用程序(
plugreg.com/plugin/Wizcorp/phonegap-facebook-plugin) -
ActionSheet: 这可以在您的应用程序中显示出色的操作菜单(
plugreg.com/plugin/EddyVerbruggen/cordova-plugin-actionsheet) -
AppRate: 这允许您的用户在 Google Play Store、Apple App Store 或 Windows Store 中为您的应用程序添加反馈和评分(
plugreg.com/plugin/pushandplay/cordova-plugin-apprate)
您可以从它们的 GitHub URL 或使用它们的插件 ID(如果已在 Cordova 插件存储库中注册)安装这些插件。以下是安装 Social Sharing 插件的两种方法。您可以使用以下命令:
$ cordova plugin add https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin.git
您也可以使用以下命令:
$ cordova plugin add nl.x-services.plugins.socialsharing
您可以参考以下完整插件列表:plugins.cordova.io 或 plugreg.com。
摘要
在本章中,您学习了有关插件以及如何在应用程序中列出、添加和删除它们的内容。使用这些技术,我们看到了如何在项目中安装 Device API 并获取设备详情。在接下来的章节中,我们将看到如何使用 Storage API 访问本地设备存储,以及如何使用 File API 列出/读取/写入文件。
第五章。使用设备存储和 Files API
你的 PhoneGap 知识正在逐步积累。现在是时候添加一些与外部数据源以及与设备本身的交互了。本章的主要目标是指导你了解 PhoneGap 的离线存储功能,并帮助你理解如何与 Files API 交互。
在本章中,你将:
-
学习如何使用
localStorage对象在设备上读取和写入数据 -
学习如何根据特定平台实现处理本地数据库的存储
-
理解数据库存储限制并学习如何处理它们
-
了解 Files API 的工作原理,以及如何组织代码以保持其清晰和可维护性
-
使用 Files API 来探索设备文件系统
-
学习如何在文件内部读取和渲染数据
-
学习如何将文件加载和保存到设备的持久存储中
应用数据存储
每个应用程序(桌面、网页或移动)都需要存储(并访问)一些数据以正常工作。数据如何存储取决于应用程序将处理的信息类型以及应用程序将在其中运行的环境。例如,一个网页应用程序可以主要依赖服务器存储,因为它在互联网上运行。大多数高级网页应用程序实现离线策略,并在用户机器上本地存储一些数据。
现代网络开发提供了几个工具,以便让用户在未连接时也能与应用程序交互:
-
LocalStorage API(
www.w3.org/TR/webstorage/#the-localstorage-attribute) -
SessionStorage API(
www.w3.org/TR/webstorage/#the-sessionstorage-attribute) -
ApplicationCache接口(
www.w3.org/TR/2011/WD-html5-20110525/offline.html) -
IndexedDB API(
www.w3.org/TR/IndexedDB/)
所有现代移动浏览器都允许开发者通过 navigator 对象处理在线和离线事件(developer.mozilla.org/en/docs/Online_and_offline_events)。
注意
重要的是要记住,关于OnLine属性的规范报告称,该属性本身是不可靠的,因为一台计算机可以连接到网络而没有互联网访问权限。
本书范围之外是对先前 API、事件和界面的全面概述。在以下章节中,我将仅讨论对构建 PhoneGap 应用程序最相关的部分:LocalStorage 和 IndexedDB。
探索 PhoneGap LocalStorage API
主要有两种网络存储类型:本地存储和会话存储。LocalStorage API 是 W3C 定义的WebStorage API 的一部分,旨在为网络客户端的键值对数据持久存储提供指导。LocalStorage API 旨在支持需要在会话之间可用的数据。换句话说,使用 LocalStorage API 保存的应用程序数据将在应用程序下次运行时再次可用。
为了访问 LocalStorage API,你必须将window对象引用到其localStorage属性。如果你在浏览器控制台中输入以下代码片段,你可以查看localStorage对象的方法和属性:
console.log(window.localStorage);
以下列表总结了可用的方法和属性:
-
key: 这返回存储在特定位置的键名;你可以通过键或索引访问localStorage数据 -
getItem: 这返回由键标识的值 -
setItem: 这将值保存到localStorage对象的特定键(即字符串)中;该方法需要一个字符串作为键和一个要存储在特定键中的值 -
removeItem: 这将从localStorage对象中删除由键标识的项 -
clear: 这将从localStorage对象中删除所有键值对 -
length: 这返回存储在localStorage对象中的项的总数
localStorage对象允许你仅以键值对的形式存储简单的字符串数据。如果你想存储更复杂的数据,你必须使用 JSON 或其他数据的字符串表示形式。当你使用 JSON 格式的字符串来存储值时,当你想要使用它时,你需要将字符串转换回 JSON。每次localStorage对象更新时,都会触发StorageEvent。此事件不能取消,并包含以下属性:
-
key: 这是一个表示已添加、删除或修改的存储中的命名键的字符串 -
oldValue: 如果更新了命名键,则这是该键的先前值;如果向localStorage对象中添加了新项,则为 null -
newValue: 这是键的新值,如果移除了项则为 null -
url: 这是调用触发此数据更改的方法的 HTML 页面的地址
小贴士
如果可以防止事件的默认操作,则 JavaScript 事件是可取消的。
请记住,存储事件不会在同一个窗口或标签页上工作;它们只为使用相同localStorage对象的其他窗口或标签页触发。
PhoneGap 的localStorage功能允许你作为开发者编写类似于浏览器的代码;它是为你处理不同平台(Android、BlackBerry WebWorks OS 6.0 及以上、iOS、Windows Phone 7 和 8、以及 Tizen)的框架。
使用localStorage对象时存在一些缺点。由于localStorage API 是同步的,访问localStorage对象所需的时间大于访问内存中对象所需的时间。因此,应用可能看起来响应较慢。此外,如前所述,复杂数据需要序列化和反序列化。这个过程可能会进一步影响应用的响应速度。您可以使用 JavaScript WebWorker来避免任何性能下降,但支持取决于平台浏览器的实现,而不是 PhoneGap。
然而,这些缺点不应该阻止你使用 LocalStorage API,因为,就像大多数性能指标一样,当你连续多次执行相同的操作时,这些性能影响真的很重要。更具体地说,由于访问localStorage对象所需的时间导致的性能下降发生在不同的标签页/窗口访问同一对象时。
当基于 PhoneGap 构建的移动应用运行时,几乎不可能同时由其他标签页/窗口访问localStorage(只有当你的应用开始使用UIWebView中的多个InAppBrowser实例时,你才应该遇到问题)。
现在我们将创建一个示例应用,以便熟悉使用localStorage对象及其支持的所有方法。
行动时间 - 在 LocalStorage 上读取和写入数据
-
打开命令行工具,使用您之前安装的 PhoneGap CLI 创建一个新的 PhoneGap 项目。这将在您的当前工作目录中创建一个名为
DeviceApi的新目录;这可以通过以下命令完成:$ phonegap create DeviceApi -
移动到您刚刚创建的目录:
$ cd DeviceApi -
在设备 API 上添加您想要测试的平台。例如,我们添加 Android 平台:
$ cordova platform add android -
删除
www目录内除index.html之外的所有文件和子目录。打开您在www根目录中找到的index.html文件,并添加以下 HTML 代码片段:<button type="button" onclick="addCountry()">Add Canada</button> <br/><br/> <button type="button" onclick="get1Data()">Get USA Data</button> <br/><br/> <button type="button" onclick="getAllData()">Get All Data</button> <br/><br/> <button type="button" onclick="removeAllData()">Remove All Countries</button> <br/><br/>此代码将添加四个按钮,这些按钮将添加新的
localStorage数据,删除数据,检索数据和清除所有localStorage数据。请注意,每个按钮都有一个onclick事件,指向一个函数。 -
现在让我们向页面添加所需的 JavaScript。首先,我们将
onDeviceReady函数绑定到deviceready事件。当页面加载时,此函数将添加 3 个localStorage数据。使用localStorage对象的setItem方法来添加新的键值对。如下所示:document.addEventListener("deviceready", onDeviceReady, false); function onDeviceReady() { localStorage.setItem("IN", "India"); localStorage.setItem("FR", "France"); localStorage.setItem("USA", "U.S.A"); } -
当用户点击添加加拿大按钮时,我们需要向
localStorage对象添加一个新的国家。我们将再次使用setItem来添加新项。完成后,我们将显示一个警告。请注意,您不能再次添加相同的键到存储中,如下所示:function addCountry() { localStorage.setItem("CA", "Canada"); alert("Canada added successfully"); } -
现在,要获取存储中的特定对象,我们应该使用
getItem方法。当存在匹配的键时,返回值。如果没有,则返回 null。在这个例子中,我们将获取USA键的值并在 alert 中显示它:function get1Data() { var usa = localStorage.getItem("USA"); alert("Country Name is " + usa); } -
要获取存储中的所有项,我们应该遍历
localStorage对象,并逐个获取键,如下面的代码所示:function getAllData() { for(var i in localStorage){ alert(localStorage.getItem(i)); } } -
clear方法用于清除应用中localStorage的所有值:function removeAllData() { window.localStorage.clear(); alert("All Countries removed successfully"); }
如removeAllData函数所示,我们可以使用window.localStorage而不是localStorage来操作localStorage对象。
本例的完整代码如下提供供您参考:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>Hello World</title>
</head>
<body>
<button type="button" onclick="addCountry()">Add Canada</button> <br/><br/>
<button type="button" onclick="get1Data()">Get USA Data</button> <br/><br/>
<button type="button" onclick="getAllData()">Get All Data</button> <br/><br/>
<button type="button" onclick="removeAllData()">Remove All Countries</button> <br/><br/>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
localStorage.setItem("IN", "India");
localStorage.setItem("FR", "France");
localStorage.setItem("USA", "U.S.A");
}
function addCountry() {
localStorage.setItem("CA", "Canada");
alert("Canada added successfully");
}
function get1Data() {
var usa = localStorage.getItem("USA");
alert("Country Name is " + usa);
}
function getAllData() {
for(var i in localStorage){
alert(localStorage.getItem(i));
}
}
function removeAllData() {
window.localStorage.clear();
alert("All Countries removed successfully");
}
</script>
</body>
</html>
刚才发生了什么?
您使用 JavaScript 在设备上保存了持久数据,这些 JavaScript 也可以在所有主流桌面浏览器中运行。请注意,每个平台都会将数据存储在不同的位置,并且这些数据可能会被设备清除。根据平台的不同,这些数据可能在应用关闭或设备重启时被删除。因此,强烈建议不要使用localStorage对象来存储关键信息。
探索 PhoneGap SQL 存储
目前,移动设备上不同浏览器中的客户端存储实现相当不一致。在 PhoneGap 项目中工作时,对每个浏览器实现进行分析非常重要,因为应用是通过 WebView 渲染的;在 iOS 上,这是 Objective-C 的UIWebView类;在 Android 上,它是android.webkit.WebView,在其他所有支持的平台上也有所不同。WebView 只是暴露了底层平台浏览器。因此,了解目标平台移动浏览器的客户端存储选项是否受支持非常重要。
以下表格总结了在撰写本文时主要浏览器移动版本的存储支持;X 标志表示该功能是否受支持。
| Android 浏览器 | Firefox OS | iOS Safari | IE 11 移动版 | Chrome for Android | |
|---|---|---|---|---|---|
| IndexedDB | X | X | X | X | X |
| Web SQL | X | --- | X | --- | X |
IndexedDB 是一个简单的平面文件数据库,具有分层键值持久性和基本索引。Web SQL基本上是浏览器中嵌入的SQLite。SQLite 是一个包含在用 C 编写的约 350 KB 的小型库中的关系数据库,被 Skype 或 Photoshop Lightroom 等软件使用。这些存储选项之间的主要区别在于,IndexedDB 是一个 NoSQL 数据库,允许您根据应用程序需求与 JavaScript 对象和索引一起工作,而 Web SQL 是一个真正的关系型客户端数据库实现。使用 Web SQL 的一个优点是您可以在后端和前端之间共享相同的查询。在运行一些测试时,您可以看到 Web SQL 可以有多快。
注意
为了在您的机器上运行相同的测试,您可以克隆位于github.com/scaljeri/indexeddb-vs-websql的 GitHub 仓库,并在您的网络浏览器中打开文件test.html。
W3C 于 2010 年 11 月 18 日停止了对 Web SQL 的支持,使 IndexedDB 成为事实上的标准。从开发者的角度来看,IndexedDB 可能看起来是一个巨大的倒退,但实际上并非如此。多年来,开发者使用键值对在客户端存储数据,并且大多数时候他们使用非结构化查询语言(UQL)或 NoSQL 来查询对象。因此,IndexedDB 应被视为客户端存储的自然演变。
PhoneGap 提供基于已弃用的 Web SQL 数据库规范的存储 API。当设备支持 Web SQL 时,应用程序将使用它。如果不支持,应用程序将使用 PhoneGap 的。对于开发者来说,将不会有任何区别,因为我们不需要更改任何代码行。
在 PhoneGap 中处理数据库存储
要在 PhoneGap 中使用Database对象,只需使用以下所示的openDatabase方法:
var size = (1024 * 1024 * 2);
var db = window.openDatabase("name", "1.0", "Test DB", size);
openDatabase方法接受以下四个(自解释)参数:
-
数据库名称
-
数据库版本
-
数据库的显示名称
-
数据库的估计大小
请记住,应用程序可以查询数据库的版本号,以了解是否需要升级数据库模式。openDatabase方法返回当前打开数据库的引用。
在前面的代码片段中,我分配了 2MB 的字节空间。在分配空间时,请考虑移动设备可能对其支持的数据库大小的限制。例如,iOS 仅允许基于 Web 的应用程序使用最多 5MB;当使用 PhoneGap 作为包装器时也是如此。
返回的数据库对象公开了两个方法,这些方法接受一个到三个参数:transaction()和readTransaction()。主要区别在于readTransaction()方法必须用于只读模式。可以传递给这些方法的参数包括:
-
一个执行一个或多个 SQL 语句的函数
-
一个处理在打开数据库时由应用程序抛出的异常的函数
-
一个处理数据库成功打开的函数
注意,transaction()和readTransaction()异步方法是 PhoneGap 框架中唯一需要在成功处理函数之前想要失败处理函数的方法。此外,成功处理函数是唯一不接收任何参数的;其他处理函数接收一个SQLTransaction对象。
SQLTransaction对象公开了executeSql方法。使用此方法,可以运行多个 SQL 语句,并将一些参数传递给语句以处理成功的 SQL 查询执行和 SQL 错误。
注意
你可以使用多个标记来将参数传递给 SQL 语句。有关可用标记的完整概述,请参阅在线文档中的www.sqlite.org/lang_expr.html。
成功处理程序接收两个参数:第一个是事务本身的引用,第二个是包含执行查询的信息和可选结果的 SQLResultSet 对象。
小贴士
当执行 SELECT 语句时,SQLResultSet 对象的 insertId 属性返回一个 Exception: DOMException 值。你可以安全地忽略它,因为它不会影响应用程序。
实践时间 - 填充本地数据库
为了巩固你刚刚学到的知识,你将创建一个新的本地数据库,向其中添加一个表,并写入和读取一些数据。
-
返回到之前示例中创建的项目,或者创建一个新的项目。清空
index.html文件的内容,并添加以下按钮标记到其中。此按钮将用于从数据库中查询数据:<button type="button" onclick="queryEmployees()">Fetch All Rows</button> -
创建一个 JavaScript 标签和
deviceready事件监听器:document.addEventListener("deviceready", onDeviceReady, false); -
在
onDeviceReady函数的主体中,我们将创建一个名为EMP的新数据库,版本号为1.0,命名为Employee Details,估计大小为 2 MB:var size = (1024 * 1024 * 2); function onDeviceReady() { var database = window.openDatabase('employee', '1.0', 'Employee Details', size); database.transaction(populateDB, onError, onSuccess); }使用
openDatabase方法创建数据库后,我们将填充数据库。我们必须为需要执行的每个操作创建一个事务,因此我们将populateDB方法作为事务的参数提供:function populateDB(tx) { tx.executeSql('DROP TABLE IF EXISTS EMP'); tx.executeSql('CREATE TABLE IF NOT EXISTS EMP (id unique, name)'); tx.executeSql('INSERT INTO EMP (id, name) VALUES (1, "John Doe")'); tx.executeSql('INSERT INTO EMP (id, name) VALUES (2, "Jane Doe")'); } function onError(err) { alert("SQL Error: " + err.code); } function onSuccess() { alert("Success!"); }executeSql方法可以接受任何标准 SQL 语句并执行它。 -
现在,我们将创建
queryEmployees函数,该函数绑定到按钮的onclick事件。此函数将打开数据库并创建一个事务以查询数据库:function queryEmployees() { var database = window.openDatabase('employee', '1.0', 'Employee Details', size); database.transaction(queryData, onError); } -
作为事务参数提供的
queryData函数执行 SQL 查询。这需要事务参数tx,并将结果集传递给onSelectSuccess函数:function queryData(tx) { tx.executeSql('SELECT * FROM EMP', [], onSelectSuccess, onError); } -
onSelectSuccess函数将事务和结果集作为参数,并执行实际的解析工作:function onSelectSuccess(tx, results) { var len = results.rows.length; alert("Total Employees : " + len); for (var i=0; i<len; i++){ alert(results.rows.item(i).id + " - " + results.rows.item(i).name); } }
本示例的完整代码提供给你快速参考。你还可以在浏览器开发者工具中验证数据库记录:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>Database Example</title>
</head>
<body>
<button type="button" onclick="queryEmployees()">Fetch All Rows</button>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
var size = (1024 * 1024 * 2);
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
var database = window.openDatabase('employee', '1.0', 'Employee Details', size);
database.transaction(populateDB, onError, onSuccess);
}
function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS EMP');
tx.executeSql('CREATE TABLE IF NOT EXISTS EMP (id unique, name)');
tx.executeSql('INSERT INTO EMP (id, name) VALUES (1, "John Doe")');
tx.executeSql('INSERT INTO EMP (id, name) VALUES (2, "Jane Doe")');
}
function onError(err) {
alert("SQL Error: " + err.code);
}
function onSuccess() {
alert("Success!");
}
function queryEmployees() {
var database = window.openDatabase('employee', '1.0', 'Employee Details', size);
database.transaction(queryData, onError);
}
function queryData(tx) {
tx.executeSql('SELECT * FROM EMP', [], onSelectSuccess, onError);
}
function onSelectSuccess(tx, results) {
var len = results.rows.length;
alert("Total Employees : " + len);
for (var i=0; i<len; i++){
alert(results.rows.item(i).id + " - " + results.rows.item(i).name);
}
}
</script>
</body>
</html>
刚才发生了什么?
你创建了一个本地数据库,用于存储和恢复与你的应用程序及其用户相关的信息。当应用程序被卸载时,数据库将自动删除。
数据库限制
在使用 PhoneGap 的 WebSQL 实现时,有一些限制需要注意(有关完整概述,请参阅 SQLite 文档中的www.sqlite.org/limits.html)。这些限制与框架本身无关,而是由于每个目标平台的 web 视图实现造成的。
在开发应用程序时,您很容易遇到的限制是数据库文件的大小限制。例如,在 WebKit 中,它取决于操作系统,从 5 MB 到 25 MB 不等。另一个您可能会遇到的限制是,自 iOS 5.1 以来,localStorage和 Web SQL 数据库已从~/Library/WebKit文件夹移动到~/Library/Caches文件夹。实际上,这个变化意味着存储的信息不再备份,并且当需要更多空间时,操作系统可以随意删除(有关 iOS 数据管理的更多信息,请参阅 Apple 开发者指南developer.apple.com/technologies/ios/data-management.html)。
为了避免描述中的问题,您可以使用 GitHub 上可用的 Sqlite 插件,适用于 Android 和 iOS (github.com/litehelpers/Cordova-sqlite-storage)。
注意
您将在本书中了解更多关于 PhoneGap 插件的内容;目前,您只需要知道插件通常是 HML/CSS/JavaScript 和本地代码的组合,用于扩展 PhoneGap 的功能。
使用此插件时,您获得的主要优势是您可以保持 SQLite 数据库在已知并可重新配置的用户数据位置,没有更多的大小限制,并且可以使用SQLcipher对数据库进行加密(有关完整参考,请参阅在线文档sqlcipher.net/documentation/)。
从开发者的角度来看,API 没有变化,只是前缀有所不同;这意味着您不需要像这样通过window对象打开数据库:
window.openDatabase();
您必须像这样引用插件:
sqlitePlugin.openDatabase();
理解文件 API
PhoneGap 文件 API 是两个不同 W3C API 的实现,即目录和系统 API 以及文件 API(您可以在 W3C 网站上找到完整的规范www.w3.org/TR/file-system-api/和www.w3.org/TR/file-upload)。PhoneGap 文件 API 并不是 W3C 规范的完整实现;缺失的部分是同步文件系统接口的实现。异步 JavaScript API 使用起来稍微复杂一些,因为您必须与多个嵌套函数一起工作,但这不应成为大问题;实际上,这是所有网络开发者都非常熟悉的事情。
注意
异步与同步 JavaScript 执行的主要区别在于,在前者的情况下,你可以同时运行多个进程,从而避免用户界面“冻结”。随着 JavaScript 中 Web Workers 的引入,可以避免这个问题,但这完全超出了本书的范围;你可以在 Mozilla 网站上找到更多关于 Web Workers 的信息,网址为developer.mozilla.org/en-US/docs/DOM/Using_web_workers。
为了访问设备文件系统,你可以使用LocalFileSystem对象的requestFileSystem方法;此对象的所有方法都在window对象中定义。该方法接受以下四个参数:
-
存储类型(临时或持久)
-
在设备存储上要分配的字节数量
-
成功处理程序
-
错误处理程序
当你想访问设备文件系统时,生成的代码看起来如下所示:
window.requestFileSystem(/*storage*/, /*size*/, onSuccess, onError);
对于storage参数,你需要指定LocalFileSystem对象中定义的以下两个伪常量之一:
-
LocalFileSystem.PERSISTENT:这表示存储无法在没有应用或用户许可的情况下被用户代理删除。 -
LocalFileSystem.TEMPORARY:这表示存储在请求空间中的文件可以被用户代理或系统删除,无需应用或用户的许可
请求的沙盒存储大小以字节为单位表示;例如,为了使代码更易读,可以使用(4 x 1024 x 1024)的语法来分配 4 KB,而不是使用 4,194,304 字节的数字。
注意
设备硬盘并未完全对应用程序视图开放。硬盘的一部分仅被分配给单个应用;这就是应用的沙盒。应用沙盒背后的理念是每个应用只能访问其自己的沙盒以及操作系统拥有的某些高级目录。高级目录的结构取决于操作系统。
onSuccess处理程序接收一个FileSystem对象作为参数。为此对象定义的两个属性是name和root。访问对象的name属性可以读取文件系统的名称;访问root属性允许你获取应用沙盒的root目录的引用。这在此处显示:
function onSuccess(fileSystem){
console.log(fileSystem.name);
var currentRoot = fileSystem.root;
}
onError处理程序接收一个FileError对象作为参数;此对象使用对象本身定义的几个伪常量来表示不同的错误,如下所示:
function onError(fileError){
console.log(fileError.code);
}
如果已知文件或目录的位置,可以使用LocalFileSystem对象的resolveLocalFileSystemURI方法来访问它。此方法接受以下三个参数:
-
文件或目录的 URI
-
成功处理程序(
onSuccess) -
错误处理程序(
onError)
如果你想访问例如 Android 设备的外部存储,可以使用以下语法:
window.resolveLocalFileSystemURI('file:///mnt/sdcard', onSuccess, onError);.
onSuccess函数接收一个DirectoryEntry或FileEntry对象作为参数,这取决于输入路径的类型(即目录或文件);onError处理程序接收一个FileError对象作为参数。
code属性的值由以下伪常量总结:
-
FileError.NOT_FOUND_ERR(返回值1):这意味着应用所需的文件或目录无法找到 -
FileError.SECURITY_ERR(返回值2):这意味着文件或目录位于应用沙盒之外,或者应用没有访问它的权限 -
FileError.ABORT_ERR(返回值3):这是在调用读取器或写入器的abort方法时抛出的 -
FileError.NOT_READABLE_ERR(返回值4):这意味着应用所需的文件或目录无法读取 -
FileError.ENCODING_ERR(返回值5):这意味着在LocalFileSystem对象的resolveLocalFileSystemURI方法中用作参数的路径或本地 URI 格式不正确 -
FileError.NO_MODIFICATION_ALLOWED_ERR(返回值6):这意味着应用尝试写入一个由于文件系统的实际状态而无法修改的文件或目录 -
FileError.INVALID_STATE_ERR(返回值7):这意味着应用访问了一个由另一个进程使用的文件 -
FileError.SYNTAX_ERR(返回值8):这是不言自明的;这是由于语法错误导致的 -
FileError.INVALID_MODIFICATION_ERR(返回值9):这意味着应用请求的修改无效;此类错误的例子是将目录移动到其自身的子目录中 -
FileError.QUOTA_EXCEEDED_ERR(返回值10):这意味着应用请求的存储量超过了允许的存储配额 -
FileError.TYPE_MISMATCH_ERR(返回值11):这意味着应用尝试访问一个文件或目录,但条目不是预期的类型(即返回的是目录而不是文件) -
FileError.PATH_EXISTS_ERR(返回值12):这意味着由于存在具有相同路径的文件或目录,应用未能创建文件或目录
读取目录和文件
只有在获得对文件系统的访问权限后,才能读取设备目录、子目录和内容。再次强调,用作requestFileSystem方法参数的onSuccess处理程序接收一个FileSystem对象。通过此对象的root属性,可以访问一个DirectoryEntry对象,然后创建一个能够读取当前目录中所有条目的DirectoryReader对象。这在此处展示:
function onSuccess(fileSystem){
var currentRoot = fileSystem.root;
var reader = currentRoot.createReader();
}
DirectoryReader 对象公开了一个名为 readEntries 的方法。它可以用来读取条目,由于文件 API 的异步特性,它接受一个成功处理程序和一个失败处理程序。类似于 resolveLocalFileSystemURI 方法所发生的情况,成功处理程序根据所列项目的类型(即目录或文件)接收一个 DirectoryEntry 或 FileEntry 对象的数组。
行动时间 - 列出文件夹
准备探索设备的持久存储文件夹。使用以下步骤:
-
打开命令行工具,使用您之前安装的 Cordova CLI 工具创建一个新的项目。这将在一个名为
FileSystem的新目录中创建一个新目录,位于您当前的工作目录中:$ cordova create FileSystem -
移动到您刚刚创建的目录:
$ cd FileSystem -
在设备 API 上添加您想要测试的平台。例如,我们添加了 Android 平台:
$ cordova platform add android -
使用以下命令添加 File API 插件:
$ cordova plugin add cordova-plugin-file -
前往
www文件夹,打开index.html文件,并在应用程序的主体中添加一个具有id值为fileslist的div元素:<div id="fileslist"></div> -
我们现在将在 JavaScript 部分添加一个
deviceready事件监听器。onDeviceReady函数必须在deviceready事件触发后调用:document.addEventListener("deviceready", onDeviceReady, false); -
在
onDeviceReady函数的主体中,请求访问设备文件系统,指定您将要定义的成功和失败处理程序,并请求 0 KB 的持久存储;您只需要在写入设备文件系统时指定配额:function onDeviceReady() { var size = 0; window.requestFileSystem(LocalFileSystem.PERSISTENT, size, onFileSystemSuccess, onFileSysError); } -
定义错误处理程序,当代码抛出错误时,它会通知您:
function onFileSysError(error){ alert("Error Code: " + error.code); } -
定义成功处理程序,并在其主体内部创建一个新的
DirectoryReader对象,并使用此对象来读取所有目录内容。我们需要传递一个新的函数parseDirectories,该函数将实际遍历目录列表。目录条目将自动作为参数传递给此函数:function onFileSystemSuccess(fileSystem){ var reader = fileSystem.root.createReader(); reader.readEntries(parseDirectories,onFileSysError); } -
定义
parseDirectories函数,并将其以下片段添加到其中:function parseDirectories(entries){ var str = '<ul>'; for (var i = 0, len = entries.length; i < len; i++) { if (entries[i].isDirectory) { str += '<li>' + entries[i].fullPath + '</li>'; } } str += "</ul>"; document.getElementById("fileslist").innerHTML+= str; }我们读取条目,并在列表中的每个条目上检查它是否是目录或文件。如果是目录,我们将目录名称追加到无序列表,如果是文件则忽略它。最后,我们将生成的无序列表(
ul)添加到我们之前创建的名为fileslist的div中。当此代码在实际设备上运行时,它将列出持久存储中的所有目录。
本例的整个源代码如下所示:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>File Example</title>
</head>
<body>
<div id="fileslist"></div>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
var size = 0;
window.requestFileSystem(LocalFileSystem.PERSISTENT, size, onFileSystemSuccess, onFileSysError);
}
function onFileSysError(error){
alert("Error Code: " + error.code);
}
function onFileSystemSuccess(fileSystem){
var reader = fileSystem.root.createReader();
reader.readEntries(parseDirectories,onFileSysError);
}
function parseDirectories(entries){
var str = '<ul>';
for (var i = 0, len = entries.length; i < len; i++) {
if (entries[i].isDirectory) {
str += '<li>' + entries[i].fullPath + '</li>';
}
}
str += "</ul>";
document.getElementById("fileslist").innerHTML+= str;
}
</script>
</body>
</html>
发生了什么?
应用程序现在可以使用异步的 Files API 从设备的持久存储中读取目录列表。
写入和读取文件数据
要将数据写入文件,应用程序只需使用 FileWriter 对象访问文件即可。为了获取 FileWriter 对象,您首先必须使用 LocalFileSystem 对象的 requestFileSystem 方法访问 DirectoryEntry 对象或 FileEntry 对象。
一旦成功访问文件系统,你可以请求一个文件,指定你想要使用create标志创建它:
Function onFileSystemSuccess(fileSystem){
var root = fileSystem.root;
root.getFile('data.txt', {create: true},
onGetFile, onGetFileError);
}
注意
文件 API 中有两个标志可以作为getFile和getDirectory方法的参数使用:create和exclusive。create标志用于指示应创建文件或目录;当create标志设置为true时,exclusive标志才会生效,并且如果文件或目录已存在,它会导致创建失败。
与其他 Files API 一样,getFile方法也是异步的,需要成功和失败处理程序。一旦在成功处理程序中,就可以使用接收到的FileEntry对象的createWriter方法创建一个FileWriter对象。createWriter方法也需要成功和失败处理程序,如下所示:
function onGetFile(file){
file.createWriter(onGetWriter, onGetWriterError);
}
再次强调,你有另外两个处理程序,这意味着只有在你调用了三个回调函数之后,你才能将一些内容写入你刚刚创建的文件:
function onGetWriter(writer){
writer.write('Hello PhoneGap Files API!');
}
小贴士
在 PhoneGap 中使用FileWriter对象无法从 JavaScript 写入二进制数据;这是框架的限制,因为它将数据作为字符串在原生和 JavaScript 层之间传递。一个可能的解决方案是编写一个插件,将 Base64 字符串转换为二进制数据。
当执行写入操作时,会发生几个事件。对于每个此类事件,FileWriter对象上都有一个相应的属性:
-
当
FileWriter对象开始写入文件时,会调用onwritestart事件;它接收一个ProgressEvent对象作为参数。 -
当
FileWriter对象成功完成写入操作时,会调用onwrite事件;它接收一个ProgressEvent对象作为参数。 -
当通过调用
FileWriter的abort方法中断写入操作时,会调用Onabort事件;它接收一个ProgressEvent对象作为参数。 -
当写入操作失败时,会调用
Onerror事件;它接收一个ProgressEvent对象作为参数。为了了解错误发生的原因,你可以访问存储在event对象的target.error属性中的FileError对象。
FileWriter对象还包含其他属性。要获取完整概述,请参考可在docs.phonegap.com/en/edge/cordova_file_file.md.html#FileWriter找到的在线指南。
使用ProgressEvent对象,你可以通过loaded、total和type属性访问已加载的字节、总字节和事件的性质(即abort、writeend等):
function onGetWriter(writer){
writer.onwrite = function(evt){
console.log(evt.loaded, evt.total, evt.type);
}
writer.write('Hello PhoneGap Files API!');
}
小贴士
如果你尝试依次调用FileWriter对象的write方法,只有第一个字符串将被添加到文件中。你必须等待writeend事件触发,才能将其他数据写入文件。
当你想读取文件时,你可以使用一个FileReader对象。此对象与FileWriter对象类似。使用它时,会发生几个事件:FileWriter对象的onabort和onerror属性与FileReader的类似。仅与FileReader对象相关的属性如下:
-
onloadstart: 当FileReader对象开始读取文件时,存储在此属性的函数将被调用;它接收一个ProgressEvent对象作为参数。 -
onload: 当读取操作成功完成后,存储在此属性的函数将被调用;它接收一个ProgressEvent对象作为参数。 -
onloadend: 当读取操作完成时(无论成功与否),存储在此属性的函数将被调用;它接收一个ProgressEvent对象作为参数。
FileReader对象允许你以以下四种不同的方式读取文件数据:
-
readAsDataURL: 这将读取文件,并将指定文件的 内容作为 Base64 编码的数据 URL 返回 -
readAsText: 这将读取文件,并以默认 UTF-8 编码将数据作为字符串返回 -
readAsBinaryString: 这将以二进制形式读取文件,并将数据作为二进制字符串返回 -
readAsArrayBuffer: 这将读取文件,并将数据作为ArrayBuffer返回
为了将你刚刚学到的知识付诸实践,现在你将看到如何解析设备的持久存储,恢复第一个可用的图像,并在 app 的 web 视图中渲染它。
这是将文本文件写入手机存储的完整源代码:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>File Example</title>
</head>
<body>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
var size = 0;
window.requestFileSystem(LocalFileSystem.PERSISTENT, size, onFileSystemSuccess, onFileSysError);
}
function onFileSystemSuccess(fileSystem){
var root = fileSystem.root;
root.getFile('data.txt', {create: true}, onGetFile, onFileSysError);
}
function onGetFile(file){
file.createWriter(onGetWriter, onFileSysError);
}
function onGetWriter(writer){
writer.onwrite = function(evt){
console.log(evt.loaded, evt.total, evt.type);
}
writer.write('Hello PhoneGap Files API!');
}
function onFileSysError(error){
alert("Error Code: " + error.code);
}
</script>
</body>
</html>
行动时间 - 读取和渲染图像
准备将设备存储中第一个可用的图像渲染到 PhoneGap 默认 app 模板中。请参考以下步骤:
-
打开命令行工具,创建一个名为
ReadingFile的新 PhoneGap 项目。 -
使用以下命令行添加 File API 插件:
$ cordova plugin add cordova-plugin-file -
进入
www文件夹,打开index.html文件,在紧随deviceready之后的 app 主div内添加一个img标签,其id值为firstImage:<img id='firstImage' /> -
进入
www/js文件夹,打开index.js文件,并定义一个名为requestFileSystem的新函数:Function requestFileSystem() { // The request of access to the file system will go here } -
定义错误处理程序以获取每个可能错误的代码:
Function onError(error){ alert(error.code); } -
在
requestFileSystem函数的主体中,使用LocalFileSystem对象的requestFileSystem函数访问设备文件系统,定义成功和失败处理程序,并在成功处理程序中访问root文件系统:window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, onError);提示
你请求的是
0字节的配额,因为你只是读取文件;你只需要在写入设备文件系统时指定配额。 -
一旦你获得对
root文件系统的访问权限,你可以在成功处理程序中创建一个DirectoryReader对象,并开始使用该对象的readEntries异步方法探索根文件系统:var root = fileSystem.root; var reader = root.createReader(); reader.readEntries(function(entries){ for (var i = 0, entry; entry = entries[i]; i++){ // Here The logic to check if the file is an image } }, onError); -
为了在
for循环中确定一个文件是否是图片,你可以首先检查条目的isFile属性,然后使用一个简单的正则表达式;当条件满足时,使用根DirectoryEntry对象的getFile方法访问文件,指定成功和失败处理程序:if (entry.isFile && (/\.(gif|jpg|jpeg|png)$/i).test(entry.name)){ root.getFile(entry.name, {create: false}, onGetFile, onError); break; } -
在 JavaScript 部分,定义
onGetFile函数,并在其主体中,通过使用FileEntry对象的file方法访问实际的文件。一旦你获得了对文件的访问权限,指定onload和onerror处理程序,并使用readAsDataURL方法读取文件,以便将结果分配给img标签的src属性,如下所示:function onGetFile(fileEntry){ fileEntry.file(function(file){ var reader = new FileReader(); reader.onload = function(evt){ var img = document.querySelector('#firstImage'); img.src = evt.target.result; }; reader.onerror = function(evt){ alert(evt.target.error.code, null); }; reader.readAsDataURL(file); }, onError); }
现在在一个真实设备上测试项目。查看以下提供的完整代码,以便快速审查:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>File Example</title>
</head>
<body>
<img id="firstImage" />
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
var size = 0;
window.requestFileSystem(LocalFileSystem.PERSISTENT, size, onFileSystemSuccess, onError);
}
function onFileSystemSuccess(fileSystem){
var root = fileSystem.root;
var reader = root.createReader();
reader.readEntries(function(entries){
for (var i = 0, entry; entry = entries[i]; i++){
if (entry.isFile &&
(/\.(gif|jpg|jpeg|png)$/i).test(entry.name)){
root.getFile(entry.name, {create: false}, onGetFile, onError);
break;
}
}
}, onError);
}
function onGetFile(fileEntry){
fileEntry.file(function(file){
var freader = new FileReader();
freader.onload = function(evt){
var img = document.querySelector('#firstImage');
img.src = evt.target.result;
};
freader.onerror = function(evt){
alert(evt.target.error.code);
};
freader.readAsDataURL(file);
}, onError);
}
function onError(error){
alert("Error Code: " + error.code);
}
</script>
</body>
</html>
刚才发生了什么?
你已经探索了设备的文件系统,并在你的应用中将找到的第一张图片作为 Base64 数据流渲染。现在,你对 File API 有了一定的了解,是时候学习如何从设备传输文件到设备了。
文件传输
PhoneGap File API 还包括FileTransfer对象。正如其名所示,这个对象允许你开发应用程序,通过互联网下载和上传文件。FileTransfer对象公开的方法是自解释的:upload、download和abort。
upload方法接受多个参数:设备上文件的路径、接收文件的 URL、成功和失败处理程序、选项对象以及一个布尔值,用于强制方法接受所有安全证书。(我在下一个片段中省略了布尔值,因为在生产中使用它不被推荐;应用程序应该只接受它被设计用来处理的协议。)
var fileTransfer = new FileTransfer();
fileTransfer.upload(fileURI, URL, onSuccess, onError, options);
options参数是一个FileUploadOptions对象。该对象允许你使用以下属性提供额外的信息:
-
chunkedMode: 这是一个布尔值,表示是否在不进行内部缓冲的情况下执行 HTTP 请求的流式传输。(对于分块传输编码的更详细描述,你可以参考en.wikipedia.org/wiki/Chunked_transfer_encoding)。 -
fileKey: 这是一个字符串,表示文件上传到服务器上表单元素下的名称;默认值是file。 -
fileName: 这是一个表示上传文件名称的字符串;默认值是image.jpg。 -
mimeType: 这是一个表示将要上传的文件 MIME 类型的字符串;默认值是image/jpg。 -
params: 这是一个对象,表示要包含在 HTTP 请求头中的键值对。
onSuccess处理程序接收一个FileEntry对象作为参数,这样你就可以立即访问信息,例如设备上的文件名和完整路径。onError处理程序接收一个FileTransferError对象;该对象的属性如下:
-
code:这是一个表示存储在FileTransferError伪常量中的四个可能错误代码之一的数字(即FILE_NOT_FOUND_ERR、INVALID_URL_ERR、CONNECTION_ERR和ABORT_ERR)。 -
source:这是一个表示源文件 URI 的字符串。 -
target:这是一个表示目标文件 URI 的字符串。 -
http_status:这是一个表示 HTTP 状态码的数字。
download方法的工作方式类似;唯一的区别是前两个参数被交换,分别是:下载文件的 URL 和系统 URI(即路径),以便在设备上存储;此外,options参数只接受 HTTP 头。如下所示:
var fileTransfer = new FileTransfer();
fileTransfer.download(URL, filePath, onSuccess, onError, options);
可以使用abort方法停止下载或上传操作,一旦调用onError处理程序,其参数的code属性值是伪常量,FileTransferError.ABORT_ERR。
注意
此外,当你设置错误的文件下载路径时,FileTransferError对象的code属性等于FileTransferError.FILE_NOT_FOUND_ERR(即值1)。
在FileTransfer对象中只定义了onprogress属性。正如其名所示,此属性用于存储一个函数,每当从或向设备传输数据块时都会调用该函数。
接下来,为了将你刚刚学到的知识付诸实践,你将下载一个文件,显示下载进度,并在下载完成后添加一个文件链接。
行动时间 - 下载并保存文件
准备下载一个文件并在 PhoneGap 默认应用模板中显示进度条和文件链接。请参考以下步骤:
-
打开命令行工具并创建一个名为
DownloadFile的新 PhoneGap 项目:$ cordova create DownloadFile -
切换到
DownloadFile目录:$ cd DownloadFile -
使用以下命令添加 File 和 FileTransfer API 插件:
$ cordova plugin add cordova-plugin-file-transfer $ cordova plugin add cordova-plugin-file -
前往
www文件夹,打开index.html文件,在deviceready标签下面的应用主div元素内添加一个带有progress值的progress标签;将value属性赋值为1,将max属性赋值为100:<progress id='progress' value='1' max='100'></progress> -
定义一个名为
onDeviceReady的新 JavaScript 函数,并将其添加到deviceready监听器中:document.addEventListener("deviceready", onDeviceReady, false); function onDeviceReady() { var size = 0; window.requestFileSystem(LocalFileSystem.PERSISTENT, size, onFileSystemSuccess, onError); } -
对于
onFileSystemSuccess方法,一旦你获得对文件系统的访问权限,创建一个新的FileTransfer对象,并调用download方法,指定远程 URL、系统根 URI 以及成功和失败处理程序:function onFileSystemSuccess(fileSystem){ var fileTransfer = new FileTransfer(); var url = 'http://s3.amazonaws.com/mislav/Dive+into+HTML5.pdf'; fileTransfer.download(encodeURI(url), fileSystem.root.toURL() + '/' + 'html5.pdf', fileDownloaded, onError); fileTransfer.onprogress = function(evt){ if (evt.lengthComputable){ var tot = (evt.loaded / evt.total) * 100; var element = document.querySelector('#progress'); element.value = Math.round(tot); } } } -
定义成功和失败事件回调函数。对于任何错误,我们将以 JSON 字符串的形式将整个
error对象警报整个系统,如下所示:function fileDownloaded(entry){ alert("Success"); } function onError(error){ alert(JSON.stringify(error)); } -
现在,在真实设备上运行你的项目。文件将被下载,进度将在进度条中显示。一旦完成,你可以在设备上验证下载的文件。
本例的完整源代码如下所示:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>File Example</title>
</head>
<body>
<progress id='progress' value='1' max='100'></progress>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
var size = 0;
window.requestFileSystem(LocalFileSystem.PERSISTENT, size, onFileSystemSuccess, onError);
}
function onFileSystemSuccess(fileSystem){
var fileTransfer = new FileTransfer();
var url = 'http://s3.amazonaws.com/mislav/Dive+into+HTML5.pdf';
fileTransfer.download(encodeURI(url),
fileSystem.root.toURL() + '/' + 'html5.pdf',
fileDownloaded,
onError);
fileTransfer.onprogress = function(evt){
if (evt.lengthComputable){
var tot = (evt.loaded / evt.total) * 100;
var element = document.querySelector('#progress');
element.value = Math.round(tot);
}
}
}
function fileDownloaded(entry){
alert("Success");
}
function onError(error){
alert(JSON.stringify(error));
}
</script>
</body>
</html>
发生了什么?
你启动了一个文件下载,显示了一个进度条,并渲染了一个指向文件的链接。你将注意到一个问题,因为大多数平台在 WebView 中不提供 PDF 阅读器。简而言之,用户既无法在应用中也无法在外部浏览器中阅读该文件。为了打开一个原生应用来阅读文件,你必须使用一个外部插件。你将在下一章中了解到如何在你的应用中集成插件以及如何解决这个问题。
摘要
在本章中,你学习了如何在设备上保存数据以及如何处理最常见的限制。你还学习了 Files API 的工作原理,并了解了它的功能。
在下一章中,你将学习如何使用 Contact API 来处理设备中的联系人以及 Camera API,以便使用设备摄像头捕捉图像。
第六章:使用联系人及摄像头 API
在上一章中,我们看到了如何充分利用文件和存储插件。现在,随着你对 PhoneGap 的了解越来越深入,是时候给设备本身添加一些交互了。本章的主要目标是帮助你理解 PhoneGap 的联系人 API 的使用,并使用摄像头和媒体捕获 API 与设备媒体进行交互。
在本章中,你将:
-
获取 PhoneGap 联系人 API 及其对象和属性的概览
-
学习如何使用联系人 API 读取和过滤设备上存储的联系人
-
理解摄像头和捕获 API 之间的区别
-
学习如何使用摄像头 API 从设备摄像头捕获图像
-
学习如何使用媒体捕获 API 处理各种媒体类型,如图像、音频和视频
联系人 API
你可以使用 PhoneGap API 轻松访问设备上存储的联系人信息。Contacts API 是 W3C 的 Pick Contacts Intent API(一个允许在 Web 应用程序内部访问用户地址簿服务的 intent)的实现。你可以在www.w3.org/TR/contacts-api/上了解更多关于 W3C 规范的信息。
Contacts API 所需的功能由名为cordova-plugin-contacts的 Contacts 插件提供。此插件支持主要平台。
对于所有支持平台的完整列表,请参阅docs.phonegap.com/en/edge/cordova_contacts_contacts.md.html#Contacts上的文档。
为了开始与设备联系人的交互,你可以使用存储在navigator对象中的contacts对象中定义的create或find方法:
var contact = navigator.contacts.create(properties);
navigator.contacts.find(contactFields, contactSuccess, contactError, contactFindOptions);
为了更好地理解这些方法是如何工作的,让我们探索与之相关的最相关对象:Contact、ContactName、ContactField、ContactFindOptions和ContactError。
联系人名称对象
ContactName对象用于在 PhoneGap 框架中存储联系人名称的所有详细信息。该对象存储在Contact对象的name属性中。
ContactName对象的属性都是字符串,且具有自解释性:
-
formatted:这代表联系人的完整名称 -
familyName:这代表联系人的姓氏 -
givenName:这代表联系人的名字 -
middleName:这代表联系人的中间名 -
honorificPrefix:这代表联系人的前缀(例如,先生或博士) -
honorificSuffix:这代表联系人的后缀(例如,Esq.)
联系人字段对象
ContactField对象是 PhoneGap 框架中用于表示Contact对象字段的通用对象。该对象的通用性质使其可以在多个字段中重复使用。
ContactField对象的属性如下:
-
type: 这是一个字符串,表示字段的类型;可能的值有家庭、工作、手机等 -
value: 这是一个字符串,表示字段的值,例如电话号码或电子邮件地址 -
pref: 这是一个布尔值,表示是否在特定字段中返回用户的首选值。注意
当
ContactField对象用于Contact对象的photos属性时,type属性表示返回图像的类型(例如,URL 或 Base64 编码的字符串)。
ContactAddress对象
ContactAddress对象是存储在Contact对象的addresses属性中的对象。addresses属性是一个数组,其中可以与每个联系人关联多个地址。
ContactAddress对象的属性如下:
-
pref: 这是一个布尔值,表示返回的ContactAddress对象是否是用户为ContactAddress对象指定的首选值 -
type: 这是一个字符串,表示存储在ContactAddress对象中的地址类型(例如,家庭和办公室) -
formatted: 这是一个字符串,表示用于显示的完整地址 -
streetAddress: 这是一个字符串,表示完整的街道地址 -
locality: 这是一个字符串,表示ContactAddress对象中包含的城市或地区 -
region: 这是一个字符串,表示ContactAddress对象中包含的州或地区 -
postalCode: 这是一个字符串,表示与ContactAddress对象中存储的地区相关的邮政编码或邮政编码 -
country: 这是一个字符串,表示存储在ContactAddress对象中的国家名称提示
在 Android 2.x(即
pref属性不受支持)和 Android 1.x 中存在一些限制。此外,iOS 不支持formatted属性。请始终参考在线文档以验证对ContactAddress对象的支持状态。
ContactOrganization对象
ContactOrganization对象代表存储联系人的公司、组织等所有详细信息。该对象存储在Contact对象的organizations属性包含的数组中。
ContactAddress对象的属性如下:
-
pref: 这是一个布尔值,表示返回的ContactOrganization对象是否是用户为ContactOrganization对象指定的首选值 -
type: 这是一个字符串,表示存储在ContactOrganization对象中的地址类型(例如,工作和其他) -
name: 这是一个字符串,表示存储在ContactOrganization对象中的组织名称 -
department: 这是一个字符串,表示联系人为之工作的组织部门 -
title:这是一个表示联系人在组织中的头衔的字符串
name、department和title属性在 iOS 上部分支持;pref和type属性在 Android 1.x 和 Android 2.x 上支持不佳。
联系人对象
Contact对象代表存储在设备数据库中的联系人的所有详细信息。可以使用对象本身定义的save、remove和clone方法将Contact对象保存、删除和从设备联系人数据库中复制。save和remove方法接受两个参数,以便处理保存或删除操作的成功或失败:
var contact = navigator.contacts.create({‘displayName': ‘Giorgio'});
contact.save(onContactSaved, onContactSavedError);
contact.remove(onContactRemoved, onContactRemovedError);
错误处理程序接收相同的ContactError对象作为参数;成功处理程序在联系人成功删除时接收保存的联系人或当前数据库的快照。
ContactError对象包含关于发生错误的code属性中的信息。可以返回的值如下:
-
ContactError.UNKNOWN_ERROR -
ContactError.INVALID_ARGUMENT_ERROR -
ContactError.TIMEOUT_ERROR -
ContactError.PENDING_OPERATION_ERROR -
ContactError.IO_ERROR -
ContactError.NOT_SUPPORTED_ERROR -
ContactError.PERMISSION_DENIED_ERROR
当创建一个新的Contact对象时,你可以在调用create方法时定义联系人的属性或逐个传递它们。Contact对象的属性如下:
-
id:这是一个用作全局唯一标识符的字符串 -
displayName:这是一个表示用于向最终用户显示的Contact对象名称的字符串 -
name:这是一个包含联系人姓名所有信息的对象;用于存储此信息的对象是ContactName -
nickname:这是一个表示联系人非正式名称的字符串 -
phoneNumbers:这是一个包含所有联系人电话号码的数组;数组项是ContactField对象的实例 -
emails:这是一个包含所有联系人电子邮件地址的数组;数组项是ContactField对象的实例 -
addresses:这是一个包含所有联系人地址的数组;数组项是ContactAddresses对象的实例 -
ims:这是一个包含所有联系人即时消息账户的数组;数组项是ContactField对象的实例 -
organizations:这是一个包含联系人所属的所有组织的数组;数组项是ContactOrganization对象的实例 -
birthday:这是一个表示联系人出生日期的Date对象 -
note:这是一个表示关于联系人的注释的字符串 -
photos:这是一个包含所有联系人照片的数组;数组项是ContactField对象的实例 -
categories:这是一个包含所有联系人定义类别的数组;数组项是ContactField对象的实例 -
urls:这是一个包含与联系人相关联的所有网页的数组;数组项是ContactField对象的实例
Contact 对象的属性在所有平台上并不完全受支持。实际上,操作系统的碎片化使得一致地处理这些信息变得困难。
例如,name、nickname、birthday、photos、categories 和 urls 属性在 Android 1.x 上不受支持。同样,categories 属性在 Android 2.x 和 iOS 上也不受支持。
在 iOS 上,photos 数组中返回的项目包含一个指向应用临时文件夹的 URL。这意味着当应用退出时,此内容将被删除,并且如果你想让用户以相同的状态找到应用,你必须处理它。displayName 属性在 iOS 上不受支持,除非定义了 ContactName,否则将返回 null。如果定义了 ContactName 对象,则返回一个复合名称或昵称。
过滤联系人数据
我们已经提到了 navigator.contacts 对象中可用的 find 方法。使用此方法,一个应用可以在设备的联系人数据库中查找一个或多个联系人。find 方法接受四个参数。第一个是一个包含必须返回的 Contact 对象字段名称的数组。第二个和第三个是成功和错误处理程序。最后一个代表你可能希望应用于当前搜索的过滤选项。
为了对当前搜索应用过滤器,你可以实例化一个新的 ContactFindOptions 对象,并填充 filter 和 multiple 属性。filter 属性是一个不区分大小写的字符串,它将在 find 方法返回的 Contact 对象的字段上作为过滤器。multiple 属性默认为 false,并且是在成功处理程序中接收多个 Contact 对象时应该使用的。尝试以下示例以了解这些属性如何工作。
行动时间 - 搜索设备联系人
现在我们将看到一个从设备获取所有联系人并将其列出的例子:
-
使用 PhoneGap CLI 创建一个新项目:
$ phonegap create ContactsApi -
将当前工作目录更改为新创建的目录:
$ cd ContactsApi -
将所需的平台添加到项目中。在这个例子中,我们将添加 Android:
$ phonegap platform add android -
将联系人插件安装到项目中:
$ phonegap plugin add cordova-plugin-contacts -
在
www/index.html文件中,你需要替换内容并添加以下代码。首先,让我们创建一个事件监听器并将其绑定到一个函数上。在下面的代码中,当设备准备好时,OnDeviceReady方法将被触发:<script type=”text/javascript” charset=”utf-8”> document.addEventListener(“deviceready”, onDeviceReady, false); </script> -
现在,让我们为
OnDeviceReady方法添加内容:function onDeviceReady() { var options = new ContactFindOptions(); options.filter = “”; options.multiple=true; var fields = [“displayName”, “name”, “addresses”]; navigator.contacts.find(fields, onSuccess, onError, options); }使用前面的代码,你正在创建一个新的
ContactFindOptions对象,并将过滤器/属性设置到该对象上。根据你设置的过滤器,该函数将返回所有匹配的联系人。在这个例子中,我们提供了一个空过滤器,这意味着我们将从设备中获取所有联系人。如果您只想限制搜索特定名称,您可以提供如所示的过滤器:
options.filter = “John”;您还可以限制函数将要返回的字段列表。如果您觉得只需要输出几个字段就足够了,您可以在字段列表中提及它们。例如,我们已将输出限制为仅显示名称、
name对象和联系人的地址。 -
当函数执行成功时,它将返回值到
OnSuccess事件,并在出现问题时触发OnError事件。因此,让我们定义OnSuccess和OnError事件来定义我们将如何处理输出:function onSuccess(contacts) { for (var i = 0; i < contacts.length; i++) { console.log(“Display Name = “ + contacts[i].displayName); } }; function onError(contactError) { alert(‘onError!'); }; -
当我们获取所有联系信息后,我们遍历
contacts对象,并在控制台中显示联系人的姓名。
以下完整代码已提供供您参考。当您在实际设备上测试代码时,您可以在控制台中看到联系人名称:
<script type=”text/javascript” charset=”utf-8”>
document.addEventListener(“deviceready”, onDeviceReady, false);
function onDeviceReady() {
var options = new ContactFindOptions();
options.filter = “”;
options.multiple=true;
var fields = [“displayName”, “name”, “addresses”];
navigator.contacts.find(fields, onSuccess, onError, options);
}
function onSuccess(contacts) {
for (var i = 0; i < contacts.length; i++) {
console.log(“Display Name = “ + contacts[i].displayName);
}
};
function onError(contactError) {
alert(‘onError!');
};
</script>
发生了什么?
您根据ContactFindOptions对象定义的选项过滤了联系数据库,并使用 PhoneGap 框架提供的 API 对结果进行了细化。
动手时间 – 添加新联系人
我们将直接通过 Contacts API 创建一个新的联系条目示例。此代码如下:
<script type=”text/javascript” charset=”utf-8”>
// Binding to the events
document.addEventListener(“deviceready”, onDeviceReady, false);
// device APIs are ready and available to use
function onDeviceReady() {
var myContact = navigator.contacts.create({“displayName”: “Purus Test”});
var phoneNumbers = [];
phoneNumbers[0] = new ContactField(‘work', ‘1234567890', false);
phoneNumbers[1] = new ContactField(‘mobile', ‘2345678901', true); // preferred
phoneNumbers[2] = new ContactField(‘home', ‘3456789012', false);
// You can add any other values of Contact object with name and phone numbers
myContact.phoneNumbers = phoneNumbers;
myContact.save(onSaveContactSuccess,onSaveContactError);
}
function onSaveContactSuccess(contact) {
alert(‘saved successfully');
};
function onSaveContactError(error) {
alert(error.code);
};
</script>
ContactField的最后一个参数期望一个布尔值,表示该字段是否为首选字段。在上一个示例中,值为true的第二个电话号码被设置为首选号码。
发生了什么?
我们已经看到如何创建一个新的Contact对象并将其使用 Contacts API 保存到设备上。
Camera API 还是 Capture API?
PhoneGap 框架实现了两个不同的 API 来访问设备上的媒体:Camera API 和 Capture API。这两个 API 之间的主要区别是,Camera API 只能访问默认的设备相机应用程序,而 Capture API 可以使用默认的音频和视频录制应用程序录制音频或视频。另一个重要区别是,Capture API 允许通过单个 API 调用进行多次捕获。
注意
Capture API 是已废弃的 W3C 标准草案的一个实现。如您所见,该草案与实际的 PhoneGap 实现有几种相似之处。
使用 Camera API 访问相机
Camera API 通过cordova-plugin-camera键标识的 Camera 插件提供对设备相机应用程序的访问。安装此插件后,应用程序可以拍照或访问用户在设备上创建的照片库和相册中存储的媒体文件。Camera API 公开了在navigator.camera对象中定义的以下两个方法:
-
getPicture:此方法打开默认的相机应用程序或允许用户根据方法接受的configuration对象中指定的选项浏览媒体库 -
cleanup: 这将清理临时存储位置中可用的任何中间照片文件(仅在 iOS 上受支持)
作为参数,getPicture方法接受一个成功处理程序、一个失败处理程序,以及可选地使用其属性指定多个相机选项的对象,如下所示:
-
quality: 这是一个介于0和100之间的数字,用于指定保存图像的质量。 -
destinationType: 这是一个用于定义成功处理程序中返回的值格式的数字。可能的值存储在以下Camera.DestinationType伪常量中:-
DATA_URL(0): 这表示getPicture方法将以 Base64 编码的字符串形式返回图像 -
FILE_URI(1): 这表示该方法将返回文件 URI -
NATIVE_URI(2): 这表示该方法将返回一个平台相关的文件 URI(例如,iOS 上的assets-library://或 Android 上的content://)
-
-
sourceType: 这是一个用于指定getPicture方法可以访问图像的位置的数字。以下可能的值存储在Camera.PictureSourceType伪常量中:PHOTOLIBRARY (0)、CAMERA (1)和SAVEDPHOTOALBUM (2):-
PHOTOLIBRARY: 这表示该方法将从设备的库中获取图像 -
CAMERA: 这表示该方法将从相机获取图片 -
SAVEDPHOTOALBUM: 这表示在用户选择图像之前,将提示用户选择相册
-
-
allowEdit: 这是一个布尔值(默认值为true),用于指示用户在确认选择之前可以对图像进行小幅度编辑;此功能仅在 iOS 上有效。 -
encodingType: 这是一个用于指定返回文件编码的数字。可能的值存储在Camera.EncodingType伪常量中:JPEG (0)和PNG (1)。 -
targetWidth和targetHeight:这些是像素宽度和高度,您希望捕获的图像缩放到这些值;可以指定这两个选项中的任意一个。当两者都指定时,图像将缩放到产生最小宽高比(图像的宽高比描述了其宽度和高度之间的比例关系)的值。 -
mediaType: 这是一个用于指定当使用Camera.PictureSourceType.PHOTOLIBRARY或Camera.PictureSourceType.SAVEDPHOTOALBUM伪常量作为sourceType时,getPicture方法必须返回哪种类型的媒体文件。可能的值存储在Camera.MediaType对象中作为伪常量,并且是PICTURE (0)、VIDEO (1)和ALLMEDIA (2)。 -
correctOrientation: 这是一个布尔值,它强制设备相机在捕获过程中纠正设备方向。 -
cameraDirection: 这是一个用于指定在捕获过程中必须使用哪个设备相机的数字。这些值存储在Camera.Direction对象中作为伪常量,并且是BACK (0)和FRONT (1)。 -
popoverOptions:这是一个在 iOS 上支持的对象,用于指定在从图库或相册选择图片时使用的弹出视图的锚点元素位置和箭头方向。 -
saveToPhotoAlbum:这是一个布尔值(默认值为false),用于将捕获的图片保存到设备默认的照片相册中。
成功处理程序接收一个参数,该参数包含文件或数据的 URI,该数据存储在文件的 Base64 编码字符串中,具体取决于 options 对象中 encodingType 属性的值。失败处理程序接收一个字符串作为参数,该字符串包含设备的原生代码错误信息。
同样,cleanup 方法接受一个成功处理程序和一个失败处理程序。这两个方法之间的唯一区别是成功处理程序不接收任何参数。cleanup 方法仅在 iOS 上受支持,可以在 sourceType 属性值为 Camera.PictureSourceType.CAMERA 且 destinationType 属性值为 Camera.DestinationType.FILE_URI 时使用。
为了将关于 Camera API 的所学知识付诸实践,让我们创建一个可以从设备默认相机应用程序中拍照的应用程序。
行动时间 - 访问设备相机
准备访问设备的相机并向用户展示捕获的图片。请参考以下步骤:
-
打开命令行工具并创建一个名为
camera的新 PhoneGap 项目:$ cordova create camera -
切换到创建的目录:
$ cd camera -
使用命令行工具,将 Android 和 iOS 平台添加到项目中:
$ cordova platforms add android $ cordova platforms add ios -
使用以下命令行添加 Camera API 插件:
$ cordova plugin add cordova-plugin-camera -
前往
www文件夹,打开index.html文件,并用以下代码替换其内容;此代码是自解释的:<!DOCTYPE html> <html> <head> <script type=”text/javascript” charset=”utf-8” src=”cordova.js”></script> <script type=”text/javascript” charset=”utf-8”> // Wait for device API libraries to load document.addEventListener(“deviceready”, onDeviceReady,false); // device APIs are available function onDeviceReady() { } function getCameraImage() { // Take picture using camera and get image source as base64-encoded string navigator.camera.getPicture(onCaptureSuccess, onError, { quality: 20, allowEdit: false, destinationType: navigator.camera.DestinationType.DATA_URL }); } // Called when a photo is successfully retrieved function onCaptureSuccess(imageData) { var smallImage = document.getElementById(‘cameraImage'); smallImage.src = “data:image/jpeg;base64,” + imageData; } // Capture any failures function onError(message) { alert(‘Error: ‘ + message); } </script> </head> <body> <button onclick=”getCameraImage();”>Capture Photo</button> <br> <img style=”display:none;width:60px;height:60px;” id=“cameraImage” src=”” /> </body> </html>当用户点击 捕获照片 按钮时,
getCameraImage()函数将被触发,进而使用navigator.camera.getPicture()方法从设备相机捕获图像,并在捕获成功的情况下将图像数据传递给onCaptureSuccess()方法。稍后,将编码字符串格式的数据源分配给<img>标签的源。 -
如果你想启用照片编辑功能,将
allowEdit属性设置为true,如下所示:navigator.camera.getPicture(onCaptureSuccess, onError, { quality: 20, allowEdit: true, destinationType: navigator.camera.DestinationType.DATA_URL }); -
一旦你进行了更改,你需要构建项目以在你的实际设备上尝试应用程序。为此,打开命令行工具并从项目的根目录启动
prepare命令,然后启动compile命令:$ cordova prepare $ cordova compile小贴士
prepare和compile命令可以从项目的任何文件夹中执行。build命令是prepare和compile命令的简写。 -
在每个目标平台上运行项目(遗憾的是,模拟器不支持相机)。为了在实际设备平台上测试应用程序,你可以使用如下所示的
run命令:$ cordova run android注意
在运行此代码之前,您需要使您的设备准备好测试,并且每个平台的步骤都不同。例如,您需要在您的 Android 设备上启用 USB 调试选项。有关每个平台的设置信息,您可以访问
cordova.apache.org/docs/en/edge/index.html。
如前几节所述,cleanup 方法仅在 iOS 上有效。这意味着您的应用程序的外观和工作方式应根据目标平台的不同而有所不同。
使用 Cordova 命令行工具创建新项目时,项目根目录下会创建一个名为 merges 的文件夹。此文件夹包含为项目添加的每个平台的一个单独文件夹;PhoneGap 项目的根文件夹如下所示:
├── config.xml
├── hooks
├── merges
├── platforms
├── plugins
├── www
└── css
└── img
└── js
└── index.html
当您必须处理特定目标平台的不同用户界面元素或业务逻辑时,您可以将要合并的文件放置在 merges/TARGET_PLATFORM 文件夹中。您可以通过实现 cleanup 方法创建 index.html 和 index.js 文件,专门针对 iOS 平台。
发生了什么?
您已访问设备相机并学习了如何使用 Cordova 的合并功能来处理 Android 和 iOS 上不同的 Camera API 实现。一旦构建了应用程序,如果您转到特定平台的文件夹(platforms/android/assets/www 和 platforms/ios/www)并打开 index.js 文件,您会发现它们彼此不同。
控制相机弹出窗口
iOS 中的 getPicture 方法(特别是在 iPad 上)当 sourceType 属性值为 Camera.PictureSourceType 对象中定义的以下伪常量之一时返回 CameraPopoverHandle 对象:SAVEDPHOTOALBUM 或 PHOTOLIBRARY。使用此对象,可以控制在调用 getPicture 方法时创建的弹出对话框的位置。
CameraPopoverHandle 对象仅公开 setPosition 方法,该方法需要一个 CameraPopoverOptions 对象作为参数。此对象允许您指定此对话框的坐标、尺寸和箭头位置:
var popoverOptions = new CameraPopoverOptions();
popoverOptions.x = 220;
popoverOptions.y = 600;
popoverOptions.width = 320;
popoverOptions.height = 480;
popoverOptions.arrowDir = Camera.PopoverArrowDirection.ARROW_DOWN;
您可以使用更紧凑的语法达到相同的结果,在 CameraPopoverOptions 对象的构造函数中指定属性:
var popoverOptions = new CameraPopoverOptions(220, 600, 320, 480, Camera.PopoverArrowDirection.ARROW_DOWN);
还可以使用 cameraOptions 对象的 popoverOptions 属性来指定坐标、位置和箭头方向:
cameraOptions.popoverOptions = {
x : 220,
y : 600,
width : 320,
height : 480,
arrowDir : Camera.PopoverArrowDirection.ARROW_DOWN
};
PopoverArrowDirection 对象中定义的伪常量与在 UIPopoverArrowDirection 类中定义的本地 iOS 常量相匹配:
Camera.PopoverArrowDirection = {
ARROW_UP: 1,
ARROW_DOWN: 2,
ARROW_LEFT: 4,
ARROW_RIGHT: 8,
ARROW_ANY: 15
};
当您想要在设备方向改变时控制对话框的位置时,此选项非常有用。在下一个示例中,您将控制对话框的位置和大小以覆盖默认行为。
注意
如果您想使用不同的 iOS 设备(例如,iPhone 5 和 4S 以测试不同的屏幕尺寸)进行开发,您必须创建一个配置文件并将其添加到设备中。您可以为所有项目创建一个配置文件,或者为每个项目使用一个文件。为了为您的设备创建新文件,您必须执行以下操作:
-
通过点击在连接设备时在 iTunes 中看到的序列号来读取设备 ID。
-
前往 Apple 开发者门户,登录,并将新的开发设备添加到您的账户。
-
创建并下载一个新的配置文件(您应该已经有证书可用,否则请阅读 Apple 开发者门户中可用的信息
developer.apple.com/support/technical/certificates/)。 -
使用 Xcode 组织者窗口(在 Xcode 中,导航到Window | Organizer)将配置文件添加到您的设备。
现在也可以直接将设备连接到 Xcode,让 Xcode 为您注册和管理配置文件。
行动时间 - 控制相册的位置
使用以下步骤更改 iPad 上默认相册对话框的位置:
-
前往您之前创建的 PhoneGap 项目的
merges/ios/js文件夹,打开index.js文件,并添加一个名为initAdditionalOptions的新函数:initAdditionalOptions: function(){ // Additional options will be defined here } -
在
initAdditionalOptions函数的主体中,指定设备相册作为getCamera方法的数据源,以及弹出对话框的大小和位置:app.cameraOptions.sourceType = Camera.PictureSourceType.SAVEDPHOTOALBUM; app.cameraOptions.popoverOptions = new CameraPopoverOptions(220, 600, 320, 480, Camera.PopoverArrowDirection.ARROW_DOWN); -
在
deviceready事件处理器的末尾添加对initAdditionalOptions函数的调用:deviceready: function() { // All the events handling initializations are here app.initAdditionalOptions(); } -
打开命令行工具,运行
prepare和compile命令(或build命令),以便在真实设备上测试项目。
刚才发生了什么?
您仅使用 JavaScript 就在 iOS 的 iPad 上处理了默认对话框的大小和位置。
媒体捕获 API
现代设备为用户提供了丰富的媒体功能;目前,人们可以录制视频、录制音频、拍照,并在他们的通信流程中使用所有这些媒体。
媒体捕获 API 与大多数 PhoneGap API 一样异步工作,并提供对设备音频、图像和视频捕获功能的访问。为了开始使用此 API,您必须像下面这样将插件安装到项目中:
$ phonegap plugin add cordova-plugin-media-capture
完成后,您可以通过navigator.device对象访问存储的capture对象:
var capture = navigator.device.capture;
一旦您获得对capture对象的访问权限,您可以通过以下属性检测设备支持的哪些视频、音频和图像格式:
-
supportedAudioModes -
supportedImageModes -
supportedVideoModes
每个属性都返回一个ConfigurationData对象的数组;数组中的每个项目代表一个支持的媒体类型。在ConfigurationData对象中定义了三个属性,您可以使用这些属性清楚地识别设备支持的媒体类型:
-
type:这是一个小写字符串,表示根据在www.ietf.org/rfc/rfc2046.txt中解释的 RFC2046 标准所支持的媒体类型(即,video/3gpp、video/quicktime、image/jpeg、audio/amr等) -
height:这是一个表示支持的图像或视频高度的数字(当对象表示支持的音频格式时,该属性返回0) -
width:这是一个表示支持的图像或视频宽度的数字(当对象表示支持的音频格式时,该属性返回0)注释
在撰写本文时,
ConfigurationData对象在任何支持的平台上都没有实现。实际上,存储在supportedAudioModes、supportedImageModes和supportedVideoModes属性中的每个数组都是空的。
capture对象公开了三种方法,以便访问设备的视频、音频和图像捕获功能:
-
captureVideo -
captureAudio -
captureImage
这些方法具有相同的语法:每个方法都接受一个成功处理程序、一个失败处理程序和一个option对象作为参数。成功处理程序在媒体捕获操作成功时被调用,并接收一个MediaFile对象的数组作为参数,该数组描述了每个捕获的文件。如果媒体捕获操作期间发生错误或用户取消操作,则调用错误处理程序,并接收一个CaptureError对象作为参数。以下示例使用设备相机捕获图像:
var capture = navigator.device.capture;
capture.captureImage(function(files){
console.log(files);
}, function(error){
console.log(error);
});
成功处理程序返回的文件数组中存储的MediaFile对象描述了捕获的媒体。MediaFile对象的属性如下:
-
fullPath:这是一个表示设备上文件路径(包括文件名)的字符串。 -
lastModifiedDate:这是自 1970 年 1 月 1 日以来的文件修改日期(有关 JavaScript 中Date对象的更多信息,请参阅developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Date) -
name:这是一个表示文件名的字符串。该名称由lastModificationDate值和文件扩展名组成。 -
size:这是一个表示文件大小的数字(以字节为单位)。 -
type:这是一个表示捕获文件的 MIME 类型的字符串(例如,image/jpeg)。
返回给错误处理程序的CaptureError对象只有一个属性,即code。该属性包含一个整数,等于CaptureError对象中定义的以下伪常量之一:
-
CaptureError.CAPTURE_INTERNAL_ERR(返回值0): 这表示设备未能捕获视频、图像或声音 -
CaptureError.CAPTURE_APPLICATION_BUSY(返回值1): 这表示捕获应用程序目前正在处理另一个捕获请求 -
CaptureError.CAPTURE_INVALID_ARGUMENT(返回值2): 这表示在调用 API 时应用使用了无效的参数(例如,limit参数的值小于 1) -
CaptureError.CAPTURE_NO_MEDIA_FILES(返回值3): 这表示用户在捕获任何内容之前退出了相机应用程序或音频捕获应用程序 -
CaptureError.CAPTURE_NOT_SUPPORTED(返回值20): 这表示请求的捕获操作不受支持
option对象因方法而异。实际上,捕获 API 为每种捕获定义了一个不同的对象:CaptureVideoOptions、CaptureAudioOptions和CaptureImageOptions。所有这些对象都具有相同的属性、限制和模式;duration属性仅在CaptureVideoOptions和CaptureAudioOptions对象中定义。limit属性的默认值是1,用于指定用户在返回应用之前可以进行的捕获次数。duration属性是捕获的最大长度(以秒为单位)。mode属性表示选定的视频或音频模式。
注意
对配置选项的支持非常零散。例如,CaptureImageOptions和CaptureVideoOptions对象的limit属性在 iOS 中不受支持。请参阅docs.phonegap.com/en/edge/cordova_media_capture_capture.md.html#Capture文档以检查实现的实际状态。
操作图像最高效的方式是通过原生代码;然而,您也可以通过 JavaScript 执行简单的图像操作。为了避免使以下示例过于复杂,我们使用了 HTML Canvas 来渲染带有一些效果的图像。
HTML 的<canvas>标签是一个容器,用于使用 JavaScript 等脚本语言动态绘制图形。它有几个内置函数,我们将在下面使用它们。
注意
有关 HTML5 Canvas 的更多信息,请参阅www.html5canvastutorials.com/。
行动时间 - 使用 canvas 操作图像
准备使用媒体捕获 API 获取的图像应用棕褐色效果。执行以下步骤:
-
打开命令行工具并创建一个名为
ImageEffect的新 PhoneGap 项目:$ cordova create ImageEffect -
将路径更改为新创建的项目:
$ cd ImageEffect -
使用以下命令添加媒体捕获 API 插件:
$ cordova plugin add cordova-plugin-media-capture -
在现有标记中添加一个
canvas标签,其id值为#manipulatedImage,以便使用它来渲染操作后的图像:<canvas id='manipulatedImage' /> -
一旦
deviceready事件被触发,访问设备相机,这允许用户仅访问一个图像:var capture = navigator.device.capture; capture.captureImage(onGetImage, onImageError, {limit: 1}); -
定义成功处理器并访问作为参数返回的数组中存储的文件信息:
function onGetImage(files){ var currentFile = files[0]; // Canvas access logic will go here } -
获取对画布的访问权限,并存储画布
2d上下文的引用,以便能够在其上绘制内容:var canvas = document.querySelector(‘#manipulatedImage'); var context = canvas.getContext(‘2d'); // Image object definition and load will go here -
创建一个
image对象,将处理器分配给onload属性,并使用MediaFile对象的fullPath属性定义对象的src属性:var image = new Image(); image.onload = function(evt) { // The image manipulation logic will go here }; image.src = currentFile.fullPath; -
在存储在
onload属性中的函数中,在画布上绘制图像,获取像素,操作它们,并将像素重新分配到画布:var width = canvas.width; var height = canvas.height; context.drawImage(this, 0, 0, width, height); var imgPixels = context.getImageData(0, 0, width, height); context.putImageData(grayscale(imgPixels), 0, 0, 0, 0, width, height);
本例的完整代码在此提供,供你理解:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>Media Capture Example</title>
</head>
<body>
<canvas id='manipulatedImage' />
<script type=”text/javascript” src=”cordova.js”></script>
<script type=”text/javascript”>
document.addEventListener(“deviceready”, onDeviceReady, false);
function onDeviceReady() {
var capture = navigator.device.capture;
capture.captureImage(onGetImage, onError, {limit: 1});
}
function onGetImage(files){
var currentFile = files[0];
var canvas = document.querySelector(‘#manipulatedImage');
var context = canvas.getContext(‘2d');
var image = new Image();
image.onload = function(evt) {
var width = canvas.width;
var height = canvas.height;
context.drawImage(this, 0, 0, width, height);
var imgPixels = context.getImageData(0, 0, width, height);
context.putImageData(grayscale(imgPixels), 0, 0, 0, 0, width, height);
};
image.src = currentFile.fullPath;
}
function onError(error){
alert(JSON.stringify(error));
}
</script>
</body>
</html>
你可以在www.html5rocks.com/en/tutorials/canvas/imagefilters/找到grayscale函数和几个图像效果。
注意
当你引用任何插件或 PhoneGap 特定的对象,如CameraPopoverOptions时,你必须等待deviceready事件被触发,以便所需的对象可用于使用:
if (typeof window.plugins.CameraPopoverOptions !== ‘undefined'){
// plugin object is available to be used
}
一些插件定义全局对象,并且可以使用它们来验证插件是否已加载并可使用:
function onDeviceReady() {
console.log(navigator.device.capture);
}
摘要
在本章中,你学习了关于联系人 API 的内容,如何从设备中查找联系人,如何过滤联系人,以及如何将新联系人保存到设备中。你还学习了相机和媒体捕获 API 之间的区别以及如何在你的应用程序中使用它们。
在下一章中,你将学习如何访问设备传感器 API 以确定设备方向、位置,以及如何实现位置 API。
第七章。访问设备传感器和位置 API
使用设备传感器为复杂应用打开了大门,这些应用可能会改善用户体验并增强现代应用的功能。对于移动开发者来说,了解设备传感器的功能和限制,以便有效地使用 PhoneGap 框架提供的 API,是非常重要的。位置数据允许移动开发者将每条信息与设备的地理位置标记。在本章中,您还将学习如何将位置 API 与您的应用结合使用。
在本章中,您将:
-
学习最常见的设备传感器及其如何用于增强用户体验
-
使用加速度计了解设备方向和设备运动事件概述
-
学习如何直接使用 JavaScript 与设备传感器协同工作
-
学习如何使用 PhoneGap 的指南针 API
-
了解地理位置及其数据在设备中的可用性
-
学习如何使用 PhoneGap 地理位置 API 以及如何在应用中集成 Google Maps API
位置数据允许移动开发者将每条信息与设备的地理位置标记。这种基于内容的标记使得使用非常具体的应用成为可能。PhoneGap 框架提供了一个简单易用、易于理解且功能强大的地理位置 API。
介绍设备传感器
人类有感官(触觉、听觉、嗅觉等);手机有数字“感官”:触觉、地理位置、方向和运动。传感器是测量物理量并将其转换为软件可理解的信号的设备组件。现代智能手机配备了各种传感器,可以在用户完成日常任务时提供支持。通过访问设备传感器,您可以增强最终用户的使用体验并开发复杂的应用。
传感器可以是基于硬件的或基于软件的。基于硬件的传感器是嵌入到手机或平板电脑中的物理组件。它们通过直接测量特定的环境属性(如加速度、地磁场强度或角度变化)来获取数据。基于软件的传感器不是物理设备,尽管它们模仿基于硬件的传感器。
典型的设备传感器包括加速度计、陀螺仪、指南针、气压计、方向传感器等。
并非所有设备及其操作系统都支持相同的传感器,因此在考虑在您的应用中使用哪些传感器之前,您必须知道您想要针对哪些设备。设备传感器通常分为以下几类:
-
运动传感器
-
环境传感器
-
位置传感器
运动传感器测量沿三个轴的加速度力和旋转力。属于这一类别的硬件部件包括加速度计、重力传感器、陀螺仪和旋转矢量传感器。环境传感器测量各种环境参数,如环境空气温度和压力、照明和湿度。气压计、光度计和温度计属于这类传感器。位置传感器测量设备的物理位置。
如前所述,每个操作系统都提供不同的传感器。从开发者的角度来看,这意味着要在不同的平台上工作,你必须了解每个平台上传感器的工作方式。当使用 PhoneGap 时,你可以安全地在不同平台上使用加速度计和指南针API。此外,你可以依赖内置浏览器的功能来获取额外的传感器信息,例如设备方向。
加速计实际上由三个加速度计组成,每个加速度计都测量沿轴 x、y 和 z 的线性路径上的速度变化(即线性加速度)随时间的变化。结合三个加速度计的数据,你可以得到设备的运动和方向。
陀螺仪始终是运动传感器的一部分,它测量围绕三个轴的旋转速率,通常是翻滚、俯仰和偏航。
磁力计测量设备周围磁场以及在没有任何强局部场的情况下,这些测量将参照地球的磁场。这样,设备能够确定其相对于地磁北极的航向;使用航向值,也可以确定设备的偏航。即使用户在设置应用中关闭了位置更新,磁航向更新也是可用的;报告的值是从0到360的正数。当用户以横向模式握持设备时,用户的实际航向是报告的航向加上 90 度。
iOS 平台提供了开发者可以期待的所有常见传感器,例如加速度计、磁力计、陀螺仪和接近传感器。
Android 平台提供了四个额外的传感器,允许你监控各种环境属性:环境湿度、亮度、环境压力和环境温度。所有传感器都是基于硬件的,并且只有当制造商将它们集成到设备中时才可用。
注意
你可以在 Google Play 商店找到 Android 传感器的完整演示;只需搜索并安装Android Sensor Box应用程序。
Windows Phone 7.5/8 平台对传感器提供了广泛的支持。你可以使用倾斜仪传感器来检测设备的俯仰、滚转和偏航,或者你可以使用四元数传感器(四元数是三维空间中两条有向线的商)来创建复杂的 3D 应用程序。有关 Windows Phone 传感器 API 的完整概述,请参阅msdn.microsoft.com/library/windows/apps/windows.devices.sensors在线文档。
设备的位置能力依赖于几个被称为位置传感器的传感器。设备通常使用多种定位方法来提供不同粒度的位置数据。位置数据的来源在准确性、启动时间和功耗特征方面有所不同,包括以下内容:
-
GPS
-
A-GPS
-
细胞塔三角测量
-
Wi-Fi 三角测量
-
IP 地址
随着传感器的持续发展,最终用户的需求在增长,市场上可用的应用程序质量也在提高。
注意
Lapka Electronics发布了一套传感器和应用程序,能够将环境数据转换为易于读取的数值。使用他们的传感器和应用程序,你可以测量电磁污染、湿度、原始食品和饮用水中硝酸盐的含量等。有关这些传感器的更多信息可在mylapka.com/在线获取。
传感器和人与计算机的交互
传感器的发展非常迅速,并且仍在快速发展,影响着创意人士如何设计应用程序。新一代应用程序依赖于语音命令、手势等,以便用户能够以更直观的方式控制应用程序。现在,应用程序能够根据收集到的传感器数据感知用户意图。使用传感器使应用程序(和计算机)更易于控制的做法被称为感知计算。这一倡议由英特尔领导,包括视频会议、游戏等多种应用。
相比之下,增强现实是关于通过计算机扩展人类与物理世界的交互方式。使用增强现实界面,你可以向外部环境添加更多信息,并创建令人惊叹且有用的应用程序。在移动设备上,增强现实应用程序的实现高度依赖于设备上的传感器,例如视频摄像头和方向传感器。
通过传感器可以实现的交互类型的一个很好的例子是 iOS 上名为Car Finder的应用程序。当用户拍照时,应用程序会存储汽车的位置,然后向用户提供找到他/她停车位置所需的信息。
对于移动开发者来说,使用传感器及其返回的数据的能力越来越重要。PhoneGap 支持的传感器有限,但 PhoneGap 只是一个包装器,使得将你的表示层与原生设备代码分离变得更容易。因此,你可以在 PhoneGap 包装器周围开始编写额外的原生代码来扩展其功能。
注意
在 Microsoft 网站上有一个关于传感器开发的有趣资源,网址为research.microsoft.com/en-us/groups/sendev/,在那里你可以找到帮助你开始使用传感器的论文和资源。
加速度计
PhoneGap 加速度计 API 允许你检测设备相对于设备方向的运动变化值。请注意,加速度计将值检测为相对于当前设备位置的增量运动。更重要的是,它考虑了重力效应(即9.81 m/s^(2)),因此当设备平放在桌子上朝上时,返回的值应该是x = 0,y = 0,和z = 9.81。
就像任何其他插件一样,在使用项目之前,你必须先安装该插件。可以使用以下命令将插件添加到项目中:
$ cordova plugin add cordova-plugin-device-motion
一旦插件安装到项目中,就会创建一个navigator.Accelerometer全局对象,并在deviceready事件触发后可用。然而,建议在使用之前仍然检查其存在:
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
if( typeof navigator.accelerometer === "undefined"){
//plugin is ready now
}
}
你可以使用getCurrentAcceleration方法或通过watchAcceleration方法设置监视器来检测设备加速度数据。这两种方法都在navigator.accelerometer对象上可用,并接受类似的参数。
getCurrentAcceleration方法接受一个成功和失败回调函数作为参数,并且不返回任何内容。watchAcceleration方法接受一个额外的参数,用于定义选项并返回当前监视器的引用。
为了持续监控加速度数据,你必须定义你想要恢复数据的频率,并将watchAcceleration方法返回的值存储在一个变量中:
var options = {frequency: 300};
var currentAcceleration = navigator.accelerator.watchAcceleration
(onSuccess, onFailure, options);
onSuccess处理程序接收一个Acceleration对象作为参数,访问其属性,使得能够读取每个轴上的加速度:
function onSuccess(acceleration) {
console.log('Acceleration X: ' + acceleration.x );
console.log('Acceleration Y: ' + acceleration.y);
console.log('Acceleration Z: ' + acceleration.z );
};
失败处理程序不接收任何参数,但在处理访问设备加速度计时可能出现的错误时非常有用:
function onError() {
console.log('Error accessing the accelerometer');
};
为了停止监控加速度计数据,只需调用定义在accelerator对象上的clearWatch方法,并传递之前用于存储watchAcceleration方法结果的变量引用即可:
navigator.accelerometer.clearWatch(currentAcceleration);
此方法不接受任何额外的处理程序。
小贴士
PhoneGap 的所有传感器 API 都以类似的方式工作;您将始终需要使用 getCurrentSENSOR 和 watchSENSOR 方法(其中 SENSOR 是传感器的名称)从传感器获取数据。为了停止监视传感器,您将始终使用 clearWatch 方法。
检测晃动
使用从加速度计 API 恢复的信息,可以了解用户是否在晃动设备。
设备方向事件
cordova-plugin-device-motion 插件仅支持访问加速度信息。为了处理方向变化,您必须依赖于目标平台浏览器的 JavaScript API。当您想要在设备方向改变时更新用户界面时,您必须使用 CSS 媒体查询;任何其他业务逻辑都可以使用 JavaScript 处理,因为 PhoneGap 使用网页视图来渲染应用的用户界面。
使用 JavaScript,您可以设置一个监听器来处理 orientationchange 事件,并设置另一个监听器来处理 deviceorientation 事件,以处理设备方向。第一个事件在设备方向每次改变时触发;第二个事件在设备物理方向改变时触发。两个监听器都必须注册到 window 对象:
window.addEventListener('orientationchange', EVENT_HANDLER);
window.addEventListener('deviceorientation', EVENT_HANDLER);
orientationchange 事件处理器通常用于检测屏幕方向改变后的屏幕方向。一旦方向改变,应用会接收到几个事件的通知。以下表格总结了这些事件和方向属性值:
| 设备和用户手势 | 触发的事件 | 方向 |
|---|---|---|
| iPad 横屏 | resize orientationchange |
090 |
| iPad 竖屏 | resize orientationchange |
900 |
| iPhone 横屏 | resize orientationchange |
090 |
| iPhone 竖屏 | resize orientationchange |
900 |
| 安卓手机横屏 | orientationchange resize |
9090 |
| 安卓手机竖屏 | orientationchange resize |
00 |
deviceorientation 事件非常强大。它将包含以下信息的 DeviceOrientationEvent 事件实例返回给处理器:
-
alpha:这返回设备绕 z 轴的旋转 -
beta:这返回设备绕 x 轴的旋转 -
gamma:这返回设备绕 y 轴的旋转
提示
为了提高您应用的性能,考虑使用 event-handler 函数仅保存当前从传感器数据到变量的值。然后,将您的计算或 DOM 操作移动到在固定时间执行的新函数中。
使用 JavaScript 处理方向
是时候将您所学的设备方向事件知识付诸实践了。让我们来处理一个非常基础的示例,该示例能够根据设备的物理方向在 div 元素中显示屏幕方向。
行动时间 - 使用 JavaScript 处理设备方向
执行以下步骤:
-
打开命令行工具,创建一个名为
orientationevents的新 PhoneGap 项目,并添加你想要针对此示例的目标平台。 -
将插件安装到你的项目中:
$ phonegap plugin add cordova-plugin-device-motion -
前往
www文件夹,打开index.html文件,并在应用下方的#deviceready主要div内添加带有#orientationID 的div:<div class="app"> <h1>Apache Cordova</h1> <div id="deviceready"> ...... </div> <div id="orientation"> </div> </div> -
前往
css文件夹,并在index.css文件内定义两个新规则,为div及其内容添加边框和更大的字体大小。你甚至可以直接将 CSS 样式添加到 HTML 页面的head部分:#orientation{ width: 230px; border: 1px solid rgb(10, 1, 1); } #orientation p{ font-size: 36px; font-weight: bold; text-align: center; } -
前往
js文件夹,打开index.js文件,并定义一个新函数,以便轻松检测设备是否可以处理orientationchange和deviceorientation事件。或者,你甚至可以将脚本直接嵌入到你的 HTML 页面中:orientationSupported: function(){ try { return 'DeviceOrientationEvent' in window && window['DeviceOrientationEvent'] !== null; } catch (e) { return false; } } -
在
deviceready函数中,如果设备支持orientationchange和deviceorientation事件,则添加两个监听器:if(orientationSupported){ window.addEventListener('orientationchange', orientationChanged); window.addEventListener('deviceorientation', updateOrientation); }else{ alert('Orientation not supported!'); } -
定义
orientationChanged事件处理程序,并使用它来在屏幕上打印当前设备方向:orientationChanged: function(){ var element = document.querySelector('#orientation'); element.innerHTML = '<p>' + window.orientation + '</p>'; } -
定义
deviceorientation事件的处理程序,并使用设备传感器的信息来改变div方向的 3D 变换:updateOrientation: function(event){ var alpha = event.alpha, beta = event.beta, gamma = event.gamma; var element = document.querySelector('#orientation'); var rotation = 'rotateZ(' + alpha + 'deg) rotate(' + beta + 'deg) rotateY(' + gamma + 'deg)'; // For brevity the browser prefixes have been removed element.style.transform = rotation; } -
再次打开命令行工具,定位主项目文件夹,然后编译应用并在你之前添加的每个平台上进行测试。
下面是这个示例的完整代码:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>Media Capture Example</title>
<style>
#orientation{
width: 230px;
border: 1px solid rgb(10, 1, 1);
}
#orientation p{
font-size: 36px;
font-weight: bold;
text-align: center;
}
</style>
</head>
<body>
<div class="app">
<h1>Apache Cordova</h1>
<div id="deviceready"></div>
<div id="orientation"></div>
</div>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
if(orientationSupported){
window.addEventListener('orientationchange', orientationChanged);
window.addEventListener('deviceorientation', updateOrientation);
}else{
alert('Orientation not supported!');
}
}
orientationSupported: function(){
try {
return 'DeviceOrientationEvent' in window &&
window['DeviceOrientationEvent'] !== null;
} catch (e) {
return false;
}
}
orientationChanged: function(){
var element = document.querySelector('#orientation');
element.innerHTML = '<p>' + window.orientation + '</p>';
}
updateOrientation: function(event){
var alpha = event.alpha,
var beta = event.beta,
var gamma = event.gamma;
var element = document.querySelector('#orientation');
var rotation = 'rotateZ(' + alpha + 'deg) rotate(' + beta + 'deg) rotateY(' + gamma + 'deg)';
// For brevity the browser prefixes have been removed
element.style.transform = rotation;
}
</script>
</body>
</html>
发生了什么?
你使用 JavaScript 处理了方向事件,并通过 PhoneGap 将结果部署到设备上。该应用能够实时获取设备屏幕方向和当前位置。
Compass
PhoneGap Compass API 允许你获取设备指向的方向。罗盘是一个传感器,可以检测设备指向的方向或航向,并使用 0 到 359.99 的值返回设备的航向(以度为单位)。Compass API 与加速度计 API 的工作方式类似;实际上,你可以读取设备的当前航向,或者定义一个监视器以连续读取航向值。
Compass API 可在 navigator 对象的 compass 属性上找到,并公开以下函数:
-
compass.getCurrentHeading: 这通过处理程序读取当前的罗盘方向 -
compass.watchHeading: 这通过处理程序在特定时间间隔读取罗盘方向,并返回对其的引用 -
compass.clearWatch: 这将停止之前定义的时间间隔读取处理程序
getCurrentHeading 和 watchHeading 函数接受非常相似的参数;唯一的区别是 watchHeading 函数的最后一个参数,允许你配置它。为了读取设备的当前航向,只需执行 getCurrentHeading 函数,指定成功和错误处理程序即可:
navigator.compass.getCurrentHeading(onSuccess, onError);
onSuccess处理器接收一个CompassHeading对象作为参数,具有以下属性:
-
magneticHeading:这是从0到359.99度的航向 -
trueHeading:这是相对于地理北极的航向,以度为单位 -
headingAccuracy:这是报告航向与真实航向之间的偏差,以度为单位 -
timestamp:这是确定此航向的时间
错误处理器接收一个CompassError对象作为参数;CompassError对象有一个名为code的属性,它返回两个可能的值,例如CompassError.COMPASS_INTERNAL_ERR或CompassError.COMPASS_NOT_SUPPORTED:
function onError (error) {
switch(true){
case error.code == CompassError.COMPASS_INTERNAL_ERR:
navigator.notification.alert('Compass Error!', null, 'Info', 'OK');
break;
case error.code == CompassError.COMPASS_NOT_SUPPORTED:
navigator.notification.alert('Compass Unavailable!', null, 'Info', 'OK');
break;
default:
navigator.notification.alert('Generic Error!', null,'Info', 'OK');
}
}
watchHeading函数与getCurrentHeading函数类似。唯一的区别是它接受一个额外的CompassOption对象,允许您设置以毫秒(即频率)为单位检索指南针航向的频率以及触发成功处理器的度数变化(即过滤器):
var options = {frequency: 300};
var currentHeading = navigator.compass.watchHeading(
onSuccess, onError, options);
为了停止监视航向值的变化,只需使用clearWatch函数和当前航向监视器的引用即可:
clearWatch(currentHeading);
注意
CompassHeading对象的trueHeading属性在 Android 上不受支持。它返回与magneticHeading相同的值,并且headingAccuracy值始终为0,因为在magneticHeading和trueHeading之间没有差异。
在 iOS 上,只有当使用watchLocation函数运行位置服务时,才会返回trueHeading属性。
创建指南针
读取设备的当前航向是开发者在多个用例中的常见任务,例如交通应用、增强现实应用或任何包含方向感的应用。让我们看看如何使用 PhoneGap 创建一个完整的指南针:
注意
用于渲染指南针的图像可在 Creative Commons 许可证下在commons.wikimedia.org/wiki/File:Compass.svg找到。在开始此示例之前,请下载该图像并创建三个单独的 PNG 文件,分别用于背景、表盘和箭头。由于这是一个 SVG 矢量文件,您可以处理图像的每一层,并按需编辑它。要编辑图像,您可以使用任何矢量编辑应用程序,例如 Adobe Illustrator 或免费应用程序 Inkscape。
动手时间 - 使用指南针 API
执行以下步骤:
-
打开命令行工具,创建一个名为
compass的新 PhoneGap 项目,并添加您想要针对此示例的目标平台。 -
使用以下命令添加 Compass API 插件:
$ cordova plugin add cordova-plugin-device-orientation -
前往
www文件夹,打开index.html文件。三个div标签用于处理指南针箭头和背景:<section id="compass"> <div id="compassbg"></div> <div id="north"></div> <div id="arrow"></div> </section> -
前往
css文件夹,打开index.css文件,并定义指南针每个元素所需的单独背景规则:#compassbg { background-image: url(../img/Compass.png); } #north { background-image: url(../img/arrow_direction.png); } #arrow { background-image: url(../img/arrow_beta.png); } #compass, #arrow, #north, #compassbg { background-repeat: no-repeat; background-size: cover; position: fixed; width: 286px; height: 286px; } -
前往
js文件夹,打开index.js文件,添加一个新变量以存储你将定义的用于监控设备航向的监视器的引用。或者,你也可以直接在页面上嵌入脚本:var currentHeading = null; -
定位到
deviceready函数,并在其中添加每 150 毫秒检查设备航向所需的代码片段:var options = {frequency: 150}; currentHeading = navigator.compass.watchHeading (onCompassSuccess,onCompassError, options); -
创建一个名为
onCompassSuccess的新函数,并在其主体内部开始读取存储在接收到的参数中的航向数据;使用它来旋转指南针元素:function onCompassSuccess(heading){ var magneticHeading = heading.magneticHeading; var trueHeading = heading.trueHeading; var compass = document.querySelector('#compassbg'); var north = document.querySelector('#north'); var compassRotation = 'rotate(' + magneticHeading + 'deg)'; var northRotation = 'rotate(' + trueHeading + 'deg)'; var compassSytle = compass.style; var northStyle = north.style; compassStyle.transform = compassRotation; northStyle.transform = northRotation; } -
定义一个函数来捕获可能出现的失败:
function onCompassError(error){ alert("Error with Compass"); } -
再次打开命令行工具,定位到主项目文件夹,编译应用程序,并在你之前添加的每个平台上进行测试。
对于我们刚才看到的例子,你可以在这里找到完整的代码:
<!DOCTYPE html>
<html>
<head>
<!--Other section removed for sake of simplicity -->
<title>Media Capture Example</title>
<style>
#compassbg {
background-image: url(../img/Compass.png);
}
#north {
background-image: url(../img/arrow_direction.png);
}
#arrow {
background-image: url(../img/arrow_beta.png);
}
#compass, #arrow, #north, #compassbg {
background-repeat: no-repeat;
background-size: cover;
position: fixed;
width: 286px;
height: 286px;
}
</style>
</head>
<body>
<section id="compass">
<div id="compassbg"></div>
<div id="north"></div>
<div id="arrow"></div>
</section>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript">
var currentHeading = null;
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
var options = {frequency: 150};
currentHeading = navigator.compass.watchHeadingonCompassSuccess, onCompassError, options);
}
function onCompassSuccess(heading){
var magneticHeading = heading.magneticHeading;
var trueHeading = heading.trueHeading;
var compass = document.querySelector('#compassbg');
var north = document.querySelector('#north');
var compassRotation = 'rotate(' + magneticHeading + 'deg)';
var northRotation = 'rotate(' + trueHeading + 'deg)';
var compassSytle = compass.style;
var northStyle = north.style;
compassStyle.transform = compassRotation;
northStyle.transform = northRotation;
}
function onCompassError(error){
alert("Error with Compass");
}
</script>
</body>
</html>
发生了什么?
你使用 PhoneGap API 实现了一个真正的、跨平台的指南针。在这个过程中,你学习了如何使用移动设备传感器的一个相当复杂的功能。
地理定位简介
术语地理定位用于指代识别物体在现实世界中的地理位置的过程。能够检测用户位置的设备越来越常见,我们现在已经习惯了根据我们的位置获取内容(地理定位)。
使用全球定位系统(GPS)——一个基于空间的卫星导航系统,它在全球范围内持续提供位置和时间信息——你现在可以获取设备的准确位置。在 20 世纪 70 年代初,美国军方创建了 Navstar,这是一个防御导航卫星系统。Navstar 是今天数十亿设备使用的 GPS 基础设施的基础。截至 2014 年 10 月,已有 68 颗 GPS 卫星成功送入地球轨道(有关过去和计划发射的详细报告,请参阅en.wikipedia.org/wiki/List_of_GPS_satellite_launches)。
设备的位置通过一个点来表示。这个点由两个组成部分组成:纬度和经度。现代设备有许多确定位置信息的方法;以下是一些方法:
-
GPS
-
IP 地址
-
GSM/CDMA 小区 ID
-
Wi-Fi 和蓝牙 MAC 地址
每种方法都提供相同的信息;变化的是设备位置的准确性。GPS 卫星持续传输可解析的信息;例如,GPS 阵列的一般健康状况,所有卫星的大致轨道位置,传输卫星的精确轨道或路径信息,以及传输时间。接收器通过计算阵列中任何可见卫星发送的信号的传输时间来计算自己的位置。
注意
测量从一点到一组卫星的距离以确定位置的过程称为 三角测量法。距离是通过使用光速作为常数以及信号离开卫星的时间来确定的。
移动开发中出现的趋势是基于 GPS 的“人脉发现”应用,如 Highlight、Sonar、Banjo 和 Foursquare。每个应用都有不同的功能,并且为不同的目的而构建,但它们都共享同一个杀手级功能:使用位置作为元数据的一部分,以便根据用户的需求过滤信息。
PhoneGap 定位 API
定位 API 不是 HTML5 规范的一部分,但它与移动开发紧密集成。PhoneGap 定位 API 和 W3C 定位 API 相互映射;两者定义了相同的方法和相对参数。有几款设备已经实现了 W3C 定位 API;对于这些设备,你可以使用原生支持而不是 PhoneGap API。
注意
根据 HTML 规范,用户必须明确允许网站或应用使用设备的当前位置。
定位 API 通过 navigator 对象的 geolocation 子对象公开,并包含以下三个方法:
-
getCurrentPosition(): 这将返回设备位置 -
watchPosition(): 这将监视设备位置的变化 -
clearWatch(): 这将停止设备位置变化的监视器
watchPosition() 和 clearWatch() 方法的工作方式与 setInterval() 和 clearInterval() 方法相同;实际上,第一个方法返回一个标识符,该标识符传递给第二个方法。getCurrentPosition() 和 watchPosition() 方法相互映射,并接受相同的参数:一个成功回调函数和一个失败回调函数,以及一个可选的 configuration 对象。configuration 对象用于指定设备位置缓存的最高年龄,以便在方法失败后设置超时,并指定应用程序是否只需要精确结果:
var options = {maximumAge: 3000, timeout: 5000,
enableHighAccuracy: true };
navigator.geolocation.watchPosition(onSuccess, onFailure, options);
小贴士
只有第一个参数是必需的,但建议始终处理失败用例。
成功处理函数接收一个 Position 对象作为参数。访问其属性,你可以读取设备的坐标和存储坐标的对象的创建时间戳:
function onSuccess(position) {
console.log('Coordinates: ' + position.coords);
console.log('Timestamp: ' + position.timestamp);
}
Position 对象的 coords 属性包含一个 Coordinates 对象;到目前为止,该对象最重要的属性是 longitude(经度)和 latitude(纬度)。使用这些属性,你可以开始将定位信息作为相关元数据集成到你的应用中。
失败处理函数接收一个 PositionError 对象作为参数。使用该对象的 code 和 message 属性,你可以优雅地处理每个可能出现的错误:
function onError(error) {
console.log('message: ' + error.message);
console.log ('code: ' + error.code);
}
message属性返回错误的详细描述,而code属性返回一个整数;可能值通过以下伪常量表示:
-
PositionError.PERMISSION_DENIED:这表示用户拒绝了应用使用设备的当前位置 -
PositionError.POSITION_UNAVAILABLE:这表示无法确定设备的位置注意
如果你想在返回
POSITION_UNAVAILABLE错误时恢复最后可用的位置,你必须编写一个使用平台特定 API 的自定义插件。Android 和 iOS 都有这个功能。你可以在stackoverflow.com/questions/10897081/retrieving-last-known-geolocation-phonegap找到详细的示例。 -
PositionError.TIMEOUT:这表示在实现成功获取新的Position对象之前,指定的超时时间已过。
注意
JavaScript 不支持 Java 和其他面向对象编程语言中的常量。术语“伪常量”指的是在 JavaScript 应用中不应更改的值。
使用设备位置信息执行的最常见任务之一是在地图上显示设备位置。你可以通过在应用中集成 Google Maps 快速完成此任务;唯一的要求是有效的 API 密钥。要获取密钥,请按照以下步骤操作:
-
访问 API 控制台
code.google.com/apis/console,并使用您的 Google 账户登录。 -
在左侧菜单中选择APIs & auth。
-
选择Google Maps JavaScript API并激活该服务。
行动时间 - 使用 Google Maps 显示设备位置
准备向 PhoneGap 默认应用模板添加地图渲染器。执行以下步骤:
-
打开命令行工具并创建一个名为
MapSample的新 PhoneGap 项目:$ cordova create MapSample -
将工作目录更改为新创建的项目:
$ cd MapSample -
将所需的平台添加到项目中。例如,我们将向此项目添加 Android:
$ cordova platform add android -
使用以下命令添加 Geolocation API 插件:
$ cordova plugin add cordova-plugin-geolocation -
前往
www文件夹,打开index.html文件,删除所有现有内容,并在body标签内添加一个div元素,其id值为#map:<div id='map'></div> -
包含将在运行时添加到应用的
cordova.js文件:<script type="text/javascript" src="img/cordova.js"></script> -
添加一个新的
script标签以包含 Google Maps JavaScript 库。将YOUR_API_KEY值替换为您的实际 Google Maps API 密钥:<script type="text/javascript" src="img/strong>&sensor=true"> </script> -
创建一个新的 CSS 样式以给
div元素及其内容一个适当的大小:#map{ width: 280px; height: 230px; display: block; margin: 5px auto; position: relative; } -
在 JavaScript 部分,定义一个名为
initMap的新函数:function initMap(lat, long){ // The code needed to show the map and the // device position will be added here } -
在函数体中,定义一个
options对象以指定地图的渲染方式:var options = { zoom: 8, center: new google.maps.LatLng(lat, long), mapTypeId: google.maps.MapTypeId.ROADMAP }; -
在
initMap函数体中添加初始化地图渲染的代码以及显示代表当前设备位置的标记的代码:var map = new google.maps.Map(document.getElementById('map'), options); var markerPoint = new google.maps.LatLng(lat, long); var marker = new google.maps.Marker({ position: markerPoint, map: map, title: 'Device\'s Location' }); -
定义一个函数作为成功处理程序使用,并在其主体中调用之前定义的
initMap函数:function onSuccess(position){ var coords = position.coords; initMap(coords.latitude, coords.longitude); } -
定义另一个函数,以便有一个能够通知用户出错的失败处理程序:
function onFailure(error){ alert(error.message); } -
进入
deviceready函数,并将调用 Geolocation API 以恢复设备位置的调用作为最后一条语句添加:navigator.geolocation.getCurrentPosition(app.onSuccess, app.onFailure, {timeout: 5000, enableAccuracy: false}); -
打开命令行工具,构建应用,然后在测试设备上运行它:
$ cordova build $ cordova run android
这里是项目的完整代码,供你参考:
<!DOCTYPE html>
<html>
<head>
<title>GeoLocation Example</title>
<style>
#map{
width: 280px;
height: 230px;
display: block;
margin: 5px auto;
position: relative;
}
</style>
</head>
<body>
<div id='map'></div>
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript" src="img/js?key=YOUR_API_KEY&sensor=true"></script>
<script type="text/javascript">
var currentHeading = null;
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
navigator.geolocation.getCurrentPosition(onSuccess, onFailure, {timeout: 5000, enableAccuracy: false});
}
function onSuccess(position){
var coords = position.coords;
initMap(coords.latitude, coords.longitude);
}
function initMap(lat, long){
var options = {
zoom: 8,
center: new google.maps.LatLng(lat, long),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map'), options);
var markerPoint = new google.maps.LatLng(lat, long);
var marker = new google.maps.Marker({
position: markerPoint,
map: map,
title: 'Device\'s Location'
});
}
function onFailure(error){
alert(error.message);
}
</script>
</body>
</html>
发生了什么?
你在应用中集成了 Google Maps。这个地图是大多数用户都熟悉的交互式地图——最常见的手势已经生效,Google 街景控制也已经启用。
提示
要在 iOS 上成功加载 Google Maps API,必须将googleapis.com和gstatic.com域名列入白名单。将项目的.plist文件作为源代码打开(右键单击文件,然后选择打开方式 | 源代码),并添加以下域名数组:
<key>ExternalHosts</key>
<array>
<string>*.googleapis.com</string>
<string>*.gstatic.com</string>
</array>
其他地理位置数据
在上一个示例中,你只使用了接收到的position对象中的latitude和longitude属性。还有其他属性可以作为Coordinates对象的属性访问:
-
海拔: 这表示设备相对于海平面的高度,单位为米 -
accuracy: 这表示纬度和经度的精度级别,单位为米;它可以用来在映射设备位置时显示精度半径 -
altitudeAccuracy: 这表示海拔的精度,单位为米 -
heading: 这表示设备相对于真北顺时针方向的方位角 -
速度: 这表示设备当前的地速,单位为每秒米
latitude和longitude属性是这些属性中支持最好的,也是在与远程 API 通信时最有用的属性。其他属性主要在你开发的应用中地理位置是其标准功能的核心组件时有用,例如使用这些数据创建与地理位置数据相关的信息流的应用。accuracy属性是这些附加功能中最重要的,因为作为应用程序开发者,你通常不知道哪个特定的传感器提供了位置信息,你可以使用accuracy属性作为查询外部服务时的范围。
有几个 API 允许你发现与地点相关的有趣数据;在这些 API 中,最有趣的是 Google Places API 和 Foursquare API。
注意
Google 地点(Google Places)和 Foursquare 在线文档组织得非常好,如果你想要深入了解这些主题,这是一个很好的起点。你可以通过developers.google.com/maps/documentation/javascript/places访问 Google 地点文档,以及developer.foursquare.com/访问 Foursquare。
摘要
在本章中,你学习了如何使用设备传感器来增强你应用程序的功能。你还学习了如何从设备获取地理位置信息,以及如何在应用程序中集成外部地理位置服务。此外,你继续了解 PhoneGap API,这些 API 允许你创建强大的原生应用程序。
在下一章中,你将开始学习使用 PhoneGap 的一些高级概念。
第八章. 高级 PhoneGap
如果您希望您的应用程序能够触及多个国家和文化的用户,请输入全球化与本地化。您的应用程序应能够支持多种语言/区域,这可以通过使用全球化 API 和相关本地化库来实现。如果您希望应用程序更先进且用户友好,请考虑支持多种手势。
在本章中,您将:
-
学习如何使用全球化 API 来支持移动应用程序用户的区域设置
-
学习如何根据用户的喜好以多种语言显示内容来本地化应用程序
-
获取提供手势支持的库的概述
-
学习如何实现多个触摸手势以增强用户体验
-
学习如何移除点击事件中引入的 300 毫秒延迟
使用全球化 API
在计算机科学中,全球化、国际化、本地化是适应不同语言、区域差异和目标市场技术要求的手段。让我们详细看看这些。
术语本地化指的是在您的应用程序能够在不同语言和当地文化规范下部署之前所需的所有活动。在开始本地化应用程序之前,您必须通过移除任何语言和文化依赖项并设计代码以便它能够适应各种语言而无需进行工程更改来国际化您的代码。然后,您可以本地化应用程序,翻译面向客户端的内容和标签,并对其进行其他适应,以便在特定区域中良好运行。
术语区域指的是用于本地化的设置或首选项的集合。区域通常描述为语言和国家对的组合,如 en-US、de-AT、it-IT 等。
术语全球化代表国际化与本地化的结合。
有些看起来奇怪的缩写中使用了数字来表示用于指代国际化、本地化和全球化的第一个和最后一个字母之间的字母数:
-
i18n: 这代表国际化 -
l10n: 这代表本地化 -
g11n: 这代表全球化
注意
软件国际化是一个很大的话题,因为它涵盖了复数、日期、特殊字符等。讨论所有这些内容超出了本书的范围。如果您想了解更多关于国际化的知识,请查看 GNU 项目 gettext (www.gnu.org/software/gettext/manual/)、globalize 项目 (github.com/jquery/globalize) 或 Jed 项目 (slexaxton.github.io/Jed/)。
PhoneGap 通过globalization对象提供的全球化 API 提供了强大的本地化支持。您可以将插件安装到项目中,如下所示:
$ cordova plugin add cordova-plugin-globalization
globalization对象是navigator对象的子对象,因此具有全局作用域。为了访问globalization对象,只需输入以下代码行即可:
var globalization = navigator.globalization;
globalization对象暴露了几个具有类似签名的异步方法。实际上,通常大多数方法接受一个参数、一个成功处理程序和一个失败处理程序,以及可选的options对象:
globalization.methodName(argument, onSuccess, onError, options);
并非所有方法都接受参数和options对象;其中一些方法仅接受成功和失败处理程序。失败处理程序接收一个GlobalizationError对象作为参数。该对象上定义了两个属性:message和code。第一个属性包含描述错误详细信息的字符串;第二个属性包含一个整数,等于在GlobalizationError对象中定义的以下伪常量之一:
-
GlobalizationError.UNKNOWN_ERROR(返回值0):这意味着发生了通用错误 -
GlobalizationError.FORMATTING_ERROR(返回值1):这意味着在格式化操作期间发生错误 -
GlobalizationError.PARSING_ERROR(返回值2):这意味着在解析操作期间发生错误 -
GlobalizationError.PATTERN_ERROR(返回值3):这意味着在恢复货币、日期或数字模式时发生错误
Globalization API 暴露了在navigator.globalization对象中定义的以下方法:
-
getPreferredLanguage:此方法返回设备当前语言的字符串标识符;字符串存储在成功处理程序接收的对象的value属性中(例如,{value: ‘English'}) -
getLocaleName:此方法根据设备的当前语言返回区域标识符;字符串存储在成功处理程序接收的对象的value属性中(例如,{value: ‘en'}) -
dateToString:此方法根据客户端的区域设置和时区将日期格式化为字符串;该方法接受一个Date对象作为第一个参数,以及一个可选的options对象作为最后一个参数:var globalization = navigator.globalization; var today = new Date(); globalization.dateToString(today, onSuccess, onError);返回的结果存储在成功处理程序接收的对象的
value属性中(例如,{value: ‘06/14/2013 12:49 PM'})。 -
stringToDate:此方法解析格式为字符串的日期,并根据设备的偏好和日历返回相应的Date对象作为成功处理程序中的参数。 -
getDatePattern:此方法返回一个对象,作为成功处理程序中的参数接收,包含以下内容:-
一个用于根据设备偏好格式化和解析日期的模式字符串
-
设备的时区
-
设备时区与通用时间之间的秒数差以及设备非夏令时时区与通用时间之间的秒数差
-
客户端的夏令时时区(例如,
{pattern: ‘dd/MM/yyyy HH:mm‘, timezone: ‘CEST‘, utc_offset: 3600, dst_offset: 3600})
此方法通过可选的
options对象接受,可以通过它指定格式长度(即short、medium、long或full)和要返回的数据(即date、time或date and time) -
-
getDateNames: 该函数返回一个包含月份或星期名称的数组,具体取决于设备的设置;数组存储在成功处理程序接收到的对象中的value属性中(即,{value: Array[12]}) -
isDayLightSavingsTime: 该函数返回一个布尔值,表示是否对作为第一个参数传入的Date对象使用设备的时区和日历实施夏令时;值存储在成功处理程序接收到的对象中的dst属性中(例如,{dst: true}) -
getFirstDayOfWeek: 该函数返回一周的第一天作为数字,取决于设备的用户偏好和日历,假设一周的天数从1开始编号(即星期日);字符串存储在成功处理程序接收到的对象中的value属性中(例如,{value: 1}) -
numberToString: 该函数将作为第一个参数传入的数字格式化为字符串,格式根据客户端的区域设置和偏好;数字存储在成功处理程序接收到的对象中的value属性中(例如,{value: ‘12,456,246'}) -
stringToNumber: 该函数将作为第一个参数传入的字符串格式化为数字,格式根据客户端的区域设置和偏好;数字存储在成功处理程序接收到的对象中的value属性中(例如,{value: 1250.04}) -
getNumberPattern: 该函数返回一个对象,作为成功处理程序中的参数,包含以下内容:-
用于格式化和解析数字的格式字符串,根据设备的偏好
-
解析和格式化数字时使用的分数位数,解析和格式化时使用的舍入增量等(例如,
{decimal: ‘.', fraction: 0, grouping: ‘,’ negative: ‘-‘, pattern: ‘#,##0.###‘, positive: ‘‘, rounding: 0, symbol: ‘.’})
-
-
getCurrencyPattern:此函数返回一个对象,该对象作为成功处理程序中的参数接收,包含一个用于根据传递给第一个参数的货币代码格式化和解析货币的模式字符串,设备的偏好设置,解析和格式化数字时使用的分数位数,解析和格式化时使用的舍入增量,用于模式的 ISO 4217 货币代码等(例如,{code: ‘EUR', decimal: ‘.', fraction: 2, grouping: ‘,’ pattern: ‘$#,##0.00;(¤#,##0.00)’, rounding: 0})。小贴士
numberToString和stringToNumber方法都接受一个可选的options对象;通过此对象的type属性,您可以指定数字的格式(例如,decimal、percent或currency)。
通过globalization对象的方法提供的数据的组合,可以处理非常复杂的情况,并为最终用户提供高度本地化的应用。全球化 API 是一个非常强大的工具,它允许您与其他 JavaScript 库协同工作。
本地化您的应用
从开发角度来看,常见的做法是将文本放置在资源字符串中,这些字符串在执行时根据用户设置加载。您可以使用几种技术来全球化您的应用,例如将翻译存储在可移植对象(PO)文件中,创建包含所有内容的 JSON 对象,或者在应用启动时动态加载本地化文件。目标是部署一个能够在运行时选择相关语言资源文件,并处理文化感知的数字和日期解析与格式化、复数、货币、特殊字符、验证等的应用。
在以下示例中,我们将学习如何使用简单的 JavaScript 库在应用中加载不同的语言字符串。
动作时间 - 渲染本地化消息
根据设备的语言设置,按照以下步骤在您的应用中渲染不同的消息:
-
打开命令行工具并创建一个名为
Globalization的新 PhoneGap 项目:$ cordova create Globalization -
使用以下命令添加全球化 API 插件:
$ cordova plugin add cordova-plugin-globalization -
使用命令行工具,添加您想用于此测试的平台(Android、Blackberry、iOS 或 Windows Phone 8):
$ cordova platforms add android -
下载并保存位于
github.com/marcelklehr/html10n.js的l10n.js文件到www/js文件夹。 -
前往
www文件夹,创建一个名为langs.json的 JSON 文件以存储所有必需的语言字符串,如下所示。JSON 文件格式是存储数据如 XML 的简单方式。该文件将为每种语言重复相同的字面量:{ “en”: { “welcome”: “Welcome”, “english”: “English”, “french”: “French”, “alert”: “It's me!” }, “fr”: { “welcome”: “Bienvenu”, “english”: “Anglais”, “french”: “Français”, “alert”: “C'est moi!” } } -
在
www目录中,编辑index.html文件,并在head部分添加以下代码行以加载用于处理多种语言的 JavaScript 文件:<script type=”text/javascript” src=”js/l10n.js”></script> -
按照以下示例加载
langs.json文件:<link rel=”localizations” href=”langs.json” type=“application/l10n+json”/> -
为
body标签定义onload函数,以正确语言值初始化加载文档:<body onload=”onLoad();”> -
创建一些 HTML 元素来尝试使用多种语言。在此示例中,我们将创建两个按钮、一个警告框和一个标题标签。当更改语言时,所有字符串值也会相应更改:
<h1 data-l10n-id=”welcome”>Welcome</h1> <button data-l10n-id=”french” onclick=”changeToFrench()”>French</button> <button data-l10n-id=”english” onclick=”changeToEnglish()”>English</button> <button data-l10n-id=”alert” onclick=”showAlert()”>Test Alert</button> -
创建一个用于初始化语言设置的函数。使用
localize方法加载提供语言的应用程序。最初,我们使用index方法用英语语言加载页面:function onLoad() { html10n.index(); html10n.localize(‘en'); } -
创建两个函数,当用户点击按钮时加载英语和法语语言:
function changeToFrench() { html10n.localize(‘fr'); } function changeToEnglish() { html10n.localize(‘en'); } -
定义一个函数,当点击测试警告按钮时,显示包含翻译字符串的警告窗口:
function showAlert() { var message = html10n.get(‘alert'); alert(message); } -
除了这些,您还可以为其他 PhoneGap/Cordova 相关活动定义
deviceready事件函数。在此示例中,它被留空:document.addEventListener(“deviceready”, onDeviceReady, false); function onDeviceReady() { // Other Stuffs } -
打开命令行工具,进入
Globalization文件夹,并在真实设备或模拟器上构建和运行应用程序:$ cordova build $ cordova run
此处提供了示例的完整源代码供您参考:
<!DOCTYPE html>
<html>
<head>
<script type=”text/javascript” src=”js/l10n.js”></script>
<link rel=”localizations” href=”langs.json” type=”application/l10n+json”/>
</head>
<body onload=”onLoad();”>
<h1 data-l10n-id=”welcome”>Welcome</h1>
<button data-l10n-id=”french” onclick=”changeToFrench()”>French</button>
<button data-l10n-id=”english” onclick=”changeToEnglish()”>English</button>
<button data-l10n-id=”alert” onclick=”showAlert()”>Test Alert</button>
<script type=”text/javascript”>
document.addEventListener(“deviceready”, onDeviceReady, false);
function onDeviceReady() {
// Other Stuffs
}
function onLoad() {
html10n.index();
html10n.localize(‘en');
}
function changeToFrench() {
html10n.localize(‘fr');
}
function changeToEnglish() {
html10n.localize(‘en');
}
function showAlert() {
var message = html10n.get(‘alert');
alert(message);
}
</script>
</body>
</html>
发生了什么?
您开发了一个能够根据用户所需的语言渲染不同文本消息的应用程序。当用户点击法语/英语按钮时,应用程序中的所有其他文本将自动更改。
添加多点触控手势支持
对于任何混合移动应用程序,触摸手势是一个重要的功能,它使得应用程序在用户手中表现得更好。
以下是可以使您的应用启用多点触控手势处理的 JavaScript 库列表。每个库都有其优点和局限性。一些与其他库有依赖关系,而一些则没有。在选择库时必须小心,因为它可能会向您的应用程序引入新的依赖项:
| 库名称 | 依赖关系 | 网址 |
|---|---|---|
| ZeptoJS | No | zeptojs.com/ |
| EventJS | No | github.com/mudcube/Event.js |
| QuoJS | No | quojs.tapquo.com/ |
| Hammer | No | hammerjs.github.io/ |
| ThumbsJS | No | mwbrooks.github.io/thumbs.js/ |
| jGestures | jQuery | jgestures.codeplex.com/ |
| DoubleTab | jQuery | github.com/technoweenie/jquery.doubletap |
| Touchable | jQuery | github.com/dotmaster/Touchable-jQuery-Plugin |
| TouchyJS | jQuery | github.com/HotStudio/touchy |
虽然还有更多库,但 Hammer 在移动应用的手势支持方面被广泛信任。Hammer 是一个开源库,可以识别由触摸、鼠标和 pointerEvents 制作出的手势。它没有任何依赖,并且压缩后的总大小小于 4 KB。
我们将使用 Hammer 举例说明如何在应用中实现触摸事件。
实施手势支持的时间
按照以下步骤使用 Hammer JavaScript 库在你的应用中实现多点触摸手势:
-
打开命令行工具并创建一个名为
hammer的新 PhoneGap 项目:$ cordova create hammer -
使用命令行工具,添加你想要用于此测试的平台(Android、Blackberry、iOS 或 Windows Phone 8):
$ cordova platform add android -
从
github.com/hammerjs/hammer.js下载与 Hammer 相关的文件,并将其保存到www/js文件夹。你可以在 GitHub 的下载包中找到hammer.min.js和hammer.js文件。你可以只保留hammer.min.js文件,删除其余的。 -
在你的
index.html文件中包含hammer.min.js文件以开始使用 Hammer:<script src=”js/hammer.min.js”></script> -
在页面主体中添加一个名为
touch的div元素。此元素将附加到我们将要创建的所有触摸事件:<div id=”touch”>Try Me</div> -
现在,让我们为“按下”手势创建事件监听器并将其附加到我们刚刚创建的
div元素:<script type=”text/javascript”> var touchId = document.getElementById(‘touch'); var hammer = new Hammer(touchId); hammer.on(“press”, function(ev) { touchId.textContent = ev.type +” gesture detected.”; }); </script”> -
打开命令行工具,进入项目文件夹,并在真实设备或模拟器上构建和运行应用:
$ cordova build $ cordova run
当你尝试在元素上应用“按下”动作时,你会看到 Hammer 已经检测到你的动作并改变了元素的文字内容。
注意
注意到 Hammer 库对开发者来说非常灵活。你可以在单个事件处理器中处理多个手势。在以下示例中,我们在单个函数中处理了平移、滑动、拖动和触摸手势。然而,过度使用它们可能会损害你的应用性能:
<script type=”text/javascript”>
var touchId = document.getElementById(myDiv);
var hammer = new Hammer(touchId);
//list of events to be handled
hammer.on(“pan swipe drag touch press”, function(ev) {
touchId.textContent = ev.type +” gesture detected.”;
});
</script”>
默认情况下禁用了捏合和旋转识别器,因为它们可能会干扰应用的行为。但是,如果你想在应用中处理这两个手势,你可以通过以下方式启用它们:
hammertime.get(‘pinch').set({ enable: true });
hammertime.get(‘rotate').set({ enable: true });
你还可以启用垂直或所有方向的拖动和滑动识别器,如以下所示:
hammertime.get(‘pan').set({ direction: Hammer.DIRECTION_ALL });
hammertime.get(‘swipe').set({ direction: Hammer.DIRECTION_VERTICAL });
注意
有关所有手势识别器和自定义选项的更多详细信息,请参阅hammerjs.github.io/getting-started/。阅读此文档将帮助你以最高效的方式使用 Hammer。
处理点击延迟
我们已经看到移动应用可以支持各种触摸手势。随着新技术的引入,Web 应用和原生移动应用之间的差异越来越接近于零,但实际上并不是零。其中一个差异是移动应用中如何处理点击事件。
当您点击按钮时,移动浏览器会等待 300 毫秒来实际触发您所点击的按钮的点击事件。实际原因是移动浏览器等待查看用户是否想要执行点击或双击操作。在 300 毫秒的延迟之后,如果没有其他轻触,则被视为单次点击。然而,如果没有双击的事件处理器,这种延迟将是不必要的。通过克服这些延迟,您可以使得您的应用程序更加响应迅速,减少卡顿。
我们将介绍一种使用名为 FastClick 的库来避免这种 300 毫秒延迟的方法,该库可在 github.com/ftlabs/fastclick 获取。步骤如下:
-
将
fastclick.js包含在您的 JavaScript 包中,或如以下所示将其添加到您的 HTML 页面中:<script type='application/javascript' src=‘/path/to/fastclick.js'></script> -
按照您通常为其他 PhoneGap/Cordova 功能性所做的方式定义
deviceready事件监听器:function onBodyLoad(){ document.addEventListener(“deviceready”, onDeviceReady, false); } -
现在,按照以下所示附加 FastClick 功能性:
function onDeviceReady() { FastClick.attach(document.body); }
在某些情况下,您可能需要 FastClick 忽略一些可能发生双击的元素。在这些情况下,您可以将 needsclick 类添加到元素中,如以下所示:
<a class=”needsclick”>Ignored by FastClick</a>
注意
对于与该主题相关的更多高级讨论,请参阅 FastClick 的 GitHub 页面。如果您正在查看 Google 提出的替代方法,请查看 developers.google.com/mobile/articles/fast_buttons。
摘要
在本章中,您学习了如何使用 PhoneGap 的 Globalization API 创建支持用户语言的本地化应用程序,以及如何创建支持多种用户语言的本地化应用程序。更重要的是,我们看到了如何更好地处理应用程序中的多点触控手势。
在下一章中,我们将介绍一些有助于使应用程序准备公开发布的主题。
第九章。准备发布
到目前为止,你已经使用 PhoneGap 创建了你的应用,定义了其界面的构建块,并学会了使用 PhoneGap 提供的各种 API。在本章中,你将巩固应用架构,并学习如何为你的最终发布到公众做准备。你可以使用各种方法优化你的应用,为用户提供一个坚实的用户体验。
在本章中,你将:
-
学习如何压缩你的 JavaScript,并了解为什么这对于移动应用尤其重要
-
学习如何引入 JavaScript 依赖项
-
了解更多关于模板引擎以及如何压缩模板文件
-
使用 PhoneGap 创建流畅的、多个视图的应用
-
学习如何创建硬件加速的过渡
-
发现如何使用适用于 Web 和移动平台的通用代码库
探索 JavaScript 压缩
在计算机编程中,我们将从源代码文件中删除不必要的字符,并将它们最终连接起来的过程称为 文件压缩。在处理 Web 标准时,我们可以压缩任何类型的文件,包括 HTML、CSS 和 JavaScript。这个过程的主要目标是减小文件大小,以加快下载时间。
在使用 PhoneGap 进行源代码压缩时,其中一个好处是性能提升。在处理移动应用时,文件会被编译成一个单独的文件,最终加载外部数据。然而,当处理使用 PhoneGap 构建的应用时,即使文件存储在本地,也必须在浏览器(即 WebView)中加载。较小的文件将执行得更快,因此最终用户将获得一个响应性更强的用户界面,体验更佳。
你可能会认为在移动设备上真正重要的是内存消耗,并且压缩不会导致内存使用量的大幅减少,因为原始文件和压缩后的文件都被解释成相同的代码。然而,有一些压缩工具可以影响运行时性能。以下几节将讨论三种最流行的压缩工具,这些工具可能有助于提高你应用的性能。
Google Closure Compiler
Google Closure 是一套开源工具,旨在帮助开发者加快现代 Web 应用的开发过程。该项目包括一个 JavaScript 优化器、全面的 JavaScript 库、服务器端和客户端模板引擎,以及 JavaScript 风格检查器和风格修复器。由于本书的范围不包括 Google Closure 的完整概述,我们将仅关注编译器。
描述编译器的一句话最好的来自在线文档:
“它不是从源语言编译到机器代码,而是从 JavaScript 编译到更好的 JavaScript。”
你可以使用以下三种方式之一使用编译器:
-
您可以在
closure-compiler.appspot.com/home在线使用它。 -
您可以从
closure-compiler.googlecode.com/files/compiler-latest.zip下载一个 Java 应用程序,并通过命令行工具执行它。 -
您可以使用 Google 提供的 API(见
developers.google.com/closure/compiler/docs/gettingstarted_api)。
当您打开在线应用程序时,您可以在左侧面板中指定您想要编译的脚本的 URL,您想要应用于输出文件的优化类型,以及您是否希望输出文件格式化为可读性。在网页应用程序的右侧面板中,您将获得一份报告,涉及文件的原始大小和优化大小,编译后的代码,警告列表,最终可能是一些错误,以及发送到 Closure Compiler API 的 POST 数据。提供的警告涉及源代码中可能出现的错误和可以执行的优化。有关可能的警告消息的参考,请访问 developers.google.com/closure/compiler/docs/error-ref。

如果您更喜欢使用命令行工具,您可以下载编译器应用程序并执行它,指定编译选项、输入文件和输出文件:
$ java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js hello.js
使用在线工具时,您将获得相同的结果;然而,使用命令行可以节省一个额外的步骤:您不需要首先上传源代码。
当您使用高级优化时,请注意,重命名过程将更加激进,未使用的代码将被删除,函数调用的主体将被替换为函数本身的主体(这个过程被称为函数内联)。
行动时间 - 使用 Closure Compiler 压缩文件
按照以下步骤使用 Google Closure Compiler 获取压缩和优化的文件:
-
下载并解压可在
dl.google.com/closure-compiler/compiler-latest.zip获取的 Closure Compiler 应用程序。 -
打开命令行工具,移动到解压文件夹,并创建一个名为
sample的文件夹。 -
在新文件夹中,创建三个文件:
index.html、test.js和index.js。您可以使用以下命令:$ echo ‘<!DOCTYPE html><html><head></head><body></body></html>' > index.html $ echo > index.js $ echo > test.js -
打开
test.js文件并定义一个自执行的函数。在函数体内部,声明两个其他函数,并返回其中一个,以便能够从另一个 JavaScript 文件(这两个函数的目的是模拟某些代码保留在闭包内部而其他代码通过返回对象公开的实际用例)中运行此代码。var test = (function(){ var main = function(){ alert(‘executing main'); internal(); }; var internal = function(){ alert(‘executing internal'); }; return { init: main } }()); -
打开
index.js文件并声明一个变量以存储自执行函数的结果,并调用函数本身返回的init函数:var myTest = test.init(); -
返回到命令行工具并针对你刚刚创建的 JavaScript 文件运行编译器:
$ java -jar closure-compiler/compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js samples/test.js samples/index.js --js_output_file samples/app.js -
打开生成的文件并查看源代码;你会得到以下 JavaScript:
alert(“executing main”);alert(“executing internal”); -
在 HTML 页面中插入指向新优化 JavaScript 文件的
script标签并在浏览器中打开它。
发生了什么?
你发现了 Closure Compiler 的 ADVANCED_OPTIMIZATIONS 编译级别的潜力。正如你所见,它相当激进。实际上,如果你使用为你的实际项目创建的文件运行相同的命令,你将无法按预期运行应用程序。简而言之,确保检查 ADVANCED_OPTIMIZATIONS 选项是否破坏了你的代码;如果是这样,你应该考虑使用不同的压缩级别。
接下来,你将了解如何使用 UglifyJS 优化和压缩 JavaScript 模块。
注意
为了获得 Closure Compiler 的全面指南,请参考在线参考 developers.google.com/closure/compiler/docs/api-tutorial3,或者只需在命令行工具中输入 $ java -jar compiler.jar --help。
UglifyJS2
当 jQuery 开始使用它时,UglifyJS 项目变得非常流行。作为 de facto 标准 JavaScript 库之一的使用,导致作者收到了很多反馈,这反过来帮助他修复了大量的错误。
项目的新版本,命名为 UglifyJS2,比之前的版本慢,但整体压缩效果要好得多,并且有更多高级功能,例如多级源映射(基本上,这是一种将合并/压缩文件映射回未构建状态的方法),就像在 Google Closure Compiler 中一样。
UglifyJS2 以 Node.js 模块的形式分发。为了安装它,你可以像安装任何其他 Node.js 模块一样进行。打开命令行工具并使用 npm 安装 uglify-js 模块:
$ sudo npm install uglify-js -g
到目前为止,压缩你的 JavaScript 文件变得容易多了。
行动时间 – 使用 UglifyJS
让我们看看如何使用与 Google Closure Compiler 一起工作的相同文件获得压缩版本:
-
打开你的命令行工具并转到为测试 Closure Compiler 创建的样本文件夹。
-
输入以下命令以连接 JavaScript 文件并运行 UglifyJS2 压缩器。对于 Windows,你可以使用
copy命令来连接文件:C:\ copy /a *.js index.js $ cat test.js index.js $ uglifyjs --inline-script -o mytest.min.js -
打开生成的文件,查看源代码;你会得到以下 JavaScript:
var test=function(){var main=function(){alert(“executing manin”);internal()};var internal=function(){alert(“executing internal”)};return{init:main}}();var test=test.init(); -
在 HTML 页面中插入
script标签并在浏览器中打开它。
发生了什么?
您创建了一个压缩版本的两个简单的 JavaScript 文件。如您所见,输出与使用 Closure Compiler 创建的输出相当不同。UglifyJS2 的一个主要特点是生成的输出不会破坏源代码。
对于完整的参考,您可以在 GitHub 上的项目页面进行检查,链接为github.com/mishoo/UglifyJS2。
注意
如果您在第四章“使用插件”中运行 UglifyJS2 以压缩文件,您将能够按预期运行应用程序。
使用 RequireJS 进行优化
RequireJS 包含一个名为r.js的优化工具,该工具将相关的脚本组合到构建层中,并通过 UglifyJS 或 Closure Compiler 对其进行压缩。该工具可以通过 Node.js 或 Java 使用。当使用 Closure Compiler 时,必须使用 Java 运行该工具。
优化器比使用普通的连接脚本更好,因为它在优化过程中运行 RequireJS,因此知道如何加载插件以及您应用程序中需要的所有 JavaScript 模块的依赖项。
要获取关于 r.js 的全面指南,请参考 GitHub 上可用的 readme 文件,链接为github.com/jrburke/r.js。
动手时间 - 使用 RequireJS 优化 JavaScript
按照以下步骤使用 Node.js 和 RequireJS 优化您的 app 的源代码:
-
使用 npm 从命令行工具安装
requirejs模块:$ sudo npm install requirejs -g -
前往您在前几章中工作的 app 的根目录,创建一个名为
build.js的文件,并向其中添加构建过程配置信息(即 JavaScript 文件夹、项目中使用的库的路径、app 的主文件名以及输出文件夹和文件名):({ baseUrl: ‘js/', paths: { mustache: ‘libs/mustache', alice: ‘libs/alice.min', text: ‘libs/require/plugins/text' }, name: ‘main', out: ‘js/main-built.js' }) -
再次打开命令行工具并执行以下命令以构建 app:
$ r.js -o build.js -
打开
index.html文件并更改 header 中的script标签中您的 app 的入口点:<script data-main=”js/main-built” src=”js/libs/require/require.js”></script> -
在浏览器中打开
index.html文件。
发生了什么?
您创建了一个压缩版本的 app 的 JavaScript 文件,这些文件被压缩到一个单独的文件中,使用构建文件指定了命令行选项。结果是,app 的代码现在使用 UglifyJS2(幕后工作的引擎)进行了优化,并且仍然可以完美运行。为了获得完整的构建选项概述,请参考 GitHub 上可用的示例构建文件,链接为github.com/jrburke/r.js/blob/master/build/example.build.js。
提示
如果你更喜欢使用 Closure Compiler 来压缩和优化应用程序的 JavaScript 文件,你必须下载 Rhino 的二进制文件(一个完全用 Java 编写的 JavaScript 开源实现),可在developer.mozilla.org/en-US/docs/Rhino/Download_Rhino找到,从 RequireJS 网站requirejs.org/docs/download.html#rjs下载 r.js,在构建文件中添加optimize: ‘closure'选项,并执行以下命令:
$ java -classpath ~/rhino1_7R4/js.jar:~/compilers/closure-compiler/compiler.jar org.mozilla.javascript.tools.shell.Main r.js build.js
在这里,classpath指的是 Rhino 和 Closure Compiler 的完整路径。
压缩工具比较
我们已经介绍了三种最受欢迎的压缩工具。每种工具都有其优缺点。一如既往,最适合你的工具是那个最能满足你需求的工具。以下表格总结了使用我们刚才讨论的工具压缩 RequireJS 文件本身可以得到的压缩结果,单位为字节:
| 文件 | 原始大小 | 压缩工具 | 压缩后大小 |
|---|---|---|---|
| RequireJS | 82944 | UglifyJS2 | 24576 |
| Google Closure | 13312 | ||
| r.js | 15360 |
如你所见,在这个例子中,Google Closure 产生了最佳结果,但这并不总是如此。如果你在流行的 RaphaelJS 库上运行相同的测试,你将得到 Google Closure Compiler 的最佳结果。结果取决于源代码的编写风格;因此,没有单一的最好工具可以使用。我更喜欢 r.js,因为它不仅可以运行压缩引擎,而且可以很好地处理插件和模块依赖。
JavaScript 任务运行器,如 Gulp 或 Grunt,可以用来创建任务,在资源有变化时自动链接、压缩和合并资源。
注意
你可能还考虑的其他压缩工具有 KJScompress、Bananascript、JSMin、ShrinkSafe 和 YUI Compressor。
使用模板引擎压缩
当你在较大的 HTML 项目上工作时,建议使用 JavaScript 模板引擎和图像压缩。当应用程序需要频繁更新数据时,模板库将非常有用。我坚信没有所谓的最佳JavaScript 模板引擎。每次你开始一个项目时,你必须决定哪个引擎最适合当前任务。例如,Underscore.js模板快速且轻量级,如果你想在应用程序中预先加载它们,那么这是一个不错的选择。当使用 jQuery 时,自然的选择似乎是ICanHaz.js,因为它将每个模板作为 jQuery 对象返回。当你需要一个更健壮的模板引擎时,Google Closure Templates可能是一个有效的选择。要比较模板引擎,请访问garann.github.io/template-chooser/。
在大多数情况下,Mustache.js完全符合应用程序的需求,因为没有逻辑在模板中,并且模板是语言无关的,允许您在前端和后端之间重用它们。有几个基于Mustache.js的模板引擎,包括Handlebars.js、Hogan.js或Pistachio。
Handlebars.js 是 Mustache.js 的超集,它添加了一些有用的功能,如块表达式、辅助函数等(有关完整概述,请参阅handlebarsjs.com/上的在线文档)。
Hogan.js 是 Twitter 的 Mustache.js 模板的非常强大的编译器。Hogan.js 还附带了一个命令行实用程序,该实用程序可以编译存储在文件夹中的所有*.mustache模板;实用程序位于hogan.js-template/bin文件夹中。有关 Hogan.js 的更多信息,请访问 GitHub 上的github.com/twitter/hogan.js。
Pistachio 不仅仅是一个基于 Mustache.js 的 JavaScript 模板引擎。它的包包含一个纯 JavaScript 编译器,可以将模板编译成自包含的 JavaScript 函数,这些函数可以在任何 JavaScript 环境中使用。
为了开始使用 Pistachio 编译器,您可以将它作为 Node.js 模块安装:
$ sudo npm install pistachio -g
安装完成后,您可以通过输入pistachio后跟您想要编译的文件路径来编译一个模板。
Pistachio 编译器的有趣特性是能够将模板编译为 AMD 模块或 CommonJS 兼容模块,并且可以将输出创建为 jQuery 对象。使用 Pistachio 编译的模板仍然是动态的,并且可以使用 Google Closure Compiler 进一步压缩。有关 Pistachio 的完整参考,请访问npmjs.org/package/pistachio。
压缩模板可以加快您的应用程序渲染速度,因为您可以将它缓存为 JavaScript 函数,并在应用程序使用时避免使用AJAX请求(涉及一些性能惩罚)不断加载和卸载它。
小贴士
如果您想在文件中包含多个模板,您只需将它们存储在script标签中,为每个标签分配一个 ID,然后使用getElementByID()文档对象方法和innerHTML HTMLElement 对象属性来渲染它:
<script type=”text/x-mustache” id=”tid...”>
/* mustache template */
</script>
行动时间 - 使用 Pistachio 编译模板
创建一个新的模板文件,并最终使用 Pistachio 将其压缩。按照以下步骤操作:
-
打开您的命令行工具并移动到包含模板文件的文件夹中。
-
输入
pistachio命令并指定输出文件名和要编译的文件名:$ pistachio --out=splash-tpl.js splash-tpl.html -
为现有模板创建一个名为,例如,
template-build.js的构建文件,以便在压缩文件时使用 UglifyJS2,指定模板名称和所需的输出文件名:({ name: ‘splash-tpl', out: ‘splash-built.js' }) -
从命令行工具运行 r.js Node 模块:
$ r.js -o template-build.js -
打开文件并检查其语法和大小。
发生了什么?
您创建了一个存储在变量中的模板文件的压缩版本。现在您可以在应用的模块中请求它,从而避免任何不必要的 XMLHttpRequest。
这种技术在处理相当大且复杂的模板时最有益。在这本书中,您将发现一些高级模板缓存技术。
创建流畅的多个应用视图
PhoneGap 的一个优点是,应用 UI 和逻辑建立在 Web 标准之上。一个移动应用由几个视图组成,允许用户与其核心功能交互。至于 Web 应用,当使用 PhoneGap 时,您可以将视图视为一个网页或网页的一部分。
您可以在您的应用中使用不同的 HTML 页面或动态更改单个 HTML 页面的标记来创建多个视图。第一种方法通常被称为多页模式;第二种方法被称为单页模式。
一般而言,多页模式最适合主要包含静态内容或主要依赖服务器进行业务逻辑的应用程序。当大部分内容是静态的,您可以使用 PhoneGap 对其进行打包,并以应用的形式交付。当业务逻辑在服务器上定义时,您可以认为客户端是您应用的表示层,并依赖于良好的移动连接来使其对用户可用。在这两种情况下,您的客户端代码应该相当简单且易于维护。
多页方法有一些缺点。例如,当用户从一个页面导航到下一个页面时,浏览器必须重新加载并解析与新页面相关联的所有 JavaScript。此外,由于 JavaScript 代码被重新加载,如果您的应用没有使用其他技术(如本地存储或 HTML5 历史状态对象)来维护它,则所有应用程序状态都会丢失。
单页模式克服了与多页方法相关的缺点。PhoneGap 和 app JavaScript 代码只需加载一次,从而消除了在页面之间传递应用程序状态的需求。这种方法的缺点是包含大部分业务逻辑的 JavaScript 变得更加复杂,并且需要在导航发生时更新 UI。单页应用程序最好使用 MVC 设计模式,并可以使用如 AngularJS 之类的库。
这两种模式之间最重要的区别在于,使用单页模式时,PhoneGap JavaScript 桥接器只需加载一次。由于 JavaScript API 与本地对应项之间的链接被创建,加载时会有一段明显的暂停。当应用加载一次 PhoneGap JavaScript API 时,UI 看起来更加响应,用户体验得到改善。
使用硬件加速的过渡
关于在智能手机和平板电脑的 Web 浏览器中使用 图形处理单元(GPU)硬件加速的讨论已经很多了。一般方案是将原本由主 CPU 计算的任务卸载到计算机显卡中的 GPU 上。(为了更好地理解硬件加速的过渡,请参阅 dev.sencha.com/blog/understanding-hardware-acceleration-on-mobile-browsers 的详细文章。)
GPU 可以加速以下操作:
-
通用布局合成
-
所有 CSS 过渡
-
CSS 3D 变换
-
所有画布绘图操作
你可以通过轻松地在样式表中定义它们或依赖外部库来使用新的 CSS 过渡创建平滑的动画。
CSS 过渡在最新的 Firefox、Safari 和 Chrome 版本中得到支持。它们在 IE 10 及以上版本中得到支持。如果某个浏览器不支持 CSS 动画,则属性将立即应用,优雅地降级。处理 CSS 过渡有几种技术。我将使用 Alice.js,这是一个有趣的 JavaScript 库,允许你在你的应用程序中执行硬件加速的过渡。
AliceJS
AliceJS(轻量级独立 CSS 引擎)是一个 JavaScript 库,它利用浏览器硬件加速功能来生成视觉效果。该库的一个优点是它不依赖于其他库,并且包含在一个单独的 JavaScript 文件中(对于完整参考和一些有趣示例,请参阅官方网站 blackberry.github.com/Alice/demos/index.html)。
每次你想使用 AliceJS 创建过渡时,你必须设置一个配置对象。这个对象根据你使用的效果或插件而变化。然而,一些配置属性在所有效果和插件之间是共享的,包括以下内容:
-
elems:这是目标元素或节点 -
rotate:这是旋转角度(以度为单位) -
perspectiveOrigin:这是锚点,可以是top-left、top-center、top-right、center等,或者是div整个大小的百分比坐标,例如,{x: 200, y: 200} -
duration:这是效果持续时间 -
timing:这是按照标准 CSS 规范的缓动函数 -
delay:这决定了动画开始前的延迟时间 -
iteration:这是迭代次数 -
direction:这指定了动画是否应该以反向模式播放 -
playstate:这是running或paused
以这种方式,可以轻松地配置基于 CSS 的动画,而无需任何额外的知识。AliceJS 有三个插件,包括几种动画类型。以下是从 Alice 文档中创建摆动效果的 Cheshire 插件的简单代码。因此,我们需要包含核心和插件文件:
<div id=”DIV1”>HERE IS ONE DIV</div>
<img id=”IMG1” src=”/myimg.gif”>
<script src=”/alice/alice.core.js”></script>
<script src=”/alice/alice.plugins.cheshire.js”></script>
<script>
alicejs.wobble({
elems: [“DIV1”, “IMG1”],
rotate: 5,
perspectiveOrigin: “center”,
duration: “200ms”,
timing: “linear”,
delay: “0ms”,
iteration: “infinite”,
direction: “alternate”,
playstate: “running”
});
</script>
正如你所见,代码相当简单;要获得完整概述,请参阅 AliceJS 的在线文档。
端口 Web 应用程序
我们知道 PhoneGap/Cordova 允许你为所有移动平台拥有一个共同的代码库。PhoneGap 帮助你打包 HTML、CSS 和 JavaScript 代码,以便可以在移动设备上安装,并可以作为不同类型的平台(如 Android、iOS、Firefox 等)上的应用程序使用。这并不令人惊讶。然而,你知道吗,通过一些规划,你可以将现有的 Web 应用程序转换为混合移动应用程序?
如果你是一名 Web 开发者,你甚至可以使用你对 Web 开发的现有知识来创建移动应用程序,并在应用市场中销售。通过避免重新开发,你可以节省大量时间和金钱。
PhoneGap/Cordova 提供了许多 API 来访问设备功能,如相机、加速度计等。这样,PhoneGap 为开发者提供了使用 Web 技术创建功能齐全的移动应用程序的能力。然而,你无法在你的 Web 应用程序中使用这些与设备相关的 API。因此,你必须设计你的代码库,使其提供模块化。
用户与 Android 应用程序交互的方式与与 iOS 应用程序交互的方式不同,等等。因此,在应用程序的设计因素上必须进行仔细的规划。你可能需要对每个平台进行微调以获得更好的用户体验。你可以使用响应式设计和媒体查询来针对多个屏幕尺寸、设备和显示目标(标准或高密度)。
为共同的代码库提供稳健的设计超出了本书的范围。然而,有许多将 Web 应用程序移植到混合移动应用程序的成功案例。
摘要
在本章中,你学习了如何优化应用程序的源代码,以及如何压缩你使用的模板;我们还看到了各种代码压缩方法。通过这些,我们已经完成了学习,现在是时候将它们付诸实践了。在下一章中,我们将创建一个应用程序来展示我们所有的 PhoneGap 知识。
第十章:一个 PhoneGap 示例项目
到目前为止,我们已经看到了如何创建 PhoneGap 项目,单独使用各种 API,以及最终如何构建应用程序。在学习了这些重要内容之后,我们需要将所有学习内容打包,创建一个包含所有 API 的实际应用程序。在这个演示应用项目中工作将帮助您理解 API 的实际用法以及如何使用它们。我已经尽量使项目尽可能简单,以便您能够轻松理解。
在本章中,您将:
-
创建一个新的 PhoneGap 项目
-
学习如何在项目中包含 Bootstrap 和其他库
-
使用 PhoneGap 提供的所有 API 开发演示应用
-
在真实设备上构建应用程序并尝试
应用中包含什么?
我们将要创建的演示应用将展示 PhoneGap/Cordova API 的所有功能。应用将列出所有部分在一个滑动菜单中,用户可以选择他们感兴趣的 API。该演示应用可在 Google Play 商店中找到,链接为 play.google.com/store/apps/details?id=com.iyaffle.phonegap。
注意
为了大家的利益,完整的项目源代码可在 GitHub 仓库中找到。读者可以从 github.com/iYaffle/PhoneGap-Demo-App 查看或下载整个源代码。祝您学习愉快!
使用的库
我们将在我们的演示应用中使用以下库。我们将找出为什么在我们的演示中使用这些库。
Twitter Bootstrap
Bootstrap 是最受欢迎的用于在网络上开发响应式项目的 HTML、CSS 和 JS 框架。Bootstrap 将成为我们的主要 UI 框架。
它可以从 www.getbootstrap.com/ 下载。
jQuery
jQuery 是一个改变了众多网页开发者生活的 JavaScript 框架。它重新定义了 JavaScript 的编码方式。它提供了一个 API,可以轻松处理 DOM 操作、事件处理、动画和 AJAX。
它可以从 www.jquery.com/ 下载。
mMenu
mMenu 是一个著名的 jQuery 插件,它为网站和 Web 应用创建滑动子菜单。它依赖于 jQuery,我们将使用它来创建我们的滑动菜单。
它可以从 www.mmenu.frebsite.nl/ 下载。
FastClick
FastClick 是一个微小但实用的库,它将帮助我们消除触摸设备中的 300 毫秒延迟。关于这方面的更多信息,请参考第八章 Handling click delays 部分,Advanced PhoneGap。
它可以从 github.com/ftlabs/fastclick 下载。
注意
本章将是一个基础章节,强烈建议读者在继续之前阅读前面的章节,以了解 API。我们已经涵盖了项目的关键方面,其余的留给读者去阅读和解释,以鼓励项目的开发。
创建项目
作为我们活动的第一步,我们需要创建一个新的 PhoneGap 项目。我们将使用 PhoneGap CLI 工具来创建项目并安装各种插件。
现在,让我们使用 PhoneGap CLI 创建一个名为PhoneGap的新项目:
C:\> phonegap create PhoneGap
创建项目后,如您所知,将创建一个新的以项目名称命名的目录。让我们将其设置为当前目录:
C:\> cd PhoneGap
现在,让我们添加 Android 作为我们的目标平台。如果您感兴趣,如果您已安装所需的 SDK,也可以添加其他平台:
C:\PhoneGap> phonegap platform add android
我们应该在项目的家目录www目录下的js文件夹中下载fastclick.js、jquery.mmenu.min.all和jquery-2.1.3.min.js库。请注意,jQuery 和 mMenu 库的所有 CSS 文件都应该放在www目录下的css目录中。现在,我们可以开始我们的项目开发。
安装插件
由于我们的应用程序将使用几乎所有 API,我们将在项目中安装它们。您现在可以安装所有这些插件,也可以在需要时安装它们。安装它们的命令如下:
-
cordova plugin add cordova-plugin-dialogs -
cordova plugin add cordova-plugin-device -
cordova plugin add cordova-plugin-vibration -
cordova plugin add cordova-plugin-camera -
cordova plugin add cordova-plugin-network-information -
cordova plugin add cordova-plugin-contacts -
cordova plugin add cordova-plugin-splashscreen -
cordova plugin add cordova-plugin-device-motion -
cordova plugin add cordova-plugin-geolocation -
cordova plugin add cordova-plugin-media-capture -
cordova plugin add cordova-plugin-file -
cordova plugin add cordova-plugin-globalization -
cordova plugin add cordova-plugin-inappbrowser -
cordova plugin add cordova-plugin-media
首页设计
对于每个应用程序,index.html页面将是主页,我们的应用程序也将从这里开始。要从头开始,清除文件的 body 内容。在index.html文件的<body>标签内,我们将添加以下代码:
<div id="page">
<div class="header">
<a href="#menu"></a>
PhoneGap Demo Home
</div>
<div class="content">
<div class="jumbotron">
<h1>Welcome PhoneGap</h1>
<img src="img/logo.png" />
<p>
<button id="openMenu" class="btn-lg btn-primary" onclick="">Start Here</button>
</p>
</div>
</div>
</div>
为了理解前面代码中的每个部分,我们需要了解 Bootstrap 组件。在 Bootstrap 中,我们可以有一个由header类标识的页面标题。页面的实际内容放置在具有content类的div元素下。这些都是标准的 Bootstrap 技术,如果您阅读 Bootstrap 文档,可以轻松理解它们。
现在,我们将添加 CSS 文件引用到页面的<head>部分,如下所示:
< link rel="stylesheet" type="text/css" href="css/demo.css">
< link rel="stylesheet" type="text/css" href="css/index.css">
现在,我们将在</body>标签上方添加所有我们将要使用的 JavaScript 文件。确保你按正确的顺序添加每个文件,以便解决依赖关系:
<script type="text/javascript" src="img/cordova.js"></script>
<script type="text/javascript" src="img/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="img/jquery.mmenu.min.all.js"></script>
<script type="text/javascript" src="img/fastclick.js"></script>
<script type="text/javascript" src="img/main.js"></script>
你可能已经注意到我们添加了一个id值为openMenu的按钮。现在,我们将为它添加事件。我们试图在用户点击按钮时打开滑动菜单。滑动菜单最近已成为一种流行的方法,因为它被用于 Facebook 移动应用中:
<script type="text/javascript">
$("#openMenu").click(function() {
$("#menu").trigger("open.mm");
});
</script>
使用提供的 CSS 样式(参考.css文件),你会看到应用如下截图所示:

现在,让我们看看滑动菜单。我们已经在单独的模板文件中定义了菜单内容,并计划使用 AJAX 将其包含在我们的页面中。我们将代码放在单独的 JS 文件main.js中,以便我们可以在每个页面中包含它。代码片段如下供您参考:
$(document).ready(function() {
$.ajax({
url: "menu.html",
success: function(result) {
$("#page").prepend(result);
$("#menu").mmenu({
"header": {
"title": "PhoneGap Demo Menu",
"add": true,
"update": true
}
});
}
});
});
menu.html文件将包含一个带有链接到我们创建的所有页面的普通列表。您可以在这里查看:链接
<nav id="menu">
<ul>
<li><a href="motion.html">Accelerometer</a></li>
<li><a href="device.html">Device Info</a></li>
<li><a href="camera.html">Camera</a></li>
<li><a href="capture.html">Capture</a></li>
<li><a href="connection.html">Connection</a></li>
<li><a href="contacts.html">Contacts</a></li>
<li><a href="file.html">Files</a></li>
<li><a href="geolocation.html">GeoLocation</a></li>
<li><a href="globalization.html">Globalization</a></li>
<li><a href="browser.html">InApp Browser</a></li>
<li><a href="notifications.html">Notifications</a></li>
<li><a href="splash.html">Splash Screen</a></li>
<li class="Spacer Label">This demo app is for learning only</li>
<li><p>For bugs, issues & suggestions please create an issue in GitHub. Feel free to fork the GitHub Repo and contribute to make it better.</p></li>
</ul>
</nav>
此代码负责获取menu.html的内容,并将其推送到id值为page的div元素下。然后,我们对包含的菜单项应用滑动选项。
正确完成此操作后,当你点击从这里开始按钮时,你将看到滑动菜单在你面前打开:

通过这样,我们已经准备好了应用的基本框架。接下来,我们将学习每个 API 及其用法。向前推进,只提供主要片段。这将有助于读者学习和尝试其余部分。
使用加速度计 API
我们现在将使用加速度计 API。使用这个 API,我们将获取加速度坐标并在应用中显示它们。为此,我们定义了一些div元素,如下所示:
<div class="content">
<br/>Acceleration X :
<div id="dataX">0</div>
<br/>
<br/>Acceleration Y :
<div id="dataY">0</div>
<br/>
<br/>Acceleration Z :
<div id="dataZ">0</div>
<br/>
<br/>TimeStamp :
<div id="timeStamp">0</div>
<br/>
<br/>
接下来,我们有两个按钮来开始和停止加速度监控:
<button id="start" onclick="onDeviceReady();" style="display:none;" class="btn-lg btn-success">Start Watching</button>
<br>
<button id="stop" onclick="stopWatch();" class="btn-lg btn-danger">Stop Watching</button>
</div>
现在,让我们来看看这个的核心 JavaScript 代码。我们通过使用onDeviceReady()函数,指示应用持续一秒监控加速度,并更新相应的div元素中的值。
<script type="text/javascript">
var watchID = null;
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
$('#stop').show();
$('#start').hide();
var options = {
frequency: 1000
}; // Update every 1 seconds
watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
}
function onSuccess(acceleration) {
$('#dataX').html(acceleration.x);
$('#dataY').html(acceleration.y);
$('#dataZ').html(acceleration.z);
$('#timeStamp').html(acceleration.timestamp);
};
function onError() {
alert('onError!');
};
function stopWatch() {
if (watchID) {
navigator.accelerometer.clearWatch(watchID);
watchID = null;
$('#start').show();
$('#stop').hide();
}
}
</script>
此输出的结果如下:

当我们想要停止监控时,通过点击停止监控按钮来触发stopWatch()函数。当我们点击开始监控按钮时,监控将再次开始,如下所示:

使用设备 API
使用设备 API,我们将获取有关我们设备的信息。我们将定义五个<p>标签来保存数据:
<p id="model"></p>
<p id="platform"></p>
<p id="version"></p>
<p id="uuid"></p>
<p id="cordova"></p>
我们有以下脚本,它将获取设备信息并将其放入 DOM 中。你可能已经注意到我们正在使用<p>元素的属性 ID 值:
<script type="text/javascript">
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
$('#model').html(device.model);
$('#cordova').html(device.cordova);
$('#platform').html(device.platform);
$('#uuid').html(device.uuid);
$('#version').html(device.version);
}
</script>
输出将如下所示:

使用相机 API
相机 API 可以用来从设备相机捕获图片,也可以从图片库中选择图片。我们将通过示例查看这两种用法:
<div class="content">
<ul class="nav nav-tabs">
<li class="active"><a href="#capture" data-toggle="tab">Capture Photo</a>
</li>
<li><a href="#album" data-toggle="tab">From Album</a>
</li>
</ul>
<div class="tab-content" id="tabs">
<div id="capture" class="tab-pane active">
<br/>
<button onclick="capturePhoto();" class="btn btn-success">Capture Camera Photo</button>
<br/>
<br/>
<img style="display:none;" id="smallImage" src="img/" class="img-responsive img-rounded" alt="Responsive image" />
</div>
<div id="album" class="tab-pane">
<br/>
<button onclick="getPhoto(Camera.PictureSourceType.SAVEDPHOTOALBUM);" class="btn btn-info">Select Image from Album</button>
<br/>
<br/>
<img style="display:none;" id="largeImage" src="img/" class="img-responsive img-rounded" alt="Responsive image" />
</div>
</div>
</div>
现在,你将能够使用你的设备相机拍照:

现在,你还将能够从你的设备相册中选择图片:

以下是用于此示例的脚本,建议你在继续此示例之前先了解关于相机 API 的内容:
<script type="text/javascript">
function capturePhoto() {
navigator.camera.getPicture(onPhotoDataSuccess, onFail, {
quality: 50,
allowEdit: true,
destinationType: Camera.DestinationType.DATA_URL
});
}
function onPhotoDataSuccess(imageData) {
var smallImage = document.getElementById('smallImage');
smallImage.style.display = 'block';
smallImage.src = "data:image/jpeg;base64," + imageData;
}
function onPhotoURISuccess(imageURI) {
var largeImage = document.getElementById('largeImage');
largeImage.style.display = 'block';
largeImage.src = "data:image/jpeg;base64," + imageURI;
}
function getPhoto(source) {
navigator.camera.getPicture(onPhotoURISuccess, onFail, {
quality: 50,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: source
});
}
function onFail(message) {
alert(message);
}
</script>
以下是示例操作的截图:

一旦你拍摄了照片,你将被要求选择或拒绝所拍摄的图片:

一旦你接受了图片,它将在应用程序中显示:

当你选择从图库中选择选项时,将显示图库应用程序以进行图片选择:

再次强调,一旦你从图库中选择了一张图片,它将在页面上显示:

使用捕获 API
通过捕获 API,我们可以捕获音频、视频和相机图片。我们将看到一个简单的代码来执行所有这些操作。以下代码将在页面上添加三个按钮,并将事件绑定到每个按钮上:
<div class="content">
<div class="btn-group-vertical" role="group" aria-label="...">
<button onclick="captureAudio();" class="btn-lg btn-success">Capture Audio</button>
<br>
<button onclick="captureImage();" class="btn-lg btn-warning">Capture Image</button>
<br>
<button onclick="captureVideo();" class="btn-lg btn-danger">Capture Video</button>
<br>
</div>
<div id="details" style="display:none">
<b>File Path : </b>
<div id="fullPath"></div>
<br/>
<b>File Name : </b>
<div id="name"></div>
<br/>
<b>Type : </b>
<div id="type"></div>
<br/>
<b>Last Modified Timestamp : </b>
<div id="lastModifiedDate"></div>
<br/>
<b>File Size (bytes) : </b>
<div id="size"></div>
</div>
</div>
以下是一个 JavaScript 代码片段;每个函数都很容易理解:
<script type="text/javascript">
function captureSuccess(mediaFiles) {
var i, len;
for (i = 0, len = mediaFiles.length; i < len; i += 1) {
uploadFile(mediaFiles[i]);
}
}
// Called if something bad happens.
function captureError(error) {
var msg = 'An error occurred during capture: ' + error.code;
navigator.notification.alert(msg, null, 'Uh oh!');
}
// A button will call this function
function captureAudio() {
$('#details').hide();
// Launch device audio recording application,
// allowing user to capture up to 1 audio clips
navigator.device.capture.captureAudio(captureSuccess, captureError, {
limit: 1,
duration: 10
});
}
// A button will call this function
function captureImage() {
$('#details').hide();
// Launch device camera application,
// allowing user to capture up to 1 images
navigator.device.capture.captureImage(captureSuccess, captureError, {
limit: 1
});
}
function captureVideo() {
$('#details').hide();
// Launch device video recording application,
// allowing user to capture up to 1 video clip
navigator.device.capture.captureVideo(captureSuccess, captureError, {
limit: 1,
duration: 10
});
}
// Upload files to server
function uploadFile(mediaFile) {
$('#fullPath').html(mediaFile.fullPath.replace(mediaFile.name, ""));
$('#name').html(mediaFile.name);
$('#type').html(mediaFile.type);
$('#lastModifiedDate').html(mediaFile.lastModifiedDate);
$('#size').html(mediaFile.size);
$('#details').show();
//Upload file using FileTransfer method not shown here
}
</script>
此操作的输出如下:

尝试使用应用程序录制你的声音:

使用连接 API
连接 API 是其中最简单的 API 之一。我们将找出设备具有哪种数据连接,然后显示它。
为了存储数据,我们定义了一个新的元素,如下所示:
<p id="status" class="list-group-item-text">
在 JavaScript 函数中,我们获取连接类型,并使用checkConnection()方法进行验证。然后,我们显示一个友好的消息:
<script type="text/javascript">
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
checkConnection();
}
function checkConnection() {
var networkState = navigator.connection.type;
var states = {
};
states[Connection.UNKNOWN] = 'Unknown Connection';
states[Connection.ETHERNET] = 'Ethernet Connection';
states[Connection.WIFI] = 'WiFi Connection';
states[Connection.CELL_2G] = 'Cell 2G Connection';
states[Connection.CELL_3G] = 'Cell 3G Connection';
states[Connection.CELL_4G] = 'Cell 4G Connection';
states[Connection.CELL] = 'Cell Generic Connection';
states[Connection.NONE] = 'No Network Connection';
$('#status').html(states[networkState]);
}
</script>
此操作的输出如下:

使用联系人 API
现在,让我们看看联系人 API 的一些高级概念。我们将做以下三件事:
-
从设备中获取 10 个电话号码;为了简单起见,我们将其限制为 10 个。
-
搜索联系人并显示他们的电话号码,以及联系人名称。
-
使用应用程序添加新联系人。
以下是 HTML 代码;由于我们使用了 Bootstrap 样式来创建标签样式,所以看起来很大:
<div id="page">
<div class="header">
<a href="#menu"></a>
Contacts API
</div>
<div class="content">
<ul class="nav nav-tabs">
<li class="active"><a href="#fetch" data-toggle="tab">Fetch</a>
</li>
<li><a href="#add" data-toggle="tab">Add</a>
</li>
</ul>
<div class="tab-content" id="tabs">
<div id="fetch" class="tab-pane active">
<br/>
<button onclick="fetchContacts('');" class="btn btn-info">Fetch 10 Contacts</button>
<button id="searchName" class="btn btn-info">Search Name</button>
<br/>
<br/>
<div id="status"></div>
<div id="panel" style="display:none">
<div class="list-group">
<a href="#" class="list-group-item active">
<h4 class="list-group-item-heading">Total Contacts</h4>
</a>
<a href="#" class="list-group-item">
<p id="count" class="list-group-item-text"></p>
</a>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Phone Number</th>
</tr>
</thead>
<tbody id="contacts">
</tbody>
</table>
</div>
</div>
<div id="add" class="tab-pane">
<form id="saveForm">
<div class="form-group">
<label for="firstName">First Name</label>
<input class="form-control" type="text" id="firstName" placeholder="Enter First Name" />
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input class="form-control" type="text" id="lastName" placeholder="Enter Last Name" />
</div>
<div class="form-group">
<label for="email">Phone Number</label>
<input class="form-control" type="tel" id="number" placeholder="Enter Number" />
</div>
<div class="form-group">
<label for="email">Email Address</label>
<input class="form-control" type="email" id="email" placeholder="Enter Email" />
</div>
<div class="form-group">
<label for="note">Note</label>
<textarea class="form-control" id="note" placeholder="Enter Notes"></textarea>
</div>
<div class="form-group">
<input class="btn btn-danger" type="button" name="save" id="saveBtn" value="Save Contact" />
</div>
</form>
</div>
</div>
</div>
</div>
进入 JavaScript 部分,以下是对每个操作的代码。阅读有关联系人 API 的部分将帮助你理解这一点。代码中还有一些与 jQuery 相关的语句:
<script type="text/javascript">
$("#searchName").click(function() {
$('#status').html("");
$('#panel').hide();
navigator.notification.prompt(
'Please enter search text',
onPrompt,
'Contact Search', ['Ok', 'Exit'],
''
);
});
$("#saveBtn").click(function() {
var firstName = document.getElementById('firstName').value;
var lastName = document.getElementById('lastName').value;
var fullName = firstName + ' ' + lastName;
var number = document.getElementById('number').value;
var note = document.getElementById('note').value;
var emailAddress = document.getElementById('email').value;
var theContact = navigator.contacts.create({
"displayName": fullName
});
theContact.note = note;
var emails = [];
emails[0] = new ContactField('email', emailAddress, false);
theContact.emails = emails;
var phoneNumbers = [];
phoneNumbers[0] = new ContactField('work', number, false);
phoneNumbers[1] = new ContactField('mobile', number, true); // preferred number
phoneNumbers[2] = new ContactField('home', number, false);
theContact.phoneNumbers = phoneNumbers;
theContact.save(onSaveSuccess, onSaveError);
});
function onSaveSuccess(contact) {
navigator.notification.alert(
"Contact Saved",
null,
'PhoneGap HandsOn Project',
'OK'
);
document.getElementById("saveForm").reset();
}
function onSaveError(contactError) {
navigator.notification.alert(
"Contact Not Saved - Error Code : " + contactError.code,
null,
'PhoneGap HandsOn Project',
'OK'
);
}
function onPrompt(results) {
if (results.buttonIndex == 1) {
if (results.input1 == "") {
navigator.notification.alert(
"Empty Search Text",
null,
'PhoneGap HandsOn Project',
'Try Again'
);
} else {
fetchContacts(results.input1);
}
}
}
function fetchContacts(filter) {
$('#panel').hide();
$('#status').html("In Progress.... Please Wait!");
var options = new ContactFindOptions();
options.filter = filter;
options.multiple = true;
var fields = ["*"];
navigator.contacts.find(fields, onSuccess, onError, options);
};
function onSuccess(contacts) {
if (contacts.length == 0) {
$('#panel').hide();
$('#status').html("No Contacts Found");
return;
}
var text = "";
var totalCount = 0;
for (var i = 0; i < contacts.length; i++) {
if (totalCount > 9) {
break;
}
if (contacts[i].phoneNumbers) {
totalCount++;
for (var j = 0; j < contacts[i].phoneNumbers.length; j++) {
text = text + '<tr><td>' + totalCount + "</td><td>" + contacts[i].displayName + "</td><td>" + contacts[i].phoneNumbers[j].value + "</td></tr>";
}
}
}
$('#contacts').html(text);
$('#count').html(contacts.length);
$('#status').html("");
$('#panel').show();
}
function onError(contactError) {
alert(contactError);
}
</script>
当你点击获取 10 个联系人按钮时,将获取并显示 10 个联系人:

将显示设备中联系人的总数和 10 个联系人,以及电话号码:

联系人姓名和电话号码出于隐私保护的原因被隐藏。您也可以在此处搜索联系人,如下所示:

当您输入搜索文本并点击确定按钮后,您将看到搜索结果:

使用文件 API
使用文件 API,我们可以在真实设备中处理文件和目录。此 API 使开发者能够轻松处理所有与文件相关的操作:
<div id="page">
<div class="content">
<div class="btn-group-vertical" role="group" aria-label="...">
<button class="btn-lg btn-success" onclick="readDirectory()">Get All Directories</button>
</div>
</div>
<div id="contents"></div>
</div>
以下是在设备文件系统中获取所有目录的代码。您可以扩展此代码以读取所有文件,以及读取、编辑和删除文件:
<script type="text/javascript">
function readDirectory() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, doDirectoryListing, null);
}
function doDirectoryListing(fileSystem) {
var dirReader = fileSystem.root.createReader();
dirReader.readEntries(gotDir, onError);
}
function gotDir(entries) {
var s = '<ul class="list-group">';
for (var i = 0, len = entries.length; i < len; i++) {
if (entries[i].isDirectory) {
s += '<li class="list-group-item">' + entries[i].fullPath + '</li>';
}
}
s += "</ul>";
$('#contents').html(s);
}
function onError(error) {
alert(error.code);
}
</script>
输出将如下所示:

使用全球化 API
全球化 API 可以帮助您根据用户的设备区域设置自定义内容,如数字、日期和货币,而不是以硬编码的格式显示。使用此 API,您可以真正创建一个真正的全球化应用程序。
让我们为将要尝试的每个选项创建菜单:
<div class="btn-group">
<button type="button" class="btn btn-success">Globalization Menu</button>
<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a id="prefLang" href="#">Language</a>
</li>
<li><a id="locale" href="#">Locale</a>
</li>
<li><a id="pattern" href="#">Date Pattern</a>
</li>
<li><a id="numPattern" href="#">Number Pattern</a>
</li>
<li><a id="currPattern" href="#">Currency Pattern</a>
</li>
<li><a id="dateStr" href="#">Date to String</a>
</li>
<li><a id="strDate" href="#">String to Date</a>
</li>
<li><a id="monNames" href="#">Month Names</a>
</li>
<li><a id="dst" href="#">DST?</a>
</li>
<li><a id="firstWeekDay" href="#">Week First Day</a>
</li>
</ul>
</div>
<hr/>
<div id="data"></div>
</div>
通过前面的代码,您将看到如下所示的截图;它有一个下拉菜单来选择要使用的选项:

现在,完整的脚本在此提供;我们将每个函数分配给了每个链接的点击事件:
<script type="text/javascript">
$("#prefLang").click(function() {
navigator.globalization.getPreferredLanguage(setLanguage, onError);
});
$("#locale").click(function() {
navigator.globalization.getLocaleName(setLocale, onError);
});
$("#dateStr").click(function() {
navigator.globalization.dateToString(new Date(), setDate, onError, {
formatLength: 'full',
selector: 'date and time'
});
});
$("#strDate").click(function() {
navigator.globalization.stringToDate('12/31/2015', setStringDate, onError, {
selector: 'date'
});
});
$("#pattern").click(function() {
navigator.globalization.getDatePattern(setPattern, onError, {
formatLength: 'short',
selector: 'date and time'
});
});
$("#monNames").click(function() {
navigator.globalization.getDateNames(setDateNames, onError, {
type: 'wide',
item: 'months'
});
});
$("#dst").click(function() {
navigator.globalization.isDayLightSavingsTime(new Date(), setDST, onError);
});
$("#firstWeekDay").click(function() {
navigator.globalization.getFirstDayOfWeek(setFDW, onError);
});
$("#numPattern").click(function() {
navigator.globalization.getNumberPattern(setNumPattern, onError, {
type: 'decimal'
});
});
$("#currPattern").click(function() {
navigator.globalization.getCurrencyPattern('USD', setCurrencyPattern, onError);
});
function setCurrencyPattern(pattern) {
$('#data').html('Currency Pattern: ' + pattern.pattern + '<br/>' +
'code: ' + pattern.code + '<br/>' +
'fraction: ' + pattern.fraction + '<br/>' +
'rounding: ' + pattern.rounding + '<br/>' +
'decimal: ' + pattern.decimal + '<br/>' +
'grouping: ' + pattern.grouping);
}
function setNumPattern(pattern) {
$('#data').html('Number Pattern : <br>' + pattern.pattern + '<br/>' +
'symbol: ' + pattern.symbol + '<br/>' +
'fraction: ' + pattern.fraction + '<br/>' +
'rounding: ' + pattern.rounding + '<br/>' +
'positive: ' + pattern.positive + '<br/>' +
'negative: ' + pattern.negative + '<br/>' +
'decimal: ' + pattern.decimal + '<br/>' +
'grouping: ' + pattern.grouping);
}
function setFDW(day) {
$('#data').html('Fist Day of Week : ' + day.value);
}
function setDST(date) {
$('#data').html('Day Light Savings : ' + date.dst);
}
function setDateNames(names) {
var str = '';
for (var i = 0; i < names.value.length; i++) {
str += names.value[i] + '<br/>';
}
$('#data').html('Month Names : <br/>' + str);
}
function setPattern(date) {
$('#data').html('Date Pattern : ' + date.pattern + ' ' + date.timezone);
}
function setDate(date) {
$('#data').html('Formatted Date : ' + date.value);
}
function setStringDate(date) {
$('#data').html('Date : ' + (date.month + 1) + '/' + date.day + '/' + date.year);
}
function setLocale(locale) {
$('#data').html('Locale Name : ' + locale.value);
}
function setLanguage(lang) {
$('#data').html('Preferred Language : ' + lang.value);
}
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}</script>
当您点击数字模式选项时,您可以看到基于手机区域设置的设备上的数字模式。也请尝试其他菜单选项。

使用 InApp 浏览器 API
InApp 浏览器是一个可以被应用控制的网页浏览器。您的应用可以在浏览器中打开网站并按需处理。
再次,我们将有一个按钮来启动 InApp 浏览器。我们创建了一个名为launchIAB的 JavaScript 函数来获取 URL 并打开它:
<button onclick="launchIAB('http://www.incredibleindia.org');" class="btn-lg btn-primary">Launch Browser</button>
输出将如下所示:

现在,我们将看到这里涉及的 JavaScript。我们处理了 InApp 浏览器的加载、开始和停止事件:
<script type="text/javascript"> var iab = null;
function loadStart(event) {
// Event object has event.type & event.url properties
alert('Loading started');
}
function loadStop(event) {
alert('Loading stopped');
}
function loadError(event) {
alert(event.type + ' - ' + event.message);
}
function onClose(event) {
alert('Browser Closed');
iab.removeEventListener('loadstart', loadStart);
iab.removeEventListener('loadstop', loadStop);
iab.removeEventListener('loaderror', loadError);
iab.removeEventListener('exit', onClose);
}
function launchIAB(url) {
iab = window.open(url, '_blank ', 'location = yes ');
iab.addEventListener('loadstart', loadStart);
iab.addEventListener('loadstop', loadStop);
iab.addEventListener('loaderror', loadError);
iab.addEventListener('exit', onClose);
}</script>
加载开始屏幕将如下所示:

使用通知 API
使用通知 API,我们可以使手机发出声音、震动、显示警报以及输入和确认对话框。因此,对于这五种操作中的每一种,我们都会有一个按钮,每个按钮都有一个触发事件。我们使用 Bootstrap 来设计这些按钮:
<div class="content">
<br/>
<div class="btn-group-vertical" role="group" aria-label="...">
<button onclick="beepNow();" class="btn-lg btn-primary">Beep 3 Times</button>
<button onclick="vibrateNow();" class="btn-lg btn-info">Vibrate 1 Sec</button>
<button onclick="alertNow();" class="btn-lg btn-success">Alert Notify</button>
<button onclick="confirmNow();" class="btn-lg btn-warning">Confirm Notify</button>
<button onclick="promptNow();" class="btn-lg btn-danger">Prompt Notify</button>
</div>
</div>
输出将如下所示:

以下代码非常直观。不要忘记注意警报、确认和提示对话框的语法:
function beepNow() {
navigator.notification.beep(3);
}
function vibrateNow() {
navigator.notification.vibrate(1000);
}
前面的两个函数将分别在您的设备上产生默认的蜂鸣声三次并振动一秒钟:
function alertNow() {
navigator.notification.alert(
'You got an alert now', // message
null, // callback
'Notifications API', // title
'Done' // buttonName
);
}
function confirmNow() {
navigator.notification.confirm(
'You like this app?', // message
onConfirm, // callback to invoke with index of button pressed
'App Feedback', // title
['Yes', 'No'] // buttonLabels
);
}
function onConfirm(buttonIndex) {
alert('Button Selected : ' + buttonIndex);
}
function promptNow() {
navigator.notification.prompt(
'Please enter your name', // message
handleAction, // callback to invoke
'Registration', // title
['Ok', 'Exit'], // buttonLabels
'Super Star Rajini' // defaultText
);
function handleAction(results) {
alert("You selected button " + results.buttonIndex + " with input '" + results.input1 + "'");
}
}
这就是如何显示警告窗口的:

确认框在以下屏幕截图中显示:

您可以使用提示对话框从用户那里获取输入:

使用启动画面 API
启动画面是在应用程序启动时看到的。尽管并非所有应用程序都有它,但它在一些著名的应用程序中可用,例如 Microsoft Office Mobile。当应用程序启动时,会出现一个全屏图像,并在几秒钟后自动关闭。这可以用来隐藏应用程序的后端加载。
我们将有一个带有点击事件的按钮来显示启动画面:
<button onclick="showScreen();">Show Splash Screen</button>
在 JavaScript 中,我们使用navigator.splashscreen.show()方法来显示启动画面,如下所示:
<script type="text/javascript">
function showScreen() {
navigator.splashscreen.show();
}
</script>
输出将如下所示:

当点击显示启动画面按钮时,您会在几秒钟后看到启动画面打开和关闭:

注意,还有一个hide()函数,我将留给你去尝试。
使用地理位置 API
使用地理位置 API,我们将获取位置坐标并使用谷歌地图在地图上绘制它们:
<div id="page">
<div class="header">
<a href="#menu"></a>
GeoLocation API
</div>
<div class="content">
<div id="geolocation"></div>
<br/>
<div id="googleMap" style="width:75%;height:75%;">Loading...</div>
</div>
</div>
id为geolocation的div元素将包含地理位置值。googleMap的div元素将包含谷歌地图:
<script type="text/javascript">
document.addEventListener("deviceready", onDeviceReady, false);
var watchID = null;
function onDeviceReady() {
// Throw an error if no update is received
var options = {
timeout: 50000
};
watchID = navigator.geolocation.watchPosition(onSuccess, onError, options);
}
function onSuccess(position) {
var element = document.getElementById('geolocation');
element.innerHTML = 'Latitude: ' + position.coords.latitude + '<br />' +
'Longitude: ' + position.coords.longitude + '<br />' +
'Altitude: ' + position.coords.altitude + '<br />' +
'Accuracy: ' + position.coords.accuracy + '<br />' +
'Altitude Accuracy: ' + position.coords.altitudeAccuracy + '<br />' +
'Heading: ' + position.coords.heading + '<br />' +
'Speed: ' + position.coords.speed + '<br />' +
'Timestamp: ' + position.timestamp + '<br />';
var myLatlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
var mapOptions = {
zoom: 4,
center: myLatlng
}
var map = new google.maps.Map(document.getElementById('googleMap'), mapOptions);
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: 'Hello World!'
});
}
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
</script>
输出将如下所示:

构建应用程序
由于我们已经完成了应用程序的开发,我们现在可以在真实设备上构建和尝试应用程序。您可以使用本地构建或使用在线 PhoneGap 服务进行构建。
要使用 Cordova 进行本地构建,请使用以下命令:
C:\PhoneGap> phonegap build
构建完成后,应用程序将为您在创建项目时添加的所有平台创建。如果您添加了 iOS 平台,您可以在platforms\android\ant-build\目录中看到 Android 应用程序(.apk),在platforms/ios/build/device目录中看到 iOS 应用程序(.ipa)。
您可以直接将 Android 的.apk文件安装到设备上。然而,对于 iOS,您需要进行配置。详细信息请参阅附录 B,发布您的应用。恭喜您使用 PhoneGap 创建了一个完整的应用程序。现在,在您的真实设备上安装应用程序并享受使用您第一个完整应用程序的感觉。
改进空间
我们看到的应用程序非常基础,有很多改进的空间。以下是对我们刚刚完成的应用程序可以进行的几件事情:
-
使用模板引擎,如 HandleBarJS
-
使用 Ionic 框架
-
使用 RequireJS 动态加载所需的库
-
处理基于硬件加速和 GPU 的动画
-
压缩源文件
摘要
我们已经学会了如何使用 PhoneGap 及其 API 创建应用程序。这只是一个开始,在实际应用程序开发中还有许多更多的挑战。我们希望这篇教程能成为你在混合应用程序开发技能上的良好基础。PhoneGap 开发不仅限于这些 API,还有其他几个有用的插件可供使用。祝你在 PhoneGap 开发中好运。
附录 A. JavaScript 快速参考表
这是一个常用 JavaScript 方法和属性的迷你参考表。对于完整列表和文档,请参阅可在 developer.mozilla.org/en/docs/JavaScript 找到的 Mozilla 开发者网络网站。
getElementById() 方法
getElementById() 方法通过其 ID 返回元素的引用:
var pic =document.getElementById("profilePic");
getElementsByTagName() 方法
getElementsByTagName() 方法返回具有给定标签名的元素集合。搜索包括根节点在内的完整文档:
var allImages =document.getElementsByTagName("img");
getElementsByName() 方法
getElementsByName() 方法通过其名称返回元素的引用:
var names =document.getElementsByName("name");
alert 方法
alert() 方法显示一个包含可选指定内容和 OK 按钮的警告对话框:
alert("This is a sample alert");
toString() 方法
toString() 方法返回表示函数源代码的字符串:
var countries = ["U.S.A", "U.K", "India", "France"];
alert(countries.toString());
parseInt() 方法
parseInt() 方法接受一个字符串并返回其数值。它可以用来将字符串类型转换为数值类型。对于浮点值,可以使用 parseFloat():
var stringOne = "1";
var intOne = parseInt(stringOne);
getDate() 方法
getDate() 方法根据本地时间返回指定日期的月份中的天数:
var day = new Date();
alert(day.getDate())
onclick 事件
当在单个元素上按下并释放指向设备按钮(通常是鼠标按钮)时,会触发 onclick 事件:
<button onclick="callFunction()">Click</button>
<script>
function callFunction() {
alert("Button Clicked");
}
</script>
ondblclick 事件
当在单个元素上双击指向设备按钮(通常是鼠标按钮)时,会触发 ondblclick 事件:
<button ondblclick="callFunction()">Click</button>
<script>
function callFunction() {
alert("Button Double Clicked");
}
</script>
window.location 对象
window.location 对象返回一个包含有关当前文档位置信息的 Location 对象。也可以不使用 window 前缀。
window.location.href 对象返回当前页面的 URL:
alert(window.location.href);
使用 jQuery 的选择器
如果你在项目中使用 jQuery,你必须知道使用 jQuery 选择器是多么容易。它提供了方便的方法来选择元素,而不是使用多个 JavaScript 函数:
-
$("p"):这通过标签名选择元素 -
$("#myID"):这根据id属性选择元素 -
$(".myClass"):这根据class属性选择元素
更多关于高级 jQuery 选择器的详细信息,请访问 www.w3schools.com/jquery/jquery_selectors.asp。
附录 B. 发布您的应用
PhoneGap 最好的特性之一是它允许您使用相同的代码库创建跨平台应用。这意味着您可以重用大部分代码,但您仍然需要为每个目标平台构建应用。您可以通过为想要支持的每个平台配置开发环境来实现这一点,或者您可以使用在线服务,如 PhoneGap Build 服务(build.phonegap.com)或 Icenium(www.icenium.com/)。两者都是基于云的服务;主要区别在于 PhoneGap Build 服务支持所有平台,而 Icenium 只支持 Android 和 iOS 平台,但确实提供了一个非常不错的在线编辑器。一旦构建完成,您必须为每个目标平台遵循不同的工作流程。
注意
此外,如果您在使用 PhoneGap 进行跨平台开发时,在开发阶段和准备构建时为每个平台使用测试设备总是一个好习惯。
在 Google Play 上发布
Google Play,之前称为Android Market,是 Android 应用的数字应用分发平台。为了在 Google Play 上发布应用,您必须使用您的 Google 账户登录并遵循play.google.com/apps/publish中概述的步骤。在注册时,您需要在添加开发者详细信息(即姓名、电话号码、电子邮件等)之前支付一次性的费用 25 美元。一旦完成注册流程,您就可以将您的应用添加到开发者控制台。
对于每个应用,您可以定义您想要分发的国家,定义您想要针对的运营商,指定它是否是免费应用(如果您想要销售应用,您必须提供一个有效的 Google Wallet 商户账户),设置 alpha 和 beta 测试组以及分阶段推出等。为了吸引新用户下载和安装应用,提供详细的信息、图标、截图等非常重要。

在support.google.com/googleplay/androiddeveloper/answer/1078870可获得的 Android 在线指南是了解所需图形资源的地方。
此外,您的应用大小必须小于 50 MB,并使用developer.android.com/tools/publishing/app-signing.html中描述的 keystore 工具进行签名。对于需要超过 50 MB 的应用,请参阅developer.android.com/google/play/expansion-files.html中的 APK 扩展详情。
注意
上传后,您的应用将在 60 分钟或更短的时间内可在 Google Play 市场提供。
在 Blackberry World 上发布
BlackBerry World(之前称为BlackBerry App World)是 BlackBerry 提供的一个应用程序分发服务,允许用户浏览、下载和更新第三方应用程序。为了在 Blackberry World 市场上发布应用程序,您需要拥有一个 BlackBerry 开发者账户(您可以在developer.blackberry.com/免费创建)。您还需要申请成为供应商,并提供您的 BlackBerry ID 信息(完成申请需要 PayPal 账户),您可以在appworld.blackberry.com/isvportal/home.do进行操作。

在您作为供应商提交应用程序后,您将收到一封确认电子邮件,要求您提供官方文件以验证您的公司信息,或者如果您作为个人向供应商门户申请,则需要提供官方政府发行的身份证件(正反面)的复印件。
注意
验证过程可能需要长达两天,因此在计划特定日期发布时,您必须仔细考虑这一点。
当您的账户得到确认后,您可以添加一个应用程序(即产品),提供应用程序的名称、描述、标志、截图以及关于应用程序的任何其他所需详细信息。有关要求的详细信息,请参阅 BlackBerry World 应用程序商店中的在线信息,网址为developer.blackberry.com/devzone/blackberryworld/preparing_your_app_for_blackberry_world.html。
将应用程序发布到 Blackberry World 还涉及一个签名过程。此过程假设您已经下载并安装了位于developer.blackberry.com/html5/downloads/的 BlackBerry 10 WebWorks SDK。在您能够对 BlackBerry 10 应用程序进行签名之前,您必须完成位于www.blackberry.com/SignedKeys的网页表单。当您的应用程序被接受时,您将通过电子邮件收到两个.csj注册文件。每个文件都附有关于文件用途的信息,分别用于生成调试令牌和为市场签名应用程序。为了注册到 RIM 签名权威机构,您必须从您的命令行工具运行位于 BlackBerry 10 WebWorks SDK 安装文件夹中的\dependencies\tools\bin文件夹中的.bar文件。此工具创建以下用于数字签名的文件:author.p12、barsigner.csk和barsigner.db。
在应用命名方面要非常小心;以品牌/公司/产品开头的应用名称意味着存在关联,并且它是一个授权或官方应用程序。如果您将您的应用命名为 YouTube Player,它肯定会遭到拒绝(相反,您可以将其命名为 Player for YouTube)。
在苹果应用商店发布
苹果应用商店是苹果公司维护的 iOS 应用数字分发平台。用户可以在应用商店中浏览并直接将应用程序安装到 iOS 设备上。尽管苹果希望应用商店成为全球产品,但实际上其市场受到国家边界的限制。换句话说,可能存在与应用商店数量一样多的不同国家应用商店。要通过苹果应用商店发布应用程序,您需要拥有苹果开发者账户(developer.apple.com/programs/register)并成为 iOS 开发者计划(developer.apple.com/programs/ios/;年费为 99 美元)的成员。第一步是在开发者门户中注册应用 ID,然后您必须创建开发和分发证书。
然后,您需要设置分发证书。为此,您首先需要从您的计算机生成证书请求,并将其上传到开发者门户。在 Mac 上,您应该通过打开实用工具中可用的钥匙串访问应用程序,然后转到钥匙串访问 | 证书助手 | 从证书颁发机构请求证书来完成此操作。输入您的电子邮件地址和姓名,并选择请求保存到磁盘以将CertificateSigningRequest.certSigningRequest文件保存到您的桌面。转到开发者门户,上传证书请求,并完成生成分发证书所需的步骤。
当证书准备就绪时,您可以通过选择要提交的应用和要使用的证书来创建一个新的分发配置文件。下载文件,然后在 Xcode 中选择窗口 | 组织者,点击设备,选择配置文件,并将具有.mobileprovision扩展名的配置文件拖到组织者中。接下来,打开构建设置面板并设置代码签名身份;这样,当您创建存档时,应用程序就会被代码签名,您可以使用 Xcode 完成发布程序。在提交应用程序时,您还必须提供描述、多个截图、图标和其他信息。有关详细信息,请参阅在线文档developer.apple.com/library/ios/#documentation/IDEs/Conceptual/AppDistributionGuide/Introduction/Introduction.html。
注意
验证过程因当前审查中的提交数量而异,但通常需要超过两天。您可以在reviewtimes.shinydevelopment.com/找到 App Store 的预估审查时间。
将您应用程序的简单版本作为首次发布可以帮助稍微加快审批过程。初始应用程序审批过程耗时最长;一旦批准,未来的更新就更容易完成。因此,将高级功能留到应用程序的后续版本中。
访问www.raywenderlich.com/8003/how-to-submit-your-app-to-apple-from-no-account-to-app-store-part-1,获取将应用程序发布到 Apple App Store 的详细教程。
在 Windows Phone Store 上发布
Windows Phone Store(以前称为Windows Phone Marketplace)是一个数字发行平台,允许用户浏览和安装由第三方开发的应用程序。用户界面以非常“Metro UI”的方式呈现,使用全景视图,用户可以浏览类别和标题,查看特色项目,并通过评分、评论、截图和定价信息获取详细信息。
要在 Windows Phone Dev Center 提交和管理应用程序,您首先必须使用 Microsoft 账户(以前称为 Windows Live ID)注册并成为会员。在注册时,您将被要求支付每年 99 美元的开发中心订阅费以及任何适用的税费。作为交换,您将能够向 Windows Phone Store 提交无限数量的付费应用程序(您也可以提交最多 100 个免费应用程序)。发布过程简单直接:您必须提供应用程序详细信息(名称、描述、截图等),然后提交与 Visual Studio 打包的 XAP 文件。在您可以将应用程序上传到商店之前,您必须打包和准备应用程序;打包过程从您基于模板创建 Windows Store 项目或项目时开始(有关打包过程的完整概述,请参阅msdn.microsoft.com/en-us/library/windows/apps/br230260.aspx上的在线文档)。
注意
验证过程相当快,但如果您作为公司注册,您需要几天时间来完成注册过程。
为了缩短审查时间,您可以使用 Windows Phone SDK 中提供的Windows 应用程序认证工具包(WACK)在本地筛选您的应用程序。它通过在提交到 Windows Store 之前,为您提供一种方式来本地筛选应用程序中的问题,从而减少了审批周期。
摘要
在本附录中,你学习了如何在不同应用商店发布你的应用以及常见问题。很明显,苹果应用商店是需要最复杂工作流程的,但它也吸引了最多的开发者。你可以通过自己或通过如 PhoneGap Build 提供的服务支持,轻松管理所有不同市场的发布。
附录 C. 相关插件资源
以下是一些与 PhoneGap 兼容的相关插件列表:
-
社交分享: 可在
www.github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin获取 -
Facebook Connect: 可在
github.com/Wizcorp/phonegap-facebook-plugin获取 -
推送通知: 可在
github.com/phonegap-build/PushPlugin获取 -
条形码扫描器: 可在
github.com/wildabeast/BarcodeScanner获取 -
操作表插件: 可在
github.com/EddyVerbruggen/cordova-plugin-actionsheet获取 -
蓝牙串行通信: 可在
github.com/don/BluetoothSerial获取 -
日历插件: 可在
github.com/EddyVerbruggen/Calendar-PhoneGap-Plugin获取 -
徽章: 可在
github.com/katzer/cordova-plugin-badge获取 -
Toast(无按钮弹出窗口): 可在
github.com/EddyVerbruggen/Toast-PhoneGap-Plugin获取 -
图片选择器: 可在
github.com/wymsee/cordova-imagePicker获取 -
图片缩放器: 可在
github.com/julianohaze/PhoneGap-Image-Resizer获取 -
oAuth: 可在
github.com/oauth-io/oauth-phonegap获取 -
Android InApp Billing: 可在
github.com/poiuytrez/AndroidInAppBilling获取 -
压缩文件处理: 可在
github.com/Adobe-Marketing-Cloud/cordova-zip-plugin获取 -
ngCordova 插件(适用于 Ionic 平台): 可在
www.ngcordova.com/获取
摘要
我们提供了一些可能对真实完整应用有用的插件。这不是插件的全列表,因为在这个附录中不可能提供完整的列表。您可以在 build.phonegap.com/plugins 或 www.plugreg.com 上搜索更多插件和描述。
附录 D. PhoneGap 工具
PhoneGap 开发者应用
在前面的章节中,我们看到了几种运行/调试我们的 PhoneGap 项目的办法。最简单的方法是使用 PhoneGap 开发者应用。这是一个适用于 Windows、Android 和 iOS 平台的移动应用。一旦安装了移动应用,你就可以轻松地测试你的项目,而无需实际构建和安装连接到同一无线网络上的物理设备上的应用。
一旦你的项目准备就绪,你可以使用 PhoneGap 命令行工具的 serve 命令来启动一个监听 IP 地址的本地服务器。

现在,在你的设备上打开已安装的应用,输入命令行上列出的 IP 地址,然后点击 连接 按钮。现在,你的桌面计算机和移动设备将通过无线连接配对,你可以在设备上看到你的应用。对代码所做的任何更改都将立即反映在设备上。那些在浏览器上不工作的设备 API,即使没有安装应用,也会在你的设备上工作。
所有平台的更多详细信息及下载链接可以在 app.phonegap.com 找到。
PhoneGap 桌面应用
为了使我们的生活更加便捷,我们现在有了桌面应用,你现在可以自由使用它,而不是使用 PhoneGap 命令行界面。你可以创建一个新的 PhoneGap 项目,导入现有的项目,并单击一下即可启动本地服务器。它仍然不是一个成熟的产品,每个月都有许多新功能被添加。

你可以在 github.com/phonegap/phonegap-app-desktop/releases 下载适用于你的 Windows 桌面或 Mac OS 的应用。
摘要
我们已经看到了一些最近推出的与 PhoneGap 相关的工具,这些工具可以帮助开发者使混合移动应用开发变得更加容易。开发者应用和桌面应用都将帮助你轻松地调试和测试你的应用。





浙公网安备 33010602011771号