安卓-Flash-开发秘籍-全-

安卓 Flash 开发秘籍(全)

原文:zh.annas-archive.org/md5/3A6CCF6F6AAB969F5B96A3C7E7AEF15A

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

随着移动 Android 操作系统的持续爆发式增长,以及智能手机和平板电脑市场中 Android 设备的普及,现在是使用 Flash 平台探索 Android 开发世界的最佳时机。Adobe 最近发布的数据显示,到 2011 年底,预计将有超过 2 亿部智能手机和平板电脑支持 Adobe AIR 应用程序。对于 2011 年,公司预计全球将有超过 1.32 亿台设备支持移动 Flash 播放器。本书提供了各种基本食谱,探索在使用这些 Flash 平台运行时,移动 Android 开发者的常见需求。

许多现有的 Flash 应用程序开发人员对为 Android 设备构建移动应用程序的前景感到兴奋,但不知从何入手?使用本书作为指南,扩展您进入移动应用程序开发的领域。在可能的情况下,本书中的食谱均使用纯 ActionScript 3 编写,让读者可以使用他们选择的工具完成每个示例。在某些情况下,我们展示了在处理特定布局和结构需求时,移动 Flex 框架的强大和灵活性。通过书中的分步示例,快速启动您的移动 Android 开发经验。

《Android Flash 开发手册》将通过大量专为 Android 设备应用程序开发设计的移动特定示例,展示其直接而实用的特点。书中包含开始所需的一切内容,以及在使用 Flash、Flex 和 AIR 开发移动 Android 应用程序时进一步提升经验的建议。

本书涵盖的主题包括开发环境配置、移动项目的创建与转换、触摸和手势的使用、在 3D 空间中响应对位置和设备移动的变化、图像、视频和音频的捕获、生成和操作、应用程序布局与结构、接入本地进程和硬件、文件系统的操作以及本地应用程序数据库的管理。本书还将介绍诸如 Android 特定设备权限、应用程序优化技术,以及在移动 Android 平台上可用的打包和分发选项等事项。

本书内容

第一章,准备使用 Android:开发环境与项目设置,展示了可以用于开发移动 Android 的 Flash 内容的多种开发环境和工具的配置。

第二章,交互体验:多点触控、手势和其他输入,向读者介绍了可以在 Flash 平台运行时中使用的一系列独特的触摸和手势交互。

第三章, 空间移动:加速计和地理定位传感器, 使你的应用程序能够精确地定位用户的地理位置,并通过板载加速计确定设备在本地的小幅度移动和倾斜。

第四章, 视觉和音频输入:相机和麦克风接入, 讨论了如何通过基于 Flash 的捕获方法和使用本地相机应用程序,从集成设备硬件捕获静态图像、视频和音频。

第五章, 富媒体展示:处理图像、视频和音频, 查看了 Flash 平台上可用的各种媒体展示机制,包括渐进式和流式视频播放、使用 Pixel Bender 着色器,甚至音频生成。

第六章, 结构适应:处理设备布局和缩放, 讨论了我们可以使用各种方法来获取有关设备显示的详细信息,并在通过移动 Flex 框架进行结构化布局时,使用这些数据来调整视觉元素的大小和位置。

第七章, 本地交互:舞台 WebView 和 URI 处理器, 展示了利用本地应用程序(如网页浏览器、电子邮件、短信、电话和地图)作为 Flash 基础体验扩展的方法。

第八章, 丰富访问:文件系统和本地数据库, 为读者提供了访问、打开和写入设备存储上的文件流,创建和管理本地 SQLite 数据库,以及在应用程序中断时保存应用程序状态的必要步骤。

第九章, 宣言保障:安全与 Android 权限, 展示了各种 Android Manifest 权限,并提供了市场筛选、加密数据库支持和其他安全相关技术的示例。

第十章, 避免问题:调试和资源考虑, 探讨了开发者可以通过利用设备 GPU、负责任地处理用户交互和内存管理技术来提高应用程序效率的方法。

第十一章,最后考虑:应用程序编译和发布,为读者提供了关于项目准备、代码签名、发布编译和通过全球 Android Market 进行分发的建议。

你需要为这本书准备的内容

要使用本书中包含的食谱,你需要访问使用 Flash Platform 开发 Android 应用程序的软件。我们推荐使用 Adobe Flash Builder 4.5、Adobe Flash Professional CS5.5 或 PowerFlasher FDT 4.2 及更高版本。这些集成开发环境之所以首选,是因为它们对移动 Android 工作流程有特定的支持,但你可以实际上使用任何你喜欢的应用程序编写代码,这些代码将被编译为 AIR for Android 并部署到移动设备上。

然而,你还需要访问以下内容(如果不是使用这些特定的 IDE):

  • Adobe AIR SDK—用于将你的 Flash 应用程序编译成 Android 的.APK 文件

  • Flex 4.5 SDK—如果你想利用移动 Flex 框架

Adobe AIR SDK 包含在 Flash Professional CS5.5 和 Flash Builder 4.5 中。Flex 4.5 SDK 包含在 Flash Builder 4.5 中。如果使用其他软件开发基于 Flash 的 Android 应用程序,可以从 Adobe 开源网站免费下载这些 SDK。

你还需要确保能够访问运行 Android 2.2 或更高版本的设备,并安装了 AIR for Android 2.5 或更高版本,以便演示食谱和测试你自己的应用程序。

本书适合的读者群体

这本书包含了各种主题的食谱,从非常简单的到更高级的。如果你是一位经验丰富的 Flash 开发者,这本书将帮助你快速了解在 Android 上可以实现的功能。对于刚接触 Flash 的新手,欢迎进入视觉丰富、快速的移动 Android 设备应用程序开发世界!如果你对 Android 上的 Flash 开发有任何兴趣,这本书会满足你的需求。

约定

在本书中,你会发现多种文本样式,用于区分不同类型的信息。以下是一些样式示例,以及它们含义的解释。

文中的代码字会如下所示:"创建一个名为recipe1.py的新文件,以放置此食谱的所有代码。"

代码块如下设置:

streamClient = new Object();
streamClient.onBWDone = onTextData;
streamClient.onTextData = onTextData;
streamClient.onMetaData = onMetaData;
streamClient.onCuePoint = onCuePoint;

新术语重要词汇以粗体显示。你在屏幕上看到的词,例如菜单或对话框中的,会在文本中以这样的形式出现:"有许多IDE(集成开发环境)可供选择,用于为 Android 设备开发 Flash 平台项目"。

注意

警告或重要提示会以这样的方框显示。

小贴士

技巧和窍门会以这样的形式出现。

读者反馈

我们始终欢迎读者的反馈。告诉我们你对这本书的看法——你喜欢或可能不喜欢的内容。读者的反馈对我们来说很重要,帮助我们开发出你真正能够充分利用的标题。

要给我们发送一般反馈,只需发送电子邮件至<feedback@packtpub.com>,并在邮件的主题中提及书名。

如果有一本书是你需要的,并且希望看到我们出版,请通过www.packtpub.com上的建议书名表单给我们发送信息,或者发送电子邮件至<suggest@packtpub.com>

如果有一个你擅长的主题,并且你对于写作或为书籍做出贡献感兴趣,请查看我们在www.packtpub.com/authors的作者指南。

客户支持

既然你现在是我们 Packt 图书的骄傲拥有者,我们有许多方法可以帮助你最大限度地利用你的购买。

下载示例代码

你可以从你在www.PacktPub.com的账户下载你购买的所有 Packt 图书的示例代码文件。如果你在其他地方购买了这本书,可以访问www.PacktPub.com/support注册,我们会直接将文件通过电子邮件发送给你。

错误更正

尽管我们已经尽力确保内容的准确性,但错误仍然可能发生。如果你在我们的书中发现了一个错误——可能是文本或代码中的错误——如果你能向我们报告,我们将不胜感激。这样做,你可以让其他读者避免沮丧,并帮助我们改进本书的后续版本。如果你发现任何错误,请通过访问www.packtpub.com/support,选择你的书籍,点击错误更正提交表单链接,并输入错误详情来报告。一旦你的错误更正得到验证,你的提交将被接受,并且错误更正将在我们网站的相应位置上传,或者添加到该标题下的现有错误更正列表中。任何现有的错误更正都可以通过在www.packtpub.com/support选择你的标题来查看。

盗版

互联网上版权材料的盗版是一个跨所有媒体持续存在的问题。在 Packt,我们非常重视保护我们的版权和许可。如果你在互联网上以任何形式遇到我们作品非法副本,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。

如果你发现了疑似盗版材料,请通过<copyright@packtpub.com>联系我们,并提供该材料的链接。

我们感谢你帮助保护我们的作者,以及我们为你提供有价值内容的能力。

问题咨询

如果你对书籍的任何方面有问题,可以联系<questions@packtpub.com>,我们将尽力解决。

第一章:准备使用 Android:开发环境和项目设置

本章将涵盖以下内容:

  • 使用 Flash Professional CS5.5 开发 Android 应用程序

  • 使用 Flash Professional CS5.5 针对 AIR for Android 进行开发

  • 使用 Flash Builder 4.5 开发 Android 应用程序

  • 使 Flash Builder 4 或 Flex Builder 能够访问 Flex Mobile SDK

  • 使用 Flash Builder 4 及以下版本开发 Android 应用程序

  • 使用 Powerflasher FDT 4.2 开发 Android 应用程序

  • 使 Powerflasher FDT 4.1 能够访问 Flex Mobile SDK

  • 使用 Powerflasher FDT 4.1 及以下版本开发 Android 应用程序

  • 将标准 Flex 项目转换为 Flex Mobile 项目

  • 在 Windows 上配置 AIR SDK 以打包 Android 应用程序

  • 在 Linux 或 Mac OS 上配置 AIR SDK 以打包 Android 应用程序

引言

有许多IDE(集成开发环境)可用于为 Android 设备开发 Flash 平台项目。我们将关注一些最受欢迎的:Adobe Flash Professional、Adobe Flash Builder 和 Powerflasher FDT。本章将包括针对每个 IDE 启动新 Android 项目的食谱,以及关于工作流程和工具集的最大化利用。您将学习如何配置每个环境以开发 Android 操作系统。

Flash Builder 和 FDT 以及 Flex 框架为 Android 开发提供了最多的支持,因为有一个简化的工作流程、控件集合和容器,特别是使用 Adobe AIR for Android 作为开发平台开发移动 Android 项目时。

Flash Professional 提供了一些工作流程工具,但主要的好处在于对环境的潜在熟悉,以及生成不依赖于 Flex 框架的项目。这个 IDE 由于其开放性,常用于游戏开发。

对于纯粹主义者或替代 IDE 的用户,也可以使用免费的 AIR SDK 工具通过命令行界面生成 Android 应用程序。

使用 Flash Professional CS5.5 开发 Android 应用程序(注意:重复内容不翻译)

Flash Professional 是构建比基于 Flex 的应用程序更轻量级的 Android 应用程序的一个不错的选择。与包含在 Flash Builder 等 IDE 中的流程相比,Flash Professional 的流程并不那么健壮,但根据开发的应用程序,它可能是更合适的选择。

Flash Professional CS5.5 已经内置了针对 Android 开发的所有必要工具!

如何操作…

在 Flash Professional CS5.5 中设置一个 AIR for Android 项目非常直接:

  1. 我们首先会在 Flash Professional 欢迎屏幕的创建新项目部分选择AIR for Android来创建一个新项目:如何操作…

  2. 然后,我们可以通过查看属性面板下的文档属性来确认我们正在针对 Android 的 AIR:如何操作…

  3. 我们还可以通过选择AIR for Android作为播放器选项,修改现有的 Flash 文档以针对 Android。

  4. 现在,只需像平常一样构建你的 Flash 项目即可。Adobe 使使用 Flash Professional CS5.5 针对 Android 的过程变得非常轻松。

工作原理…

使用 Flash Professional CS5.5,我们拥有比以往任何时候都多的编译器选项。按照前一部分所述步骤操作,可以确保通过向发布设置中添加一些针对 Android 的特定编译器选项,使你的项目能够以桌面 Flash Player 或桌面 AIR 为目标,转而针对 Android 的 AIR。

还有更多…

如果针对 Android 的移动 Flash Player 开发,我们将不需要为 AIR 运行时配置任何内容。要针对 Flash Player,我们只需牢记移动 Android 设备固有的限制和差异。

使用 Flash Professional CS5.5 针对 Android 的 AIR

Flash Professional 是构建比基于 Flex 的对应产品更轻量级 Android 应用程序的一个不错的选择。与包含在像 Flash Builder 这样的 IDE 中的工作流程相比,Flash Professional 的情况并不那么健壮,但根据开发中的应用程序,它可能是更合适的选择。

如何操作…

使用 Flash Professional 针对 Android 的 AIR 有两种方法:

  1. 首先,在 Flash Professional 欢迎屏幕的从模板创建部分选择AIR for Android创建一个新项目:如何操作…

  2. 这将提供几个针对AIR for Android的模板供选择。为你设备选择合适的模板:如何操作…

  3. 或者,创建一个新的 ActionScript 3.0 项目,并通过转到文件 | 发布设置打开你的发布设置。

  4. 这将打开一个对话框,允许你选择目标平台。在这种情况下,我们要选择AIR Android作为适当的播放器如何操作…

  5. 现在,你将能够调整针对 Android 的应用程序安装程序设置,并将项目编译成.apk文件。

工作原理…

使用 Flash Professional 的最新版本,我们拥有比以往任何时候都多的编译器选项。按照上述步骤操作,可以确保通过向发布设置中添加一些针对 Android 的特定编译器选项,使你的项目能够以桌面 Flash Player 或桌面 AIR 为目标,转而针对 Android 的 AIR。

还有更多…

如果针对 Android 的移动 Flash Player 开发,我们将不需要为 AIR 运行时配置任何内容。要针对 Flash Player,我们只需牢记移动 Android 设备固有的限制和差异。

另请参阅…

若要了解更多关于使用 Flash Professional 编译针对 Android 应用程序的 AIR 的信息,你将需要参考第十一章,最终考虑:应用程序编译与分发

使用 Flash Builder 4.5 开发安卓应用程序

Flash Builder 4.5 已经配备了开始使用 ActionScript 或移动 Flex 框架开发移动应用程序所需的一切。对于那些不熟悉 ActionScript 和 Flex 之间区别的人来说,基本上,Flex 框架提供了一套预配置的组件、布局和数据控制,用于构建 Flash 应用程序,而单独使用 ActionScript 时,则必须从头开始编写所有内容。Flex 4.5 包括移动特性,如针对设备运行优化过的组件皮肤、新的ViewNavigator应用程序类型,该类型专为移动体验量身定制,并包括对移动优化组件集的触摸和手势支持。

如何操作…

作为正常的 ActionScript 项目或 Flex 项目,我们必须明确创建一个 ActionScript 移动项目或 Flex 移动项目:

  1. 在 Flash Builder 的包资源管理器中,右键点击某个空白区域并选择新建 | Flex 移动项目新建 | ActionScript 移动项目如何操作…

  2. 然后,我们将为移动项目命名,并选择 Flash Builder 应在本地机器上存储项目文件的位置。

  3. 下一步允许我们选择目标平台,在本例中是谷歌安卓,并定义使用哪个应用程序模板(如果你正在使用移动 Flex 框架)。我们还可以通过初始视图标题输入设置默认的View名称。

  4. 此外,我们还将选择应用程序是否根据设备倾斜来自动重新定位,通过自动重新定位选项。我们可以选择通过选中全屏复选框来以全屏显示应用程序。

  5. 在此屏幕上要做的最后一个选择是,通过选择为不同屏幕密度自动缩放应用程序复选框并选择适当的应用程序 DPI 设置,来确定我们是否希望移动组件中使用密度感知皮肤。如何操作…

  6. 项目设置的其他部分实际上与 Flash Builder 中的任何其他项目都一样。

它是如何工作的…

在 Flash Builder 中设置新项目时的选择决定了哪些库会被导入并在应用程序中使用。定义一个移动应用程序不仅会包括针对移动设备的具体组件皮肤,还会限制我们使用不适合此类用途的组件。我们还可以完全访问移动特定的应用程序结构,如移动ViewNavigatorActionBarTabBar。这些对移动 Flex 框架的补充可以大大加快有状态移动 Android 应用程序的开发,因为它们处理的是应用程序结构、导航控制和布局。

另请参阅…

你实际上可以使用之前版本的 Flash Builder 来编译 Android 应用程序的 AIR。查看下一个指南,使 Flash Builder 4 或 Flex Builder 能够访问 Flex Mobile SDKs,以获取这方面的示例。

使 Flash Builder 4 或 Flex Builder 能够访问 Flex Mobile SDKs

你不一定需要最新版本的 Flash Builder 来编写 Android 应用程序。本指南将展示如何将最新的 Flex SDK 集成到较旧版本的 Flash Builder(甚至是 Flex Builder)中,以利用移动框架的改进。

注意

尽管我们将能够使用新的组件集和为 Android 简化的结构,但许多工作流增强,如支持新的移动应用程序视图结构、优化具有触摸和手势支持的组件皮肤以及在新版 Flash Builder 中找到的其他便利功能,将无法使用,我们将不得不使用 AIR SDK 和命令行工具编译应用程序以供分发。

如何操作…

以下步骤用于配置旧版本的 Flash Builder 以进行 Android 开发:

  1. 访问 Adobe 开源网站 opensource.adobe.com/,找到最新的 Flex SDK 构建版本。

  2. 下载最新 Adobe Flex SDK 的ZIP文件并将其解压到硬盘上你能够记住的位置,例如C:\SDKs\Flex

  3. 启动 Flash Builder,前往窗口 | 首选项

  4. 向下滚动至Flash Builder菜单项,选择Installed Flex SDKs。你现在将看到 Flash Builder 中当前可用的每个 SDK 列表:如何操作…

  5. 点击标记为Add的按钮,浏览到你最近下载的 Flex SDK 的位置。

  6. 为对话框提供一个有意义的名称并点击OK。例如,Flex 4.5。如果我们想要非常具体,可以始终使用完整的构建名称,如:Flex 4.5.0.16076如何操作…

  7. 现在Flex 4.5 SDK 可以在你的应用程序中使用。要在项目中使用它,只需在创建新项目或在现有项目中修改Flex Compiler属性时选择此 SDK 即可。如何操作…

工作原理…

在 Flash Builder 中使用较新的 Flex SDK 版本,使我们能够访问移动主题选项和其他特定 API,这些 API 在之前的 SDK 版本中是不可用的。这也会将移动类暴露给代码提示和其他 IDE 工作流构建。

还有更多内容...

如果更改项目中使用的 Flex SDK 版本,我们可能会因为框架从版本到版本之间的变化而收到许多警告或错误。只需通过项目文件,并纠正问题面板中出现的每个警告或错误,以解决任何问题。

如果开发针对 Android 上 Flash Player 的项目,你只需要注意设备和操作系统的限制。

另请参阅...

需要特别注意的是,Flash Builder 4.5 之前的版本将不包括编译项目到.APK(Android 应用程序文件扩展名)的能力,你需要使用免费提供的 AIR SDK 来编译你的项目。有关如何执行此操作的信息,请参见第十一章。

还值得一提,虽然你可以使用较旧的 Flash Builder 版本为 Android 开发应用程序,但你将无法获得较新版本提供的许多好处,例如代码补全。

使用 Flash Builder 4 及以下版本开发 Android 应用程序

在 Flash Builder 4 中开发移动 Android 应用程序,我们需要配置 Flash Builder 以访问移动 Flex SDK。如果你还没有以这种方式配置 Flash Builder 或 Flex Builder,请参阅之前的菜谱。

如何操作...

在 Flash Builder 4.5 之前的版本中并没有内置特定的移动工作流程或工具。通过采取以下步骤,我们可以确保项目将是移动兼容的:

  1. 在 Flash Builder 中,右键点击包资源管理器面板并选择新建 | Flex 项目。或者,我们可以选择ActionScript 项目,但这不会包括任何移动端的好处,因为实际的 Flex SDK 组件将不会被使用。然而,值得注意的是,由于 ActionScript 项目不依赖于如此重的框架,它们通常会比 Flex 项目表现得更好。如何操作...

  2. 将会出现新建 Flex 项目对话框,在对话框中你需要提供一个项目名称,并选择是创建面向Web还是Desktop的项目。如果这个项目将编译为 AIR for Android,我们需要确保选择Desktop,因为这种应用程序类型将针对 Adobe AIR 运行时。如果创建一个面向浏览器中 Flash Player 的项目,我们将选择Web

  3. 在选择桌面时,我们还需要确保为 Android 项目选择了一个移动增强版的 Flex SDK。Flex 4.5 及其以上版本包含了开发健壮 Android 应用程序所需的一切功能。如何操作…

  4. 我们必须做的最后一件事是确保项目将使用移动版的 Flex SWCs。为了在项目的主容器中声明 <s:ViewNavigatorApplication><s:TabbedViewNavigatorApplication>,必须能够访问这些特定的 SWCs,否则 Flash Builder 会报告错误。

  5. 新建 Flex 项目对话框的最后一部分允许我们确保包含移动 SWCs。您会注意到 mobilecomponents.swc 并未包含在我们的项目中。选择标签为 库路径 的选项卡,并点击标签为 添加 SWC: 的按钮。如何操作…

  6. 当出现添加 SWC对话框时,浏览到所选 Flex SDK 的位置。假设我们将 SDK 解压到 C:\SDKs\Flex4,我们现在将浏览到 C:\SDKs\Flex\frameworks\libs\mobile,选择 mobilecomponents.swc 文件,并点击打开。这将向我们的应用程序添加对移动组件的支持。

  7. 完成项目设置。我们现在能够使用移动特定的容器和控制组件,而不会收到 Flash Builder 的错误,但我们必须进行一些调整才能正确编译应用程序。

  8. 在项目中找到 AIR 描述符文件。它通常被命名为类似 {MyProject}-app.xml 的名称,并位于项目根目录。打开这个文件,将 <visible> 属性更改为 true。如果该节点已被注释掉,可能需要取消注释。

  9. 包资源管理器中右键点击项目,并选择属性

  10. 选择Flex 编译器菜单选项,并在附加编译参数中添加以下内容:theme=C:\{SDK Location}\frameworks\themes\Mobile\mobile.swc

  11. 最后,将主应用程序文件的根节点从 <s:Application> 切换到 <s:ViewNavigatorApplication>。我们现在可以使用移动 Flex 框架组件来编写和编译应用程序。

工作原理…

在 Flash Builder 中指定我们要创建的项目类型时,IDE 会自动提供 Flex 框架的某些部分,以便我们可以使用项目所需的所有组件。Flash Builder 4 及其早期版本没有附带任何移动版的 Flex SDK,并且不提供针对 Android 项目的流程。因此,我们必须明确告诉 Flash Builder 使用这些额外的框架组件。

前一节步骤中提到的应用程序描述符文件用于以各种方式配置 AIR 应用程序:设置初始窗口属性、Chrome 属性,甚至系统图标。

另请参阅…

需要注意的是,Flash Builder 4.5 之前的版本将不包括将项目编译为 .APK(Android 应用程序文件扩展名)的能力,你将需要使用免费提供的 AIR SDK 编译你的项目。有关如何执行此操作的信息,请参阅 第十一章。

值得一提的是,尽管你可以使用旧版本的 Flash Builder 开发 Android 应用程序,但你将无法获得新版本提供的大部分好处,例如代码补全功能。

使 Powerflasher FDT 4.1 能够访问 Flex Mobile SDK

Powerflasher FDT 是一个越来越受欢迎的 Flash 平台项目开发环境。FDT 4 配备了开始开发 ActionScript 和 Flex 应用程序所需的一切,但 FDT 4.1 及以下版本不支持任何移动工作流程,也不包含支持移动的 Flex SDK。

如何操作…

配置 Powerflasher FDT 4 以进行 Android 开发的步骤很少:

  1. 访问 Adobe 开源网站 opensource.adobe.com/,找到最新版本的 Flex SDK。

  2. 下载最新 Adobe Flex SDK 的 ZIP 文件,并将其解压到硬盘上你容易记住的位置,例如 C:\SDKs\Flex

  3. 启动 FDT 并转到 窗口 | 首选项

  4. 滚动到 FDT 菜单项,选择 已安装的 SDK。你现在可以看到当前你复制的 FD 中可用的每个 SDK 的列表:如何操作…

  5. 点击标有 添加 的按钮,浏览到你最近下载的 Flex SDK 的位置。

  6. 为对话框提供一个有意义的名称,然后点击 确定。例如,Flex 4.5:如何操作…

  7. 现在,Flex 4.5 SDK 可用于你的应用程序。要在项目中使用它,只需在创建新项目或在现有项目中修改 Flex 编译器 属性时选择此 SDK:如何操作…

它的工作原理…

Powerflasher FDT 4 是一个基于 Eclipse 的 IDE(与 Flash Builder 类似),采用了很多相同的方法来扩展应用程序和添加 SDK 包。在 FDT 中使用较新版本的 Flex SDK 可以让我们访问移动主题选项和其他在之前 SDK 版本中不可用的特定 API。

另请参阅…

需要注意的是,Flash Builder 4.5 之前的版本将不包括将项目编译为 .APK(Android 应用程序文件扩展名)的能力,你将需要使用免费提供的 AIR SDK 编译你的项目。有关如何执行此操作的信息,请参阅 第十一章。

值得一提的是,虽然你可以使用 Flash Builder 的旧版本为 Android 开发应用程序,但你将无法获得新版本提供的一些好处,例如代码补全。

使用 Powerflasher FDT 4.1 及以下版本开发 Android 应用程序

要在 FDT 4.1 中开发移动 Android 应用程序,我们将需要配置 FDT 以启用对移动 Flex SDK 的访问。如果你还没有以这种方式配置 FDT,请参阅前面的菜谱。

如何操作…

在 FDT 4.2 之前的版本中,没有特定的移动工作流或工具。通过执行以下步骤,我们可以确保项目将具有移动兼容性:

  1. 在 FDT 中,右键点击Flash Explorer面板,选择新建 | 新建 Flash 项目:如何操作…

  2. 将会出现新建 Flash 项目对话框,你必须在其中提供项目名称,并选择是使用ActionScript 3还是Flex创建项目。我们需要确保选择Flex 4,因为这包括 Spark 组件,如果使用适当版本的 Flex SDK,它们可以是移动友好的。如何操作…

  3. 下一个部分将允许我们选择一个特定的 Flex SDK 用于我们的项目。我们应该为 Android 项目选择一个增强移动版的 Flex SDK。Flex 4.5 及以上版本包括我们开始开发健壮的 Android 应用程序所需的一切。如何操作…

  4. 我们必须做的最后一件事是确保移动版的 Flex SWC 将在我们的项目中使用。为了声明项目的主要容器为<s:ViewNavigatorApplication><s:TabbedViewNavigatorApplication>,这些特定的 SWC 必须是可访问的,否则 FDT 将报告错误。

  5. 下一个部分允许我们确保包含移动 SWC。选择标签为SDK Library的选项卡,然后点击标签为选择 SWCs的按钮

  6. 你会注意到mobile\mobilecomponents.swc没有包含在我们的项目中。选中此 SWC 旁边的复选框,然后按确定按钮继续:如何操作…

  7. 现在,我们将能够使用特定的移动容器和控制组件,而不会从 FDT 收到错误。

工作原理…

在 FDT 中指定我们想要创建的项目类型时,程序会自动提供 Flex 框架的某些部分,以便我们可以使用项目所需的所有组件。FDT 4.1 及更早版本不附带任何支持移动设备的 Flex SDK,也不提供 Android 项目的相关工作流。因此,我们必须明确告诉 FDT 使用以下额外的框架组件:

  • ViewNavigatorApplication:这包括一个ViewNavigator堆栈结构,我们可以将不同的视图推送到堆栈顶部,并向用户展示最顶层的视图。

  • TabbedViewNavigatorApplication: 这包括在应用程序内拥有多个ViewNavigator堆栈的能力,通过TabBar用户界面元素进行控制。

另请参阅…

需要注意的是,Flash Builder 4.5 之前的版本将无法将项目编译为.APK(Android 应用文件扩展名),你需要使用免费提供的 AIR SDK 来编译项目。有关如何执行此操作的信息,请参阅第十一章。

还值得一提的是,虽然你可以使用较旧版本的 Flash Builder 开发 Android 应用程序,但你将无法获得较新版本提供的大部分好处,例如代码补全功能。

将标准 Flex 项目转换为 Flex Mobile 项目

目前在 Flash Builder(或 FDT)中没有工作流可以将现有应用程序转换为移动 Android 应用程序。根据被转换应用程序的复杂程度和 Flex 的版本,这项转换任务可能从非常简单到异常复杂不等。在本教程中,我们将使用基本的 Flex 结构演示一个更简单的示例。

如何操作…

创建一个新的移动项目,并将所有必要的文件复制到其中,保留用于移动项目的代码部分,并修改任何不支持的组件。

在此示例中,我们将使用一个简单的 Flex 项目,该项目针对桌面 AIR,目前只包含一个按钮组件:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication 

>
<s:Button x="10" y="10" width="300" height="200" label="Button"/>
</s:WindowedApplication>

要将此项目转换为新的 Flex Mobile 项目,请执行以下步骤:

  1. 菜单中选择文件 | 新建 | Flex Mobile 项目

  2. 为项目设置对话框提供有关新移动项目的详细信息。

    注意

    项目不能与环境中任何现有项目的名称相同。

  3. 从原始项目中的项目文件夹中复制所有文件到这个新的移动项目中,不包括项目描述文件({myApp }.xml)Default Application文件。

  4. 现在,将旧Default Application文件中的所有内容复制并粘贴到与你的移动项目一起创建的Default Application文件中。一旦复制完成,右键点击主应用程序文件并选择设为默认应用程序

  5. 将所有<s:WindowedApplication>的实例更改为<s:ViewNavigatorApplication>(或者,<s:TabbedViewNavigatorApplication>)。

    注意

    与标准的 AIR <s:WindowedApplication>一样,项目中只能存在一个<s:ViewNavigatorApplication><s:TabbedViewNavigatorApplication>实例。

  6. 查看你的问题面板,以了解是否需要进一步修改。

  7. 如果你没有使用任何旧的 Halo 组件(mx 命名空间),建议你为打开的<s:ViewNavigatorApplication>标签删除命名空间声明。

  8. <s:ViewNavigatorApplication> 标签添加一个 firstView 属性。这应该指向当你设置移动项目时自动创建的 View

  9. 由于可视化 UI 元素不能直接位于 <s:ViewNavigatorApplication /> 节点内,我们必须将 <s:Button /> 实例包裹在 <fx:Declarations> </fx:Declarations> 标签集中,或者将其移动到特定的 View 中。

提示

下载示例代码

你可以从你的www.PacktPub.com账户下载你所购买的所有 Packt 图书的示例代码文件。如果你在其他地方购买了这本书,可以访问www.PacktPub.com/support注册,我们会将文件直接通过电子邮件发送给你。

你的Default Application文件现在应如下所示:

<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication 
 firstView="views.MobileFlexProjectHomeView">
<fx:Declarations>
<s:Button x="10" y="10" width="447" height="106" label="Button"/>
</fx:Declarations>
</s:ViewNavigatorApplication>

此外,此应用程序的视图可能如下所示:

<?xml version="1.0" encoding="utf-8"?>
<s:View 
 title="MobileFlexProjectHomeView ">
</s:View>

有关 Flex Mobile 项目的结构信息,请查看以下资源:opensource.adobe.com/wiki/display/flexsdk/Developer+Documentation

工作原理…

使用 Flex 时,你的应用程序的根标签在很大程度上决定了整个项目可用的 API 和结构。确保我们选择正确的根标签对于项目的目标平台和能力非常重要。对于在 Android 上的 AIR,我们将使用 ViewNavigatorApplicationTabbedViewNavigatorApplication。桌面应用程序将使用 Application 或 WindowedApplication 标签。如果你使用 Flex 构建的 Flash 内容要在浏览器中的 Flash Player 部署,无论是在移动端还是桌面端,你都会为你的项目使用一个直接的 Application 标签。

更多信息…

如果你不想处理大量转换,并且只是开始一个将同时在桌面和移动端共享相同代码库的新项目,那么你可能考虑使用 Flex Library 项目,以允许不同的项目共享相同的底层代码库。

阅读 Flex 4 库使用说明文档:help.adobe.com/en_US/flashbuilder/using/WS6f97d7caa66ef6eb1e63e3d11b6c4d0d21-7fe6.html

在 Windows 上配置 AIR SDK 以打包适用于 Android 应用的 AIR

如果我们使用开源的 AIR SDK(软件开发工具包)与另一个 IDE 或甚至在简单的文本编辑器中编辑我们的项目,我们仍然可以通过命令行工具编译适用于 Android 分发的应用程序。

如何操作…

如果您还没有 Adobe AIR SDK,您必须首先从www.adobe.com/products/air/sdk/下载,并将其文件解压到硬盘上的一个目录中,例如C:\SDKs\AIR。您还必须在操作系统中设置一个指向 AIR SDK 下的bin目录的PATH变量。

如果您使用的是 Windows 系统,请通过以下步骤设置环境变量:

  1. 打开系统属性对话框。有多种方式可以进入这个对话框,最直接的方法是右键点击我的电脑,然后选择属性

  2. 从左侧菜单中选择高级系统设置

  3. 点击此窗口底部的按钮,上面写着环境变量

  4. 在此窗口中点击PATH变量,并选择编辑:如何操作…

  5. 现在,只需将您的bin目录的位置添加到变量集合中:如果变量值列表中的最后一个条目没有以分号结束,您必须在每个新条目前添加一个。例如:C:\SDKs\AIR\bin如何操作…

  6. 这样应该就设置好了。点击几次确定,然后打开命令提示符以验证我们是否正确设置了。输入adt -version并按下回车。如果一切正常,ADT 会返回一个类似adt version "2.5.0.00000"这样的版本字符串。

工作原理…

在操作系统中设置一个PATH变量,这样我们就可以在系统的任何位置调用 AIR Android 编译器 ADT,而无需遍历文件目录并指定长路径名。

参见以下内容…

如果使用 Linux 或 Mac 操作系统,您也可以在终端内设置特定的环境变量。有关示例,请参阅下一食谱《在 Linux 或 Mac OS 上配置 AIR SDK 以打包 Android 应用程序的 AIR》。

在 Linux 或 Mac OS 上配置 AIR SDK 以打包 Android 应用程序的 AIR

如果我们使用开源的 AIR SDK 与其他 IDE 配合使用,甚至是在简单的文本编辑器中编辑我们的项目,我们仍然可以通过命令行工具编译在 Android 上分发的应用程序。

如何操作…

如果您还没有 Adobe AIR SDK,您必须首先从www.adobe.com/products/air/sdk/下载,并将其文件解压到硬盘上的一个目录中:例如/home/joseph/SDKs/AIR。您还必须在操作系统的启动脚本中设置一个指向 AIR SDK 下的bin目录的PATH变量。

我们将通过以下步骤设置环境变量:

  1. 打开终端

  2. 现在,我们必须创建 shell 配置文件。在终端窗口中输入以下内容:在 Mac 上输入cat >> .bash_profile,在 Ubuntu 上输入cat >> .bashrc(每个 Linux 发行版对于启动脚本可能有各自的命名约定)。

  3. 接下来,输入 export PATH=$PATH:/home/joseph/SDKs/AIR/bin 来设置 PATH 变量,使其指向 AIR 开发工具的 bin 目录。按下 Enter

  4. 输入 Ctrl+Shift+D 以结束此进程。

  5. 现在我们将检查是否所有内容都已适当地添加。在 终端 中输入 cat .bashrc 并按下 Enter。您应该会看到返回的 PATH 命令:如何操作…

  6. 您可能需要登出您的个人资料,然后再重新登录,以便系统获取新的环境变量。

  7. 在重新登录您的个人资料后,再次打开 终端

  8. 在终端中输入 echo $PATH 并按下 Enter。这应该会显示 PATH 变量中包含的所有内容,包括我们的 AIR bin 目录的位置。

  9. 这样应该就可以了。我们现在将验证是否正确设置了 AIR SDK。输入 adt -version 并按下 Enter。如果一切正常,ADT 将返回一个类似 adt version "2.5.0.00000" 的版本字符串!如何操作…

它是如何工作的…

在操作系统中设置 PATH 变量,使我们能够从系统的任何位置调用 AIR Android 编译器 ADT,而无需遍历文件目录并指定长路径名。

另请参阅…

请注意,您可能需要登出您的会话,然后再重新登录,以便新的 PATH 变量生效。如果使用的是 Windows 操作系统,您也可以设置特定的环境变量。有关此示例,请参阅前面的食谱,在 Windows 上配置 AIR SDK 以打包适用于 Android 应用的 AIR

第二章:交互体验:多点触控、手势和其他输入

本章节将涵盖以下内容:

  • 检测支持的设备输入类型

  • 检测设备是否支持多点触控

  • 验证常见交互的具体手势支持

  • 使用手势放大显示对象

  • 使用手势平移显示对象

  • 使用手势滑动显示对象

  • 使用手势旋转显示对象

  • 访问原始触摸点数据

  • 基于触摸点数据创建自定义手势

  • 模拟安卓长按交互

  • 程序化地调用虚拟键盘

  • 应对安卓软键盘交互

  • 应对轨迹球和 D-Pad 事件

引言

通过触摸和手势与设备交互的能力是移动计算突出的特点之一,Flash 平台在安卓上完全支持多点触控和手势。本章将介绍拦截和响应用户交互的不同方式,无论是通过简单的触摸点还是复杂的手势,以及更传统的物理和虚拟键盘输入。在移动安卓设备上充分利用这一点对于流畅的体验至关重要。

本章节中的所有示例均表示为纯 ActionScript 3 类,不依赖于外部库或 Flex 框架。因此,我们可以将这些示例应用到我们希望的任何 IDE 中。

检测支持的设备输入类型

安卓设备上有多种输入类型可供选择,根据我们正在从事的项目,可能需要验证特定设备是否支持预期的用户交互模式。幸运的是,有许多 ActionScript 类可以帮助我们发现设备在用户输入方面的功能。

如何操作...

我们需要使用内部类来检测是否支持多点触控:

  1. 首先,将以下类导入到项目中,以便检查各种设备上的输入类型:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.system.Capabilities;
    import flash.system.TouchscreenType;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Keyboard;
    import flash.ui.KeyboardType;
    import flash.ui.Mouse;
    
    
  2. 声明一个 TextFieldTextFormat 对象,以允许在设备上输出可见内容:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置 TextField,应用 TextFormat,并将其添加到 DisplayList 中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 32;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 现在,我们将简单地通过检查这些类调用一系列属性返回的数据。以下示例中,我们是在以下方法中执行此操作:

    protected function checkInputTypes():void {
    traceField.appendText("Touch Screen Type: " + flash.system.Capabilities.touchscreenType + "\n");
    traceField.appendText("Mouse Cursor: " + flash.ui.Mouse. supportsCursor + "\n");
    traceField.appendText("Physical Keyboard Type: " + flash. ui.Keyboard.physicalKeyboardType + "\n");
    traceField.appendText("Virtual Keyboard: " + flash.ui.Keyboard. hasVirtualKeyboard + "\n");
    }
    
    
  5. 结果将类似于以下内容:如何操作...

工作原理...

当调用时,Flash 平台运行时能够报告某些设备功能。报告的数据将允许我们根据运行时检测到的输入类型定制用户体验。

以下是四种可以报告的输入类型的基本概述:

flash.system.Capabilities.touchscreenType

调用此方法将返回一个String常量,值为FINGERSTYLUSNONE。它告诉我们设备上是否支持某种形式的直接屏幕交互,如果是,是哪种形式。在 Android 设备上,这将始终返回FINGER

flash.ui.Mouse.supportsCursor

调用此方法将返回一个Boolean值,为truefalse。它简单地告诉我们设备上是否有持久鼠标光标。在 Android 设备上,这很可能会始终返回false

flash.ui.Keyboard.physicalKeyboardType

调用此方法将返回一个String常量,值为ALPHANUMERICKEYPADNONE。它告诉我们设备上是否有某种专用的物理键盘,如果有,是哪种类型。在 Android 设备上,这很可能会始终返回NONE,尽管某些 Android 型号确实有物理键盘。

flash.ui.Keyboard.hasVirtualKeyboard

调用此方法将返回一个Boolean值,为truefalse。它简单地告诉我们设备上是否有虚拟(软件)键盘。在 Android 设备上,这很可能会始终返回true

检测设备是否支持多点触控

在针对 Android 操作系统的项目开发中,确保设备实际上支持多点触控总是一个好主意。在 Android 手机上,这可能总是如此,但 Google TV 或 AIR for TV 设备呢?其中许多也是基于 Android 的,但大多数电视根本没有触摸控制。永远不要假设任何设备的功能。

如何操作...

我们需要使用内部类来检测是否支持多点触控。

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    
    
  2. 声明一个TextFieldTextFormat对象,以允许在设备上可见输出:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 然后,只需调用Multitouch.supportsGestureEventsMultitouch.supportsTouchEvents,即可检查这些功能,如下面的方法所示:

    protected function checkMultitouch():void {
    traceField.appendText(String("Gestures: " + Multitouch.supportsGestureEvents) + "\n");
    traceField.appendText(String("Touch: " + Multitouch.supportsTouchEvents));
    }
    
    
  5. 这些属性中的每一个都将返回一个Boolean值,truefalse,表示设备支持,如下所示:如何操作...

工作原理...

检测设备是否支持触摸或手势事件将决定作为开发者的你,在细化用户体验方面有多少自由度。如果这些项目中的任何一个返回为 false,那么就需要你(如果可能的话)提供一种替代方式让用户与应用程序交互。这通常是通过Mouse事件完成的:

  • 触摸事件:如单指轻触等基本交互。

  • 手势事件:更复杂的用户交互解释,如捏合、缩放、滑动、平移等。

还有更多...

需要注意的是,尽管特定设备可能支持手势事件或触摸事件,但在使用 Flash 平台工具时,我们必须将Multitouch.inputMode明确设置为其中之一。

验证常见交互中特定手势的支持

在处理 Android 设备时,触摸和手势是用户与设备交互的主要机制。如果我们想在 Flash Player 和 AIR 中使用一些预定义的手势,可以按照以下方式操作。

如何操作...

要发现设备支持哪些特定的手势,执行以下操作:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个TextFieldTextFormat对象,以允许在设备上输出可见内容:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 使用以下命令为多触控 API 设置特定的输入模式以支持手势:

    Multitouch.inputMode = MultitouchInputMode.GESTURE;
    
    
  5. 调用Multitouch.supportedGestures将返回一个包含设备上 Flash 支持的所有手势名称的String对象Vector

    var supportedGestures:Vector.<String> = Multitouch.supportedGestures;
    
    
  6. 然后,我们可以寻找特定的手势或手势集进行监听,或者在必要时退回到其他交互事件。

    for(var i:int=0; i < supportedGestures.length; ++i) {
    trace(supportedGestures[i]);
    }
    
    
  7. 我们可以在一个方法内执行所有这些必要功能:

    protected function checkGestures():void {
    Multitouch.inputMode = MultitouchInputMode.GESTURE;
    if(Multitouch.supportedGestures){
    var supportedGestures:Vector.<String> = Multitouch.supportedGestures;
    for(var i:int=0; i <supportedGestures.length; ++i) {
    traceField.appendText(supportedGestures[i] + "\n");
    }
    }else{
    traceField.appendText("no gesture support!");
    }
    }
    
    
  8. 结果将类似于以下所示:如何操作...

工作原理...

Flash 播放器和 AIR 在为 Android 开发者提炼信息至关键细节方面做得非常出色。了解特定设备支持哪些手势,将使我们能够为应用程序定制事件交互,并在必要时提供后备交互。

还有更多...

在我们的示例类中,我们还通过Multitouch.supportedGestures检查以确保至少支持一些手势。如果设备确实提供了手势支持,我们可能需要向用户提供警告,解释应用程序由于硬件限制可能无法达到最佳性能。

除了在flash.events.TransformGestureEvent包中包含的更常见的诸如缩放、滑动、旋转和平移等手势之外,还有其他较少见的手势,如双指轻触,可以在flash.events.GestureEventflash.events.PressAndTapGestureEvent类中找到。如果设备支持,所有这些都将由Multitouch.supportedGestures引用。

使用手势放大显示对象

捏合和拉扯是在支持多触控输入的触摸屏上经常使用的手势。将两个手指靠近会缩小对象,而将两个手指分开会使对象在设备上变大。

如何操作...

本示例在一个Shape对象内使用Graphics API 绘制一个正方形,将其添加到Stage中,然后为缩放手势事件设置监听器,以适当缩放Shape

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.events.TransformGestureEvent;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个Shape对象,我们将在其上执行手势操作:

    private var box:Shape;
    
    
  3. 接下来,构建一个方法来处理我们的Sprite的创建并将其添加到DisplayList中:

    protected function setupBox():void {
    box = new Shape();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-150,-150,300,300);
    box.graphics.endFill();
    addChild(box);
    }
    
    
  4. 将多触点 API 的特定输入模式设置为支持触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,并注册GESTURE_ZOOM事件的事件监听器。在这种情况下,每当应用程序检测到缩放手势时,onZoom方法将被触发:

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.GESTURE;
    stage.addEventListener(TransformGestureEvent. GESTURE_ZOOM, onZoom);
    }
    
    
  5. 为了使用捏合和缩放的接受行为,我们可以根据事件监听器返回的缩放因子调整舞台上对象的缩放比例。

    protected function onZoom(e:TransformGestureEvent):void {
    box.scaleX *= e.scaleX;
    box.scaleY *= e.scaleY;
    }
    
    
  6. 结果手势将以以下方式影响我们的视觉对象:如何操作...

注意

插图由 Gestureworks 提供(www.gestureworks.com)。

工作原理...

由于我们将Multitouch.inputMode设置为通过MultitouchInputMode.GESTURE的手势,因此我们能够监听并响应一系列预定义的手势。在这个例子中,我们监听TransformGestureEvent.GESTURE_ZOOM事件,以便设置我们的Shape对象的缩放比例。通过将当前的缩放属性与事件报告的缩放值相乘,我们可以根据这个手势调整对象的缩放比例。

还有更多内容...

请注意,我们绘制正方形的方式是将Shape的注册点位于可见Shape的中心。我们这样做很重要,因为DisplayObject将基于注册点和变换点进行放大和缩小。

在使用 Flash Professional 中的绘图工具时,请确保将你的MovieClip符号的注册点设置为居中,以便正确工作。

另请参阅...

TransformGestureEvent.GESTURE_ZOOM只是我们在使用 Flash Platform 运行时和 Android 设备时可以使用的四个主要变换手势之一。参考以下食谱以获取这些手势的完整概述:

  • 使用手势平移显示对象

  • 使用手势滑动显示对象

  • 使用手势旋转显示对象

使用手势平移显示对象

平移DisplayObject是通过同时用两个手指触摸屏幕,然后沿着我们想要平移对象的屏幕方向移动两个手指来完成的。这通常用于占据比屏幕更大的对象,或者已经放大到只有部分在给定时间内在屏幕上可见的对象。

如何操作...

这个例子使用Graphics API 在Shape对象内绘制一个正方形,将其添加到Stage中,然后为平移手势事件设置监听器,以适当缩放Shape

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.events.TransformGestureEvent;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个Shape对象,我们将在这个对象上执行手势操作:

    private var box:Shape;
    
    
  3. 接下来,构建一个方法来处理我们的Shape的创建并将其添加到DisplayList中。我们特别努力确保我们的Shape比屏幕大得多,以便可以有效地进行平移:

    protected function setupBox():void {
    box = new Shape();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-150,-150,300,300);
    box.graphics.endFill();
    box.graphics.lineStyle(10, 0x440000, 1);
    box.graphics.moveTo(0, -800);
    box.graphics.lineTo(0, 800);
    box.graphics.moveTo(-800, 0);
    box.graphics.lineTo(800, 0);
    addChild(box);
    }
    
    
  4. 设置特定的输入模式以支持多点触控 API 的触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,并注册GESTURE_PAN事件的事件监听器。在这种情况下,每当应用程序检测到缩放手势时,onPan方法将被触发:

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.GESTURE;
    stage.addEventListener(TransformGestureEvent. GESTURE_PAN, onPan);
    }
    
    
  5. 我们现在可以响应我们的平移事件返回的数据。在这个例子中,我们只是根据平移偏移数据简单地改变了Shape的 x 和 y 位置:

    protected function onPan(e:TransformGestureEvent):void {
    box.x += e.offsetX;
    box.y += e.offsetY;
    }
    
    
  6. 结果手势将以以下方式影响我们的视觉对象:如何操作...

注意

图形由 Gestureworks 提供(www.gestureworks.com)。

工作原理...

由于我们将Multitouch.inputMode设置为通过MultitouchInputMode.GESTURE的手势,因此我们能够监听并响应一系列预定义的手势。在这个例子中,我们监听TransformGestureEvent.GESTURE_PAN事件,以便改变我们的Shape对象的 x 和 y 位置。通过调整我们的Shape的坐标通过报告的偏移数据,我们可以按照用户期望的方式调整对象的位置。

还有更多...

请注意,在某些设备上执行此操作通常很困难(因为你必须同时用两个手指触摸屏幕),而其他设备可能根本不支持它。作为后备,我们总是可以使用startDrag()stopDrag()方法来模拟平移。

另请参阅...

TransformGestureEvent.GESTURE_PAN只是我们在使用 Flash Platform 运行时和 Android 设备时可以使用的一组四个主要转换手势之一。参考以下食谱以获取这些手势的完整概述:

  • 使用手势缩放 DisplayObject

  • 使用手势滑动显示对象

  • 使用手势旋转显示对象

使用手势滑动显示对象

滑动是 Android 设备上最常见的动作之一,并且有充分的理由。无论是快速翻阅一系列照片,还是在应用程序的状态之间移动,滑动手势都是用户所期望的。通过简单地触摸屏幕并在相反的方向快速向上、下、左或右滑动,即可完成滑动动作。

如何操作...

这个例子在Shape对象内使用Graphics API 绘制一个正方形,将其添加到Stage中,然后设置一个监听器来监听滑动手势事件,以便根据滑动的方向将Shape实例移动到屏幕边缘:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.events.TransformGestureEvent;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个Shape对象,我们将对其执行手势操作:

    private var box:Shape;
    
    
  3. 接下来,构建一个方法来处理我们的Shape的创建并将其添加到DisplayList中:

    protected function setupBox():void {
    box = new Shape();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-150,-150,300,300);
    box.graphics.endFill();
    addChild(box);
    }
    
    
  4. 将多触点 API 的特定输入模式设置为支持触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,并注册TransformGestureEvent.GESTURE_SWIPE事件的事件监听器:

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.GESTURE;
    stage.addEventListener(TransformGestureEvent. GESTURE_SWIPE, onSwipe);
    }
    
    
  5. 我们现在可以响应滑动事件返回的数据。在这种情况下,我们只是根据滑动偏移数据简单地移动Shape的 x 和 y 位置:

    protected function onSwipe(e:TransformGestureEvent):void {
    switch(e.offsetX){
    case 1:{
    box.x = stage.stageWidth - (box.width/2);
    break;
    }
    case -1:{
    box.x = box.width/2;
    break;
    }
    }
    switch(e.offsetY){
    case 1:{
    box.y = stage.stageHeight - (box.height/2);
    break;
    }
    case -1:{
    box.y = box.height/2;
    break;
    }
    }
    }
    
    
  6. 结果手势将以以下方式影响我们的视觉对象:如何操作...

注意

提供的手势图由 Gestureworks(www.gestureworks.com)提供。

工作原理...

由于我们将Multitouch.inputMode设置为通过MultitouchInputMode.GESTURE的手势,因此我们能够监听并响应许多预定义的手势。在这个例子中,我们监听TransformGestureEvent.GESTURE_SWIPE事件,以便改变我们的Shape对象的 x 和 y 位置。通过调整Shape的坐标,通过报告的偏移数据,我们可以按照用户期望的方式调整对象的位置。

通过这个例子我们可以看到,事件监听器返回的offsetXoffsetY值将分别是 1 或-1。这使得我们很容易确定注册的手势方向:

  • 向上滑动: offsetY = -1

  • 向下滑动: offsetY = 1

  • 向左滑动: offsetX = -1

  • 向右滑动: offsetX = 1

还有更多内容...

在响应滑动事件时,可能需要提供一些过渡动画,使用内置的补间机制或外部补间引擎。有许多优秀的 ActionScript 补间引擎作为开源软件免费提供。这些引擎与某些手势结合使用,可以为应用程序用户提供更愉快的使用体验。

我们可以考虑在应用程序中使用以下流行的补间引擎:

TweenLite:www.greensock.com/tweenlite/

GTween:www.gskinner.com/libraries/gtween/

另请参阅...

TransformGestureEvent.GESTURE_SWIPE只是我们在使用 Flash Platform 运行时和 Android 设备时可用的一组四个主要转换手势之一。参考以下食谱以获取这些手势的完整概述:

  • 使用手势放大显示对象

  • 使用手势平移显示对象

  • 使用手势旋转显示对象

使用手势旋转显示对象

旋转是通过在物体的不同点按住两个手指,然后一个手指绕另一个手指顺时针或逆时针移动来完成的。这将导致屏幕上的物体旋转。旋转可以与平移和缩放手势结合使用,为用户提供对图像或其他DisplayObject的完全控制。

如何操作...

这个例子在Shape对象内使用Graphics API 绘制一个正方形,将其添加到Stage,然后设置一个监听器来监听Rotate手势事件,以便围绕其注册点适当地旋转Shape实例:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.events.TransformGestureEvent;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个Shape对象,我们将对其执行手势操作:

    private var box:Shape;
    
    
  3. 接下来,构建一个方法来处理我们的Shape的创建并将其添加到DisplayList中。

    protected function setupBox():void {
    box = new Shape();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-150,-150,300,300);
    box.graphics.endFill();
    addChild(box);
    }
    
    
  4. 将多触点 API 的特定输入模式设置为支持触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,并为GESTURE_ROTATE事件注册一个事件监听器。在这种情况下,每当应用程序检测到旋转手势时,都会触发onRotate方法:

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.GESTURE; stage.addEventListener(TransformGestureEvent.GESTURE_ROTATE, onRotate);
    }
    
    
  5. 我们现在可以响应旋转事件返回的数据。在这个例子中,我们只是将从事件监听器返回的rotation值简单地赋给我们的Shaperotation参数,以执行适当的旋转:

    protected function onRotate(e:TransformGestureEvent):void {
    box.rotation += e.rotation;
    }
    
    
  6. 结果手势将以以下方式影响我们的视觉对象:如何操作...

注意

提供的手势图由 Gestureworks (www.gestureworks.com)提供。

工作原理...

由于我们将Multitouch.inputMode设置为通过MultitouchInputMode.GESTURE的手势,因此我们能够监听并响应一系列预定义的手势。在这个例子中,我们正在监听TransformGestureEvent.GESTURE_ROTATE事件,以便将返回的rotation值赋给我们的Shape对象。

在大多数情况下,实际上无需对此数据进行进一步计算,但我们可以通过允许(例如)一个DisplayObject的旋转影响另一个DisplayObject的旋转,甚至影响Stage上的多个DisplayObjects的旋转,来进行更高级的旋转交互。

还有更多...

请注意,我们是以这种方式绘制正方形,使得Shape的注册点位于可见Shape的中心。我们这样做很重要,因为DisplayObject将基于注册点和变换点进行旋转。

在使用 Flash Professional 的绘图工具时,请确保将你的MovieClip符号的注册点设置为居中,以便正确工作。

另请参阅...

TransformGestureEvent.GESTURE_ROTATE只是我们在使用 Flash Platform 运行时和 Android 设备时可以使用的一组四个主要变换手势之一。参考以下食谱以获取这些手势的完整概述:

  • 使用手势放大显示对象

  • 使用手势平移显示对象

  • 使用手势滑动显示对象

访问原始触摸点数据

有时 Flash Player 和 AIR 内置的预定义手势对于某些应用程序交互来说是不够的。这个示例将演示如何通过 Flash Player 或 AIR API 访问操作系统报告的原始触摸数据。

如何操作...

要在项目中读取原始触摸数据,请执行以下步骤:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个TextField和一个TextFormat对象,以允许在设备上可见输出:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "left";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 设置多触摸 API 的特定输入模式以支持触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量。我们还在以下方法中为TouchEvent数据注册一组监听器:

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMove);
    stage.addEventListener(TouchEvent.TOUCH_END, touchEnd);
    }
    
    
  5. 为了在每次触摸交互结束后清除我们的TextField,我们将构建以下函数:

    protected function touchEnd(e:TouchEvent):void {
    traceField.text = "";
    }
    
    
  6. 然后,我们可以从触摸事件中读取各种属性以某种方式解释。可以从返回的事件对象中派生出压力、坐标、大小等事件:

    protected function touchMove(e:TouchEvent):void {
    traceField.text = "";
    traceField.appendText("Primary: " + e.isPrimaryTouchPoint + "\n");
    traceField.appendText("LocalX: " + e.localX + "\n");
    traceField.appendText("LocalY: " + e.localY + "\n");
    traceField.appendText("Pressure: " + e.pressure + "\n");
    traceField.appendText("SizeX: " + e.sizeX + "\n");
    traceField.appendText("SizeY: " + e.sizeY + "\n");
    traceField.appendText("StageX: " + e.stageX + "\n");
    traceField.appendText("StageY: " + e.stageY + "\n");
    traceField.appendText("TPID: " + e.touchPointID + "\n");
    }
    
    
  7. 结果将类似于以下所示:如何操作...

工作原理...

设备中注册的每个触摸点都关联有一系列特定的属性。通过注册一组监听器来检测这些交互,我们可以读取这些数据,应用程序也可以做出适当的反应。在我们的示例中,我们只是通过TextField显示这些值,但这正是构建压力敏感的游戏机制或其他自定义手势所需的确切数据。

请注意,在一个允许多于一个触摸点的设备上,我们可以使用同一个监听器读取两个触摸点的数据。多个触摸点通过舞台上的位置和touchPointID来区分。在设计复杂手势时,或者当我们需要精确地跟踪每个触摸点时,我们会使用这些 ID 来区分触摸点。

还有更多...

需要注意的是,当Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT时,我们将无法利用 Flash Player 和 AIR 通过简化手势 API 提供的预定义手势。将Multitouch.inputMode设置为MultitouchInputMode.GESTURE将允许我们在应用程序中使用常见的预定义手势事件。

基于触摸点数据创建自定义手势

使用原始触摸数据,我们可以定义自定义手势以开发应用程序中使用的独特交互。我们通过基于原始触摸事件传递的数据进行计算来实现这一点。

如何操作...

在此示例中,我们将创建一个对角线滑动手势,它可以返回四个独立的值,让我们知道对角线滑动的方向。

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个TextField和一个TextFormat对象,以允许在设备上可见文本输出:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们将设置两个附加对象以帮助跟踪我们的手势,一个名为drawAreaShape通过图形 API 绘制手势,以及trackBeginObject,这是一个简单的对象,我们可以使用它来保存我们的初始触摸坐标以与触摸结束时的坐标进行比较:

    private var drawArea:Shape;
    private var trackBeginObject:Object;
    
    
  4. 现在,我们将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 32;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  5. 接下来,我们将在其中设置我们的Shape,并使用Graphics API 绘制手势:

    protected function setupDrawArea():void {
    drawArea = new Shape();
    addChild(drawArea);
    }
    
    
  6. 通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,为多触控 API 设置特定的输入模式以支持触摸输入。在这个例子中,我们将注册一组监听器来检测Stage上的触摸移动。这将有助于为我们的手势跟踪提供视觉反馈,并保存我们的初始触摸坐标以与触摸结束时的坐标进行比较。

  7. 我们也将通过同样的方法初始化我们的跟踪Object

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    trackBeginObject = new Object();
    stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBegin);
    stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMove);
    stage.addEventListener(TouchEvent.TOUCH_END, touchEnd);
    }
    
    
  8. 构造一个名为touchBegin的方法,以初始化我们手势的开始并保存坐标数据以供稍后比较。我们将确保注册的触摸点是通过测试TouchEvent.isPrimaryTouchPoint布尔属性来确定是可能是多个触摸中的第一个触摸点。

    protected function touchBegin(e:TouchEvent):void {
    if(e.isPrimaryTouchPoint){
    drawArea.graphics.clear();
    drawArea.graphics.lineStyle(20, 0xFFFFFF, 0.8);
    trackBeginObject.x = e.stageX;
    trackBeginObject.y = e.stageY;
    drawArea.graphics.moveTo(e.stageX, e.stageY);
    }
    }
    
    
  9. 构造另一个名为touchMove的方法,以接受触摸移动数据并绘制我们的视觉反馈:

    protected function touchMove(e:TouchEvent):void {
    if(e.isPrimaryTouchPoint){
    drawArea.graphics.lineTo(e.stageX, e.stageY);
    }
    }
    
    
  10. 构造一个名为touchEnd的最终方法,通过我们之前保存的trackBeginObject将结束触摸数据坐标与开始时的坐标进行比较,然后确定它是什么样的手势。在这种情况下,我们将结果作为String输出到之前创建的TextField中:

    protected function touchEnd(e:TouchEvent):void {
    if(e.isPrimaryTouchPoint){
    if(e.stageX > trackBeginObject.x && e.stageY > trackBeginObject.y){
    traceField.text = "Diagonal Gesture: TL -> BR";
    }elseif(e.stageX < trackBeginObject.x && e.stageY > trackBeginObject.y){
    traceField.text = "Diagonal Gesture: TR -> BL";
    }elseif(e.stageX < trackBeginObject.x && e.stageY < trackBeginObject.y){
    traceField.text = "Diagonal Gesture: BR -> TL";
    }elseif(e.stageX > trackBeginObject.x && e.stageY < trackBeginObject.y){
    traceField.text = "Diagonal Gesture: BL -> TR";
    }
    }
    }
    
    
  11. 结果将类似于以下所示:如何操作...

注意

图形由 Gestureworks 提供(www.gestureworks.com)。

工作原理...

由于我们可以访问所有原始触摸点数据,因此我们可以利用常规 ActionScript 元素(如Object, VectorArray实例)从开始到结束追踪触摸交互的生命周期。根据追踪的数据,例如坐标位置,触摸压力等,我们可以进行计算并确定交互是否合格为我们想要跟踪的手势。

在我们前面的例子中,我们对合格手势的判断相当宽松。为了更加严格,我们还可以计算不同触摸点的距离,甚至追踪从触摸开始到触摸结束的时间,以确保手势正是我们要寻找的,因此是用户有意的。

还有更多...

实际上,有许多手势库可以作为 Flash Player 和 AIR 运行时的内置手势库的替代品。快速进行网络搜索应该可以让我们访问到这些库,其中许多是免费的开放源码软件。最受欢迎的第三方手势库是Gesture Works,可以从gestureworks.com/下载。

模拟 Android 长按交互

Android 操作系统中内置的最有用的交互之一是长按。当用户轻触特定区域并持续几秒钟而不释放时,就会实现这一功能。虽然 Flash Player 和 Android 的 AIR 都没有将长按交互作为多点触控手势事件库的一部分,但通过这两个运行时模拟这一交互是相当简单的。

如何操作...

我们将通过使用 ActionScript Timer对象以及TouchPoint事件来模拟 Android 的长按交互。

  1. 首先,将以下类导入到您的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.events.TimerEvent;
    import flash.events.TouchEvent;
    import flash.geom.Rectangle;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.utils.Timer;
    
    
  2. 声明一个Sprite对象,我们将在其上进行长按操作,以及一个Timer对象:

    private var box:Sprite;
    private var lpTimer:Timer;
    
    
  3. 设置我们的Timer对象以测量注册长按所需的时间;在这个例子中,是 1000 毫秒。此外,我们现在注册一个监听器,以检测Timer周期是否完成:

    protected function setupTimer():void {
    lpTimer = new Timer(1000,1);
    lpTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timerEnd);
    }
    
    
  4. 接下来,构建一个方法来处理我们Sprite的创建并将其添加到DisplayList中:

    protected function setupBox():void {
    box = new Sprite();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-100,-100,200,200);
    box.graphics.endFill();
    addChild(box);
    }
    
    
  5. 将多点触控 APIs 的特定输入模式设置为支持触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量。为了模拟长按,我们必须在每次触摸交互的开始通过TouchEvent.TOUCH_BEGIN启动一个定时器。当触发TouchEvent.TOUCH_END或其他触摸取消事件时,将停止Timer,重置我们的“长按”。

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    box.addEventListener(TouchEvent.TOUCH_BEGIN, touchBegin);
    box.addEventListener(TouchEvent.TOUCH_END, touchEnd);
    box.addEventListener(TouchEvent.TOUCH_OUT, touchEnd);
    box.addEventListener(TouchEvent.TOUCH_ROLL_OUT, touchEnd);
    }
    
    
  6. 构造一个方法,在触摸交互开始时修改我们的Sprite。我们将稍微放大Sprite并改变 alpha 属性以表示已激活某些功能。此时,我们通过Timer开始测量长按:

    protected function touchBegin(e:TouchEvent):void {
    box.scaleX += 0.1;
    box.scaleY += 0.1;
    box.alpha = 0.8;
    lpTimer.start();
    }
    
    
  7. Timer被设置为在 1000 毫秒后完成一次触发。在这个触发点上,我们可以在应用程序内执行必要的操作。在这个例子中,我们使我们的Sprite可以被拖拽:

    protected function timerEnd(e:TimerEvent):void {
    var dragBounds:Rectangle = new Rectangle(box.width/2, box.height/2, stage.stageWidth-box.width, stage.stageHeight-box.height);
    box.startDrag(true, dragBounds);
    }
    
    
  8. 触摸结束时应该停止我们的Timer并取消与我们的Sprite发生的任何拖拽事件。在这里,我们将Spritescalealpha恢复到静止状态:

    protected function touchEnd(e:TouchEvent):void {
    lpTimer.stop();
    box.stopDrag();
    box.scaleX = 1;
    box.scaleY = 1;
    box.alpha = 1;
    }
    
    
  9. 结果手势将以以下方式影响我们的视觉对象:如何操作...

注意

插图由 Gestureworks 提供(www.gestureworks.com)。

工作原理...

我们的示例需要按住一秒钟来触发函数调用,这导致一个Shape对象可以在Stage上拖动。这是通过监听TOUCH_BEGIN事件,然后监控Timer来判断这是否是有意的长按交互来实现的。如果一秒钟内没有TOUCH_END事件,那么我们就让Shape可拖动。一旦触发Timer,我们就修改了Shape的缩放和透明度,以表示它现在是一个可拖动的对象。释放Shape将完成这个交互。

还有更多...

长按功能最常见的用途是重新定位某些视觉元素,正如我们在这里所做的那样,或者唤起菜单操作,因为安卓用户非常习惯于在设备上使用这种类型的交互。

程序化唤起虚拟键盘

在大多数情况下,只需将焦点放在文本输入字段上就会唤起虚拟键盘。失去焦点将关闭虚拟键盘。也许我们需要应用程序在没有用户交互的情况下这样做,或者在某些应用状态进入时立即这样做以方便用户。

如何操作...

我们配置了一个Shape,通过分配给它的Tap触摸事件来切换安卓虚拟键盘的开启和关闭。

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.SoftKeyboardEvent;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个Shape以及一个TextFieldTextFormat对象。这些将用于交互和视觉反馈。

    private var tapBox:Sprite;
    private var tapBoxField:TextField;
    private var tapBoxFormat:TextFormat;
    
    
  3. 接下来,构建一个方法来处理我们Sprite的创建并将其添加到DisplayList中。点击这个Sprite将允许我们唤起或隐藏虚拟键盘。我们还将构建一个TextField和相关的TextFormat对象在Sprite内,以允许我们向用户提供有状态的消息。

    protected function setupBox():void {
    tapBox = new Sprite();
    tapBox.graphics.beginFill(0xFFFFFF, 1);
    tapBox.x = stage.stageWidth/2;
    tapBox.y = stage.stageHeight/2 - 200;
    tapBox.graphics.drawRect(-200,-100,400,160);
    tapBox.graphics.endFill();
    tapBoxFormat = new TextFormat();
    tapBoxFormat.bold = true;
    tapBoxFormat.font = "_sans";
    tapBoxFormat.size = 42;
    tapBoxFormat.align = "center";
    tapBoxFormat.color = 0x333333;
    tapBoxField = new TextField();
    tapBoxField.defaultTextFormat = tapBoxFormat;
    tapBoxField.selectable = false;
    tapBoxField.mouseEnabled = false;
    tapBoxField.multiline = true;
    tapBoxField.wordWrap = true;
    tapBoxField.width = tapBox.width;
    tapBoxField.height = tapBox.height;
    tapBoxField.x = -200;
    tapBoxField.y = -80;
    tapBoxField.text = "Tap to Toggle Virtual Keyboard";
    tapBox.addChild(tapBoxField);
    addChild(tapBox);
    }
    
    
  4. 通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,为多点触控 API 设置特定的输入模式以支持触摸输入,并在DisplayObject上注册一个事件监听器,这将用于触发安卓虚拟键盘的激活和停用。在这种情况下,是一个TouchEvent.TOUCH_TAP事件。触摸点击相当于鼠标点击事件。我们还可以为一系列虚拟键盘事件注册多个监听器。为了让DisplayObject能够唤起虚拟键盘,我们需要将其needsSoftKeyboard属性设置为true。在这里注册的SoftKeyboardEvent监听器是可选的。

    protected function setupTouchEvents():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    tapBox.needsSoftKeyboard = true;
    tapBox.addEventListener(TouchEvent.TOUCH_TAP, touchTap);
    tapBox.addEventListener (SoftKeyboardEvent. SOFT_KEYBOARD_ACTIVATING, vkActivating);
    tapBox.addEventListener(SoftKeyboardEvent. SOFT_KEYBOARD_ACTIVATE, vkActivate);
    tapBox.addEventListener(SoftKeyboardEvent. SOFT_KEYBOARD_DEACTIVATE, vkDeactivate);
    }
    
    
  5. 为了使用前面定义的SoftKeyboardEvent监听器,我们必须创建各种方法,在检测到每次活动时执行。这样,我们可以在激活过程中监听、与虚拟键盘交互,甚至阻止某些事件触发,或者在检测到虚拟键盘完全完成激活或停用时进行拦截。

    protected function vkActivating(e:SoftKeyboardEvent):void {
    trace("Virtual Keyboard ACTIVATING");
    }
    protected function vkActivate(e:SoftKeyboardEvent):void {
    trace("Virtual Keyboard ACTIVATED");
    }
    protected function vkDeactivate(e:SoftKeyboardEvent):void {
    trace("Virtual Keyboard DEACTIVATED");
    }
    
    
  6. 要调用虚拟键盘,我们只需在 DisplayObject 上调用 requestSoftKeyboard(),其 needsSoftKeyboard 属性已设置为 true。在这里,我们检查 needsSoftKeyboard 是否设置为 true,并据此切换此属性。

    protected function touchTap(e:TouchEvent):void {
    if(tapBox.needsSoftKeyboard == true){
    tapBox.requestSoftKeyboard();
    tapBoxField.text = "Virtual Keyboard is Up";
    tapBox.needsSoftKeyboard = false;
    }else{
    tapBox.needsSoftKeyboard = true;
    tapBoxField.text = "Virtual Keyboard is Down";
    }
    }
    
    
  7. 要关闭虚拟键盘,用户需要点击一个 DisplayObject,其 needsSoftKeyboard 属性已设置为 false

  8. 结果将类似于以下所示:如何操作...

它是如何工作的...

为了通过 ActionScript 调用 Android 虚拟键盘,我们必须将交互式 DisplayObjects.needsSoftKeyboard 属性设置为 true。这将允许我们注册一个轻触监听器,并在触发轻触事件时调用 requestSoftKeyboard(),在屏幕上显示虚拟键盘。

点击任何 needsSoftKeyboard 属性设置为 false(默认状态)的 DisplayObject 将关闭虚拟键盘。在我们的前一个示例中,我们将此属性从 true 切换到 false,以便 DisplayObject 作为切换控件。

还有更多...

虽然使用 SoftKeyboardEvent 类通过 ActionScript 激活或关闭 Android 虚拟键盘并不是必须的,但它包含在示例类中,因为它允许我们使用一组额外的监听器函数来响应这些事件。

响应 Android 软键交互

AIR for Android 不包括支持调用通常出现在屏幕底部的原生操作系统选项菜单。但是,有一些方法可以模拟原生行为,我们将在本节中探讨这些方法。

在 Android 上,back 按钮的正常行为是通过应用程序状态向后退,直到回到主界面。再次按下 back 按钮将退出应用程序。默认情况下,AIR for Android 应用程序也具有这种行为。如果我们想覆盖这个默认行为,我们必须设置一个机制来拦截此交互并阻止它。

如何操作...

我们可以通过标准的 ActionScript 事件监听器响应软键事件。

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.KeyboardEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Keyboard;
    
    
  2. 声明一个 TextFieldTextFormat 对象,以允许在设备上显示可见输出:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 然后,我们将设置我们的 TextField,应用 TextFormat,并将其添加到 DisplayList 中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 32;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 现在,我们需要在 Stage 上设置一个事件监听器来响应键盘按键:

    protected function registerListeners():void {
    stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
    }
    
    
  5. 然后,我们将在 keyDown 方法中编写一个 switch/case 语句,以对特定的软键事件执行不同的操作。在这种情况下,我们将特定菜单项的名称输出到我们的 TextField

    protected function keyDown(e:KeyboardEvent):void {
    var key:uint = e.keyCode;
    traceField.text = key + " pressed!\n";
    switch(key){
    case Keyboard.BACK:{
    e.preventDefault();
    traceField.appendText("Keyboard.BACK");
    break;
    }
    case Keyboard.MENU:{
    traceField.appendText("Keyboard.MENU");
    break;
    }
    case Keyboard.SEARCH:{
    traceField.appendText("Keyboard.SEARCH");
    break;
    }
    }
    }
    
    
  6. 结果将类似于以下所示:如何操作...

它是如何工作的...

我们像为物理或虚拟键盘注册监听器一样,为这些 Android 设备软键注册监听器。如果使用 AIR for Android 开发 Android 应用程序,我们还可以通过Keyboard类访问BACK, MENUSEARCH常量。

注册键盘keyDown监听器,然后通过 switch/case 语句响应特定的键值,使我们能够适当地响应用户交互。例如,如果检测到MENU软键的交互,我们可以显示一个选项菜单。

还有更多...

Android 设备上还有一个HOME软键。这个按键无法通过 ActionScript 捕获,因为它仅用于从任何打开的应用程序返回用户到 Android 主屏幕。

注意

当我们想要取消BACK键的默认 Android 行为时,必须使用keyDown事件,因为keyUp事件将触发得太晚,根本无法捕获。

响应轨迹球和 D-Pad 事件

一些 Android 设备具有我们可以利用的额外物理输入。例如,摩托罗拉 Droid 有一个滑盖键盘,包括一个方向性的 D-pad,而 HTC Nexus One 有一个内置的轨迹球控制。

如何操作...

我们可以通过标准的 ActionScript 事件监听器响应轨迹球和 D-pad 事件。

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.KeyboardEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Keyboard;
    
    
  2. 声明一个Shape以及一个TextFieldTextFormat对象。这些将用于交互和视觉反馈。

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var box:Shape;
    
    
  3. 然后,我们将设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 32;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 接下来,构建一个方法来处理我们的Shape的创建并将其添加到DisplayList中。

    protected function setupBox():void {
    box = new Shape();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-100,-100,200,200);
    box.graphics.endFill();
    addChild(box);
    }
    
    
  5. Stage上设置一个事件监听器,以响应键盘按键:

    protected function registerListeners():void {
    stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
    }
    
    
  6. 现在,我们只需要编写一个 switch/case 语句,以响应 D-pad/轨迹球事件执行不同的动作。在这种情况下,我们改变Shape的位置,并将keyCode输出到TextField

    protected function keyDown(e:KeyboardEvent):void {
    var key:uint = e.keyCode;
    traceField.text = key + " pressed!";
    switch(key){
    case Keyboard.UP:{
    box.y -= 20;
    break;
    }
    case Keyboard.DOWN:{
    box.y += 20;
    break;
    }
    case Keyboard.LEFT:{
    box.x -= 20;
    break;
    }
    case Keyboard.RIGHT:{
    box.x += 20;
    break;
    }
    case Keyboard.ENTER:{
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    break;
    }
    }
    }
    
    
  7. 结果将类似于以下所示:如何操作...

工作原理...

我们像注册物理键盘上的Keyboard.UP, Keyboard.DOWN, Keyboard.LEFT, Keyboard.RIGHT以及Keyboard.ENTER键一样,为这些特殊控件注册监听器。在这个例子中,我们根据 D-pad/轨迹球被按下,在每个方向上移动目标Shape并重置位置。我们还将keyCode值输出到文本字段中。

还有更多...

需要注意的是,大多数 Android 设备没有这种专门的输入机制。如果我们注册了映射到这些键的事件,我们应始终提供一种替代方案。

第三章:空间移动:加速度计和地理定位传感器

本章节将涵盖以下内容:

  • 检测 Android 设备是否支持加速度计

  • 检测 Android 设备在 3D 空间中的移动

  • 调整加速度计传感器更新间隔

  • 通过加速度计传感器更新显示对象位置

  • 根据设备倾斜在竖屏和横屏之间切换

  • 检测设备是否支持地理定位传感器

  • 检测用户是否禁用了地理定位传感器

  • 获取设备地理定位传感器数据

  • 调整地理定位传感器更新间隔

  • 通过地理坐标获取地图数据

引言

Android 设备不仅配备了触摸面板、虚拟键盘和其他输入机制,还包括用于检测 3D 空间变化的加速度计传感器,以及细粒度(卫星)和粗粒度(三角测量)的地理定位。本章将探讨如何在基于 Flash 平台的 Android 应用中有意义地利用这些传感器。

本章中的所有内容都表示为纯 ActionScript 3 类,并不依赖于外部库或 Flex 框架。因此,我们可以将这些示例用于我们希望的任何 IDE 中。

检测 Android 设备是否支持加速度计

在针对 Android 操作系统开发项目时,确保设备支持某些传感器(如加速度计)总是一个好主意。在 Android 手机的情况下,这可能总是如此,但我们绝不能假设任何设备的功能。

如何实现...

我们需要使用加速度计 API 类来检测是否支持加速度计:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.sensors.Accelerometer;
    
    
  2. 声明一个TextFieldTextFormat对象对,以允许在设备上输出可见内容:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 然后,只需调用Accelerometer.isSupported来确认对此功能的支持:

    protected function checkAccelerometer():void {
    traceField.appendText("Accelerometer: " + Accelerometer.isSupported + "\n");
    }
    
    
  5. 此调用将返回一个布尔值truefalse,表示设备对此传感器的支持情况:如何实现...

工作原理...

检测设备是否包含加速度计传感器将决定用户是否能有效利用依赖于此类数据的应用程序。如果我们的查询返回为 false,那么我们有责任通知用户或提供某种替代方式,以从设备收集加速度数据作为互动形式。

检测 Android 设备在 3D 空间中的移动

Accelerometer类与设备的动作传感器协同工作,在设备在 3D 空间移动时测量并报告运动和加速度坐标。为了测量这些数据并对这些测量做出反应,我们必须执行某些操作,以便在我们的应用程序中收集加速度数据。

如何操作...

我们需要使用某些 ActionScript 类,以便监控加速度传感器的反馈:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.AccelerometerEvent;
    import flash.sensors.Accelerometer;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextField和一个TextFormat对象对,以便在设备上输出可见内容,以及一个Accelerometer对象:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var accelerometer:Accelerometer;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 我们现在必须实例化一个Accelerometer对象,以便注册一个AccelerometerEvent监听器。在这种情况下,我们将它调用一个名为movementDetected的函数。我们还首先检查设备是否支持Accelerometer API,通过检查Accelerometer.isSupported属性:

    protected function registerListeners():void {
    if(Accelerometer.isSupported) {
    accelerometer = new Accelerometer();
    accelerometer.addEventListener(AccelerometerEvent.UPDATE, movementDetected);
    }else{
    traceField.text = "Accelerometer not supported!";
    }
    }
    
    
  5. 我们现在能够通过movementDetected方法监控并响应设备移动:

    protected function movementDetected(e:AccelerometerEvent):void {
    traceField.text = "";
    traceField.appendText("Time: " + e.timestamp + "\n");
    traceField.appendText("X: " + e.accelerationX + "\n");
    traceField.appendText("Y: " + e.accelerationY + "\n");
    traceField.appendText("Z: " + e.accelerationZ + "\n");
    }
    
    
  6. 输出将类似于这样:如何操作...

它的工作原理...

通过注册一个事件监听器到AccelerometerEvent.UPDATE,我们能够检测到 Android 设备上的动作传感器报告的变化。有四个属性通过此事件报告回来:accelerationX, accelerationY, accelerationZtimestamp

  • accelerationX: 一个Number类型的值,它测量沿着 x 轴的加速度,当设备直立放置时,x 轴从左到右。当设备向右移动时,表示为正加速度。向左移动则表示为负数。

  • accelerationY: 一个Number类型的值,它测量沿着 y 轴的加速度,当设备直立放置时,y 轴从下到上。当设备向上移动时,表示为正加速度。向下移动则表示为负数。

  • accelerationZ: 一个Number类型的值,它测量沿着 z 轴的加速度,z 轴垂直于设备表面。当设备移动使得表面朝向天空时,表示为正加速度。将表面定位在地面上方角度时,将表示为负数。

  • timestamp: 一个int类型的值,它测量自应用程序初始化以来经过的毫秒数。这可以用来随时间跟踪更新事件。

还有更多...

加速度传感器在创建基于平衡的 Android 游戏时经常被使用,例如让一个球基于设备倾斜通过迷宫,但我们也可以以任何我们想要的方式来使用这些数据,以监测空间、倾斜或基于其他动作的变化。

调整加速度传感器更新间隔

虽然大多数应用程序可能默认的加速度计传感器更新间隔就足够了,但如果我们要针对特定目的加快或减慢这个间隔该怎么办?

如何操作...

我们需要使用Accelerometer类中包含的方法来更改加速度计传感器更新间隔:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.AccelerometerEvent;
    import flash.events.TouchEvent;
    import flash.sensors.Accelerometer;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一些要在示例中使用的对象。首先,一个TextField和一个TextFormat对象对,以便在设备上允许可见输出,以及一个Accelerometer对象。

  3. 然后,我们还需要使用一个Number来跟踪我们的间隔量。

  4. 还需要两个Sprite对象供用户与之交互。

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var accelerometer:Accelerometer;
    private var accelerometerInterval:Number;
    private var boxUp:Sprite;
    private var boxDown:Sprite;
    
    
  5. 我们现在将设置TextField,应用TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0xFFFFFF;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  6. 为了通过触摸检测用户输入,我们将创建两个Sprite实例并将每个实例添加到Stage中。为了在我们与这些对象注册的任何事件监听器中区分Sprite实例,我们将为每个Sprite提供一个唯一的name属性:

    protected function setupBoxes():void {
    boxUp = new Sprite();
    boxUp.name = "boxUp";
    boxUp.graphics.beginFill(0xFFFFFF, 1);
    boxUp.x = 20;
    boxUp.y = stage.stageHeight/2;
    boxUp.graphics.drawRect(0,0,100,80);
    boxUp.graphics.endFill();
    addChild(boxUp);
    boxDown = new Sprite();
    boxDown.name = "boxDown";
    boxDown.graphics.beginFill(0xFFFFFF, 1);
    boxDown.x = stage.stageWidth - 120;
    boxDown.y = stage.stageHeight/2;
    boxDown.graphics.drawRect(0,0,100,80);
    boxDown.graphics.endFill();
    addChild(boxDown);
    }
    
    
  7. 首先,我们还要检查设备是否实际支持加速度计 API,通过检查Accelerometer.isSupported属性。

  8. 然后,我们需要将多点触控 API 的特定输入模式设置为通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量来支持触摸输入。

  9. 每个 Sprite 将注册一个TouchEvent.TOUCH_TAP监听器,这样它就能够通过触摸轻敲来调用一个方法来改变更新间隔。

  10. 现在,我们可以实例化一个Accelerometer对象并调用setRequestedUpdateInterval方法,此方法调用需要传入以毫秒为单位的间隔。

  11. 我们还将注册一个事件监听器以响应任何设备移动:

    protected function registerListeners():void {
    if(Accelerometer.isSupported) {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    boxUp.addEventListener(TouchEvent.TOUCH_TAP, shiftInterval);
    boxDown.addEventListener(TouchEvent.TOUCH_TAP, shiftInterval);
    accelerometer = new Accelerometer();
    accelerometerInterval = 100;
    accelerometer.setRequestedUpdateInterval (accelerometerInterval);
    accelerometer.addEventListener(AccelerometerEvent.UPDATE, movementDetected);
    }else{
    traceField.text = "Accelerometer not supported!";
    }
    }
    
    
  12. 我们的shiftInterval方法现在将响应我们创建的两个Sprite框拦截的任何触摸轻敲。我们将检查每个Sprite被赋予的name属性,并相应地调整accelerometerInterval

    protected function shiftInterval(e:TouchEvent):void {
    switch(e.target.name){
    case "boxUp":{
    accelerometerInterval += 100;
    break;
    }
    case "boxDown":{
    accelerometerInterval -= 100;
    break;
    }
    }
    if(accelerometerInterval < 0){
    accelerometerInterval = 0;
    }
    accelerometer.setRequestedUpdateInterval(accelerometerInterval);
    }
    
    
  13. 加速度计传感器更新间隔现在将调用以下函数,该函数将通过我们的TextField输出检测到的移动和间隔数据:

    protected function movementDetected(e:AccelerometerEvent):void {
    traceField.text = "Interval: " + accelerometerInterval + "\n\n";
    traceField.appendText("Time: " + e.timestamp + "\n");
    traceField.appendText("X: " + e.accelerationX + "\n");
    traceField.appendText("Y: " + e.accelerationY + "\n");
    traceField.appendText("Z: " + e.accelerationZ + "\n");
    }
    
    
  14. 结果将类似于以下内容:如何操作...

工作原理...

通过setRequestedUpdateInterval()设置加速度计更新间隔,我们能够根据特定应用程序中的情况调整此间隔。在前面演示类中,我们渲染了两个作为增加和减少TouchEvent.TOUCH_TAP事件受体的Sprites。轻敲这些DisplayObjects将会增加或减少加速度计更新间隔,这个间隔通过屏幕上的TextField进行监控。

还有更多内容...

请注意,默认的加速度传感器更新间隔取决于运行我们应用程序的设备。这种策略也可以用来尝试平衡不同设备间的间隔。

通过加速度事件更新显示对象位置

创建 Android 设备上的各种游戏或应用程序时可以使用加速度传感器。这种数据更频繁的用途之一是响应加速度更新事件数据,更新 StageDisplayObject 的位置。

如何操作...

我们需要使用某些 ActionScript 类,以便通过 DisplayObject 实例监听加速度反馈。在这个例子中,我们将使用一个简单的 Shape 对象,根据这些数据改变其位置:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.AccelerometerEvent;
    import flash.sensors.Accelerometer;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 我们现在将声明一些要在示例中使用的对象。首先,一个 TextFieldTextFormat 对象对,以及一个 Shape 以便在设备上显示输出。

  3. 我们还必须声明一个 Accelerometer 对象,以便监听和响应设备移动:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var box:Shape;
    private var accelerometer:Accelerometer;
    
    
  4. 我们现在将设置 TextField,应用 TextFormat,并将其添加到 DisplayList 中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0xFFFFFF;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  5. 创建一个名为 box 的新 Shape 对象,使用 Graphics API 绘制一个矩形,并将其添加到 Stage 上:

    protected function setupBox():void {
    box = new Shape();
    box.graphics.beginFill(0xFFFFFF, 1);
    box.x = stage.stageWidth/2;
    box.y = stage.stageHeight/2;
    box.graphics.drawRect(-100,-100,200,200);
    box.graphics.endFill();
    addChild(box);
    }
    
    
  6. 我们现在必须实例化一个 Accelerometer 对象,以便注册一个 AccelerometerEvent 监听器。在这种情况下,我们将让它调用一个名为 movementDetected 的函数。我们还要首先检查设备是否支持 Accelerometer API,通过检查 Accelerometer.isSupported 属性:

    protected function registerListeners():void {
    if(Accelerometer.isSupported) {
    accelerometer = new Accelerometer();
    accelerometer.addEventListener(AccelerometerEvent.UPDATE, movementDetected);
    }else{
    traceField.text = "Accelerometer not supported!";
    }
    }
    
    
  7. 现在,我们可以通过调整 Shape 对象的 x 和 y 坐标,通过 movementDetected 方法监听和响应设备移动,基于 AccelerometerEvent.UPDATE 事件报告的 accelerationXaccelerationY 数据。

  8. 在以下函数中,我们将执行一系列检查,以确保当设备倾斜时 Shape 不会移出 Stage。我们还将输出 Sprite 的 x 和 y 属性到一个 TextField

    protected function movementDetected(e:AccelerometerEvent):void {
    traceField.text = "";
    var speed:Number = 20;
    if(box.x > box.width/2){
    box.x -= Math.floor(e.accelerationX*speed);
    }else{
    box.x = box.width/2;
    }
    if(box.x < stage.stageWidth-(box.width/2)){
    box.x -= Math.floor(e.accelerationX*speed);
    }else{
    box.x = stage.stageWidth-(box.width/2);
    }
    if(box.y > box.height/2){
    box.y += Math.floor(e.accelerationY*speed);
    }else{
    box.y = box.height/2;
    }
    if(box.y < stage.stageHeight-(box.height/2)){
    box.y += Math.floor(e.accelerationY*speed);
    }else{
    box.y = stage.stageHeight-(box.height/2);
    }
    traceField.appendText("box.x: " + box.x + "\n");
    traceField.appendText("box.y: " + box.y + "\n");
    }
    
    
  9. 结果输出将类似于以下内容:如何操作...

工作原理...

通过注册 AccelerometerEvent.UPDATE 事件监听器,我们可以检测到 Android 设备上运动传感器报告的变化。使用 ActionScript,我们可以对这些运动和倾斜的变化做出响应,如代码示例所示,根据报告的传感器数据在屏幕上移动 DisplayObject

在示例中,我们不仅在屏幕上移动 Shape 对象,同时通过多个条件语句考虑对象的宽度、高度和检测到的屏幕尺寸,确保形状不会离开屏幕。

根据设备倾斜切换横屏和竖屏

大多数 Android 设备允许用户以纵向和横向视图交互。当设备以 y 轴从上到下对齐时,启用纵向模式;而通过将设备持握以 y 轴从左到右测量时,启用横向模式。通过使用加速度计传感器报告的数据,我们可以知道这些移动何时发生并在我们的应用程序内响应该移动。

如何操作...

我们需要使用Accelerometer API 来检测设备旋转和倾斜:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.AccelerometerEvent;
    import flash.sensors.Accelerometer;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 我们现在将声明一些在示例中要使用的对象。首先,一个TextFieldTextFormat对象对,以允许在设备上输出可见内容。

  3. 我们还必须声明一个Accelerometer对象,以便监控并响应用户设备的移动:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var accelerometer:Accelerometer;
    
    
  4. 现在,我们将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0xFFFFFF;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  5. 然后,我们必须创建一个Accelerometer实例,并为其分配一个类型为AccelerometerEvent.UPDATE的事件监听器。每当检测到加速度计数据发生变化时,这将触发movementDetected方法。我们还首先检查设备是否实际支持加速度计 API,通过检查Accelerometer.isSupported属性:

    protected function registerListeners():void {
    if(Accelerometer.isSupported) {
    accelerometer = new Accelerometer();
    accelerometer.addEventListener(AccelerometerEvent.UPDATE, movementDetected);
    }else{
    traceField.text = "Accelerometer not supported!";
    }
    }
    
    
  6. 在我们的movementDetected方法中,我们只需监控传感器报告的加速度数据,并相应地调整我们的应用程序。我们还将输出数据到我们的TextField以监控设备移动:

    protected function movementDetected(e:AccelerometerEvent):void {
    traceField.text = "";
    traceField.appendText("Time: " + e.timestamp + "\n");
    traceField.appendText("X: " + e.accelerationX + "\n");
    traceField.appendText("Y: " + e.accelerationY + "\n");
    traceField.appendText("Z: " + e.accelerationZ + "\n");
    if(e.accelerationY > 0.5){
    traceField.appendText("\n\n\nPORTRAIT");
    }else{
    traceField.appendText("\n\n\nLANDSCAPE");
    }
    }
    
    
  7. 结果将类似于以下这样:如何操作...

工作原理...

当我们的应用程序检测到加速度移动时,movementDetected方法将报告有关设备x, yz轴的数据。如果我们监控所报告的加速度值,我们可以以考虑垂直方向的方式响应用户设备的倾斜,从而知道是否需要调整Stage上的元素以适应纵向或横向观看。

还有更多内容...

在这个示例中,我们使用纯 ActionScript 来检测加速度计传感器数据并响应该数据。在开发应用程序时使用移动 Flex 框架,通过在Flex Mobile Project设置中选择Mobile Settings对话框中的Automatically reorient(自动重新定向)选项,我们可以让框架处理设备方向。

还有更多...

另请参阅...

第六章,结构适应性:处理设备布局和缩放,也包含有关使用其他检测方法适应设备方向变化的信息。

检测设备是否支持地理定位传感器

在针对 Android 操作系统开发项目时,确保某些传感器(如地理定位传感器)实际上在设备上得到支持总是一个好主意。在 Android 设备的情况下,这可能总是如此,但我们绝不应假设任何设备的功能。

如何操作...

我们需要使用内部类来检测地理定位 API 是否得到支持:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.sensors.Geolocation;
    
    
  2. 声明一个TextFieldTextFormat对象对,以允许在设备上可见输出:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将TextField添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 然后,只需调用Geolocation.isSupported以确认对此功能的的支持:

    protected function checkGeolocation():void {
    traceField.appendText("Geolocation: " + Geolocation.isSupported);
    }
    
    
  5. 此调用将返回一个布尔值truefalse,表示设备是否支持此传感器。此结果将输出到我们创建的TextField中:如何操作...

工作原理...

检测设备是否包含地理定位传感器将决定用户是否可以有效利用依赖于此类数据的应用程序。如果我们的查询返回为 false,那么由我们来通知用户或提供某种替代方式来收集此类数据。这通常由用户手动输入特定位置数据来处理。

另请参阅…

应用开发者必须通过一个 Android 清单文件请求地理定位传感器的可用性。为了让我们的应用程序使用这些传感器,必须在清单文件中声明权限。更多信息请参见第九章,清单保证:安全与 Android 权限

检测用户是否已禁用地理定位传感器

有许多原因可能导致 Android 地理定位传感器在我们的应用程序中不可用。用户可能为了节省电池寿命而关闭了此传感器,或者也许是我们作为开发者没有通过 Android 清单文件提供足够的权限以允许地理定位访问。无论如何,检查并如果传感器被禁用,以友好的提示回应是一个好主意。

如何操作...

我们需要检查Geolocation类中包含的muted属性:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.StatusEvent;
    import flash.sensors.Geolocation;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextFieldTextFormat对象对,以允许在设备上可见输出以及一个Geolocation对象:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var geo:Geolocation;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将TextField添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 现在,我们必须实例化一个Geolocation实例,并注册一个事件监听器,以确定在应用程序运行期间地理定位是否被禁用。

    注意

    现在我们已经定义了一个Geolocation实例,我们也可以随时简单地检查muted属性。

    protected function registerListeners():void {
    geo = new Geolocation();
    geo.addEventListener(StatusEvent.STATUS, checkGeolocationMuted);
    traceField.appendText("Geolocation Disabled? \n\n" + geo.muted);
    }
    
    
  5. 一旦我们调用了这个方法,检查 muted 属性。如果这返回true,我们可以访问设备地理位置传感器;如果返回false,那么我们知道传感器已被禁用:

    protected function checkGeolocationMuted(e:StatusEvent):void {
    traceField.appendText("Geolocation Disabled? \n\n" + geo.muted);
    }
    
    
  6. 结果将显示在设备屏幕上,如下截图所示:如何操作...

工作原理...

一旦我们构建了一个Geolocation实例,我们就可以访问该类的muted属性。通过检查Geolocation对象的muted属性,我们可以在应用程序中禁用地理位置功能,提示用户手动输入他们的位置,或者简单地通知用户必须启用设备上的地理位置传感器才能继续。

还有更多...

如我们的示例所示,Geolocation对象可以注册一个status事件,当muted属性发生变化时会提醒我们。我们可以使用它在应用程序运行时检测属性变化并相应地作出响应。

另请参阅…

应用程序开发者必须通过 Android 清单文件请求地理位置传感器的可用性。为了让我们的应用程序使用这些传感器,必须在清单文件中声明权限。更多信息请参见第九章。

获取设备地理位置传感器数据

Geolocation类可以用来揭示一组完整的属性,用于在全球范围内跟踪设备位置。这对于地图、天气、旅行和其他位置感知应用程序很有用。为了测量这些数据并对这些测量做出反应,我们必须执行某些操作。

如何操作...

我们需要使用某些 ActionScript 类来允许监控地理位置反馈:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.GeolocationEvent;
    import flash.sensors.Geolocation;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextFieldTextFormat对象对,允许设备上可见输出,以及一个Geolocation对象:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var geolocation:Geolocation;
    
    
  3. 我们现在将设置我们的TextField,应用TextFormat,并将TextField添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 我们现在必须实例化一个Geolocation对象,以注册一个GeolocationEvent监听器。在这种情况下,我们将调用一个名为geolocationUpdate的函数。我们还首先检查设备上是否实际支持 Geolocation API,通过检查Geolocation.isSupported属性:

    protected function registerListeners():void {
    if(Geolocation.isSupported) {
    geolocation = new Geolocation();
    geolocation.addEventListener(GeolocationEvent.UPDATE, geolocationUpdate);
    }else{
    traceField.text = "Geolocation not supported!";
    }
    }
    
    
  5. 我们现在能够通过geolocationUpdate方法监控并响应用户移动设备。在这种情况下,我们将收集到的数据输出到一个TextField

    protected function geolocationUpdate(e:GeolocationEvent):void {
    traceField.text = "";
    traceField.appendText("altitude: " + e.altitude + "\n");
    traceField.appendText("heading: " + e.heading + "\n");
    traceField.appendText("horizontal accuracy: " + e.horizontalAccuracy + "\n");
    traceField.appendText("latitude: " + e.latitude + "\n");
    traceField.appendText("longitude: " + e.longitude + "\n");
    traceField.appendText("speed: " + e.speed + "\n");
    traceField.appendText("timestamp: " + e.timestamp + "\n");
    traceField.appendText("vertical accuracy: " + e.verticalAccuracy);
    }
    
    
  6. 输出将如下所示:如何操作...

工作原理...

通过注册一个事件监听器到GeolocationEvent.UPDATE,我们能够检测到 Android 设备上报的地理传感器变化。请注意,并不是每个 Android 设备都能报告所有这些属性;这将取决于所使用的设备。通过这个事件报告回的共有八个可能的属性:altitude, heading, horizontalAccuracy, latitude, longitude, speed, timestampverticalAccuracy

  • altitude: 一个Number类型的值,表示当前的海拔高度,以米为单位。

  • heading: 一个Number类型的值,表示移动的方向,以度为单位。

  • horizontalAccuracy: 一个Number类型的值,表示传感器测量的水平精度,以米为单位。

  • latitude: 一个Number类型的值,表示当前设备的纬度,以度为单位。

  • longitude: 一个Number类型的值,表示当前设备的经度,以度为单位。

  • speed: 一个Number类型的值,表示每秒的速度,以米为单位。

  • timestamp: 一个int类型的值,表示自应用程序初始化以来的毫秒数。

  • verticalAccuracy: 一个Number类型的值,表示传感器测量的垂直精度,以米为单位。

调整地理传感器更新间隔

尽管大多数应用程序可能默认的地理传感器更新间隔就足够了,但如果我们想为特定目的加快或减慢这个间隔呢?

如何操作...

我们需要使用Geolocation类中包含的方法来更改地理传感器更新间隔:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.GeolocationEvent;
    import flash.events.TouchEvent;
    import flash.sensors.Geolocation;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一些在示例中要使用的对象。首先是一个TextField和一个TextFormat对象,以便在设备上允许可见输出,以及一个Geolocation对象。

  3. 然后,我们还需要使用一个Number来跟踪我们的间隔量。还需要两个Sprite对象供用户交互。

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var geolocation:Geolocation;
    private var geolocationInterval:Number;
    private var boxUp:Sprite;
    private var boxDown:Sprite;
    
    
  4. 我们现在将设置我们的TextField,应用一个TextFormat,并将TextField添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  5. 为了检测用户的触摸输入,我们将创建两个Sprite实例并将它们各自添加到Stage中。为了在注册这些对象的事件监听器中区分Sprite实例,我们将为每个Sprite提供一个唯一的名称属性:

    protected function setupBoxes():void {
    boxUp = new Sprite();
    boxUp.name = "boxUp";
    boxUp.graphics.beginFill(0xFFFFFF, 0.6);
    boxUp.x = 20;
    boxUp.y = stage.stageHeight/2;
    boxUp.graphics.drawRect(0,0,100,80);
    boxUp.graphics.endFill();
    addChild(boxUp);
    boxDown = new Sprite();
    boxDown.name = "boxDown";
    boxDown.graphics.beginFill(0xFFFFFF, 0.6);
    boxDown.x = stage.stageWidth - 120;
    boxDown.y = stage.stageHeight/2;
    boxDown.graphics.drawRect(0,0,100,80);
    boxDown.graphics.endFill();
    addChild(boxDown);
    }
    
    
  6. 我们首先检查设备是否实际支持 Geolocation API,通过检查Geolocation.isSupported属性。

  7. 我们还需要将多点触控 APIs 的特定输入模式设置为支持触摸输入,通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量。每个Sprite将注册一个TouchEvent.TOUCH_TAP监听器,这样它就能够通过触摸点击来调用一个方法,以改变更新间隔。

  8. 现在,我们还可以实例化一个Geolocation对象并调用setRequestedUpdateInterval方法,该方法需要传递一个以毫秒为单位的间隔到方法调用中。

  9. 我们将注册一个事件监听器以响应任何设备移动:

    protected function registerListeners():void {
    if(Geolocation.isSupported) {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    boxUp.addEventListener(TouchEvent.TOUCH_TAP, shiftInterval);
    boxDown.addEventListener(TouchEvent.TOUCH_TAP, shiftInterval);
    geolocation = new Geolocation();
    geolocationInterval = 100;
    geolocation.setRequestedUpdateInterval(geolocationInterval);
    geolocation.addEventListener(GeolocationEvent.UPDATE, geolocationUpdate);
    }else{
    traceField.text = "Geolocation not supported!";
    }
    }
    
    
  10. 我们的shiftInterval方法现在将响应我们创建的两个Sprite框拦截的任何触摸点击。我们将检查每个Sprite被赋予了什么名称属性,并相应地调整accelerometerInterval

    protected function shiftInterval(e:TouchEvent):void {
    switch(e.target.name){
    case "boxUp":{
    geolocationInterval += 100;
    break;
    }
    case "boxDown":{
    geolocationInterval -= 100;
    break;
    }
    }
    if(geolocationInterval < 0){
    geolocationInterval = 0;
    }
    geolocation.setRequestedUpdateInterval(geolocationInterval);
    }
    
    
  11. 现在地理传感器更新间隔将会调用以下函数,该函数将通过我们的TextField输出检测到的移动和间隔数据:

    protected function geolocationUpdate(e:GeolocationEvent):void {
    traceField.text = "Interval: " + geolocationInterval + "\n\n";
    traceField.appendText("altitude: " + e.altitude + "\n");
    traceField.appendText("heading: " + e.heading + "\n");
    traceField.appendText("horizontal accuracy: " + e.horizontalAccuracy + "\n");
    traceField.appendText("latitude: " + e.latitude + "\n");
    traceField.appendText("longitude: " + e.longitude + "\n");
    traceField.appendText("speed: " + e.speed + "\n");
    traceField.appendText("timestamp: " + e.timestamp + "\n");
    traceField.appendText("vertical accuracy: " + e.verticalAccuracy);
    }
    
    
  12. 结果将类似于以下截图:如何操作...

工作原理...

通过setRequestedUpdateInterval()设置地理定位更新间隔,我们能够根据特定应用程序中的情况调整此间隔。在前一节的demonstration类中,我们渲染了两个作为增加和减少TouchEvent.TOUCH_TAP事件受体的Sprites。点击这些DisplayObjects将会增加或减少地理定位更新间隔,这通过屏幕上的TextField进行监控。

还有更多...

请注意,默认的地理定位传感器更新间隔取决于运行我们应用程序的设备。这种策略也可以用来尝试平衡不同设备之间的间隔。然而,有些事情完全在我们的控制之外。例如,如果用户深处建筑物内并且 GPS 信号差,更新间隔实际上可能超过一分钟。应考虑此类各种因素。

通过地理坐标获取地图数据

使用地理坐标获取地图是 ActionScript 地理定位 API 的基本用途之一。在本教程中,我们将研究如何使用 Google 地图 API for Flash 在舞台上渲染地图,并基于设备地理传感器报告的纬度和经度坐标生成标记。

准备中...

在开始本教程之前,我们需要采取几个步骤。这些步骤将准备我们的项目与适当的代码库,并允许我们访问 Google 地图服务:

  1. 首先,我们必须从code.google.com/apis/maps/documentation/flash/下载 Google 地图 API for Flash。

  2. 该软件包将包括两个独立的.swc文件。一个用于 Flex,另一个用于 ActionScript 项目。在此示例中,我们将提取纯AS3 .swc到本地硬盘。

  3. 从同一个 URL(在第一点)点击阅读注册 Google 地图 API 密钥的链接以生成 API 密钥并注册一个 URL。完成此示例你需要这两样东西。准备中...

  4. 现在,通过在 Flash Builder 中通过ActionScript Build Path属性对话框添加.swc(你也可以直接将.swc拖到libs目录中),或者在 Flash Professional 中通过Advanced ActionScript Properties对话框,将 Google Maps SDK 包含到你的开发环境中:准备就绪...

  5. 现在我们准备进行食谱操作。

如何操作...

我们需要创建我们的地图DisplayObject,为Geolocation API 更新生成事件监听器,并根据我们当前的位置调整地图属性:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.GeolocationEvent;
    import flash.geom.Point;
    import flash.sensors.Geolocation;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 接下来,我们希望导入 Google Maps SDK 中包含的许多类。这些类允许我们在Stage上渲染Map,监听特定于地图的事件,并在我们当前的位置渲染Marker

    import com.google.maps.LatLng;
    import com.google.maps.Map;
    import com.google.maps.MapEvent;
    import com.google.maps.MapType;
    import com.google.maps.overlays.Marker;
    
    
  3. 我们现在将创建一些在本示例中要使用的对象引用。首先,一个TextFieldTextFormat对象对,以便在设备上允许可见输出,以及一个Geolocation对象。

  4. 然后,我们还需要使用MapLatLng对象来渲染我们位置的地图:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var geolocation:Geolocation;
    private var map:Map;
    private var coordinates:LatLng;
    
    
  5. 现在我们准备通过传递我们在注册 Google 时设置好的 API 密钥和 URL 来创建我们的Map,并将Map添加到显示列表中:

    protected function setupMap():void {
    map = new Map();
    map.key = "{GOOGLE_MAPS_API_KEY}";
    map.url = "{APP_URL}";
    map.sensor = "true";
    map.setSize(new Point(stage.stageWidth, stage.stageHeight));
    addChild(map);
    }
    
    
  6. 我们现在将设置我们的TextField,应用TextFormat,并将TextField添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  7. 重要的是,我们需要为地理位置更新和Map完成事件注册监听器,这样我们才能读取坐标数据,并知道我们的Map何时准备好交互。我们还首先检查设备是否实际支持 Geolocation API,通过检查Geolocation.isSupported属性来实现:

    protected function registerListeners():void {
    if(Geolocation.isSupported) {
    geolocation = new Geolocation();
    geolocation.addEventListener(GeolocationEvent.UPDATE, geolocationUpdate);
    map.addEventListener(MapEvent.MAP_READY, mapReady);
    }else{
    traceField.text = "Geolocation not supported!";
    }
    }
    
    
  8. 由于地理位置更新是在本地处理的,这很可能是我们的第一个事件监听器被触发。我们将通过此事件从设备地理位置传感器提供的数据中获取longitude(经度)和latitude(纬度),并由此创建一个LatLong对象,在初始化时将其输入到Map中:

    protected function geolocationUpdate(e:GeolocationEvent):void {
    traceField.text = "";
    traceField.appendText("latitude:\n" + e.latitude + "\n\n");
    traceField.appendText("longitude:\n" + e.longitude);
    coordinates = new LatLng(e.latitude, e.longitude);
    }
    
    
  9. 一旦我们的mapReady监听器方法触发,我们就已经有了通过Map显示我们当前坐标的必要坐标信息,同时在这个精确的位置渲染一个简单的Marker

    protected function mapReady(e:MapEvent):void {
    map.setCenter(coordinates, 16, MapType.NORMAL_MAP_TYPE);
    var marker:Marker = new Marker(map.getCenter());
    map.addOverlay(marker);
    }
    
    
  10. 结果将类似于这样:如何操作...

它的工作原理...

通过接入像 Google Maps 这样的地图服务,我们可以监听本地设备地理位置更新,并将必要的数据输入到地图服务中以执行众多任务。

在这个例子中,我们只是将Map中心对准设备坐标,并在Map上放置一个Marker覆盖层。每次使用这类服务时,彻底阅读文档了解服务的可能性和限制总是一个好主意。

url 属性应设置为在线位置,其中描述了应用程序的目的和范围,根据谷歌的要求。

注意事项

我们将 Map 实例的 sensor 属性设置为 true。如果 Map 是基于谷歌设备地理位置传感器反应数据,这是必需的。如果我们只是允许用户输入坐标并通过这种方式调整 Map 的位置,我们会将 sensor 属性设置为 false

还有更多...

在这个案例中,我们使用了谷歌地图 API 的 Flash 版本。它相当健壮,但您可能希望使用其他地图系统,如 Yahoo! 地图、MapQuest 或其他服务。这是没问题的,因为它们都需要类似的信息;只是具体的 API 设置会有所不同。

第四章:视觉和音频输入:相机和麦克风访问

本章将涵盖以下内容:

  • 检测相机和麦克风支持

  • 使用传统相机 API 保存捕捉到的图像

  • 使用移动设备 CameraUI API 保存捕捉到的照片

  • 使用移动设备 CameraUI API 保存捕捉到的视频

  • 使用设备麦克风监控音频样本数据

  • 记录麦克风音频样本数据

引言

相机和麦克风是大多数移动设备和 Android 设备上的标准配件。本章将涵盖从访问相机和拍照,录制视频数据,以及从设备麦克风捕获原始音频并将其编码为 WAV 或 MP3 以便在其他平台和系统上使用的一切内容。

本章中的所有示例都表示为纯 ActionScript 3 类,并且不依赖于外部库或 Flex 框架。因此,我们可以使用我们希望的任何 IDE 中的这些示例。

检测相机和麦克风支持

几乎所有的 Android 设备都配备了用于捕捉静态图像和视频的相机硬件。现在许多设备都拥有前后置摄像头。了解默认设备相机是否可通过我们的应用程序使用非常重要。我们绝不能假设某些硬件的可用性,无论它们在设备中多么普遍。

同样,当捕捉视频或音频数据时,我们也需要确保能够访问设备麦克风。

如何操作...

我们将确定我们的 Android 设备上可用的音频和视频 API:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.media.Camera;
    import flash.media.CameraUI;
    import flash.media.Microphone;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextFieldTextFormat对象对,以允许在设备上可见输出:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用TextFormat,并将TextField添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 现在,我们必须检查这些对象的isSupported属性。我们在这里创建一个方法来对所有三个进行检查,并将结果写入TextField:

    protected function checkCamera():void {
    traceField.appendText("Camera: " + Camera.isSupported + "\n");
    traceField.appendText("CameraUI: " + CameraUI.isSupported + "\n");
    traceField.appendText("Microphone: " + Microphone.isSupported + "\n");
    }
    
    
  5. 我们现在知道特定设备的视频和音频输入功能,并可以相应地做出反应:如何操作...

工作原理...

这三个类都拥有一个属性isSupported,我们可以随时调用它来验证在特定 Android 设备上的支持情况。传统的Camera和针对移动设备的CameraUI都指的是同一硬件相机,但它们是处理闪光灯与相机本身交互的完全不同的类,因为CameraUI依赖于设备默认相机应用程序完成所有捕捉工作,而Camera仅在 Flash 环境中工作。

注意

以这种方式也支持传统的Microphone对象。

还有更多...

需要注意的是,尽管许多 Android 设备配备了不止一个摄像头,但只有主摄像头(和麦克风)会对我们的应用程序可见。随着 Android 的发展,可能会增加对多个摄像头和其他传感器的支持。

使用传统的摄像头 API 保存捕捉到的图像

当通过 Flash 播放器为网页编写应用程序,或者为桌面应用使用 AIR 时,我们可以通过 ActionScript 访问Camera类。这使得我们可以访问连接到我们使用的任何机器上的不同摄像头。在 Android 上,我们仍然可以使用Camera类来访问设备上的默认摄像头,并获取它提供的视频流用于各种事情。在本例中,我们将简单地从Camera的输入中抓取一个静态图像,并将其保存到 Android 的CameraRoll中。

如何操作...

我们将构建一个Video对象来绑定Camera的流,并使用BitmapData方法捕获并保存我们渲染的图像,使用移动设备的CameraRoll API:

  1. 至少,我们需要将以下类导入到我们的项目中:

    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.media.Camera;
    import flash.media.CameraRoll;
    import flash.media.Video;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 现在我们必须声明进行摄像头访问和文件引用所需的实例对象:

    private var video:Video;
    private var camera:Camera;
    private var capture:BitmapData;
    private var cameraRoll:CameraRoll;
    private var videoHolder:Sprite;
    
    
  3. 初始化一个Video对象,传入所需的宽度和高度,并将其添加到DisplayList

    protected function setupVideo():void {
    videoHolder = new Sprite();
    videoHolder.x = stage.stageWidth/2;
    videoHolder.y = stage.stageHeight/2;
    video = new Video(360, 480);
    videoHolder.addChild(video);
    video.x = -180;
    video.y = -240;
    videoHolder.rotation = 90;
    addChild(videoHolder);
    }
    
    
  4. 初始化一个Camera对象,并使用setMode方法来指定宽度、高度和每秒帧数,然后再将Camera附加到DisplayList上的Video

    protected function setupCamera():void {
    camera = Camera.getCamera();
    camera.setMode(480, 360, 24);
    video.attachCamera(camera);
    }
    
    
  5. 我们现在将在Stage上注册一个类型为TOUCH_TAPTouchEvent监听器。这将使用户可以通过点击设备屏幕来捕获摄像头显示的快照:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    stage.addEventListener(TouchEvent.TOUCH_TAP, saveImage);
    }
    
    
  6. 要从摄像头输入中捕获图像,我们将初始化我们的BitmapData对象,使其与Video对象的宽度和高度相匹配,并使用draw方法将Video的像素转换为BitmapData

  7. 要将我们获取的图像保存到设备上,我们必须初始化一个CameraRoll对象,并调用addBitmapData(),传入我们使用Video对象像素创建的BitmapData对象。我们还将确定此设备是否支持addBitmapData()方法,通过验证CameraRoll.supportsAddBitmapData是否等于true

    protected function saveImage(e:TouchEvent):void {
    capture = new BitmapData(360, 480);
    capture.draw(video);
    cameraRoll = new CameraRoll();
    if(CameraRoll.supportsAddBitmapData){
    cameraRoll.addBitmapData(capture);
    }
    }
    
    

    如何操作...

  8. 如果我们现在检查我们的 Android 图库,我们会找到保存的图像:如何操作...

工作原理...

这大部分操作与在桌面上的正常 Flash 平台开发完全相同。将一个Camera附加到一个Video上,将Video添加到DisplayList,然后根据你的特定应用程序进行需要的操作。在本例中,我们只是简单地捕获显示的BitmapData作为图像。

然而,CameraRoll类是特定于移动应用开发的,因为它总是指的是设备相机存储所产生照片的目录。如果你想要将这些图片保存在不同的目录中,我们可以使用FileFileReference对象来实现,但这需要用户进行更多操作。

注意,在使用Camera类时,相机的硬件方向是横屏的。我们可以通过将应用限制为横屏模式,或者像我们在示例类中所做的那样通过旋转和额外的操作来处理这个问题。在这种情况下,我们使用了videoHolder.rotation对图像应用了 90 度旋转,以在读取BitmapData时考虑这个偏移。具体应用如何处理,可能不需要这样做。

还有更多...

传统 Camera 对象的其他用例包括将视频流发送到 Flash Media Server 进行直播,增强现实应用,或者实时点对点聊天。

另请参阅...

为了访问相机和存储,我们需要添加一些 Android 权限,分别是CAMERAWRITE_EXTERNAL_STORAGE。关于如何进行,请参考第十一章,最终考虑:应用程序编译和分发

使用移动端 CameraUI API 保存捕获的照片

使用新的CameraUI API(在移动 AIR SDK 中可用),我们可以执行与正常Camera API 不同的捕获过程。Mobile CameraUI类将利用默认的 Android 相机应用程序以及我们的自定义应用程序来捕获一张照片。

如何操作...

我们将设置一个CameraUI对象来调用原生的 Android 相机来捕获一张照片:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MediaEvent;
    import flash.events.TouchEvent;
    import flash.media.CameraUI;
    import flash.media.MediaType;
    import flash.media.MediaPromise;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextFieldTextFormat对象对,以便在设备上显示输出。这个示例还需要声明一个CameraUI对象:

    private var camera:CameraUI;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用TextFormat,并将TextField添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 22;
    traceFormat.align = "center";
    traceFormat.color = 0xFFFFFF;
    traceField = newTextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 实例化一个新的CameraUI实例,它将用于启动设备相机应用程序,并将文件信息返回给我们。如果某个特定设备不支持CameraUI对象,则会向我们的TextField输出一条消息表示这一点:

    protected function setupCamera():void {
    if(CameraUI.isSupported) {
    camera = new CameraUI();
    registerListeners();
    }else{
    traceField.appendText("CameraUI is not supported...");
    }
    }
    
    
  5. CameraUI对象上添加一个事件监听器,这样我们就可以知道捕获何时完成。我们还将为Stage注册一个触摸事件来启动捕获:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    camera.addEventListener(MediaEvent.COMPLETE, photoReady);
    stage.addEventListener(TouchEvent.TOUCH_TAP, launchCamera);
    }
    
    
  6. 要在 Android 设备上使用默认的相机应用,我们需要调用launch方法,并传入MediaType.IMAGE常量以指定我们希望拍摄一张照片:

    protected function launchCamera(e:TouchEvent):void {
    camera.launch(MediaType.IMAGE);
    }
    
    
  7. 现在,默认的安卓相机将初始化,允许用户拍摄照片。用户点击确定后,焦点将返回到我们的应用程序。如何操作...

  8. 最后,一旦我们完成捕获过程,将触发一个类型为MediaEvent.COMPLETE的事件,调用我们的photoReady方法。从中我们可以确定有关我们捕获的照片的某些细节。

    protected function photoReady(e:MediaEvent):void {
    var promise:MediaPromise = e.data;
    traceField.appendText("mediaType: " + promise.mediaType + "\n");
    traceField.appendText("relativePath: " + promise.relativePath + "\n");
    traceField.appendText("creationDate: " + promise.file.creationDate + "\n");
    traceField.appendText("extension: " + promise.file.extension + "\n");
    traceField.appendText("name: " + promise.file.name + "\n");
    traceField.appendText("size: " + promise.file.size + "\n");
    traceField.appendText("type: " + promise.file.type + "\n");
    traceField.appendText("nativePath: " + promise.file.nativePath + "\n");
    traceField.appendText("url: " + promise.file.url + "\n");
    }
    
    
  9. 输出将类似于这样:如何操作...

工作原理...

调用CameraUI.launch方法将请求安卓设备打开默认的相机应用程序,并允许用户拍照。在完成捕获过程并确认捕获的照片后,焦点将返回到我们的应用程序,同时返回包含在MediaEvent.COMPLETE事件对象中的一组关于新文件的数据。

在这一点上,我们的应用程序可以对返回的数据执行各种操作,甚至可以在应用程序中打开文件,假设文件类型可以被运行时加载和显示。

还有更多内容...

如果设备没有挂载存储卡,则默认的相机应用程序将不会加载。还需要注意的是,如果在捕获过程中设备内存不足,安卓可能会在过程完成前终止我们的应用程序。

另请参阅...

我们将在第五章讨论通过 AIR for Android 应用程序显示图像:富媒体展示:处理图像、视频和音频。

使用移动 CameraUI API 保存捕获的视频

使用新的CameraUI API(在移动 AIR SDK 中可用),我们可以执行与正常Camera API 不同的捕获过程。移动CameraUI类将利用默认的安卓相机应用程序,以及我们的自定义应用程序来捕获视频。

如何操作...

我们将设置一个CameraUI对象来调用原生的安卓相机以捕获视频:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MediaEvent;
    import flash.events.TouchEvent;
    import flash.media.CameraUI;
    import flash.media.MediaPromise;
    import flash.media.MediaType;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个TextFieldTextFormat对象对,以允许在设备上可见输出。对于此示例,还必须声明一个CameraUI对象:

    private var camera:CameraUI;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将TextField添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 22;
    traceFormat.align = "center";
    traceFormat.color = 0xFFFFFF;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 实例化一个新的CameraUI实例,它将用于启动设备相机应用程序并将文件信息返回给我们。如果特定设备不支持CameraUI对象,则会在我们的TextField中输出一条消息指示这一点。

    protected function setupCamera():void {
    if(CameraUI.isSupported) {
    camera = new CameraUI();
    registerListeners();
    }else{
    traceField.appendText("CameraUI is not supported...");
    }
    }
    
    
  5. CameraUI对象添加一个事件监听器,以便我们知道捕获何时完成。我们还将向Stage注册一个触摸事件来启动捕获:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    camera.addEventListener(MediaEvent.COMPLETE, videoReady);
    stage.addEventListener(TouchEvent.TOUCH_TAP, launchCamera);
    }
    
    
  6. 为了在安卓设备上使用默认相机应用程序,我们需要调用launch方法,并传入MediaType.VIDEO常量以指定我们希望捕获视频文件:

    protected function launchCamera(e:TouchEvent):void {
    camera.launch(MediaType.VIDEO);
    }
    
    
  7. 现在,默认的安卓相机将初始化,允许用户拍摄一些视频。当用户点击确定后,焦点将返回到我们的应用程序:如何操作...

  8. 最后,一旦我们完成捕获过程,将触发一个类型为MediaEvent.COMPLETE的事件,调用我们的videoReady方法。从中我们可以了解有关捕获的视频文件的某些详细信息:

    protected function videoReady(e:MediaEvent):void {
    var promise:MediaPromise = e.data;
    traceField.appendText("mediaType: " + promise.mediaType + "\n");
    traceField.appendText("relativePath: " + promise.relativePath + "\n");
    traceField.appendText("creationDate: " + promise.file.creationDate + "\n");
    traceField.appendText("extension: " + promise.file.extension + "\n");
    traceField.appendText("name: " + promise.file.name + "\n");
    traceField.appendText("size: " + promise.file.size + "\n");
    traceField.appendText("type: " + promise.file.type + "\n");
    traceField.appendText("nativePath: " + promise.file.nativePath + "\n");
    traceField.appendText("url: " + promise.file.url + "\n");
    }
    
    
  9. 输出将类似于这样:如何操作...

工作原理...

调用CameraUI.launch方法将请求安卓设备打开默认的相机应用程序,并允许用户捕获一些视频。在完成捕获过程并确认捕获的视频文件后,焦点将连同包含在MediaEvent.COMPLETE事件对象中的一组新文件数据一起返回到我们的应用程序。

在这一点上,我们的应用程序可以对返回的数据执行各种操作,甚至可以在应用程序中打开文件,假设文件类型可以被运行时加载和显示。这对于视频来说非常重要,因为某些设备将使用各种编解码器来编码捕获的视频,并非所有这些编解码器都与 Flash 平台兼容。

还有更多...

如果设备没有挂载存储卡,则默认相机应用程序将无法加载。还需要注意的是,如果在捕获过程中设备内存不足,安卓可能会在过程完成前终止我们的应用程序。

此外,除了MediaEvent.COMPLETE,我们还可以在类似过程中使用许多其他事件。例如,注册一个类型为Event.CANCEL的事件监听器,以响应用户取消视频保存。

另请参阅...

我们将在第五章中讨论通过 AIR for Android 应用程序播放视频文件。

使用设备麦克风监控音频样本数据

通过监控从安卓设备麦克风通过 ActionScript Microphone API 返回的样本数据,我们可以收集有关正在捕获的声音的许多信息,并在我们的应用程序内执行响应。这种输入可以用于实用程序、学习模块,甚至游戏。

如何操作...

我们将设置一个事件监听器,以响应通过Microphone API 报告的样本数据:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.SampleDataEvent;
    import flash.media.Microphone;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextFieldTextFormat对象对,以允许在设备上可见输出。本示例还需要声明一个Microphone对象:

    private var mic:Microphone;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置TextField,应用TextFormat,并将TextField添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 现在,我们必须实例化我们的Microphone对象,并根据我们的需求和偏好调整codec, rate, silenceLevel等来设置它。这里我们使用setSilenceLevel()来确定应用程序应视为“声音”的最小输入水平,并将rate属性设置为44,表示我们将以 44kHz 的速率捕获音频数据。将setLoopBack()属性设置为 false 将防止捕获的音频通过设备扬声器播放:

    protected function setupMic():void {
    mic = Microphone.getMicrophone();
    mic.setSilenceLevel(0);
    mic.rate = 44;
    mic.setLoopBack(false);
    }
    
    
  5. 一旦我们实例化了Microphone对象,我们就可以注册各种事件监听器。在这个例子中,我们将监控来自设备麦克风的音频采样数据,因此我们需要为SampleDataEvent.SAMPLE_DATA常量注册我们的监听器:

    protected function registerListeners():void {
    mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onMicData);
    }
    
    
  6. 由于Microphone API 从 Android 设备输入生成采样数据,我们现在可以通过多种方式对此做出响应,因为我们能够访问有关Microphone对象本身的信息,更重要的是,我们可以访问采样字节,从而执行许多高级操作:

    public function onMicData(e:SampleDataEvent):void {
    traceField.text = "";
    traceField.appendText("activityLevel: " + e.target.activityLevel + "\n");
    traceField.appendText("codec: " + e.target.codec + "\n");
    traceField.appendText("gain: " + e.target.gain + "\n");
    traceField.appendText("bytesAvailable: " + e.data.bytesAvailable + "\n");
    traceField.appendText("length: " + e.data.length + "\n");
    traceField.appendText("position: " + e.data.position + "\n");
    }
    
    
  7. 输出将类似于这样。前三个值来自Microphone本身,后三个来自Microphone采样数据:如何操作...

它是如何工作的...

当我们实例化一个Microphone对象并注册一个SampleDataEvent.SAMPLE_DATA事件监听器时,我们可以轻松监控 Android 设备麦克风的各项属性以及正在收集的相关采样数据。然后我们可以以多种方式对这些数据做出响应。一个例子是依据Microphone.activityLevel属性在Stage上移动对象。另一个例子是将采样数据写入ByteArray以便稍后分析。

所有这些属性意味着什么?

  • activityLevel: 这是一个表示接收到的声音量的测量值

  • codec: 这表示正在使用的编解码器:Nellymoser 或 Speex

  • gain: 这是麦克风为声音信号提供的增强量

  • bytesAvailable: 这揭示了从当前位置到采样数据byteArray末尾的字节数量

  • length: 让我们知道采样数据byteArray的总长度

  • position: 这是我们的采样数据byteArray中的当前位置,以字节为单位

另请参阅...

为了访问麦克风,我们需要添加一些 Android 权限以RECORD_AUDIO。有关如何进行此操作的信息,请参考第十一章。

记录麦克风音频采样数据

对于开发者来说,使用从 Android 麦克风收集的音频采样数据最基础的事情之一,就是捕获数据并在应用程序中以某种方式使用它。本教程将演示如何保存和回放捕获的麦克风音频采样数据。

如何操作...

我们将使用一个事件监听器来响应通过Microphone API 报告的样本数据,通过将捕获的音频数据写入ByteArray,然后通过Sound对象在内部播放:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.SampleDataEvent;
    import flash.events.TouchEvent;
    import flash.media.Microphone;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.utils.ByteArray;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 声明一个TextFieldTextFormat对象对,以便在设备上输出可见内容。这个例子还需要声明一个Microphone对象。为了存储和播放样本数据,我们还需要声明一个ByteArray,以及一个SoundSoundChannel对:

    private var mic:Microphone;
    private var micRec:ByteArray;
    private var output:Sound;
    private var outputChannel:SoundChannel;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置TextField,应用TextFormat,并将TextField添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 然后,实例化一个Microphone对象,并根据我们的需求和偏好调整codecratesilenceLevel等来设置它。这里我们使用setSilenceLevel()来确定应用程序应考虑的最小输入级别作为“声音”,并将rate属性设置为44,表示我们将以 44kHz 的速率捕获音频数据。将setLoopBack()属性设置为 false 将防止捕获的音频通过设备扬声器播放。我们还将实例化一个ByteArray来保存所有拦截到的音频样本:

    protected function setupMic():void {
    mic = Microphone.getMicrophone();
    mic.setSilenceLevel(0);
    mic.rate = 44;
    mic.setLoopBack(false);
    micRec = new ByteArray();
    }
    
    
  5. 一旦我们实例化了MicrophoneByteArray对象,我们就可以注册一个事件监听器来启用触摸交互。一个简单的轻触就足够了:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    stage.addEventListener(TouchEvent.TOUCH_TAP, startRecording);
    traceField.text = "Tap to Record";
    }
    
    
  6. 一旦用户启动录音,我们将监控来自设备麦克风的音频样本数据,因此需要为SampleDataEvent.SAMPLE_DATA常量注册我们的监听器:

    protected function startRecording(e:TouchEvent):void {
    stage.removeEventListener(TouchEvent.TOUCH_TAP, startRecording);
    stage.addEventListener(TouchEvent.TOUCH_TAP, stopRecording);
    mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onMicData);
    traceField.text = "Recording Audio \nTap to Stop";
    }
    
    
  7. 由于Microphone API 从 Android 设备输入生成样本数据,我们可以访问音频样本数据字节,我们可以将其写入ByteArray以供以后使用:

    protected function onMicData(e:SampleDataEvent):void {
    micRec.writeBytes(e.data);
    }
    
    
  8. 要停止录音,我们需要从Microphone对象中移除SampleDataEvent.SAMPLE_DATA事件监听器:

    protected function stopRecording(e:TouchEvent):void {
    mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onMicData);
    stage.removeEventListener(TouchEvent.TOUCH_TAP, stopRecording);
    stage.addEventListener(TouchEvent.TOUCH_TAP, playBackAudio);
    traceField.text = "Tap to Playback";
    }
    
    
  9. 为了准备播放,我们将实例化一个新的Sound对象,并在其上注册一个SampleDataEvent.SAMPLE_DATA事件,就像我们之前对Microphone对象所做的那样。我们还将实例化一个SoundChannel对象,并调用我们Sound对象的play()方法来播放捕获的Microphone音频:

    protected function playBackAudio(e:TouchEvent):void {
    stage.removeEventListener(TouchEvent.TOUCH_TAP, playBackAudio);
    micRec.position = 0;
    output = new Sound();
    output.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataRequest);
    outputChannel = output.play();
    traceField.text = "Playing Audio";
    }
    
    
  10. 当我们对Sound对象调用play()方法时,它将从名为onSampleDataRequest的方法中开始收集生成的样本数据。我们现在需要创建这个方法,并让它遍历我们之前写入ByteArray对象的字节,这实际上是我们的捕获过程的反操作。

  11. 为了在应用程序中提供适当的播放,我们必须提供 2048 到 8192 个样本数据。建议尽可能使用更多的样本,但这还取决于采样频率。

    注意

    请注意,我们在同一个循环中两次调用writeFloat(),因为我们需要将数据表示为立体声对,每个通道一个。

  12. 在本例中使用 writeBytes() 时,我们实际上是通过 SampleDataEventSound 对象将声音数据输出,从而使应用程序能够产生声音:

    protected function onSampleDataRequest(e:SampleDataEvent):void {
    var out:ByteArray = new ByteArray();
    for(var i:int = 0; i < 8192 && micRec.bytesAvailable; i++ ) {
    var micsamp:Number = micRec.readFloat();
    // left channel
    out.writeFloat(micsamp);
    // right channel
    out.writeFloat(micsamp);
    }
    e.data.writeBytes(out);
    }
    
    
  13. 输出到我们的 TextField 的内容将根据当前应用程序状态的变化而变化:如何操作...

工作原理...

当我们实例化一个 Microphone 对象并注册一个 SampleDataEvent.SAMPLE_DATA 事件监听器时,我们可以轻松监控相关样本数据的收集,并将这些数据写入 ByteArray 以便稍后播放。随着新样本的到来,更多的数据被添加到 ByteArray 中,随着时间的推移构建声音数据。

通过向 Sound 对象注册 SampleDataEvent.SAMPLE_DATA 事件监听器,我们指示它在我们调用 play() 时主动寻找由特定方法生成的音频数据。在我们的示例中,我们遍历构建的 ByteArray并通过此方法将音频数据输出,通过 Sound 对象和相关联的 SoundChannel 实际播放录制的音频。

另请参阅...

ActionScript 中字节的使用是一个复杂的主题。要了解更多关于这个话题的信息,我们推荐 Thibault Imbert 的书 "你能用字节做什么?",该书可在www.bytearray.org/?p=711免费获取。

想要阅读关于音频文件播放的配方,请查看第五章。有关将捕获的音频数据保存到 Android 设备的信息,请参考第八章: 丰富访问:文件系统和本地数据库

第五章:富媒体展示:处理图像、视频和音频

本章节将涵盖以下食谱:

  • 从设备 cameraRoll 加载照片

  • 将 Pixel Bender 着色器效果应用于加载的图像

  • 从本地文件系统或通过 HTTP 播放视频文件

  • 通过 RTMP 播放远程视频文件

  • 从本地文件系统或通过 HTTP 播放音频文件

  • 生成音频频谱可视化器

  • 为您的应用程序生成音频音调

引言

本章节将包含多种展示图像数据和播放视频及音频流的食谱。这些食谱中包括的例子演示了从设备相机库加载图像的能力,对加载的图像应用 Pixel Bender 着色器,通过不同协议播放音频和视频,以及从声音生成视觉数据和原始声音数据。

Flash 平台作为全球领先的视频分发平台而闻名。在以下几页中,我们将看到这种体验和影响力绝不仅限于桌面和基于浏览器的计算。随着 AIR 2.6 和 Flash Player 10.2 中提供的 StageVideo 等新功能,Flash 正在成为在保持设备电池寿命的同时提供更佳用户体验的更强大的视频交付平台。

从设备 cameraRoll 加载照片

安卓操作系统有一个中央存储库,用于存储用户可能安装的各种相机应用程序捕获的照片。AIR for Android 中提供了 API,允许 Flash 开发者专门针对这个存储库进行操作,并在应用程序中显示。

如何操作...

我们必须使用移动CameraRoll API 直接浏览到设备相机胶卷,并选择一张照片以显示:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MediaEvent;
    import flash.events.TouchEvent;
    import flash.filesystem.File;
    import flash.media.CameraRoll;
    import flash.media.MediaPromise;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 声明一个CameraRoll对象和一个Loader,用于在选定照片后显示照片:

    private var loader:Loader;
    private var cameraRoll:CameraRoll;
    
    
  3. 我们将创建我们的Loader对象,将其添加到Stage中,并注册一个事件监听器,以便在照片加载后适当缩放:

    protected function setupLoader():void {
    loader = new Loader();
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, sizePhoto);
    stage.addChild(loader);
    }
    
    
  4. 对于CameraRoll本身,我们需要做的就是实例化它,然后添加一个事件监听器,以便在用户选择要显示的照片时触发。我们应该始终检查设备是否支持CameraRoll.browseForImage(),通过检查supportsBrowseForImage属性:

    protected function setupCameraRoll():void {
    if(CameraRoll.supportsBrowseForImage){
    cameraRoll = new CameraRoll();
    cameraRoll.addEventListener(MediaEvent.SELECT, imageSelected);
    registerListeners();
    }else{
    trace("CameraRoll does not support browse for image!");
    }
    }
    
    
  5. 我们现在将在Stage上注册一个类型为TOUCH_TAPTouchEvent监听器。这将使用户能够通过轻敲设备屏幕来调用浏览对话框,从CameraRoll中选择照片。

    注意

    我们将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量,以便我们的应用程序接受触摸事件。

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    stage.addEventListener(TouchEvent.TOUCH_TAP, loadFromCameraRoll);
    }
    
    
  6. 一旦从用户交互中调用了以下方法,我们就可以对我们之前设置的CameraRoll对象调用browseForImage()方法。这将打开 Android 设备上的默认图库应用,允许用户从他们的收藏中选择一张照片。如果设备上有不止一个图库应用,用户将首先通过一个原生的 Android 对话框选择在这个事件中使用哪一个。我们的应用将失去焦点,这由操作系统处理,一旦做出选择,就会返回到我们的应用。

    protected function loadFromCameraRoll(e:TouchEvent):void {
    cameraRoll.browseForImage();
    }
    
    
  7. 在这里,我们可以看到 Android 上的默认图库应用。用户可以在做出选择之前花尽可能多的时间浏览各种收藏和照片。如何操作…

  8. 当用户在原生的 Android 图库应用中进行了有效的选择后,焦点将返回到我们的应用,并返回一个包含MediaPromise对象的事件。Loader类有一个特定的方法loadFilePromise(),专门用于这类操作。现在我们将MediaPromise通过这个方法传递。

    protected function imageSelected(e:MediaEvent):void {
    var promise:MediaPromise = e.data;
    loader.loadFilePromise(promise);
    }
    
    
  9. 一旦我们使用loadFilePromise()MediaPromise对象传递给Loader,它将被加载到Stage上。在这里,我们将执行一个额外的操作,调整Loader的大小以适应我们的Stage的约束条件。

    protected function sizePhoto(e:Event):void {
    loader.width = stage.stageWidth;
    loader.scaleY = loader.scaleX;
    }
    
    
  10. 加载到Stage上的结果图像将如下所示:如何操作…

工作原理…

ActionScript 的CameraRoll API 专门针对 Android 设备上的照片存储位置。每当用户进行一些交互,调用我们应用中的CameraRoll.browseForImage()方法时,默认的 Android 图库应用将启动,允许用户从他们的收藏中选择一个图像文件。

一旦用户在图库应用中选择了照片,他们将被返回到我们的 AIR for Android 应用,并带有一个MediaPromise对象,通过这个对象我们可以确定文件的一些信息,甚至可以直接将照片加载到我们的应用中。

还有更多…

在这个例子中,我们探讨了如何从CameraRoll将图像加载到Stage上的Loader中。当然,一旦照片被加载,我们可以对它进行很多操作。关于这方面的例子,请看下一个食谱:对已加载的图像应用 Pixel Bender 着色器效果

对已加载的图像应用 Pixel Bender 着色器效果

一旦我们将视觉对象加载到我们的应用程序中,由于这一切都是基于 Flash 的,我们可以进行各种强大的视觉操作。在这个例子中,我们将从本地文件系统加载一个预先选择的照片,然后对其应用各种 Pixel Bender 着色器,极大地改变它的外观。

准备工作…

本食谱使用了 Pixel Bender 着色器。你可以在 Adobe Exchange 下载.pbj文件,或者创建自己的文件。

如果你决定编写自己的 Pixel Bender 内核,可以从www.adobe.com/devnet/pixelbender.html免费下载 Pixel Bender 工具包,并使用它编译各种着色器,以便在 Flash 和 AIR 项目中使用。

该工具包允许你使用 Pixel Bender 内核语言(以前称为 Hydra)编写内核,并提供图像预览和分离属性操作的机制,这些可以暴露给 ActionScript。

准备就绪…

要了解关于编写 Pixel Bender 着色器的良好资源,请查看位于www.adobe.com/devnet/pixelbender.html的文档。

在这个示例中,我们还引用了 Android 图像库中存在的照片,我们之前使用默认相机应用程序捕获的。你可以做同样的事情,或者将图像文件与应用程序一起打包以便后续引用。

如何操作…

我们现在将从本地设备存储中加载一个预定的图像,并对其应用多个 Pixel Bender 着色器:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Loader;
    import flash.display.Shader;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.TouchEvent;
    import flash.filters.ShaderFilter;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 对于这个示例,我们首先需要声明许多不同的对象。我们将声明一个String常量来保存图像的路径和一个Loader,用于显示照片。URLRequestURLLoader对象对将用于加载我们的.pbj文件。Array将用于保存我们将要加载的每个.pbj的名称。使用int来跟踪我们当前从Array集合中加载的着色器。最后,声明一个ShaderShaderFilter对,以将加载的.pbj应用到我们的Loader上。

    private const photoURL:String = " {local file path or http address}";
    private var loader:Loader;
    private var urlRequest:URLRequest;
    private var urlLoader:URLLoader;
    private var pbjArray:Array;
    private var currentFilter:int;
    private var shader:Shader;
    private var shaderFilter:ShaderFilter;
    
    
  3. 下一步是初始化我们的Array,并用我们将要加载到应用程序中的 Pixel Bender 着色器文件引用来填充它。这些文件可以通过 Adobe Exchange、网络上的其他位置获取,或者使用 Pixel Bender 工具包编写:

    protected function setupArray():void {
    pbjArray = new Array();
    pbjArray[0] = "dot.pbj";
    pbjArray[1] = "LineSlide.pbj";
    pbjArray[2] = "outline.pbj";
    }
    
    
  4. 然后,我们创建Loader对象,将其添加到Stage中,并注册一个事件监听器,以便在照片加载后适当缩放:

    protected function setupLoader():void {
    loader = new Loader();
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, sizePhoto);
    stage.addChild(loader);
    }
    
    
  5. 我们现在将为Loader注册一个类型为TOUCH_TAPTouchEvent监听器。这将允许用户点击加载的图像以循环浏览各种 Pixel Bender 着色器。我们还设置currentFilter int0,这将表示我们的Array中的第一个位置:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    loader.addEventListener(TouchEvent.TOUCH_TAP, loadShader);
    currentFilter = 0;
    }
    
    
  6. 要将照片加载到Loader实例中以便在我们的应用程序中显示,我们将调用load()方法,并传入先前声明的photoURL String常量以及新的URLRequest

    protected function loadPhotograph():void {
    loader.load(new URLRequest(photoURL));
    }
    
    
  7. 文件加载后,我们将执行一个操作,调整Loader的大小以适应我们的Stage的约束:

    protected function sizePhoto(e:Event):void {
    loader.width = stage.stageWidth;
    loader.scaleY = loader.scaleX;
    }
    
    
  8. 加载到Stage上的原始图像,在没有应用任何着色器的情况下,将如下所示:如何操作…

  9. 每当用户在Loader实例上执行触摸点击时,此方法将会执行。基本上,我们正在使用之前设置的着色器位置Array中的值来设置URLRequest,从已记录到currentFilter对象的当前索引中提取值。

  10. 在我们调用URLLoader.load()方法之前,我们必须显式地将dataFormat属性设置为URLLoaderDataFormat.BINARY常量。这确保了当我们的文件加载时,它被视为二进制文件而不是文本。

  11. 注册了一个Event.COMPLETE监听器,一旦着色器加载完毕,就会调用applyFilter方法。

  12. 最后,我们可以递增我们的currentFilter值,或者将其设置回0,具体取决于我们在Array的长度上的位置:

    protected function loadShader(e:TouchEvent):void {
    urlRequest = new URLRequest(pbjArray[currentFilter]);
    urlLoader = new URLLoader();
    urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
    urlLoader.addEventListener(Event.COMPLETE, applyFilter);
    urlLoader.load(urlRequest);
    if(currentFilter < pbjArray.length-1){
    currentFilter++;
    }else{
    currentFilter = 0;
    }
    }
    
    
  13. 为了实际将加载的.pbj应用到我们的Loader上,我们首先将二进制数据分配给一个新的Shader对象。然后,这个对象通过ShaderFilter的构造函数传递,最后作为一个Array应用到我们的Loaderfilters属性上。

    protected function applyFilter(e:Event):void {
    shader = new Shader(e.target.data);
    shaderFilter = new ShaderFilter(shader);
    loader.filters = [shaderFilter];
    }
    
    
  14. 当用户点击图像时,我们会遍历可用的 Pixel Bender 着色器,并依次应用到加载的照片上。结果图像循环如下所示:如何操作...

工作原理...

使用 Pixel Bender 着色器是在应用程序中进行强大视觉处理的一种简单直接的方式。在此配方中,我们将图像加载到Loader对象中,构建一个.pbj文件引用的Array,通过URLLoader传递。当用户与加载的图像交互时,我们将加载一个.pbj文件,并根据接收到的数据构建一个Shader。最后,我们可以基于此对象构建一个ShaderFilter,并通过Loader.filters属性将其传递给图像。

还有更多内容...

在此示例中,我们将探讨如何将图像加载到Stage上的Loader中,并在用户交互时应用 Pixel Bender 着色器。当然,你可以将这些着色器应用到任何你喜欢的DisplayObject上,包括视频!

要找到一个各种 Pixel Bender 文件用于此类示例的好地方,可以访问 Adobe Exchange。访问 Exchange 网站:www.adobe.com/exchange

从本地文件系统或通过 HTTP 播放视频文件

在 Android 设备上,我们拥有完整的 Flash 播放器(和 Adobe AIR),因此视频文件的播放与在桌面上一样简单。主要考虑的是视频是否针对移动设备播放进行了优化。

准备工作...

此配方涉及播放与应用程序一起打包的视频文件。我们可以同样轻松地引用 HTTP 地址,甚至是 Android 设备上的本地存储,只要它是可以通过 Flash Platform 运行时播放的文件格式和编解码器。你需要提前准备这个文件。

如何操作...

我们将创建一个Video对象,将其添加到Stage中,并通过基本的NetConnectionNetStream对来流式传输文件:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.NetStatusEvent;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 对于这个配方,我们首先需要声明许多不同的对象。在这种情况下,我们将一个视频文件与应用程序本身打包在一起;我们将声明一个引用这个文件的String常量。

  3. 下一个对象集合与实际的视频流有关。声明一个Video对象以显示通过我们的本地NetConnection传入的NetStream数据。我们还将声明一个Object,以绑定特定的、必要的函数来进行视频播放。

  4. 最后,我们将声明一个TextFieldTextFormat对,以将文本消息传递到设备显示屏上:

    private const videoPath:String = "assets/test.m4v";
    private var video:Video;
    private var streamClient:Object;
    private var connection:NetConnection;
    private var stream:NetStream;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  5. 现在,我们将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来为我们执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  6. 接下来,设置我们的视频连接;我们将创建一个名为streamClient的新对象,我们将使用它将一些辅助函数绑定到我们的流对象上。必须创建一个Video对象并将其添加到DisplayList中,以便用户实际查看视频流。最后,我们创建一个NetConnection,将streamClient分配给它的client属性,注册一个事件监听器以监控连接状态,然后调用connect()方法,传入null作为连接参数,因为在这个例子中我们没有使用任何类型的媒体服务器。

  7. 我们可能并不总是希望将Video.smoothing属性设置为 true;在这种情况下,由于我们不确定视频的确切大小,我们将启用它以平滑通过缩放可能发生的任何潜在图像失真:

    protected function setupVideoConnection():void {
    streamClient = new Object();
    streamClient.onTextData = onTextData;
    streamClient.onMetaData = onMetaData;
    streamClient.onCuePoint = onCuePoint;
    video = new Video();
    video.smoothing = true;
    addChild(video);
    connection = new NetConnection();
    connection.client = streamClient;
    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
    connection.connect(null);
    }
    
    
  8. 一旦我们确定NetConnection已成功连接,以下方法将从我们的onNetStatus函数中调用。在这个方法中,创建一个新的NetStream对象,通过我们的NetConnection流式传输视频。我们还将streamClient分配给client属性,并注册一个事件监听器以监控流状态。要通过我们的Video对象显示流,请使用attachStream()方法,并传入我们的NetStream对象。现在,只需调用play()方法,传入我们的videoPath常量,并指向视频文件位置:

    protected function connectStream():void {
    stream = new NetStream(connection);
    stream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
    stream.client = streamClient;
    video.attachNetStream(stream);
    stream.play(videoPath);
    }
    
    
  9. 在以下代码片段中定义的onNetStatus方法,可以与我们的NetStreamNetConnection对象一起使用,以便根据返回的不同状态消息做出决策。在这个例子中,我们要么在NetConnection成功连接后触发connectStream方法,要么在确定NetStream播放成功后执行一些缩放和布局。

  10. 要查看所有支持的 NetStatusEvent 信息代码的完整列表,请访问:help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/NetStatusEvent.html#info

    protected function onNetStatus(e:NetStatusEvent):void {
    traceField.appendText(e.info.code + "\n");
    switch (e.info.code) {
    case "NetConnection.Connect.Success":
    connectStream();
    break;
    case "NetStream.Buffer.Full":
    video.width = stage.stageWidth;
    video.scaleY = video.scaleX;
    traceField.y = video.height;
    break;
    }
    }
    
    
  11. 接下来的三个步骤包括绑定到 NetConnectionNetStreamclient 属性的方法。这些方法必须是客户端对象的一部分,否则可能会抛出错误,因为它们是预期的方法。onTextData 方法在流式文件中遇到文本时触发:

    public function onTextData(info:Object):void {
    traceField.appendText("Text!\n");
    }
    
    
  12. onMetaData 方法在流元数据加载到应用程序中时触发。这为我们提供了许多有用的信息,如流宽度、高度和持续时间:

    public function onMetaData(info:Object):void {
    traceField.appendText("Duration: " + info.duration + "\n");
    traceField.appendText("Width: " + info.width + "\n");
    traceField.appendText("Height: " + info.height + "\n");
    traceField.appendText("Codec: " + info.videocodecid + "\n");
    traceField.appendText("FPS: " + info.videoframerate + "\n");
    }
    
    
  13. onCuePoint 方法在流式文件中遇到嵌入的提示点时触发:

    public function onCuePoint(info:Object):void {
    traceField.appendText("Cuepoint!\n");
    }
    
    
  14. 结果应用程序的界面将类似于以下屏幕渲染:如何操作…

工作原理…

整个工作流程与为桌面开发时的流程几乎完全相同。在 Flash 中播放视频时,我们首先必须为 NetStream 建立一个 NetConnection。一旦 NetConnection 连接,我们创建 NetStream 并将它们绑定在一起。将 Video 对象添加到 Stage 将使流可以在我们的设备上观看,只要我们将 NetStream 绑定到它上面。此时,我们可以通过简单地调用 play() 方法,在 NetStream 上播放我们希望的任何文件。

在处理 NetConnectionNetStream 时,我们总是需要创建一些辅助函数。这些函数包括注册事件监听器以检测特定状态事件,以及定义一个自定义的 client 属性以及关联的方法,这些方法将符合已建立的工作流程的预期。

还有更多…

在这个例子中,我们播放的是与应用程序打包在一起的视频文件。从设备图库播放视频文件也同样简单(假设用于压缩视频的编解码器由 Flash 和 AIR 支持),或者通过无线网络连接从可用位置渐进式地流式传输视频。

通过 Flash Player 或 AIR 播放的视频文件必须是 Flash Platform 运行时支持的类型。

有效的视频文件类型包括:

  • FLV

  • MP4

  • M4V

  • F4V

  • 3GPP

Flash Platform 运行时支持 H.264 标准的每个级别和配置文件,并保持对 FLV 的完全支持。然而,针对 Android 推荐的分辨率如下:

  • 4:3 视频: 640 × 480, 512 × 384, 480 × 360

  • 16:9 视频: 640 × 360, 512 x 288, 480 × 272

当打包这样一个应用程序时,需要确保包含作为应用程序包一部分分发的文件,可以通过使用 GUI(如果您的 IDE 支持)或者在命令行编译过程中作为额外的文件包含它们。

通过 RTMP 播放远程视频流

除了可以通过本地文件系统或远程 HTTP 网络地址播放视频之外,我们还可以使用 Flash 媒体服务器和 RTMP 协议将视频文件流式传输到 Android 设备上。如果可以使用这样的流媒体服务器,那么在将视频部署到移动 Android 设备时可以充分利用它。

准备就绪…

本食谱涉及播放一个已经部署在 Flash 媒体服务器上的视频文件。如果你没有生产服务器的访问权限,实际上可以免费设置一个开发者版本的 FMS。想要了解更多关于通过实时消息传递协议RTMP)流式传输视频的信息,你可以查看以下资源:www.adobe.com/products/flashmediaserver/

如何操作…

我们将创建一个Video对象,将其添加到Stage中,并通过NetConnectionNetStream对通过 RTMP 流式传输文件:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.NetStatusEvent;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 对于这个食谱,我们首先需要声明许多不同的对象。在这种情况下,我们使用 Flash 媒体服务器通过 RTMP 进行流式传输;我们将声明一个指向 FMS 应用程序路径的String常量。

  3. 下一个对象集合与实际的视频流有关。声明一个Video对象以显示通过我们的本地NetConnection传入的NetStream数据。我们还将声明一个Object,以绑定特定必要的功能,用于视频播放。

  4. 最后,我们将声明一个TextFieldTextFormat对,将文本消息传递到设备显示屏上:

    private const fmsPath:String = "rtmp://fms/vod";
    private var video:Video;
    private var streamClient:Object;
    private var connection:NetConnection;
    private var stream:NetStream;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  5. 我们现在将设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  6. 现在设置我们的视频连接;我们将创建一个名为streamClient的新对象,我们将用它将一些辅助函数绑定到我们的流对象上。必须创建一个Video对象并将其添加到DisplayList中,以便用户实际查看视频流。

  7. 最后,我们创建一个NetConnection,将其client属性分配给streamClient,注册一个事件监听器来监控连接状态,然后调用connect()方法,传入预定义的fmsPath常量作为连接参数。这是因为我们必须在继续之前连接到 Flash 媒体服务器上的这个应用程序实例。

    protected function setupVideoConnection():void {
    streamClient = new Object();
    streamClient.onBWDone = onTextData;
    streamClient.onTextData = onTextData;
    streamClient.onMetaData = onMetaData;
    streamClient.onCuePoint = onCuePoint;
    video = new Video();
    video.smoothing = true;
    addChild(video);
    connection = new NetConnection();
    connection.client = streamClient;
    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
    connection.connect(fmsPath);
    }
    
    
  8. 一旦我们确定NetConnection成功连接,以下方法将从我们的onNetStatus函数中调用。在此方法中,创建一个新的NetStream对象,通过我们的NetConnection流式传输视频。我们还将streamClient分配给client属性,并注册事件监听器以监控流状态。

  9. 要通过我们的Video对象显示流,请使用attachStream()方法,并传入我们的NetStream对象。

  10. 现在,只需调用play()方法,传入一个标识特定流或文件的String,通过 RTMP 播放。你会注意到,由于我们使用基于 H.264 的文件格式,因此必须以mp4:为流名称前缀。如果是直播或通过 FLV 流式传输,则不需要前缀。

    protected function connectStream():void {
    stream = new NetStream(connection);
    stream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
    stream.client = streamClient;
    video.attachNetStream(stream);
    stream.play("mp4:test.m4v");
    }
    
    
  11. onNetStatus方法,如以下代码片段中定义的,可以与我们的NetStreamNetConnection对象一起使用,以便根据返回的不同状态消息做出决策。在这个例子中,我们要么在NetConnection成功连接后触发connectStream方法,要么在确定NetStream正在成功播放后执行一些缩放和布局操作:

    protected function onNetStatus(e:NetStatusEvent):void {
    traceField.appendText(e.info.code + "\n");
    switch (e.info.code) {
    case "NetConnection.Connect.Success":
    connectStream();
    break;
    case "NetStream.Buffer.Full":
    video.width = stage.stageWidth;
    video.scaleY = video.scaleX;
    traceField.y = video.height;
    break;
    }
    }
    
    
  12. 下三个步骤包括绑定到NetConnectionNetStream的客户端属性的方法。这些方法必须作为客户端对象的一部分存在,否则可能会抛出错误,因为它们是预期的方法。onBWDone方法特别适用于通过 RTMP 传输的文件。它会在流媒体服务器完成对客户端可用带宽的估算后触发。

    public function onBWDone():void {
    traceField.appendText("BW Done!\n");
    }
    
    
  13. onTextData方法在流文件中遇到文本时触发。

    public function onTextData(info:Object):void {
    traceField.appendText("Text!\n");
    }
    
    
  14. onMetaData方法在流元数据加载到应用程序时触发。这为我们提供了许多有用的信息,如流宽度、高度和持续时间:

    public function onMetaData(info:Object):void {
    traceField.appendText("Duration: " + info.duration + "\n");
    traceField.appendText("Width: " + info.width + "\n");
    traceField.appendText("Height: " + info.height + "\n");
    traceField.appendText("Codec: " + info.videocodecid + "\n");
    traceField.appendText("FPS: " + info.videoframerate + "\n");
    }
    
    
  15. onCuePoint方法在流文件中遇到嵌入的提示点时触发:

    public function onCuePoint(info:Object):void {
    traceField.appendText("Cuepoint!\n");
    }
    
    
  16. 生成的应用程序将类似于以下屏幕渲染:如何操作…

它的工作原理…

在回放 RTMP 流时,我们首先必须为NetStream建立一个NetConnection以便传输。NetConnection将尝试连接到在 Flash 媒体服务器地址上定义的特定应用程序。一旦NetConnection连接,我们创建NetStream并将它们绑定在一起。将Video对象添加到Stage将使流可以在我们的设备上观看,只要我们将NetStream附加到它上面。此时,我们可以通过简单地调用play()方法,在NetStream上播放我们希望的任何文件。

在处理NetConnectionNetStream时,总是需要创建许多辅助函数。这些函数包括注册事件监听器以检测特定状态事件,以及定义一个自定义client属性和关联的方法,这些方法将由已建立的工作流程预期。

还有更多内容...

在此示例中,我们通过 Flash Media Server 在互联网上通过 RTMP 位置流式传输视频文件。你可以使用相同的技术通过 RTMP 流式传输音频文件,或者编写一个使用设备摄像头视频聊天应用程序。虽然这里我们演示了如何从零开始生成一个Video对象,但请记住,还有各种组件解决方案可供选择,例如随 Flash Professional 提供的FLVPlayBack控件以及 Flex 框架中的VideoDisplayVideoPlayer组件。这项技术有着无限的可能性!

从本地文件系统或通过 HTTP 播放音频文件

通过 Android 设备上的 Flash Platform 运行时播放音频文件相当直接。我们可以指向与应用程序捆绑的文件,正如本配方所示,设备存储上的文件,或者远程网络连接上的文件。无论文件位于何处,播放都是通过相同的方式完成的。

如何操作...

我们必须将音频文件加载到Sound对象中,然后才能操作播放、音量、声道平衡等属性。在此配方中,我们将允许用户通过旋转一个基本的旋钮来控制音量:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TransformGestureEvent;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundTransform;
    import flash.net.URLRequest;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 对于这个配方,我们前端必须声明许多不同的对象。我们将从声音对象组开始,包括SoundSoundChannelSoundTransform。这些对象将允许我们完全控制此配方的音频。我们还将创建一个Sprite,作为用户交互点。最后,我们将声明一个TextFieldTextFormat对,将文本消息传递到设备显示屏上:

    private var sound:Sound;
    private var channel:SoundChannel;
    private var sTransform:SoundTransform;
    private var dial:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 为了创建音量旋钮,我们将初始化一个新的Sprite对象,并使用graphics API 在其内部绘制一个旋钮的表示。然后,我们将这个Sprite添加到Stage中:

    protected function setupDial():void {
    dial = new Sprite();
    dial.graphics.beginFill(0xFFFFFF, 1);
    dial.x = stage.stageWidth/2;
    dial.y = stage.stageHeight/2;
    dial.graphics.drawCircle(0,0,150);
    dial.graphics.endFill();
    dial.graphics.lineStyle(5,0x440000);
    dial.graphics.moveTo(0, -150);
    dial.graphics.lineTo(0, 0);
    addChild(dial);
    }
    
    
  5. 现在,我们将开始设置与音频相关的对象。初始化我们的Sound对象,并通过URLRequest将一个MP3文件加载到其中。

  6. 接下来,我们将通过创建一个SoundTransform并将0.5作为volume值(在 ActionScript 中注册的范围是0 - 1)传递给音量,将声音的初始音量设置为 50%。

  7. 为了播放Sound,我们将创建一个SoundChannel对象,将我们的SoundTransform分配给它的soundTransform属性,并通过Sound.Play()方法最终设置SoundChannel

    protected function setupSound():void {
    sound = new Sound();
    sound.load(new URLRequest("assets/test.mp3"));
    sTransform = new SoundTransform(0.5, 0);
    channel = new SoundChannel();
    channel.soundTransform = sTransform;
    channel = sound.play();
    traceField.text = "Volume: " + sTransform.volume;
    }
    
    
  8. 通过将Multitouch.inputMode设置为MultitouchInputMode.GESTURE常量,为多点触控 API 设置特定的输入模式以支持触摸输入。我们还将为Sprite注册一个TransformGestureEvent.GESTURE_ROTATE事件的监听器,以截获用户交互:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.GESTURE; dial.addEventListener(TransformGestureEvent. GESTURE_ROTATE, onRotate);
    }
    
    
  9. 当用户旋转Sprite时,我们希望相应地调整播放音量。为了实现这一点,我们将根据手势事件收到的数据调整Sprite的旋转。然后我们可以将Sprite的旋转转换为一个有效的音量数字,并修改SoundTransform以反映这一点,这将提高或降低我们的音频音量:

    protected function onRotate(e:TransformGestureEvent):void {
    dial.rotation += e.rotation;
    sTransform.volume = (dial.rotation+180)/360;
    channel.soundTransform = sTransform;
    traceField.text = "Volume: " + sTransform.volume;
    }
    
    
  10. 生成的应用程序将类似于以下屏幕渲染:如何操作…

它是如何工作的…

我们通过URLRequest将音频文件加载到 ActionScript 中的Sound对象中,以便我们的应用程序可以使用它。通过调用Sound上的play()方法可以实现简单的播放,但我们通过将声音播放分配给SoundChannel对象可以保留更多的控制权,因为我们可以通过构建和分配SoundTransform对象来控制诸如立体声声像和音量等方面。在这个食谱中,我们修改了SoundTransform的音量,然后将其分配给正在播放我们SoundSoundChannel.soundTransform属性,从而改变声音。

还有更多…

在这个例子中,我们播放的是与应用程序打包在一起的文件。从设备文件系统播放音频文件(假设 Flash 和 AIR 支持用于压缩音频的编解码器)或者通过 HTTP 从网络连接可访问的位置渐进式流式传输文件也同样简单。

通过 Flash Player 或 AIR 播放的音频文件必须是 Flash Platform 运行时支持的类型。

有效的音频格式包括:

  • FLV

  • MP3

  • AAC+

  • HE-AAC

  • AAC v1

  • AAC v2

当打包这样的应用程序时,需要确保包含作为应用程序包一部分分发的文件,如果你的 IDE 支持,可以通过 GUI 包含它们,或者在命令行编译过程中作为额外的文件包含。

生成音频频谱可视化器

在播放音频时能够生成某种视觉反馈对用户非常有用,因为他们将能够看到即使设备音量被静音或调低,播放仍在进行。从音频生成视觉在某些游戏中或在监控音频输入水平时也很有用。

如何操作…

我们将一个MP3文件加载到一个Sound对象中。通过使用SoundMixer.computeSpectrum()方法,我们可以访问实际正在播放的字节,并使用Sprite graphics API 用这些数据构建可视化:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TimerEvent;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundMixer;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    
    
  2. 对于这个配方,我们首先需要声明许多不同的对象。我们将从SoundSoundChannel声音对象对开始。这些对象将使我们能够完全控制这个配方的音频。我们还将创建一个Sprite对象,它将作为绘制音频频谱数据的画布。最后,我们将声明一个Timer,以便每隔几毫秒刷新声音频谱可视化:

    private var sound:Sound;
    private var channel:SoundChannel;
    private var spectrum:Sprite;
    private var timer:Timer;
    
    
  3. 为了构建我们将绘制可视化元素的画布,我们必须初始化一个Sprite,在graphics API 上定义特定的线条样式,并将其添加到Stage上:

    protected function setupSpectrum():void {
    spectrum = new Sprite();
    addChild(spectrum);
    }
    
    
  4. 我们将使用Timer来确定我们将在容器Sprite中刷新可视化的频率。在这种情况下,我们将它设置为每 100 毫秒触发一次TIMER事件,也就是每秒 10 次。

    protected function registerTimer():void {
    timer = new Timer(100);
    timer.addEventListener(TimerEvent.TIMER, onTimer);
    }
    
    
  5. 现在我们将开始设置我们的音频相关对象。初始化我们的Sound并通过URLRequest加载一个MP3文件。为了播放Sound,我们将创建一个SoundChannel对象,将我们的SoundTransform分配给它的soundTransForm属性,并最终通过Sound.Play()方法设置SoundChannel。现在我们的Sound已经加载并准备就绪,我们可以开始运行我们的Timer

    protected function setupSound():void {
    sound = new Sound();
    sound.load(new URLRequest("assets/test.mp3"));
    channel = new SoundChannel();
    channel = sound.play();
    timer.start();
    }
    
    
  6. 最后,构建一个类似于以下的方法,该方法将从全局 Flash SoundMixer中提取字节数据,并使用graphics API 基于这些数据绘制可视化。我们首先初始化此方法中将要使用的几个变量,并运行SoundMixer类中的computeSpectrum()。这将用创建我们的视觉效果所需的所有声音样本数据填充我们的ByteArray

  7. 在遍历数据时,我们可以使用graphics API 在我们的Sprite容器中绘制线条、圆形或任何我们想要的内容。在这个例子中,我们绘制一系列线条以创建频谱可视化。由于这被设置为每 100 毫秒更新一次,因此它成为播放声音的持续变化的视觉指示器。

    protected function onTimer(e:TimerEvent):void {
    var a:Number = 0;
    var n:Number = 0;
    var i:int = 0;
    var ba:ByteArray = new ByteArray();
    SoundMixer.computeSpectrum(ba);
    spectrum.graphics.clear();
    spectrum.graphics.lineStyle(4, 0xFFFFFF, 0.8, false);
    spectrum.graphics.moveTo(0, (n/2)+150);
    for(i=0; i<=256; i++) {
    a = ba.readFloat();
    n = a*300;
    spectrum.graphics.lineTo(i*(stage.stageWidth/256), (n/2)+150);
    }
    spectrum.graphics.endFill();
    }
    
    
  8. 结果应用程序将类似于以下屏幕渲染:如何操作…

它的工作原理…

SoundMixer类提供了对computeSpectrum()方法的访问,该方法能够捕获通过 Flash Player 或 AIR 播放的任何声音的快照,并将其写入一个ByteArray对象。共有 512 个Number值写入ByteArray;前 256 个代表左声道,剩下的 256 个代表右声道。根据您需要的可视化类型,可能不需要全部 512 个值,正如本例所示。

为了生成确定使用图形 API 绘制线条位置的价值,我们使用ByteArray.readFloat(),它从字节数据流中读取一个 32 位的浮点值,并将其转换为一个Number。由于这个值表示该特定样本的具体声音数据,我们可以使用它通过图形 API 绘制一系列线条,形成我们的可见频谱。

还有更多…

你可以通过简单的搜索在网上找到大量的方法和公式。这种生成性可视化的可能性确实是无限的,但在决定将任何可视化引擎推进多远时,我们必须考虑到这些设备上低于正常的硬件规格。

为你的应用程序生成音频音调

在应用程序中打包大量的声音文件是一种包含音频的方法。另一种方法是运行时生成声音数据。在这个配方中,我们将生成一些简单的正弦音调,这些音调根据检测到的触摸压力而变化。

如何操作…

我们将探讨如何根据用户的触摸压力生成音频样本字节数据,并将其输入到Sound对象中以产生各种音调:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.SampleDataEvent;
    import flash.events.TouchEvent;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.utils.ByteArray;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 对于这个配方,我们首先必须声明多个不同的对象。我们将从由SoundSoundChannel组成的声波对象对开始。这些对象将允许我们对这个配方的音频进行完全控制。我们还将创建一个Number,用来通过用户触摸获取压力信息。最后,我们将声明一个TextFieldTextFormat对,用于在设备显示屏上传递文本消息:

    private var sound:Sound;
    private var channel:SoundChannel;
    private var touchPressure:Number;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 我们现在将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 现在我们将开始设置与音频相关的对象。初始化一个SoundSoundChannel对象对。这些将在后面用来回放我们生成的音频数据:

    protected function setupSound():void {
    sound = new Sound();
    channel = new SoundChannel();
    }
    
    
  5. 将多点触控 APIs 的特定输入模式设置为通过将Multitouch.inputMode设置为MultitouchInputMode.TOUCH_POINT常量来支持触摸输入。我们还将为SampleDataEvent.SAMPLE_DATA事件注册一个监听器,一旦我们通过先前建立的SoundChannelSound对象play(),这些请求就会开始。

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    stage.addEventListener(TouchEvent.TOUCH_BEGIN, onTouch);
    sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataRequest);
    channel = sound.play();
    }
    
    
  6. 每当检测到触摸事件时,我们将会通过以下方法来监控它。基本上,我们修改touchPressure Number,这将用于计算我们的正弦波生成:

    protected function onTouch(e:TouchEvent):void {
    touchPressure = e.pressure;
    traceField.text = "Pressure: " + touchPressure;
    }
    
    
  7. 我们最后的方法将在当前播放的Sound对象请求新的样本数据以回放时执行。我们将使用ByteArray.writeFloat()方法将生成的音频数据发送回我们的Sound对象,在每个样本请求时进行回放:

    protected function onSampleDataRequest(e:SampleDataEvent):void {
    var out:ByteArray = new ByteArray();
    for( var i:int = 0 ; i < 8192; i++ ) { out.writeFloat(Math.sin((Number(i+e.position)/ Math.PI/2))*touchPressure);
    out.writeFloat(Math.sin((Number(i+e.position)/ Math.PI/2))*touchPressure);
    }
    e.data.writeBytes(out);
    }
    
    
  8. 结果应用程序将根据通过触摸施加的压力量产生可变音调,并且应该类似于以下屏幕渲染:如何操作…

它是如何工作的…

当注册了SampleDataEvent事件监听器的 ActionScript Sound对象在播放启动时,它将作为一个插座。我们必须通过一个函数提供样本数据,这个函数生成数据,并将样本传递给等待的Sound对象。样本的数量可以在 2048 到 8192 之间变化,在这种情况下,我们尽可能提供多的样本数据。Adobe 提供的生成正弦波的通用公式是:Math.sin((Number(loopIndex+SampleDataEvent.position)/Math.PI/2))乘以 0.25。由于我们是根据记录的触摸点压力修改公式,所以我们用这个记录的值来代替乘数。这改变了应用程序产生的音频输出。

还有更多内容...

对于更可控的生成声音库,存在 ActionScript 库,可以免费使用,或者根据库的不同可能需要付费。我建议查看一下Sonoport

第六章:结构适应性:处理设备布局和缩放

本章将涵盖以下内容:

  • 检测可用的屏幕边界和分辨率

  • 检测屏幕方向变化

  • 在运行时跨设备缩放视觉元素

  • 在 Flash Professional CS5.5 中基于舞台大小调整视觉元素

  • 在 Flash Professional CS5.5 中使用项目面板

  • 将 Flex 应用程序锁定为横屏或竖屏模式

  • 定义一个空的 Flex 移动应用程序

  • 定义一个基于视图的 Flex 移动应用程序

  • 定义一个具有多个部分的 Flex 移动标签应用程序

  • 在 Flex 移动应用程序中使用启动画面

  • 在 Flex 移动项目中配置 ActionBar 以与 ViewNavigator 一起使用

  • 在 Flex 移动项目中为单一视图隐藏 ActionBar 控件

  • 在 Flex 移动项目中所有视图中隐藏 ActionBar 控件

简介

由于运行 Android 系统的硬件设备种类繁多,开发在不同分辨率下都能正确显示和运行的应用程序可能是一项挑战。幸运的是,Flash 平台非常适合这项工作。无论是使用 Flex SDK 中的默认布局机制,还是编写自己的布局和缩放逻辑,都有许多需要考虑的事项。

在本章中,我们将探讨在使用 Flex 框架进行移动应用程序开发时处理布局机制的问题,并探索纯 ActionScript 项目的各种注意事项。

检测可用的屏幕边界和分辨率

当为桌面或笔记本电脑制作应用程序时,我们不必过多考虑我们实际可用的屏幕空间,或者 每英寸像素(PPI) 分辨率。我们可以假设至少有一个 1024x768 的屏幕供我们使用,并且我们可以确定这是一个 72 PPI 的显示。对于移动设备来说,这一切都不同了。

对于移动设备显示屏,我们的应用程序基本上可以是全屏或几乎全屏;也就是说,除了通知栏。这些设备屏幕的大小可以从仅仅几像素到几百像素不等。然后,我们还必须考虑不同的宽高比以及屏幕肯定能显示 250 PPI 或更高的事实。我们必须有一套新的检查机制,以便根据设备进行应用程序布局的修改。

如何操作…

在运行时,我们可以监控许多设备功能,并通过调整屏幕上的各种视觉元素做出反应:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.system.Capabilities;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 我们现在将声明一个TextFieldTextFormat组合,以将文本消息传递到设备显示屏上:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的 TextField,应用一个 TextFormat,并将其添加到 DisplayList 中。在这里,我们创建一个方法来为我们执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 最后一步是创建一个方法来收集我们进行任何进一步布局修改或 UI 组件所需的所有数据。在这个例子中,我们读取Stage.stageHeightStage.stageWidth以获取可用区域。我们可以将其与Capabilities.screenResolutionXCapabilities.screenResolutionY进行比较,以获取实际的显示分辨率。

  5. 其他重要信息包括Capabilities.touchscreenType以确定触摸屏是预期使用手指还是触笔,Capabilities.pixelAspectRatio以获取像素宽高比(尽管这通常是 1:1),以及最重要的是我们使用Capabilities.screenDPI来发现显示器的 PPI 测量值:

    protected function readBounds():void {
    traceField.appendText("Stage Width: " + stage.stageWidth + "\n");
    traceField.appendText("Stage Height: " + stage.stageHeight + "\n");
    traceField.appendText("Pixel AR: " + Capabilities.pixelAspectRatio + "\n");
    traceField.appendText("Screen DPI: " + Capabilities.screenDPI + "\n");
    traceField.appendText("Touch Screen Type: " + Capabilities.touchscreenType + "\n");
    traceField.appendText("Screen Res X: " + Capabilities.screenResolutionX + "\n");
    traceField.appendText("Screen Res Y: " + Capabilities.screenResolutionY);
    }
    
    
  6. 结果应用程序将显示如下截图所示:如何操作…

工作原理…

通过flash.display.Stageflash.system.Capabilities类,我们可以了解很多关于应用程序正在运行的特定设备显示屏的信息,并让应用程序以某种方式对此作出反应。在这个例子中,我们将收集到的信息输出到一个TextField中,但这些数据也可以用来根据Stage分辨率调整视觉元素的位置、大小或布局。

检测屏幕方向变化

由于大多数 Android 设备至少有两种屏幕方向,即纵向和横向,因此在为这些设备开发时,了解当前的屏幕方向以正确显示应用程序用户界面元素是非常有用的。

如何操作…

我们将在我们的Stage上注册一个事件监听器,以监听StageOrientationEvent的变化:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageOrientation;
    import flash.display.StageScaleMode;
    import flash.events.StageOrientationEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 我们现在将声明一个TextFieldTextFormat对,以将文本信息传递到设备显示屏上:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建了一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    addChild(traceField);
    }
    
    
  4. 下一步将注册一个事件监听器,以检测屏幕方向的变化。我们通过在Stage上监听StageOrientationEvent.ORIENTATION_CHANGE事件来实现这一点:

    protected function registerListeners():void {
    stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE, onOrientationChange);
    }
    
    
  5. 当检测到StageOrientationEvent.ORIENTATION_CHANGE事件时,它将调用一个名为onOrientationChange的方法。我们将创建这个方法,并使用它将表示新方向的文本常量写入TextField。我们还将在此处调用一个方法来调整我们的布局:

    protected function onOrientationChange(e:StageOrientationEvent):void {
    traceField.appendText(e.afterOrientation+"\n");
    reformLayout();
    }
    
    
  6. 最后,我们将使用reformLayout方法调整屏幕上的任何视觉组件以匹配我们新的Stage尺寸。这里,我们简单调整了我们的TextField对象的大小:

    protected function reformLayout():void {
    traceField.width = stage.stageWidth;
    traceField.height = stage.stageHeight;
    }
    
    
  7. 结果应用程序将显示如下截图所示:如何操作…

工作原理…

基本上,这是一个简单的事件监听器,与具有各种可能方向设置的设备相关联。我们在Stage上注册类型为StageOrientationEvent.ORIENTATION_CHANGE的事件监听器,并接收两个重要数据返回:StageOrientationEvent.beforeOrientationStageOrientationEvent.afterOrientation。这些事件结果中包含的值将报告设备方向常量。

有四个可能被报告的常量:

  1. StageOrientation.DEFAULT

  2. StageOrientation.ROTATED_LEFT

  3. StageOrientation.ROTATED_RIGHT

  4. StageOrientation.UPSIDE_DOWN

再次强调,这些只是可能性。有些设备不支持这四个常量中的所有,因此我们必须谨慎,不能想当然。

还有更多内容…

实际上,有多种方法可以检测屏幕方向变化。一种是通过Timer监控Stage.orientation并相应地做出反应。另一种涉及测试Accelerometer值以检测方向变化。然而,使用StageOrientationEvent是最直接的方法,它为我们提供了事件触发前后的方向信息,这非常有用。

另请参阅…

若想了解如何通过Accelerometer API 完成类似任务,请参阅第三章,空间移动:加速度计和地理定位传感器。

在运行时跨设备缩放视觉元素

安卓设备间广泛的每英寸像素(PPI)测量和整体屏幕分辨率差异,使得在创建视觉元素时,特别是在制作交互式元素时,难以进行大小和布局决策。一般认为,一个半英寸的物理测量正方形是便于用户用指尖触摸的理想大小。在本教程中,我们将演示如何确保在设备间保持相同的物理规格。

如何操作…

我们将在屏幕上创建一些视觉元素,这些元素的大小基于检测到的设备显示 PPI 进行物理测量:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.StageOrientation;
    import flash.events.StageOrientationEvent;
    import flash.system.Capabilities;
    
    
  2. 下一步将是声明将在我们的应用程序中使用的一些对象。我们将创建三个Shape对象,用于演示这种特定的布局和大小调整技术。同时,我们还设置两个Number对象,用于在确定应用程序中的大小和位置时保存特定的测量值:

    private var boxTopLeft:Shape;
    private var boxTopRight:Shape;
    private var boxBottom:Shape;
    private var halfInch:Number;
    private var fullInch:Number;
    
    
  3. 现在,我们必须将我们的视觉元素绘制到Stage上。如前所述,我们的目标是物理分辨率为半英寸作为最小测量值。因此,我们首先进行计算,以确定半英寸和一英寸在像素中的表示。

  4. 我们将在左上角创建一个方块,在右上角创建另一个方块;每个方块都是半英寸见方,并根据可用的Stagewidthheight进行定位。在屏幕最底部将放置一个更大的方块,其宽度将延伸至Stage的整个宽度:

    protected function setupBoxes():void {
    halfInch = Capabilities.screenDPI * 0.5;
    fullInch = Capabilities.screenDPI * 1;
    boxTopLeft = new Shape();
    boxTopLeft.graphics.beginFill(0xFFFFFF, 1);
    boxTopLeft.x = 0;
    boxTopLeft.y = 0;
    boxTopLeft.graphics.drawRect(0, 0, halfInch, halfInch);
    boxTopLeft.graphics.endFill();
    addChild(boxTopLeft);
    boxTopRight = new Shape();
    boxTopRight.graphics.beginFill(0xFFFFFF, 1);
    boxTopRight.x = stage.stageWidth - halfInch;
    boxTopRight.y = 0;
    boxTopRight.graphics.drawRect(0, 0, halfInch, halfInch);
    boxTopRight.graphics.endFill();
    addChild(boxTopRight);
    boxBottom = new Shape();
    boxBottom.graphics.beginFill(0xFFFFFF, 1);
    boxBottom.x = 0;
    boxBottom.y = stage.stageHeight - fullInch;
    boxBottom.graphics.drawRect(0, 0, stage.stageWidth, fullInch);
    boxBottom.graphics.endFill();
    addChild(boxBottom);
    }
    
    
  5. Stage上注册一个类型为StageOrientationEvent.ORIENTATION_CHANGE的事件监听器。这将检测设备方向变化并通知我们,以便我们可以适当地调整和重新定位视觉元素:

    protected function registerListeners():void { stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE, onOrientationChange);
    }
    
    
  6. 以下方法将在我们的应用程序检测到每次方向变化时触发。在这种情况下,我们并不太关心当前的实际方向是什么,但会重新定位(必要时重新调整大小)Stage上的任何视觉元素,以正确地重新排列屏幕。我们再次使用我们的数值测量来执行这些操作:

    protected function onOrientationChange(e:StageOrientationEvent):void {
    boxTopLeft.x = 0;
    boxTopLeft.y = 0;
    boxTopRight.x = stage.stageWidth - halfInch;
    boxTopRight.y = 0;
    boxBottom.x = 0;
    boxBottom.y = stage.stageHeight - fullInch;
    boxBottom.width = stage.stageWidth;
    }
    
    
  7. 结果应用程序的显示将类似于我们在以下屏幕截图中所看到的:如何实现…

工作原理…

视觉组件大小调整的一个好方法是,将报告的Capabilities.screenDPI乘以您想要达到的任何物理尺寸。例如,如果我们想要确保某些触摸元素在设备上的宽度正好是半英寸,可以使用以下公式:

private var halfInch:Number = Capabilities.screenDPI * 0.5;

在此示例中,我们设置了一些变量,这些变量表示物理半英寸和全英寸的计算,然后在创建我们的元素进行布局和大小调整时应用这些变量。如果检测到设备方向发生变化,我们将根据新的Stage尺寸调整我们的布局,并适当地调整视觉元素的大小。由于两个顶部的Shapes是半英寸的方块,我们只需调整它们的xy坐标,但底部的形状还需要在每次方向变化时调整其width以填满屏幕宽度。

在 Flash Professional CS5.5 中根据舞台大小调整视觉元素的缩放

Flash Professional CS5.5 引入了一项功能,使针对各种设备分辨率的目标定位变得更加容易,即当Stage大小调整时,Flash 能够重新调整和定位视觉元素。这使得我们可以轻松地修改针对特定分辨率和设备的 FLA 文件。

如何实现…

我们将展示如何使用随舞台缩放内容以针对不同的屏幕分辨率:

  1. 在这里,我们看到一个针对 Nexus S 设备的480x800布局的演示应用程序。在属性面板中,点击大小控制旁边的扳手图标:如何实现…

  2. 我们希望调整显示分辨率以匹配 Droid2,因此我们将文档设置更改为反映480x854的显示分辨率以匹配此设备。此外,我们可以选择随舞台缩放内容,这将按比例缩放我们的视觉元素:如何实现…

  3. 点击确定按钮后,我们可以看到舞台已调整大小,我们的视觉元素现在位于舞台中心。由于我们只调整了应用程序的高度,视觉元素的布局会根据可以在编辑 | 首选项 | 常规 | 缩放内容中调整的设置重新定位。如果不清除这个复选框,元素会在缩放舞台并选择缩放内容时居中,如下所示。

  4. 为了进一步演示,我们将调整舞台大小以匹配假想的 Android 平板设备的分辨率。在属性面板中,再次点击大小控制旁边的扳手图标:如何操作…

  5. 我们假想的平板分辨率为800x1000,因此我们将再次调整宽度和高度设置,选择随舞台缩放内容,然后点击标记为确定的按钮:如何操作…

  6. 新的缩放功能现在更加明显,我们可以通过参考最初标记我们初始分辨率的指南,查看应用程序资源被缩放的情况。在这个阶段,我们可以对应用程序布局进行进一步的调整,以确保在目标设备上显示的效果完全符合我们的预期:如何操作…

如果我们想要以视觉方式针对多个设备,可以使用此技术为每个设备构建一个 FLA 文件,并共享代码库。尽管许多设备可以使用完全相同的.fla生成的应用程序,但这取决于目标设备的分辨率以及我们想要对每个设备进行多少调整。

工作原理…

使用 Flash Professional CS5.5 及其以上版本,我们现在在调整舞台尺寸时,可以增加缩放舞台上内容的功能。这对于移动 Android 开发来说非常棒,因为设备间存在如此多的显示分辨率差异。缩放内容的能力使得 FLA 文档的布局调整变得迅速,当编译成.APK文件时,可以针对特定设备。

还有更多…

需要注意的是,我们视觉元素的缩放将始终以保持其原始宽高比的方式进行。如果新的宽高比与原始宽高比不同,将需要进一步调整以使布局适合我们针对的任何设备。

使用 Flash Professional CS5.5 中的项目面板

在 Flash Professional 中设计应用程序布局传统上一直很麻烦,因为它需要手动组织各种 FLA 文件,并通过代码和资产管理之间的某种机制来同步它们之间的更改。Flash Professional CS5.5 试图通过新的项目结构减轻这种负担,包括在项目文档之间共享作者时间的 Flash 库资产的能力。

如何操作…

我们将配置一个 Flash 项目,这将允许我们针对多个屏幕分辨率使用相同的共享资产池,跨设备针对的 FLAs:

  1. 通过在欢迎屏幕上选择创建新项目 | Flash 项目打开项目面板,或者通过应用程序菜单中的文件 | 新建 | Flash 项目创建一个新的 Flash Professional 项目:如何操作…

  2. 将会出现创建新项目的面板,允许我们配置一个新的Flash 项目。我们将提供一个项目名称,定义一个用于存放项目文件的根文件夹,并选择一个播放器。在针对 Android 的 AIR 的情况下,我们一定要选择AIR 2.6或您希望针对的最新版本的 AIR:如何操作…

  3. Flash 项目结构允许我们在一个项目中定义多个不同的 FLA 文档,这些文档针对各种分辨率和布局。这里,例如,我们创建了针对 Droid、EVO 和 Nexus One 移动 Android 设备的具体文档。除了这些文档,我们还有一个AuthortimeSharedAssets.fla文件,这是 Flash Professional 自动为我们生成的。这将包含我们其他文档之间共享的任何资产。如何操作…

  4. 现在,当我们设计和开发应用程序资产时,我们可以将每个资产标记为作者时间共享资产,这可以在所有文档之间链接,使得在这个特定项目中的资产管理比其他情况下更有组织。要将资产标记为共享,只需点击它旁边的复选框:如何操作…

  5. 在项目中将特定资产标记为在文档之间共享确实使其可共享,我们还必须确保在相关的文档中包含资产,以便在特定设备文档中在作者时间内访问它。

  6. 例如,如果我们有两个.fla文件,希望共享一个名为"RedBall"的 MovieClip 符号,我们首先在一个.fla中定义"RedBall",并将其在库中标记为共享。这样会将符号放入我们的AuthortimeSharedAssets.fla文件中,但在我们实际将其引入第二个.fla之前,其他任何.fla都无法使用它。此时,在任何.fla中进行的任何修改都会因为项目中的共享资产链接而在这两个文件之间共享。

它的工作原理…

AuthortimeSharedAssets.fla 文件包含了所有跨多个 FLA 文件共享的 Flash 资源。这使得我们可以在一个文件中修改共享资源,并且这些更改会影响到所有使用它的项目文档。通过多个针对不同目标分辨率布局的 FLA 文件,设计师在构建应用程序用户界面时具有极大的灵活性。所有这些界面元素通过这种新的项目结构链接起来,保持了工作的有序性和整洁性。

还有更多内容…

新的 Flash 项目面板及其相关项目结构不仅允许通过多个 FLA 文件进行作者时间资源共享和多设备定位,而且文件结构现在完全兼容 Flash Builder。这使得开发人员可以在 Flash Professional 中启动 Flash 项目,并通过在 Flash Builder 中导入项目文件夹继续编辑。

将 Flex 应用程序冻结为横向或纵向模式

有时我们希望将应用程序布局限制为特定的宽高比,横向或纵向。在使用 Flex 框架构建 Android 项目时,实现这一点非常简单。

如何操作…

我们可以通过修改 AIR 应用程序描述符文件来为我们的应用程序冻结特定的宽高比:

  1. 默认情况下,当我们定义一个新的 Flex 移动项目时,会创建一个应用程序描述符 XML 文件。这个文件包括一个专门用于应用程序 initialWindow 配置的节点。它将类似于以下代码:

    <initialWindow>
    <autoOrients>true</autoOrients>
    <fullScreen>false</fullScreen>
    <visible>true</visible>
    <softKeyboardBehavior>none</softKeyboardBehavior>
    </initialWindow>
    
    
  2. 我们希望以两种方式修改这个节点的内容。首先,将 autoOrients 标签设置为 false。这将防止应用程序在设备旋转时重新定位:

    <initialWindow>
    <autoOrients>false</autoOrients>
    <fullScreen>false</fullScreen>
    <visible>true</visible>
    <softKeyboardBehavior>none</softKeyboardBehavior>
    </initialWindow>
    
    
  3. 现在,我们将添加一个 aspectRatio 标签,并为其赋予两个值之一,landscapeportrait

    <initialWindow>
    <autoOrients>false</autoOrients>
    <aspectRatio>landscape</aspectRatio>
    <fullScreen>false</fullScreen>
    <visible>true</visible>
    <softKeyboardBehavior>none</softKeyboardBehavior>
    </initialWindow>
    
    
  4. 当我们在设备上测试这个应用程序时,即使将其竖直持握,在纵向模式下,我们的应用程序仍然锁定为横向:如何操作…

工作原理…

应用程序描述符文件非常强大,因为它可以定义我们应用程序的许多元素,而无需编辑任何 MXML 或 ActionScript。在这个例子中,我们正在修改项目 initialWindow 节点内的标签;将 autoOrients 设置为 false 并添加一个 aspectRation 标签,将我们应用程序的宽高比设置为 landscapeportrait。进行这些编辑将确保无论用户如何旋转设备,我们的应用程序都在固定的宽高比下运行。

还有更多内容…

Flash Professional CS5.5 的用户会发现,他们可以通过 AIR for Android 设置 对话框轻松调整这些属性。可以从 属性 面板或从 文件 | AIR for Android 设置 访问:

还有更多内容…

另请参阅…

我们将在第九章中更深入地探讨应用程序描述符文件,清单保证:安全性和安卓权限

定义一个空白 Flex 移动应用程序

在 Flash Builder 中创建一个Flex 移动项目时,它会附带许多默认视图和布局控件,包括 ActionBar 控件和 ViewNavigator 容器。这些控件对于许多类型的项目非常有用,但并非所有项目都会从这些额外结构中受益。有时从空白项目开始并逐步构建会更好。

如何操作…

定义一个空白 Flex 移动应用程序有两种方法。

在 Flash Builder 中创建一个新的 Flex 移动项目时:

  1. 定义你的项目位置并点击下一步

  2. 现在,只需在应用程序模板区域选择空白,然后继续你的项目设置:如何操作…

第二种方法是修改现有的Flex 移动项目以移除某些移动相关结构:

  1. 你的移动项目最初将包含以下 MXML:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication xmlns:fx= "http://ns.adobe.com/mxml/2009"
    
    firstView="views.MainHomeView">
    </s:ViewNavigatorApplication>
    
    
  2. 我们现在将以多种方式修改这部分内容。首先,将你的 ViewNavigatorApplication 标签更改为 Application 标签:

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    
    firstView="views.MainHomeView">
    </s:Application>
    
    
  3. 移除代码中所有的 View 引用:

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    >
    </s:Application>
    
    

这两种方法都将创建一个空白 Flex 移动应用程序:

如何操作…

工作原理…

决定 Flex 移动项目中是否存在 ActionBar 和其他移动相关结构的是应用程序是否为 spark.components.ViewNavigatorApplicationspark.components.TabbedViewNavigatorApplication 类型。当你的 Flex 移动项目使用更传统的 spark.components.Application 时,ActionBar, TabBarViewStack 将不再存在于项目中或无法使用。

有关上述结构的更多信息,请查看接下来的几个食谱,其中描述了在启用了 ViewNavigator 的项目中工作的方法。

还有更多…

在一段时间后对 Flex 移动项目进行修改不是一个好主意,因为那时你可能会深深依赖于 ViewStack

定义一个基于 Flex 移动视图的应用程序

基于视图的 Flex 移动应用程序为我们提供了许多非常有用的控件和容器,这些控件和容器专门针对移动应用程序开发的布局和结构。包括屏幕顶部的 ActionBarViewNavigator 控件。

如何操作…

创建基于 Flex 移动视图的应用程序有两种方法。

在 Flash Builder 中创建一个新的 Flex 移动项目时:

  1. 定义你的项目位置并点击下一步

  2. 现在,只需在应用程序模板区域选择基于视图的应用程序,然后继续你的项目设置:如何操作…

第二种方法是修改现有的 Flex 项目,以添加某些与移动相关的结构:

  1. 你的 Flex 项目最初将包含以下 MXML:

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    >
    </s:Application>
    
    
  2. 我们现在将以几种方式修改这一点。首先,将你的Application标签更改为ViewNavigatorApplication标签:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
    >
    </s:ViewNavigatorApplication>
    
    
  3. 在当前项目源文件夹内创建一个名为MainHomeView.mxmlView MXML 文件,作为示例。在这种情况下,我们是在项目结构中的views包内创建它。重要的是要认识到每个ViewNavigatorApplication都包含任意数量的单个视图。一个View是一种可以通过ViewNavigator管理以展示或关闭移动 Flex 应用程序内各种“屏幕”的 Flex 容器类型:

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="HomeView">
    </s:View>
    
    
  4. 现在,我们必须将我们刚刚创建的文件指向ViewNavigatorApplicationfirstView属性:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
    
    firstView="views.MainHomeView">
    </s:ViewNavigatorApplication>
    
    

这两种方法都可以定义一个基于 Flex 移动视图的应用程序。

如何操作…

它是如何工作的…

决定ActionBar是否存在于 Flex 移动项目中的是应用程序是否为spark.components.ViewNavigatorApplication(或spark.components.TabbedViewNavigatorApplication)类型。通过将我们的应用程序定义为ViewNavigatorApplication,我们可以访问所有这些移动特定的结构和控件,包括强大的ViewNavigator,通过它我们可以管理所有的应用程序视图。

一个View定义了应用程序内的一个特定“屏幕”,用户在使用应用程序时可能会切换到许多不同的视图。我们可以通过ViewNavigator管理所有这些视图,当应用程序在使用时,它会自动为我们保存视图历史。因此,当用户与 Android 后退按钮互动时,可以重新访问之前的视图。

定义一个具有多个部分的 Flex 移动标签应用程序

使用 Flex 框架设置一个移动 Android 项目可以像我们想要的那么简单或复杂。超越ViewNavigatorApplication的一步是TabbedViewNavigatorApplication,它包括拥有多个内容部分的能力,每个部分都有自己的ViewNavigatorView集合。定义一个TabbedViewNavigatorApplication将允许我们访问TabBar

如何操作…

配置 Flex 移动标签应用程序有两条路径。

在 Flash Builder 中创建一个新的 Flex 移动项目时:

  1. 定义你的项目位置并点击下一步 >

  2. 现在,只需在应用程序模板区域选择标签式应用程序,然后继续你的项目设置:如何操作…

第二种方法是修改现有的 Flex 项目,以添加某些与移动相关的结构:

  1. 你的 Flex 项目最初将包含以下 MXML:

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    >
    </s:Application>
    
    
  2. 我们现在将以几种方式修改这一点。首先,将你的Application标签更改为TabbedViewNavigatorApplication标签:

    <?xml version="1.0" encoding="utf-8"?>
    <s:TabbedViewNavigatorApplication 
    >
    </s:TabbedViewNavigatorApplication>
    
    
  3. 在当前项目源文件夹内创建一组View MXML 文件。在本例中,我们将在项目结构中的views包内创建它们:

    TabOne.mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="Tab One">
    <s:layout>
    <s:VerticalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
    </s:layout>
    <s:Label text="Tab View: #1" />
    </s:View>
    
    

    TabTwo.mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="Tab Two">
    <s:layout>
    <s:VerticalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
    </s:layout>
    
    
    <s:Label text="Tab View: #2" />
    </s:View>
    
    

    TabThree.mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="Tab Three">
    <s:layout>
    <s:VerticalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
    </s:layout>
    <s:Label text="Tab View: #3" />
    </s:View>
    
    
  4. 现在,我们必须通过将一系列ViewNavigator声明嵌套在我们的TabbedViewNavigatorApplication结构中,来指向我们刚刚创建的文件。每个都将指向我们刚刚创建的独特View MXML 文件之一:

    <?xml version="1.0" encoding="utf-8"?>
    <s:TabbedViewNavigatorApplication 
    >
    <s:ViewNavigator label="Tab One" width="100%" height="100%" firstView="views.TabOne"/>
    <s:ViewNavigator label="Tab Two" width="100%" height="100%" firstView="views.TabTwo"/>
    <s:ViewNavigator label="Tab Three" width="100%" height="100%" firstView="views.TabThree"/>
    </s:TabbedViewNavigatorApplication>
    
    

这些方法中的任何一种都将定义一个 Flex 移动标签应用程序:

如何操作…

它是如何工作的…

在 Flex 移动项目中是否包含TabBar是由应用程序是否为spark.components.TabbedViewNavigatorApplication类型来定义的。当在 Flex 移动项目中使用更传统的spark.components.Application时,TabBarViewStack在项目中不再存在或可用。

还有更多…

需要注意的是,当使用TabbedViewNavigator时,每个标签都有自己专用的ViewNavigator,每个都有自己的视图堆栈。除非从其他来源(如共享数据池)获取,否则ViewNavigotor实例之间没有机制共享数据,这需要由开发人员定义。

在 Flex 移动应用程序中使用启动画面

安卓版的 Adobe AIR 是一个优秀的运行时环境,用于构建和分发安卓应用程序,但与原生开发相比,它有一些权衡。根据应用程序的大小,它可能需要几秒钟的时间为用户加载所有内容。移动 Flex 框架允许我们定义一个启动画面,让用户在启动应用程序时知道应用程序正在加载,并为整个体验增添一点额外的装饰。

如何操作…

我们将配置应用程序,在应用程序加载过程中显示启动画面:

  1. 在定义 Flex 移动项目时,我们需要确保ViewNavigatorApplicationTabbedViewNavigatorApplication(取决于你的项目)是当前选定的 MXML 标签,并进入设计视图。

  2. 接下来,我们将修改属性面板中通用区域内的几个设置。在这里,浏览到一个图像文件以嵌入启动画面,并将启动画面缩放模式设置为无,信箱,拉伸缩放如何操作…

  3. 进入源代码视图,MXML 文档将如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
    
    applicationDPI="240" firstView="views.SplashScreenHomeView"
    splashScreenImage="@Embed('assets/splash.png')"
    splashScreenScaleMode="stretch"
    title="Splash!">
    </s:ViewNavigatorApplication>
    
    
  4. 当然,你可以从这里修改我们刚刚配置的任何设置,指向另一个文件进行嵌入或更改缩放模式。我们将在主应用程序标签中添加一个名为splashScreenMinimumDisplayTime的属性,并将其值设置为希望启动画面图像显示的最短持续时间(毫秒):

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
    
    applicationDPI="240" firstView="views.SplashScreenHomeView"
    splashScreenImage="@Embed('AndroidSplash.png')"
    splashScreenScaleMode="stretch"
    splashScreenMinimumDisplayTime="2000"
    title="Splash!">
    </s:ViewNavigatorApplication>
    
    
  5. 当用户在他们的设备上运行应用程序时,他们会看到一个精美的启动画面,标识应用程序并告知它们正在加载:如何操作…

工作原理…

在主应用程序文件上设置splashScreenImage属性,可以在用户加载应用程序时向其显示一个嵌入的自定义图像。添加splashScreenMinimumDisplayTime属性允许我们定义启动画面显示的最短时间(以毫秒为单位)。如果应用程序加载时间超过这个定义的时间,启动画面将根据需要继续显示。启动画面还可以通过设置splashScreenScaleMode属性接受特定的缩放模式行为:

  • splashScreenScaleMode设置为none会以原始分辨率呈现我们定义的图像,不做任何修改。这可能无法接受,因为设备屏幕分辨率差异很大。

  • splashScreenScaleMode设置为letterbox将把启动图像缩放到由设备显示分辨率定义的框架中,但在图像未覆盖的区域会显示空白填充。

  • splashScreenScaleMode设置为stretch将拉伸定义的图像以适应由设备显示分辨率定义的框架,填充整个显示区域。由于图像可能不成比例地缩放,这种设置可能会导致一些失真。

  • splashScreenScaleMode设置为zoom将把启动图像缩放到由设备显示分辨率定义的框架中,不允许任何填充。它将通过裁剪图像的某些部分来填充整个显示区域。这可能是不希望的,因为用户可能无法看到图像的某些部分。

例如:一个 480x800 像素的图像在 320x480 的设备显示屏上呈现时如下所示:

工作原理…

在 Flex 移动项目中配置 ActionBar,以便与 ViewNavigator 一起使用。

Flex 移动ViewNavigatorApplicationTabbedViewNavigatorApplication包含一个名为ActionBar的特殊控件,其中包含三个可编辑的子容器。我们可以通过修改项目文档中的 MXML 来定义这些子容器的内容。

如何操作…

修改文档 MXML 来自定义我们的ActionBar内容。在这个例子中,我们将定义一些交互式图像控件,并在应用程序ViewStack中提供一个丰富的标题图像。

  1. 当我们第一次配置新的 Flex 移动项目时,主 MXML 文档将如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication
    
    firstView="views.CustomActionBarHomeView">
    </s:ViewNavigatorApplication>
    
    
  2. ActionBar包含三个独立区域,我们可以在其中定义额外的控件,它们分别是navigationContenttitleContentactionContent容器。如何操作…

  3. 我们首先在我们的主应用程序 MXML 中定义一个navigationContent节点。在其中定义一个 Spark Image控件,嵌入一个导航图片,这将作为用户返回到我们应用程序“主页”屏幕的方式:

    <s:navigationContent>
    <s:Image source="@Embed('images/home.png')"/>
    </s:navigationContent>
    
    
  4. 现在,定义titleContent容器,并在其中创建一个Image控件,嵌入作为我们应用程序标题的图片:

    <s:titleContent>
    <s:Image source="@Embed('images/title.png')"/>
    </s:titleContent>
    
    
  5. 最后,定义一个actionContent节点,并在其中嵌入另一个图片,就像我们对navigationContent容器所做的那样。这将作为一个关闭按钮:

    <s:actionContent>
    <s:Image source="@Embed('images/close.png')"/>
    </s:actionContent>
    
    
  6. 然后,我们将在 MXML 中设置一个script块,以包含我们将要编写的任何函数:

    <fx:Script>
    <![CDATA[
    ]]>
    </fx:Script>
    
    
  7. 在我们的脚本块中定义一个方法,当用户按下navigationContent子级的Image时,通过调用ViewNavigator.popToFirstView()方法将用户返回到我们的初始View

    private function goHome(e:MouseEvent):void {
    navigator.popToFirstView();
    }
    
    
  8. 定义一个第二种方法,当用户按下actionContent子级的Image时退出应用程序:

    private function closeApp(e:MouseEvent):void {
    NativeApplication.nativeApplication.exit();
    }
    
    
  9. 现在,我们将通过为每个交互式ActionBarImage控件分配点击事件来完成此示例,使用我们之前创建的方法注册它们:

    <s:navigationContent>
    <s:Image click="goHome(event)" source="@Embed('images/home.png')"/>
    </s:navigationContent>
    <s:actionContent>
    <s:Image click="closeApp(event)" source="@Embed('images/close.png')"/>
    </s:actionContent>
    
    
  10. 我们还将以这种方式定义两个View mxml 文件,以便这些ActionBar控件对于此示例清晰起作用。初始View将包括一个按钮,以便使用ViewNavigator.push()方法导航到次要View。调用此方法时,我们只需传入对特定应用程序应允许用户交互的视图的引用。我们可以选择性地传入第二个参数,其中包含要传递给View的数据。

  11. 从次要View,用户可以通过点击ActionBar上的退出Image退出应用程序,按 Android 返回按钮,或者点击ActionBar上的主页Image来调用ViewNavigator.popToFirstView()方法,返回到初始的应用程序状态:

    自定义 ActionBar 的 HomeView.mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="Home View">
    <s:layout>
    <s:VerticalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
    </s:layout>
    <fx:Script>
    <![CDATA[
    protected function switchView():void {
    this.navigator.pushView(views.CustomActionBarSecondaryView);
    }
    ]]>
    </fx:Script>
    <s:Label text="Home View: Hit the EXIT icon to exit." />
    <s:Button label="Go to Secondary View" click="switchView()"/>
    </s:View>
    CustomActionBarSecondaryView.mxml
    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="Secondary View">
    <s:layout>
    <s:VerticalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
    </s:layout>
    <s:Label text="Secondary View: Hit the HOME icon to pop to the first view or the EXIT icon to exit." />
    </s:View>
    
    
  12. 当我们在设备上运行应用程序时,ActionBar将如下显示:如何操作…

工作原理…

Flex移动端的ActionBar是一个优秀的结构元素,可以广泛应用于各种 Android 移动应用程序中。三个容器区域:navigationContenttitleContentactionContent与其他 Flex 容器的行为类似。ActionBar中的内容及其功能实际上取决于应用程序开发人员,以及这对目标用户是否有意义。我们必须确保考虑可用的空间量以及这如何在不同设备间变化。

在处理ViewNavigator时,移动开发人员应该熟悉许多重要方法。我们在这里将简要提及它们。

popToFirstView()方法会移除ViewNavigator中除最底层视图外的所有视图,实质上是让应用程序返回到“主页”视图。popView()方法将当前视图从导航堆栈中弹出,向用户展示上一个视图。

pushView()方法将一个新的视图推送到ViewNavigator导航堆栈的顶部,使其成为当前视图。为此,必须将有效的View对象引用作为此方法的参数传入。

还有更多内容…

我们还可以通过在前一节中概述的ViewNavigator方法的最后一个参数中传递一个过渡引用来管理视图过渡。例如,如果我们想用翻转的立方体替换正常的滑动过渡,可以通过以下步骤实现:

  1. 导入以下类:

    import spark.transitions.FlipViewTransition;
    import spark.transitions.FlipViewTransitionMode;
    import spark.transitions.ViewTransitionDirection;
    
    
  2. 调用创建我们过渡的方法,并将其作为ViewNavigator.popView()的参数传递。创建过渡时,我们可以定义诸如持续时间、移动方向以及ActionBar控件是否与视图内容一起动画等事项:

    protected function removeViews():void {
    var androidTransition:FlipViewTransition = new FlipViewTransition();
    androidTransition.duration = 500;
    androidTransition.direction = ViewTransitionDirection.UP;
    androidTransition.transitionControlsWithContent = false;
    androidTransition.mode = FlipViewTransitionMode.CUBE;
    this.navigator.popView(androidTransition);
    }
    
    

在开发移动 Flex 项目时,我们可以探索许多不同的过渡类型。这仅是使用其中一种类型的方法示例。

在 Flex 移动项目的单个视图中隐藏 ActionBar 控件

您可能想使用ViewNavigatorApplication容器的ViewNavigator结构和功能,但只是想在特定应用程序视图中隐藏ActionBar

如何操作…

将 View 的actionBarVisible属性设置为true。以下示例显示如何根据按钮点击为特定View打开和关闭ActionBar

  1. 定义一个新的基于 Flex 移动视图的应用程序:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
    
    firstView="views.MainHomeView">
    </s:ViewNavigatorApplication>
    
    
  2. 在一个views包中创建一个名为MainHomeView.mxml的新 MXML 文件,这将定义此应用程序的主要视图:

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="HomeView">
    </s:View>
    
    
  3. 在我们刚才创建的 MXML 文件中定义一个Button组件,这构成了我们的ViewNavigatorApplicationfirstView:

    <s:Button x="10" y="10" label="Toggle"/>
    
    
  4. 然后,我们将在 MXML 中设置一个script块,以包含我们将要编写的任何函数:

    <fx:Script>
    <![CDATA[
    ]]>
    </fx:Script>
    
    
  5. 现在,创建一个名为toggleActionBar的函数,并在其中创建一个if语句,检查我们ViewactionBarVisible属性是true还是false。根据当前的Boolean值,我们将切换到相反的值:

    protected function toggleActionBar():void {
    if(actionBarVisible){
    actionBarVisible = false;
    }else{
    actionBarVisible = true;
    }
    }
    
    
  6. 最后,我们只需在Button组件上创建一个点击事件处理程序,以调用刚才创建的函数:

    <s:Button x="10" y="10" label="Toggle" click="toggleActionBar()"/>
    
    
  7. 现在,这个Button可以在切换时打开和关闭ActionBar如何操作…

工作原理…

应用程序中的每个View都有一个actionBarVisible属性。设置actionBarVisible = false; 将隐藏特定View上的ActionBar控件。这实际上非常灵活,因为我们可以根据当前所在的View按需打开或关闭ActionBar控件。

还有更多内容…

我们从View中移除ActionBar控件的方法与从TabbedViewNavigatorApplication项目中移除TabBar的方法类似,通过设置以下内容:

tabbedNavigator.tabBar.visible = false;
tabbedNavigator.tabBar.includeInLayout

第七章:本地交互:StageWebView 和 URI 处理程序

本章将涵盖以下食谱:

  • 在默认的 Android 浏览器中打开网站

  • 在应用程序内渲染网站

  • 管理 StageWebView 历史记录

  • 使用 StageWebView 和 ActionScript 加载广告

  • 在 Flex 移动项目中使用 StageWebView 加载广告

  • 从应用程序拨打电话

  • 从应用程序发送短信

  • 从应用程序调用 Google 地图

  • 使用应用程序 URI 调用 Android 市场

  • 从应用程序发送电子邮件

引言

传统上,Flash 平台开发者无法将 HTML 网站渲染为应用程序的一部分;随着 AIR for Android 中 StageWebView 的引入,这一切都改变了。本章包括关于这种机制与普通显示列表对象的不同之处,以及如何有效地使用它的小贴士。我们还将探讨 URI 处理功能,它允许我们接入 Android 设备上的本地应用程序,如网页浏览器、电子邮件客户端、地图和电话。

在默认的 Android 浏览器中打开网站

类似于桌面 Flash 和 AIR 应用程序,默认的系统 Web 浏览器可以通过 flash.net 包中的类在用户交互的基础上调用。在 Android 上,由于所有应用程序都占用一个完整的窗口,因此我们必须特别注意这可能会在用户与我们的应用程序交互时造成干扰。例如,当用户接到电话或短信必须退出应用程序时。

如何操作...

应用程序调用 navigateToURL 并传入一个新的 URLRequest 将打开默认的 Web 浏览器。在这个例子中,我们将在检测到 TOUCH_TAP 事件时打开一个网站:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一个 Sprite 作为我们的交互元素,以及一个 TextFieldTextFormat 对作为按钮标签:

    private var fauxButton:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的 TextField,应用一个 TextFormat 对象,并使用图形 API 构造一个具有简单背景填充的 Sprite。我们按钮构建的最后一步是将 TextField 添加到 Sprite 中,然后将 Sprite 添加到 DisplayList 中。在这里,我们创建一个方法来执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "left";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Invoke Browser";
    traceField.x = 30;
    traceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 如果我们现在在设备上运行应用程序,交互式 Sprite 应如下所示:如何操作...

  5. 我们现将 Multitouch.inputMode 设置为通过 MultitouchInputMode.TOUCH_POINT 常量响应原始触摸事件。在 Sprite 按钮上注册一个类型为 TouchEvent.TOUCH_TAP 的事件监听器。这将检测用户发起的任何触摸点击事件,并调用名为 onTouchTap 的方法,该方法包含我们的其余逻辑:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  6. 一旦检测到轻触,我们的onTouchTap方法将被触发,调用navigateToURL并传入一个包含我们想要从应用程序中打开的 HTTP 或 HTTPS 地址的URLRequest

    protected function onTouchTap(e:TouchEvent):void {
    navigateToURL(newURLRequest("http://memoryspiral.com/"));
    }
    
    
  7. 当我们在设备上运行应用程序时,只需轻触按钮就会调用原生的网络浏览器应用程序并加载我们的URL 请求:如何操作...

工作原理...

当我们的应用程序用户轻触我们创建的交互式Sprite时,他们会离开我们的应用程序,进入默认的安卓网络浏览器,因为我们提供的 URL 通过网络加载,显示请求的网页。这是通过navigateToURL方法传递一个URLRequest实现的,这与我们在桌面应用程序中实现相同功能的方式非常相似。

还有更多...

虽然从我们的应用程序中调用 Android 网络浏览器可能非常有用,但能够将网页加载到应用程序中而不必在应用程序之间跳转则更有趣。当然,用户可以使用 Android 返回按钮从浏览器返回到我们的应用程序(如果它仍然打开),但还有方法可以确保更无缝的体验。接下来的几个食谱将描述如何实现这一点。

在应用程序中渲染网站

使用 Flash 内容,传统上不可能在应用程序中显示完全渲染的 HTML 网站。Adobe AIR 最初通过允许将网页加载到桌面应用程序中并仅通过桌面HTMLLoader类通过内部 AIR 构建的 web kit 渲染引擎来改变这一点。在 Android 上,AIR 允许我们通过使用StageWebView来做类似的事情。

如何操作...

我们将构建一个新的StageWebView实例,在移动 Android 应用程序中显示一个网页:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.TouchEvent;
    import flash.geom.Rectangle;
    import flash.media.StageWebView;
    import flash.net.URLRequest;
    import flash.net.navigateToURL;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一个Sprite作为我们的交互元素,以及一个TextFieldTextFormat对作为按钮标签。此外,声明一个StageWebView实例以及一个Rectangle来定义我们的视口:

    private var fauxButton:Sprite;
    private var swv:StageWebView;
    private var swvRect:Rectangle;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的TextField,应用一个TextFormat对象,并使用图形 API 构建一个具有简单背景填充的Sprite。按钮构建的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList中。这里,我们创建一个方法来为我们执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "none";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Load Website";
    traceField.x = 30;
    traceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 创建一个方法来构建我们的StageWebView对象,通过定义一个新的Rectangle,设定我们希望StageWebView视口在应用程序中的位置和大小。在这个例子中,我们根据之前创建的Sprite的位置以及应用程序Stage的尺寸来确定我们的Rectangle的属性。

  5. 在构造我们的 StageWebView 实例之前,通过调用 StageWebView.isSupported 来检查是否支持 StageWebView 是一个好习惯。实际上,要创建一个 StageWebView 对象,我们只需进行简单的实例化并将应用程序 stage 分配给 StageWebView.stage。现在,将先前构建的 Rectangle 分配给 StageWebView viewport 属性:

    protected function setupStageWebView():void {
    swvRect = new Rectangle(0,fauxButton.y+fauxButton. height+40,stage.stageWidth,stage. stageHeight-fauxButton.y+fauxButton.height+40);
    if(StageWebView.isSupported){
    swv = new StageWebView();
    swv.stage = this.stage;
    swv.viewPort = swvRect;
    }
    }
    
    
  6. 如果我们现在在设备上运行应用程序,带有伴随 StageWebView 的交互式 Sprite 应如下所示:如何操作...

  7. 我们现在将 Multitouch.inputMode 分配给通过 MultitouchInputMode.TOUCH_POINT 常量响应原始触摸事件。在 Sprite 按钮上注册一个类型为 TouchEvent.TOUCH_TAP 的事件监听器。这将检测用户发起的任何触摸点击事件,并调用名为 onTouchTap 的方法,该方法将实例化页面加载。我们还将为 StageWebView 对象注册一个类型为 Event.COMPLETE 的事件,以确定页面加载何时完成:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    swv.addEventListener(Event.COMPLETE, locationChanged);
    }
    
    
  8. 当检测到触摸点击时,我们的 onTouchTap 方法将被触发,调用 navigateToURL;它将开始使用 StageWebView.loadURL() 加载网页,传入页面地址作为 String 参数:

    protected function onTouchTap(e:TouchEvent):void {
    swv.loadURL("http://memoryspiral.com/");
    }
    
    
  9. 一旦页面加载完成,我们可以收集有关已加载内容的信息,例如页面 title。在这种情况下,我们将页面 title 分配给我们的 TextField 作为示例:

    protected function locationChanged(e:Event):void {
    traceField.text = e.target.title;
    }
    
    
  10. 当网页完全加载后,生成的应用程序将如下所示:如何操作...

工作原理...

StageWebView 类将使用主机操作系统上的默认网页控件来渲染视口中显示的任何 HTML 内容。需要注意的是,StageWebView 并不是传统 Flash DisplayList 的一部分,不能像将视觉元素添加到 DisplayList(通过 addChild())那样以常规方式添加到我们的应用程序中。

由于 StageWebView 不属于传统的 DisplayList,我们必须使用另一种方式来定义它在 stage 上的位置以及它将占用的空间。这是通过将 Rectangle 对象分配给 StageWebView.viewPort 属性来完成的。StageWebView 类还需要一个 stage 属性,将其分配给当前应用程序的 stage。只要这两个属性正确分配,视口就会出现在我们的应用程序中。

注意

由于 StageWebView 不是 DisplayList 的一部分,一旦我们使用完毕,应该始终对其调用 dispose() 方法,以便从应用程序中完全移除。

还有更多...

如前一部分所述,当调用StageWebView时,AIR for Android 将使用原生的 WebKit 渲染引擎。WebKit 被众多流行的网络浏览器使用,包括 Android 浏览器、Apple Safari 和 Google Chrome。值得注意的是:WebKit 实际上是 Adobe AIR 桌面运行时的一部分。关于 WebKit 的更多信息,请访问www.webkit.org/

管理 StageWebView 历史

在为 Android 开发应用程序时,AIR 允许我们通过使用 StageWebView 类来渲染完整的网站。我们还可以利用StageWebView实例的导航历史,并在应用程序中以不同的方式应用它。

如何操作...

一旦用户在我们的StageWebView实例中加载了一些页面,我们就可以通过导航历史前后导航:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.LocationChangeEvent;
    import flash.events.TouchEvent;
    import flash.geom.Rectangle;
    import flash.media.StageWebView;
    import flash.net.URLRequest;
    import flash.net.navigateToURL;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明两个Sprite对象作为我们的交互元素,以及一个TextFieldTextFormat对作为地址指示器。此外,声明一个StageWebView实例以及一个Rectangle来定义我们的视口:

    private var prevButton:Sprite;
    private var nextButton:Sprite;
    private var swv:StageWebView;
    private var swvRect:Rectangle;
    private var addressField:TextField;
    private var addressFormat:TextFormat;
    
    
  3. 现在,我们将创建两个方法,用来构建我们的前进和后退历史控制,并将它们添加到stage上。为每个方法实例化一个新的Sprite,并添加一个唯一的name属性,指定交互的预期功能。我们稍后可以通过touch tap事件读取它,以确定哪个Sprite被点击。使用图形 API 绘制基本背景,并在将每个Sprite添加到DisplayList之前在stage上进行定位:

    protected function setupPrevButton():void {
    prevButton = new Sprite();
    prevButton.name = "prev";
    prevButton.graphics.beginFill(0xFFFFFF, 1);
    prevButton.graphics.drawRect(0, 0, 50, 50);
    prevButton.graphics.endFill();
    prevButton.x = 0;
    prevButton.y = 0;
    addChild(prevButton);
    }
    protected function setupNextButton():void {
    nextButton = new Sprite();
    nextButton.name = "next";
    nextButton.graphics.beginFill(0xFFFFFF, 1);
    nextButton.graphics.drawRect(0, 0, 50, 50);
    nextButton.graphics.endFill();
    nextButton.x = stage.stageWidth - 50;
    nextButton.y = 0;
    addChild(nextButton);
    }
    
    
  4. 为了完成我们的地址指示器,我们继续设置我们的TextField并应用一个TextFormat对象。在这个例子中,我们将TextFieldstage上居中(位于两个交互式Sprites之间)以模拟网络浏览器的地址栏。创建一个方法来执行所有这些操作以及一些样式增强,并将默认的加载中字符串分配给TextField,以让用户知道正在发生的事情。

    protected function setupAddressBar():void {
    addressFormat = new TextFormat();
    addressFormat.bold = true;
    addressFormat.font = "_sans";
    addressFormat.size = 26;
    addressFormat.align = "center";
    addressFormat.color = 0xFFFFFF;
    addressField = new TextField();
    addressField.defaultTextFormat = addressFormat;
    addressField.autoSize = "left";
    addressField.selectable = false;
    addressField.mouseEnabled = false;
    addressField.text = "Loading...";
    addressField.x = 60;
    addressField.y = 8;
    addChild(addressField);
    }
    
    
  5. 创建一个方法来构建我们的StageWebView对象,通过定义一个新的Rectangle,设定我们希望StageWebView在应用程序中显示的位置和大小。在这个例子中,我们根据之前创建的SpriteTextField对象的位置以及应用程序Stage的尺寸来确定我们Rectangle的属性。

  6. 在构建我们的StageWebView实例之前,通过调用StageWebView.is supported来检查是否支持StageWebView是一个好习惯。实际上,要创建一个StageWebView对象,我们只需进行简单的实例化并将应用程序stage分配给StageWebView.stage。现在,将之前构建的Rectangle分配给StageWebViewviewport属性:

    protected function setupStageWebView():void {
    swvRect = new Rectangle(0,addressField.y+addressField.
    height+40,stage.stageWidth ,stage.stageHeight-addressField. y+addressField.height+40);
    if(StageWebView.isSupported){
    swv = new StageWebView();
    swv.stage = this.stage;
    swv.viewPort = swvRect;
    }
    }
    
    
  7. 我们现在将Multitouch.inputMode设置为通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在两个Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用一个名为onTouchTap的方法,该方法将根据点击的Sprite决定是在导航历史中后退还是前进。我们还会在StageWebView对象上注册一个类型为LocationChangeEvent.LOCATION_CHANGE的事件,以确定页面加载何时完成。最后,我们可以调用StageWebView.loadURL,传入一个网页地址作为唯一参数。这将开始加载我们的默认位置:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    prevButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    nextButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    swv.addEventListener(LocationChangeEvent.LOCATION_CHANGE, locationChanged);
    swv.loadURL("http://memoryspiral.com/");
    }
    
    
  8. 如果我们现在运行这个应用,我们会看到所有的交互元素都出现在舞台上,并且我们想要的网页会在StageWebView实例中渲染出来:如何操作...

  9. 当检测到Sprite交互时,我们通过检查实例化后直接提供的name属性来确定被点击的特定Sprite。这样,我们就知道是否应该使用historyBack()historyForward()方法尝试通过历史记录向前或向后移动。为了检测我们是否真的可以这样做,我们可以首先检查设备上是否启用了后退或前进历史,如下面的代码片段所示:

    protected function onTouchTap(e:TouchEvent):void {
    switch(e.target.name){
    case "prev":
    if(swv.isHistoryBackEnabled){
    swv.historyBack();
    }
    break;
    case "next":
    if(swv.isHistoryForwardEnabled){
    swv.historyForward();
    }
    break;
    }
    }
    
    
  10. 当我们StageWebView实例正在渲染的当前位置发生变化时,我们会像标准网络浏览器的地址栏一样,用当前的 URL 更新我们的TextField

    protected function locationChanged(e:LocationChangeEvent):void {
    addressField.text = e.location;
    }
    
    
  11. 用户现在可以通过点击各种超链接,在StageWebView的历史记录中前后导航,如下面的截图所示:如何操作...

工作原理...

StageWebView类将使用主机操作系统上的默认网络控件来渲染视口中显示的任何 HTML。需要注意的是,StageWebView不是传统 Flash DisplayList的一部分,不能像将视觉元素添加到DisplayList(通过addChild())那样以常规方式添加到我们的应用程序中。

要管理StageWebView的历史记录,我们可以使用historyBack()historyForward()方法,在应用内沿着用户的历史记录进行导航。

注意

除非用户开始点击超链接并在StageWebView实例中实际进行导航,否则这两种方法不会执行任何操作。我们基本上是创建了一个小型的网络浏览器。

使用 StageWebView 通过 ActionScript 加载广告

使用 Flash 平台进行移动 Android 开发时,最受追捧的功能之一是在应用中包含来自如 Google AdSense 或 AdMob 等服务的广告。这使得开发者可以免费向用户分发应用程序,但仍然可以从应用内显示的广告中获得收入。

如何操作...

StageWebView 为移动应用开发开辟了众多可能性,其中之一就是能够在运行中的应用程序中加载基于 HTML 的广告。在以下示例中,我们将看看如何轻松管理这一点:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TimerEvent;
    import flash.geom.Rectangle;
    import flash.media.StageWebView;
    import flash.utils.Timer;
    
    
  2. 我们现在将声明一个 StageWebView 实例以及一个 Rectangle 来定义我们的视口。最后,设置一个 Timer,作为刷新我们广告的机制。

    private var swv:StageWebView;
    private var swvRect:Rectangle;
    private var adTimer:Timer;
    
    
  3. 创建一个方法来构建我们的 StageWebView 对象,通过定义一个新的 Rectangle 来确定 StageWebView 在应用中的位置和大小。在构建 StageWebView 实例之前,最好先调用 StageWebView.isSupported 来检查是否支持 StageWebView

  4. 实际上,要创建一个 StageWebView 对象,我们只需进行简单的实例化并将应用程序的 stage 赋值给 StageWebView.stage。现在,将之前构建的 Rectangle 赋值给 StageWebViewviewport 属性,或者使用 loadURL() 加载一个网页,传入页面地址作为 String

    protected function setupStageWebView():void {
    swvRect = new Rectangle(0, 0, stage.StageWidth, 70);
    if(StageWebView.isSupported){
    swv = new StageWebView();
    swv.stage = this.stage;
    swv.viewPort = swvRect;
    swv.loadURL("http://memoryspiral.com/admob.html");
    }
    }
    
    
  5. 如果我们还没有这样做,为了使其正确运行,我们必须在服务器上设置一个网页,以便与我们选择的广告服务进行接口交互。在这个例子中,我们使用 AdMob (www.admob.com/),因为广告针对的是移动网络和移动设备应用。

  6. 这里有一个重要的事情,就是确保通过 CSS 将 bodymarginpadding 设置为 0,以避免广告周围出现任何空间。StageWebView 本质上只是运行 HTML,因此如果我们不稍作修改,默认的 HTML 渲染引擎(在 Android 中,这是 web Kit)将简单地通过其默认设置解释所有风格元素。

  7. 你需要将 pubid 属性替换成你自己的,或者注册一个不同的广告服务。使用这个代码片段作为参考,创建你自己的 HTML 文件,并将其存储在服务器上,然后通过你的特定应用程序调用,正如这个例子中所做的那样:

    <html>
    <head>
    <style type="text/css">
    body {
    background-color: #333;
    margin: 0px;
    padding: 0px;
    }
    </style>
    </head>
    <body>
    <script type="text/javascript">
    var admob_vars = {pubid: 'xxxxxxxxxxx',bgcolor: '000000',text: 'FFFFFF',ama: false,test: true};
    </script>
    <script type="text/javascript" src="img/iadmob.js"></script>
    </body>
    </html>
    
    
  8. 下一步是设置我们的 Timer,以每 10 秒更换一次广告。我们通过实例化一个新的 Timer 对象并传递 10000 毫秒(或者你选择的时间量)来实现这一点。现在,注册一个类型为 TimerEvent.Timer 的事件监听器,以便每次 Timer 达到 10 秒时触发我们构建的方法。要启动 Timer,我们调用 Timer.start()

    protected function setupTimer():void {
    adTimer = new Timer(10000);
    adTimer.addEventListener(TimerEvent.TIMER, onTimer);
    adTimer.start();
    }
    
    
  9. 剩下的就是创建我们的onTimer方法,以便每次Timer达到 10 秒时重新加载StageWebView实例。这将再次调用网络,下拉 HTML,从而重新调用广告服务脚本。

    protected function onTimer(e:TimerEvent):void {
    swv.reload();
    }
    
    
  10. 每次我们的Timer触发时,页面都会刷新,揭示我们应用程序中的新广告:如何操作...

工作原理...

StageWebView类将使用主机操作系统上的默认 Web 控件来渲染视口中显示的任何 HTML。需要注意的是,StageWebView不是传统 Flash DisplayList的一部分,不能像将视觉元素添加到DisplayList(通过addChild())那样以常规方式添加到我们的应用程序中。

为了在应用程序中实际渲染广告,我们可以首先使用loadURL()加载一个网页,传入页面地址作为String。该地址应指向与我们所选择的广告服务接口的 HTML 文档,我们之前已经注册过。通常,这些服务会提供一个 JavaScript 代码块,让你放入你的 HTML 中,它会在页面加载时调用广告。要刷新我们的视口并加载新的广告,我们可以简单地调用StageWebView.reload()。在我们的示例中,我们使用Timer每 10 秒执行此操作。

更多内容...

尽管我们决定在本例中使用 AdMob,但开发者通常可以包括他们喜欢的任何广告系统。在以下屏幕截图中,我以完全相同的方式从 Google AdSense 获取广告。但您会注意到,使用正常版本的 AdSense(不使用移动内容单元)时,广告不会以智能方式适应屏幕。AdMob 专为移动设备设计,因此在这些情况下效果更好。将来,除了这里提到的两个广告提供商之外,应该还有许多新的机会。我们还必须记住,这些都是第三方服务,可能会随时更改。

更多内容...

在 Flex 移动项目中使用 StageWebView 加载广告

由于StageWebView实例不是DisplayList的一部分,在ViewNavigatorApplication中使用它可能会出现感知上的问题。主要问题是StageWebView总是位于所有其他对象的顶层,并且无法与特定视图中的其他项目一起过渡。在本教程中,我们将研究这个问题,并展示一些应对StageWebView对象不规则行为的技术。

准备工作...

在本例中,我们将使用 Google AdSense 的移动内容 | 广告单元。您需要访问www.google.com/adsense/注册 AdSense 账户,并配置一个移动内容广告单元

准备工作...

如果你已经有了 AdMob 账户(或其他服务),你可以选择使用它,或者甚至为本演示创建一个简单的广告。

如何操作...

我们将创建一个新的ViewNavigatorApplication,其中包含两个不同的视图,演示StageWebView如何存在于这个结构之外,如何从视图中移除StageWebView,并提供对额外广告服务系统的引用。

在这个例子中,将涉及多个文件;我们将通过不同的部分来组装它们,以便更清晰。

创建 HTML 文件以展示我们的广告

如果我们还没有这样做,为了使其正确工作,我们必须在服务器上设置一个网页以与 Google AdSense 进行接口。你可能希望将以下示例中的client属性替换为你自己的。使用这段代码作为参考,在服务器上创建你自己的 HTML 文件,并通过你的特定应用程序调用:

<html>
<head>
<style type="text/css">
body {
background-color: #333;
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<script type="text/javascript"><!--
// XHTML should not attempt to parse these strings, declare them CDATA.
/* <![
CDATA[ */
window.googleAfmcRequest = {
client: 'your-id-goes-here',
format: '320x50_mb',
output: 'html',
slotname: '5725525764',
};
/* ]]> */
//--></script>
<script type="text/javascript" src="img/show_afmc_ads.js"></script>
</body>
</html>

为我们的 ViewNavigatorApplication 创建 MXML 文件

  1. 首先,我们创建主应用程序文件,其根节点为ViewNavigatorApplication,以便利用它提供的基于视图的布局。如有需要,我们可以设置applicationDPI,并使用firstView属性引用初始View。我们将在本例稍后定义这个View。在继续之前,让我们注册一个名为init()的方法,以便在应用程序完成后执行:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
     applicationDPI="160"
    firstView="views.FlexAdsHomeView" applicationComplete="init()">
    </s:ViewNavigatorApplication>
    
    
  2. 创建一个脚本块以保存我们应用程序的所有 ActionScript 代码。进行此操作的代码将在另一个步骤中定义,以便更清晰。

    <fx:Script>
    <![
    CDATA[
    ]]>
    </fx:Script>
    
    
  3. 现在,我们将在ActionBar中添加一些功能,具体来说是在navigationContent节点中添加两个Button控件。这两个Button控件将调用ViewNavigator.pushView()方法。这个方法接受一个View引用作为参数,当调用时,会将该View置于我们的视图栈顶部:

    <s:navigationContent>
    <s:Button label="V1" click="navigator.pushView(views.FlexAdsHomeView)"/>
    <s:Button label="V2" click="navigator.pushView(views.FlexAdsOtherView);"/>
    </s:navigationContent>
    
    
  4. 现在,我们将为本例组装两个视图。在每个View中放置一个Button控件以及一个click事件处理程序,该处理程序将调用主应用程序文件中的方法以切换广告的显示和隐藏:

    FlexAdsHomeView.mxml

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
    
    title="Primary View" >
    <s:Button y="120" label="Toggle Ads" horizontalCenter="0" click="this.parentApplication.toggleAds()"/>
    </s:View>
    
    

    FlexAdsOtherView.mxml

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
    
    title="Secondary View">
    <s:Button y="120" label="Toggle Ads" horizontalCenter="0" click="this.parentApplication.toggleAds()"/>
    </s:View>
    
    

生成 ActionScript 代码以连接所有内容

这段代码将存在于我们之前定义的主应用程序文件的script块中:

  1. 首先,将以下类导入到项目中:

    import flash.events.TimerEvent;
    import flash.geom.Rectangle;
    import flash.media.StageWebView;
    import flash.utils.Timer;
    
    
  2. 我们现在将声明一个StageWebView实例以及一个Rectangle以定义我们的视口。最后,设置一个Timer,它将作为刷新我们广告的机制:

    private var swv:StageWebView;
    private var swvRect:Rectangle;
    private var adTimer:Timer;
    
    
  3. 设置前面提到的初始化函数,它将简单地调用我们将要构建的方法来设置StageWebView实例和我们的广告刷新Timer

    protected function init():void {
    setupStageWebView();
    setupTimer();
    }
    
    
  4. 创建一个方法来构建我们的StageWebView对象,通过定义一个新的Rectangle,设置我们希望StageWebView在应用程序中显示的位置和大小。在构建StageWebView实例之前,最好通过调用StageWebView.isSupported来检查是否支持StageWebView

  5. 实际上要创建一个StageWebView对象,我们只需简单地实例化并将其分配给应用程序的stageStageWebView.stage。现在,将之前构建的Rectangle赋值给StageWebViewviewport属性,然后使用loadURL()加载一个网页,传入页面地址作为String

    protected function setupStageWebView():void {
    swvRect = new Rectangle(0, 68, stage.stageWidth, 76);
    if(StageWebView.isSupported){
    swv = new StageWebView();
    swv.stage = this.stage;
    swv.viewPort = swvRect;
    swv.loadURL("http://memoryspiral.com/adsense.html");
    }
    }
    
    
  6. 要从各个视图中切换广告的显示与隐藏,我们只需检查StageWebView.viewPort是否为null,根据这个结果,要么将其设置为一个Rectangle对象,要么赋值为null。如果viewPortnull,广告将不再对用户可见:

    public function toggleAds():void {
    if(swv.viewPort != null){
    swv.viewPort = null;
    }else{
    swv.viewPort = swvRect;
    }
    }
    
    
  7. 下一步是设置我们的Timer,以每 8 秒更换一次广告。我们通过实例化一个新的Timer对象,传入 8000 毫秒(或您选择的时间量)来实现这一点。现在,注册一个类型为TimerEvent.Timer的事件监听器,以便每次Timer达到 8 秒时触发我们构建的方法。要启动Timer,我们调用Timer.start()

    protected function setupTimer():void {
    adTimer = new Timer(8000);
    adTimer.addEventListener(TimerEvent.TIMER, onTimer);
    adTimer.start();
    }
    
    
  8. 剩下的就是创建我们的onTimer方法,以便每次Timer达到 10 秒时重新加载StageWebView实例。这将再次调用网络,拉取 HTML,从而重新调用广告服务脚本:

    protected function onTimer(e:TimerEvent):void {
    swv.reload();
    }
    
    
  9. 当运行应用程序时,广告将立即在StageWebView实例中显示,并且我们的初始View将呈现给用户。此时,用户可以与ActionBar交互,并在每个View之间切换。即使View内容随着应用程序ViewNavigator的切换而变化,StageWebView实例仍将保持原位。在任何时候,用户都可以通过任一View中的Button实例切换广告的显示与隐藏:生成将所有内容联系在一起的 ActionScript 代码

工作原理...

ViewNavigatorApplication中使用StageWebView一开始可能会觉得有些麻烦,如果我们记住这个特定对象的限制,并以一种经过深思熟虑的方式管理StageWebView,那么创建一个可行的实现并不那么困难。

还有更多内容...

如果我们想完全从应用程序中移除一个StageWebView对象,我们可以调用StageWebView.dispose(),这将移除StageWebView对象,使其能被垃圾收集器处理。即使我们以这种方式移除StageWebView实例,如果需要,我们仍然可以创建一个新的实例。

从应用程序中拨打电话

由于 Android 操作系统具有众多出色的功能和强大的实力,很容易让人忘记这些设备首先是电话。在本教程中,我们将展示如何从应用内部调用本地 Android 电话工具,并传递一个要拨打的电话号码。

如何操作...

应用程序调用navigateToURL并传入带有正确tel: URI 的新URLRequest,将打开默认的电话应用,并加载指定的电话号码,准备拨号。在这个例子中,我们将在检测到TOUCH_TAP事件时执行此操作:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一个Sprite作为我们的交互元素,以及一个TextFieldTextFormat对,作为按钮标签:

    private var fauxButton:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们继续设置我们的TextField,应用一个TextFormat对象,并使用图形 API 构建一个具有简单背景填充的Sprite。我们按钮构建的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList中。这里,我们创建一个方法来为我们执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "left";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Invoke Phone";
    traceField.x = 30;
    traceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 如果我们现在在设备上运行应用,交互式Sprite应该会如以下截图所示:如何操作...

  5. 我们现在将Multitouch.inputMode设置为通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用一个名为onTouchTap的方法,其中包含我们剩余的逻辑:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  6. 当检测到一次触摸点击时,我们的onTouchTap方法将被触发,调用navigateToURL并传入一个包含tel: URI 前缀以及我们应用中想要拨打的电话号码的URLRequest

    protected function onTouchTap(e:TouchEvent):void {
    navigateToURL(new URLRequest("tel:15555554385"));
    }
    
    
  7. 当我们在设备上运行应用时,只需在按钮上简单触摸点击,就会调用本地电话应用,并显示我们指定的电话号码:如何操作...

工作原理...

当我们的应用用户触摸点击我们创建的交互式Sprite时,他们会从我们的应用中跳转出来,进入默认的 Android 电话工具。这次调用还会提供一个电话号码,这是通过navigateToURL方法传递带有tel: URI 前缀的URLRequest来指定此通话的。

从应用中发送短信

在 Android 上使用 Flash,我们有能力通过flash.net包中的类根据用户交互调用原生的 Android 短信工具。不幸的是,我们无法为短信提供任何内容。在 Android 上,由于所有应用程序都占用一个完整的窗口,因此我们必须特别留意这可能会在用户与我们的应用程序交互时造成任何干扰。

如何操作...

应用程序调用navigateToURL并传入带有正确sms: URI 前缀的新URLRequest将打开默认的短信工具,并加载指定的电话号码,准备好发短信。在这个例子中,我们将在检测到TOUCH_TAP事件时执行此操作:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一个Sprite作为我们的交互元素,以及一个TextFieldTextFormat对作为按钮标签:

    private var fauxButton:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的TextField,应用一个TextFormat对象,并使用图形 API 构建一个带有简单背景填充的Sprite。我们按钮构建的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList中。在这里,我们创建了一个方法来执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "left";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Invoke SMS";
    traceField.x = 30;
    traceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 如果我们现在在设备上运行应用程序,交互式Sprite应该如下所示:如何操作...

  5. 我们现在将Multitouch.inputMode设置为通过常量MultitouchInputMode.TOUCH_POINT响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用名为onTouchTap的方法,其中包含我们的其余逻辑:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  6. 一旦检测到触摸点击,我们的onTouchTap方法将被触发,调用navigateToURL,并传入一个包含tel: URI 前缀以及我们想要从应用程序中拨打的电话号码的URLRequest

    protected function onTouchTap(e:TouchEvent):void {
    navigateToURL(new URLRequest("sms:15555554385"));
    }
    
    
  7. 在此阶段,我们将失去应用程序焦点,并显示 Android 短信工具,预先填充了我们想要的电话号码,并准备好撰写短信:如何操作...

  8. 最后,当我们点击发送,我们的短信将通过使用的电话号码发送给指定的收件人。在这个例子中,当然这不是一个真实的电话号码:如何操作...

工作原理...

当我们的应用程序用户触摸点击我们创建的交互式Sprite时,他们会从我们的应用程序中退出,进入默认的 Android 短信工具。这次调用还提供了一个电话号码,这是通过navigateToURL方法传递带有sms: URI 前缀的URLRequest分配给这条短信的。这样,我们就可以轻松地让应用程序用户访问电话号码发短信,而无需他们输入数字序列。

从应用程序中调用谷歌地图

由于大多数安卓设备都是移动设备,开发者和用户都期望能够使用某种类型的地图。安卓操作系统由谷歌管理,该公司在网页上拥有悠久的优秀地图技术历史。这对于开发者来说非常棒,因为我们可以在安卓上的非常酷的地图应用程序上搭便车,并从我们的应用程序中传入各种坐标。

如何操作...

让应用程序检测设备的地理坐标,调用navigateToURL,并传入一个格式正确的 URL 的URLRequest以访问安卓地图应用程序:

  1. 首先,将以下类导入到你的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.events.GeolocationEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.sensors.Geolocation;
    
    
  2. 我们现在将声明一个Sprite作为我们的交互元素,以及一个TextFieldTextFormat对作为按钮标签。我们将使用Geolocation API,因此为此目的声明一个对象,以及用于保存纬度和经度数据值的Number变量:

    private var fauxButton:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var geo:Geolocation;
    private var longitude:Number;
    private var latitude:Number;
    
    
  3. 现在,我们继续设置我们的TextField,应用一个TextFormat对象,并使用图形 API 构建一个带有简单背景填充的Sprite。构建按钮的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList中。在这里,我们创建一个方法来执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "left";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Invoke Maps";
    traceField.x = 30;
    traceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    applicationGoogle maps, invokingfauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 如果我们现在在设备上运行应用程序,交互式Sprite应该会如以下截图所示显示:如何操作...

  5. 我们现在将Multitouch.inputMode设置为通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用一个名为onTouchTap的方法,其中包含我们剩余的逻辑:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  6. 在检测到触摸点击事件时,我们将设置一个Geolocation对象,并为它分配一个事件监听器,专门监听GeolocationEvent.UPDATE事件。我们将不再需要监听TouchEvent.TOUCH_TAP事件,因此可以移除它以允许垃圾回收:

    protected function onTouchTap(e:TouchEvent):void {
    fauxButton.removeEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    geo = newGeolocation();
    geo.addEventListener(GeolocationEvent.UPDATE, onGeoEvent);
    }
    
    
  7. 一旦收集到Geolocation数据并将其报告回我们的应用程序,onGeoEvent方法将会触发,为我们提供需要传入到原生安卓地图应用程序的longitudelatitude数据。

  8. 为了完成我们的流程,我们将调用navigateToURL,并传入一个包含http://maps.google.com/ URL 的URLRequest,后面跟着一个查询字符串,其中包含来自我们的Geolocation更新事件数据的latitudelongitude值。既然我们现在有了所需的所有数据,可以移除GeolocationEvent.UPDATE事件监听器:

    protected function onGeoEvent(e:GeolocationEvent):void {
    geo.removeEventListener(GeolocationEvent.UPDATE, onGeoEvent);
    longitude = e.longitude;
    latitude = e.latitude;
    navigateToURL(new URLRequest("http://maps.google.com/?q="+ String(latitude)+", "+String(longitude)));
    }
    
    
  9. 由于此示例中使用的 URI 前缀仅为 http://,因此一个模型对话框将出现在我们的应用程序上方,询问我们是否希望使用 浏览器地图 应用程序打开 URLRequest。我们将选择 地图。勾选 默认使用此操作 复选框将防止将来出现此对话框:如何操作...

  10. 最后,地图 应用程序将出现,并根据我们应用程序能够检测到的纬度和经度 Geolocation 坐标向用户展示视图:如何操作...

工作原理...

当我们应用程序的用户触摸点击我们创建的交互式 Sprite 时,我们会配置一个 Geolocation 对象来监听位置数据。一旦获取到这些数据,我们就可以通过 navigateToURL 方法传递带有 http:// URI 前缀的 URLRequest 来召唤 maps.google.com。我们还添加了一个由收集的 Geolocation 纬度和经度数据形成的查询字符串,告诉 地图 应用程序在我们的地图上导航的确切坐标。

还有更多...

一种替代从设备传感器检测 Geolocation 数据的方法是在应用程序中存储各种坐标,然后向用户提供多个选择。这对于一个专门的餐厅应用程序很有用,例如,允许用户轻松在地图上查看位置。

使用应用程序 URI 调用 Android Market

Android Market 是 Android 平台独有的,有一个专门的应用程序,允许用户轻松搜索、查找并安装设备上的应用程序。Android 允许开发者通过传递特定的搜索词来利用 Market 应用程序。

如何操作...

我们将构建一个小应用程序来调用 navigateToURL 函数,并通过带有 market: URI 前缀的 URLRequest 对象传递一个预定义的搜索词。这将打开 Android Market 应用程序,并让它为我们执行搜索。在这个例子中,一旦检测到 TOUCH_TAP 事件,我们将打开一个新的请求:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一个 Sprite 作为我们的交互元素,以及一个 TextFieldTextFormat 对,作为按钮标签:

    private var fauxButton:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们继续设置我们的 TextField,应用一个 TextFormat 对象,并使用图形 API 构造一个具有简单背景填充的 Sprite。按钮构建的最后一步是将 TextField 添加到我们的 Sprite 中,然后将 Sprite 添加到 DisplayList 中。这里,我们创建了一个方法来执行所有这些操作,并进行一些样式增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "left";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Invoke Market";
    traceField.x = 30;
    traceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 如果我们现在在设备上运行应用程序,交互式 Sprite 应该如以下屏幕截图所示出现:如何操作...

  5. 现在,我们将Multitouch.inputMode设置为通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用名为onTouchTap的方法,该方法包含我们的其余逻辑。

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  6. 一旦检测到触摸点击,我们的onTouchTap方法将被触发,调用navigateToURL并传入带有market: URI 前缀的URLRequest,其中包含我们希望应用程序针对市场库存执行的搜索词:

    protected function onTouchTap(e:TouchEvent):void {
    navigateToURL(new URLRequest("market://search?q=Fractured Vision Media, LLC"));
    }
    
    
  7. 当我们在设备上运行应用程序时,只需点击按钮,就会调用安卓市场应用程序,并针对我们传递的搜索词进行搜索:如何操作...

工作原理...

当我们的应用程序用户触摸点击我们所创建的交互式Sprite时,他们会从我们的应用程序中被带到安卓市场应用程序中,在那里会立即针对我们请求中指定的搜索词进行搜索。安卓市场应用程序会向用户展示当前库存中找到的所有应用程序。例如,传入我们应用程序的确切标题,将允许用户在应用程序内手动检查更新。传入我们的公司或开发者名称,则会显示我们提供给用户浏览的所有应用程序。

如果需要更具体的信息,还可以执行其他搜索查询。

要搜索特定的应用程序,我们可以使用以下格式:

navigateToURL(new URLRequest("market://search?q=pname:air.com.fracturedvisionmedia.SketchNSave"));v

要搜索特定的发布者,我们使用以下格式(注意我们在查询字符串中使用反斜杠""字符来转义引号):

navigateToURL(new URLRequest("market://search?q=pub:\"Fractured Vision Media, LLC\""));

从应用程序发送电子邮件

类似于桌面 Flash 和 AIR 应用程序,基于用户交互,可以通过flash.net包中的类调用默认的系统电子邮件客户端。在 Android 上,由于所有应用程序都占用整个窗口,我们必须特别留意这可能会在用户与我们的应用程序交互时造成干扰。

如何操作...

当应用程序调用navigateToURL并通过带有mailto: URI 前缀的新URLRequest传递电子邮件地址时,将打开默认的电子邮件工具。在这个例子中,一旦检测到TOUCH_TAP事件,我们就会打开一封新的电子邮件:

  1. 首先,将以下类导入到您的项目中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.TouchEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们现在将声明一个Sprite作为我们的交互元素,以及一个TextFieldTextFormat对,作为按钮标签:

    private var fauxButton:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的TextField,应用一个TextFormat对象,并使用图形 API 构建一个具有简单背景填充的Sprite。我们按钮构建的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList。在这里,我们创建了一个方法来执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 42;
    traceFormat.align = "center";
    traceFormat.color = 0x333333;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.autoSize = "left";
    traceField.selectable = false;
    traceField.mouseEnabled = false;
    traceField.text = "Invoke Email";
    traceField.x = 30;
    applicatione-mail, sending fromtraceField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(traceField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, traceField.width+60, traceField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = 60;
    addChild(fauxButton);
    }
    
    
  4. 如果我们现在在设备上运行应用程序,交互式Sprite应该如下所示:如何操作...

  5. 我们现在将Multitouch.inputMode分配给通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用一个名为onTouchTap的方法,其中包含我们的其余逻辑:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  6. 一旦检测到触摸点击,我们的onTouchTap方法将被触发,调用navigateToURL并传递带有mailto: URI 前缀的URLRequest,其中包含我们想要从应用程序中打开的电子邮件地址,如果需要,还可以包含一个主题参数:

    protected function onTouchTap(e:TouchEvent):void {
    navigateToURL(new URLRequest("mailto:info@fracturedvisionmedia. com?subject=Email%20From%20Adobe%20AIR%20on%20Android!"));
    }
    
    
  7. 当我们在设备上运行应用程序时,只需简单地在按钮上触摸点击,就会调用本地电子邮件客户端,并用我们从应用程序传递的值填充它。如何操作...

它是如何工作的...

当我们的应用程序用户触摸点击我们所创建的交互式Sprite时,他们会从我们的应用程序中被带出到默认的安卓电子邮件客户端。这是通过使用带有mailto: URI 前缀的URLRequest传递所需的电子邮件地址,并通过navigateToURL方法附加一系列参数来实现的,这与我们在桌面或网络应用程序中实现相同功能的方式非常相似。

还有更多...

当然,我们完全可以在内部处理电子邮件的应用程序中编写代码,就像在网页应用程序上一样。只要我们能够访问具有电子邮件功能的服务器,这对于某些应用程序来说可能是首选。

第八章:丰富的访问:文件系统和本地数据库

本章将涵盖以下内容:

  • 从设备存储中打开本地文件

  • 将文件保存到设备存储

  • 通过本地共享对象跨会话保存数据

  • 使用 Flex 自动保存应用程序状态

  • 创建本地 SQLite 数据库

  • 提供默认的应用程序数据库

  • 使用 FlexORM 自动化数据库任务

引言

许多文件系统属性在桌面和移动设备之间是共享的,但在处理应用程序状态保存以应对会话中断,或者简单地在会话之间保存数据时,Android 设备上有特定的使用场景。本章将介绍加载和保存单个文件、创建和管理本地数据库、处理本地共享对象以及使用移动 Flex 框架保存导航状态的技巧。

从设备存储中打开本地文件

通常,我们可能需要从应用程序存储或 Android 设备上的其他位置读取某些文件。在以下示例中,我们将对简单的文本文件执行此操作,但这也可用于读取各种文件,从图像数据到编码的MP3音频字节。

如何操作...

在应用程序中使用flash.filesystem包中的各种类来打开本地文件数据:

  1. 首先,我们需要导入以下类:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 我们现在开始定义一组在整个应用程序中使用的常量和变量。初始化一个String常量以保留文件路径,该路径将在示例中使用。我们还需要一个File和一个相应的FileStream以在应用程序中打开文本文件,以及一个TextFieldTextFormat对作为我们的最终输出显示:

    private const PATH:String = "android.txt";
    private var file:File;
    private var stream:FileStream;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 20;
    traceField.width = stage.stageWidth-40;
    traceField.height = stage.stageHeight-40;
    addChild(traceField);
    }
    
    
  4. 实际上,为了在应用程序中打开文件,我们首先会实例化我们的File对象,并通过File.applicationDirectory将其分配给当前应用程序目录。然后,我们可以通过传递常量并使用File.resolvePath()方法指定该位置中的文件。

  5. 此过程的第二部分涉及实例化一个FileStream,这将使我们能够执行余下的流程。在FileStream上注册一个类型为Event.COMPLETE的事件监听器。最后,调用FileStream.openAsync(),传入先前定义的File作为第一个参数,然后是FileMode。我们只是要读取这个文件的字节,因此使用FileMode.READ

    protected function beginFileOpen():void {
    file = new File();
    file = File.applicationDirectory;
    file = file.resolvePath(path);
    stream = new FileStream();
    stream.addEventListener(Event.COMPLETE, fileOpened);
    stream.openAsync(file, FileMode.READ);
    }
    
    
  6. 一旦FileStream完成了工作,我们的fileOpened方法将被触发,允许我们以纯文本(由File.systemCharset指定)读取File字节并将其分配给我们的TextField。每当我们完成与FileStream对象的操作时,我们必须调用它的close()方法:

    protected function fileOpened(e:Event):void {
    traceField.text = stream.readMultiByte(stream.bytesAvailable, File.systemCharset);
    stream.close();
    }
    
    
  7. 当我们在设备上编译并运行应用程序时,它应该如下所示:如何操作...

工作原理...

我们可以通过创建一个File引用,并通过FileStream打开该引用,在应用程序中打开一个文件。这个过程完成后,我们可以通过直接赋值或处理加载的字节来处理文件本身的内容。在这个例子中,我们读取文本文件的内容并将其输出到应用程序中的基本TextFieldFileStream类有许多不同的方法和属性,可以更有效地用于不同类型的文件和处理过程。例如,我们在这里使用FileStream.openAsync()方法实际打开FileStream。我们同样也可以使用FileStream.open()方法,但使用openAsync()将允许我们使用事件监听器,以便我们可以自信地处理加载的数据。重要的是要阅读这些文档,并选择最适合您特定情况的操作。

我们可以使用flash.filesystem.File类的静态属性,快速访问各种存储位置。以下是这些属性的列表:

  • File.applicationStorageDirectory: 独特的应用程序存储目录[读写]

  • File.applicationDirectory: 应用程序安装目录[只读]

  • File.desktopDirectory: 映射到 SD 卡根目录[读写]

  • File.documentsDirectory: 映射到 SD 卡根目录[读写]

  • File.userDirectory: 映射到 SD 卡根目录[读写]

要全面了解File类,请参考 Adobe LiveDocs:

help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/filesystem/File.html

还有更多...

在这个例子中,我们打开了一个文本文件,但任何文件都可以以类似的方式打开和处理。然而,如果你没有良好的背景知识来了解这些文件是如何工作的,读取复杂文件类型的字节可能会非常困难,对于较大的文件,由于你可能对加载的字节执行了大量处理,在移动设备上这个过程可能会很慢。

将文件保存到设备存储

有多种方法可以将应用程序中的数据保存到本地设备存储中。音频、图像和文本数据都可以由用户创建,并保存到应用程序定义的位置,或者允许用户选择在 Android 设备中的特定位置保存文件。在这个例子中,我们将通过生成一个简单的文本文件来演示这一点。

如何操作...

我们将允许用户在我们的应用程序内选择基本文本文件的位置和名称,并将其保存到他们的 Android 设备上:

  1. 首先,我们需要导入以下类:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.TouchEvent;
    import flash.filesystem.File;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 在这个应用程序中,我们需要声明多个对象。一个String常量将用于定义我们的文件名。接下来,我们声明一个File对象,最终用于将我们的文本文件保存到磁盘。一个TextFieldTextFormat组合将把文本信息传递到设备显示上。最后,声明一个Sprite作为我们的交互元素,以及一个额外的TextFieldTextFormat组合作为按钮标签:

    private const FILE_NAME:String = "airandroid.txt";
    private var file:File;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    private var fauxButton:Sprite;
    private var buttonField:TextField;
    private var buttonFormat:TextFormat;
    
    
  3. 现在,我们将继续设置TextField,应用TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来为我们执行所有这些操作。确保将TextField.type设置为input,以允许用户输入!

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 44;
    traceFormat.align = "center";
    traceFormat.color = 0x000000;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.type = "input";
    traceField.border = true;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.background = true;
    traceField.border = true;
    traceField.x = 20;
    traceField.y = 20;
    traceField.width = stage.stageWidth-40;
    traceField.height = 250;
    addChild(traceField);
    }
    
    
  4. 现在,我们将继续设置我们的TextField,应用一个TextFormat对象,并使用图形 API 构建一个带有简单背景填充的Sprite。我们按钮构建的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList中。这里,我们创建一个方法来为我们执行所有这些操作,并进行一些风格上的增强:

    protected function setupTextButton():void {
    buttonFormat = new TextFormat();
    buttonFormat.bold = true;
    buttonFormat.font = "_sans";
    buttonFormat.size = 42;
    buttonFormat.align = "center";
    buttonFormat.color = 0x333333;
    buttonField = new TextField();
    buttonField.defaultTextFormat = buttonFormat;
    buttonField.autoSize = "left";
    buttonField.selectable = false;
    buttonField.mouseEnabled = false;
    buttonField.text = "Save as File";
    buttonField.x = 30;
    buttonField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(buttonField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, buttonField.width+60, buttonField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) (fauxButton.width/2);
    fauxButton.y = traceField.y+traceField.height+40;
    addChild(fauxButton);
    }
    
    
  5. 如果我们运行应用程序,我们可以看到所有内容在显示上的布局情况。在这一点上,我们也可以自由编辑TextField,它作为我们文本文件的输入:如何操作...

  6. 我们现在将Multitouch.inputMode分配给通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸轻触事件,并调用一个名为onTouchTap的方法,其中包含我们的其余逻辑:

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    }
    
    
  7. 当用户与应用程序交互并在按钮上轻触以将任何文本输入保存为文件时,将触发以下方法。在这个函数中,我们首先创建一个新的File对象,并在调用File.save()之前注册一个类型为Event.COMPLETE的事件监听器。File.Save()方法需要两个参数:要创建的文件内容以及文件名称:

    protected function onTouchTap(e:TouchEvent):void {
    file = new File();
    file.addEventListener(Event.COMPLETE, fileSaved);
    file.save(traceField.text, FILE_NAME);
    }
    
    
  8. 一旦用户输入一些文本并点击按钮将其保存为文件,Android 将产生一个覆盖层,请求确认执行保存操作。此时,用户可以重命名文件或选择其他位置保存。默认情况下,文件保存在设备 SD 卡的根目录中。如果我们想避免保存对话框,可以采用flash.filesystem.FileStream类来实现:如何操作...

  9. 保存成功完成后,我们可以移除事件监听器,清除输入的TextField并将按钮标签TextField更改为让用户知道一切已正确保存:

    protected function fileSaved(e:Event):void {
    fauxButton.removeEventListener(TouchEvent.TOUCH_TAP, onTouchTap);
    file.removeEventListener(Event.COMPLETE, fileSaved);
    traceField.text = "";
    buttonField.text = "File Saved!";
    }
    
    
  10. 下图展示了用户在成功保存后将会看到的内容:如何操作...

  11. 用户现在可以使用文件浏览器或其他应用程序在默认的 Android 文本查看器中打开文本文件,如下面的截图所示:如何操作...

它是如何工作的...

将纯文本文件写入设备存储相当直接。这个过程涉及创建一个File对象,然后调用该对象的save()方法。使用此方法,我们传递要保存的文件内容以及所需的文件名。请注意,虽然在这种情况下我们传递的是简单文本,但我们也可以保存音频文件或图像形式的字节。如果我们需要对整个过程进行更多控制,我们还可以使用FileStream对象来设置各种编码,并以更多方式写入字节。使用FileStream还可以让我们将新信息追加到先前创建的文件中,并且避免了本例中出现的保存对话框。

还有更多...

您需要为任何写入本地文件的应用程序提供访问本地文件系统的权限,通过 Android 的清单文件进行设置。关于这方面的更多信息,请参见第九章,清单保证:安全与 Android 权限。

通过本地共享对象跨会话保存数据

共享对象在基于浏览器的 Flash 应用程序中已经使用了多年。它们有时被称为“Flash Cookies”或“超级 Cookies”,并提供与基于浏览器的普通 Cookies 类似的许多功能,但更适合 Flash 环境。通常,使用 Web 上的 Flash 应用程序保存此类数据需要明确的权限;然而,使用 AIR 使我们摆脱了这些限制中的许多。

如何操作...

创建一个本地SharedObject以在会话之间保存特定的应用程序数据。我们将使用一个交互式Sprite来直观地说明这一点:

  1. 首先,我们需要导入以下类:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.TouchEvent;
    import flash.geom.Point;
    import flash.net.SharedObject;
    import flash.net.SharedObjectFlushStatus;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 然后,我们需要声明一些在此应用程序中使用的对象。声明一个 SharedObject,用于保存会话数据。Point 对象将用于将坐标写入 SharedObjectSprite 将作为用户交互元素和此示例的视觉参考。最后,声明一个 TextFieldTextFormat 对,用于在设备显示屏上传递文本消息。

    private var airSO:SharedObject;
    private var ballPoint:Point;
    private var ball:Sprite;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将继续设置我们的 TextField,应用 TextFormat,并将其添加到 DisplayList 中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTextField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "center";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 20;
    traceField.width = stage.stageWidth-40;
    traceField.height = stage.stageHeight-40;
    addChild(traceField);
    }
    
    
  4. 我们需要设置一个交互式对象,让用户根据触摸移动它,这个对象的坐标最终将在应用程序会话之间保留。让我们使用图形 API 创建一个基本的圆形 Sprite

    protected function setupBall():void {
    ball = new Sprite();
    ball.graphics.beginFill(0xFFFFFF);
    ball.graphics.drawCircle(0, 0, 60);
    ball.graphics.endFill();
    ball.x = stage.stageWidth/2;
    ball.y = 260;
    addChild(ball);
    }
    
    
  5. 在深入这个示例之前,我们必须对我们声明的 SharedObject 执行一些操作。首先,在我们的 SharedObject 实例上调用 SharedObject.getLocal("airandroid")。如果存在名为 airandroidSharedObject,这将读取它;如果 SharedObject 尚不存在,这个调用将为我们创建它。

  6. 现在,我们可以检查 SharedObjectdata 属性中是否存在 ballPoint 对象。如果是这样,这意味着我们之前已经完成了一个会话,可以将 ballPoint xy 属性赋给我们的 ballSprite

    protected function setupSharedObject():void {
    airSO = SharedObject.getLocal("airandroid");
    if(airSO.data.ballPoint != undefined){
    ball.x = airSO.data.ballPoint.x;
    ball.y = airSO.data.ballPoint.y;
    traceField.text = "Existing Shared Object!";
    }else{
    traceField.text = "No Shared Object Found!";
    }
    }
    
    
  7. 当我们第一次运行应用程序时,我们会被告知没有检测到共享对象,并且球被放置在默认位置:如何操作...

  8. 我们现在将 Multitouch.inputMode 设置为通过 MultitouchInputMode.TOUCH_POINT 常量响应原始触摸事件。在圆形 Sprite 上注册两个类型为 TouchEvent.TOUCH_MOVETouchEvent.TOUCH_END 的事件监听器。这将检测用户发起的任何触摸事件,并调用特定方法来处理每一个。

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    ball.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
    ball.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
    }
    
    
  9. 当在我们的 Sprite 上检测到 TouchEvent.TOUCH_MOVE 事件时,onTouchMove 方法将被触发,使我们能够改变 Spritexy 坐标,从而允许用户在 Stage 上拖动它。

    protected function onTouchMove(e:TouchEvent):void {
    ball.x = e.stageX;
    ball.y = e.stageY;
    }
    
    
  10. 当我们的应用程序在 Sprite 对象上检测到 TouchEvent.TOUCH_END 事件时,我们将利用这个机会将 Sprite xy 坐标包装在一个 Point 对象中,并将其赋值给我们的 SharedObject。为了执行这个操作,我们首先将 Sprite 坐标赋给我们的 Point 对象,然后将其赋给我们的 SharedObjectdata 属性。

  11. 为了将 SharedObject 写入本地文件系统,我们必须调用 SharedObject.flush()。我们可以将 flush() 命令的返回值赋给一个 String,以便监控和响应其状态。在这个示例中,我们仅使用 switch/case 语句检查 SharedObjectFlushStatus 并在我们的 TextField 中写入一条消息,让用户知道正在发生的情况。

    protected function onTouchEnd(e:Event):void {
    ballPoint = new Point(ball.x, ball.y);
    airSO.data.ballPoint = ballPoint;
    var flushStatus:String;
    flushStatus = airSO.flush();
    if(flushStatus != null) {
    switch(flushStatus) {
    case SharedObjectFlushStatus.FLUSHED:
    traceField.text = "Ball location x:" + ball.x + "/y:" + ball.y + " saved!";
    break;
    default:
    traceField.text = "There was a problem :(";
    break;
    }
    }
    }
    
    
  12. 用户现在可以通过触摸并移动球体来与球体互动。当用户停止与球体互动时,这些坐标会被保存到我们的本地共享对象中:如何操作...

    注意

    如果用户存在,并且在将来的某个时间再次打开应用程序,本地共享对象将被读取,并根据这些保留的数据重新定位球体。为了在设备上真正测试这一点,开发者需要使用 Android 设置菜单下的应用程序管理功能来结束应用程序,或者使用第三方“任务杀手”以确保应用程序完全停止。

    如何操作...

工作原理...

Flash 中的SharedObject与网络浏览器中使用的 cookie 实现非常相似。它最初在基于浏览器的 Flash 中实现,以便当开发人员希望跨用户会话保留小块数据时,能够提供类似的体验。幸运的是,这在 AIR 中同样有效,并可以作为我们 Android 应用程序中的简单存储使用。

要读取SharedObject,只需调用它的getLocal()方法,传入我们希望检索的SharedObject的名称。要保存SharedObject,我们为其分配新数据并调用flush()方法,该方法将把新信息保存到磁盘。

还有更多...

在这个实例中我们使用了一个本地SharedObject,但也可以根据需要将此类数据保存到本地或远程数据库、文本或 XML 文件,甚至使用远程SharedObject

使用 Flex 自动存储应用程序状态

尽管很多时候我们需要在会话被其他设备功能(如来电)中断时存储特定的应用程序参数,但移动 Flex 框架确实提供了一定程度的会话保留,这可以自动为我们处理。

如何操作...

通过启用persistNavigatorState,指示 Flex 自动为我们保留应用程序状态:

  1. 我们首先会创建一个新的移动 Flex 项目,其中包含两个视图,我们将其简称为firstsecond。初始的ViewNavigatorApplication文件将如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
     firstView="views.first">
    </s:ViewNavigatorApplication>
    
    
  2. 在我们的first视图中添加一个按钮,这将使我们能够从那里推送second视图:

    <s:Button label="Engage Second State" click="navigator.pushView(views.second);"/>
    
    
  3. 在我们的second视图中添加一个按钮,允许我们返回到first视图。现在我们可以来回导航,构建我们的ViewNavigator历史记录:

    <s:Button label="Engage First State" click="navigator.pushView(views.first)"/>
    
    
  4. 为了让 Flex 在会话被中断的情况下既保存我们的ViewNavigator历史记录,又保留我们在该历史记录中的当前位置,我们将修改ViewNavigatorApplication以包含一个名为persistNavigatorState的属性,并将其设置为true。我们还将声明一个creationComplete事件,它将调用一个名为init()的函数。我们将使用它来设置一些额外的功能:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication 
     firstView="views.first"
    persistNavigatorState="true" creationComplete="init()">
    </s:ViewNavigatorApplication>
    
    
  5. 在 MXML 中创建一个Script标签,并导入FlexEvent类:

    <fx:Script>
     <![CDATA[
     import mx.events.FlexEvent;
     ]]>
     </fx:Script>
    
    
  6. 现在,我们必须声明我们的init()方法,该方法将在creationComplete时被调用。在这个方法中,我们将在应用程序上注册一个类型为FlexEvent.NAVIGATOR_STATE_SAVING的事件监听器:

    public function init():void {
    this.addEventListener(FlexEvent.NAVIGATOR_STATE_SAVING, stateSaving);
    }
    
    
  7. 每当应用程序在退出时通过 Flex 持久化管理器开始保存应用程序状态时,我们的stateSaving方法将被触发,允许我们执行额外的操作,甚至可以在FlexEvent上调用preventDefault(),以便在退出之前让我们的逻辑接管。在开发和测试中,我们可以轻松在这个方法中设置断点,以便检查应用程序状态。

    protected function stateSaving(e:FlexEvent):void {
    // Interception Code
    }
    
    
  8. 当我们编译并运行我们的应用程序时,它将显示在下个截图中所示的样子。从我们的第一个视图翻到第二个视图,并来回多次,将填充应用程序ViewNavigator的历史记录:如何操作...

  9. 如果应用程序会话被电话或其他事件中断,导航历史和当前视图将被保留。当再次运行应用程序时,用户将能够从中断发生的地方继续操作:如何操作...

它的工作原理...

当使用移动 Flex 框架时,我们可以在应用程序中启用persistNavigatorState选项。这将自动保存我们的ViewNavigator历史记录,并记住在应用程序会话中断时我们正在交互的视图。它通过将会话信息保存到设备上的本地共享对象来实现这一点。保存的数据包括有关应用程序版本号、完整的导航堆栈和当前导航视图的信息。

此外,当应用程序开始退出时,我们可以拦截FlexEvent.NAVIGATOR_STATE_SAVING事件,并执行我们自己的期望操作,例如将关键应用程序数据保存到文件系统、本地共享对象,甚至是 SQLite 数据库。

创建本地 SQLite 数据库

Adobe AIR 从一开始就支持嵌入式 SQLite 数据库。这是我们在 Android 应用程序中存储结构化信息的最佳方式之一。SQLite 是一个软件库,它实现了一个自包含、无服务器、零配置、事务性的 SQL 数据库引擎。它创建的数据库文件就是单独的.db文件,可以通过网络传输、复制和删除,就像其他任何文件类型一样。

如何操作...

我们将创建一个带有本地 SQLite 数据库的移动应用程序,该程序可以使用 SQL 查询语言,允许用户添加新记录并基于这些条目运行简单查询:

  1. 首先,导入这个示例所需的以下类:

    import flash.data.SQLConnection;
    import flash.data.SQLStatement;
    import flash.data.SQLResult;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.TouchEvent;
    import flash.filesystem.File;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    
    
  2. 我们需要声明一些在此应用程序中使用的对象。一个SQLConnection将允许我们与本地 SQLite 数据库进行交互。第一个TextFieldTextFormat对将作为用户输入的输入字段。另一个TextFieldTextFormat对将把文本信息传递到设备显示屏上。最后,声明一个Sprite作为我们的交互元素,以及一个最后的TextFieldTextFormat对作为按钮标签:

    private var sqlConnection:SQLConnection;
    private var itemField:TextField;
    private var itemFormat:TextFormat;
    private var fauxButton:Sprite;
    private var buttonField:TextField;
    private var buttonFormat:TextFormat;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们继续设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作。确保将TextField.type设置为input,以允许用户输入!

    protected function setupTextField():void {
    itemFormat = new TextFormat();
    itemFormat.bold = true;
    itemFormat.font = "_sans";
    itemFormat.size = 44;
    itemFormat.align = "center";
    itemFormat.color = 0x000000;
    itemField = new TextField();
    itemField.defaultTextFormat = itemFormat;
    itemField.type = "input";
    itemField.border = true;
    itemField.multiline = true;
    itemField.wordWrap = true;
    itemField.background = true;
    itemField.border = true;
    itemField.x = 20;
    itemField.y = 20;
    itemField.width = stage.stageWidth-40;
    itemField.height = 60;
    addChild(itemField);
    }
    
    
  4. 对于我们的交互式Sprite,我们将设置一个TextField,应用一个TextFormat对象,并使用图形 API 构建一个具有简单背景填充的Sprite。构建按钮的最后一步是将TextField添加到我们的Sprite中,然后将Sprite添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作,并进行一些样式增强:

    protected function setupTextButton():void {
    buttonFormat = new TextFormat();
    buttonFormat.bold = true;
    buttonFormat.font = "_sans";
    buttonFormat.size = 42;
    buttonFormat.align = "center";
    buttonFormat.color = 0x333333;
    buttonField = new TextField();
    buttonField.defaultTextFormat = buttonFormat;
    buttonField.autoSize = "left";
    buttonField.selectable = false;
    buttonField.mouseEnabled = false;
    buttonField.text = "Insert to DB";
    buttonField.x = 30;
    buttonField.y = 25;
    fauxButton = new Sprite();
    fauxButton.addChild(buttonField);
    fauxButton.graphics.beginFill(0xFFFFFF, 1);
    fauxButton.graphics.drawRect(0, 0, buttonField.width+60, buttonField.height+50);
    fauxButton.graphics.endFill();
    fauxButton.x = (stage.stageWidth/2) - (fauxButton.width/2);
    fauxButton.y = itemField.y+itemField.height+40;
    addChild(fauxButton);
    }
    
    
  5. 我们最后的视觉元素包括另一个TextFieldTextFormat对,用于在设备上显示数据库记录:

    protected function setupTraceField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "left";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = fauxButton.y+fauxButton.height+40;
    traceField.width = stage.stageWidth-40;
    traceField.height =stage.stageHeight - traceField.y;
    addChild(traceField);
    }
    
    
  6. 我们现在将Multitouch.inputMode分配为通过MultitouchInputMode.TOUCH_POINT常量响应原始触摸事件。在Sprite按钮上注册一个类型为TouchEvent.TOUCH_TAP的事件监听器。这将检测用户发起的任何触摸点击事件,并调用一个名为onTouchTap的方法来执行额外操作。

    protected function registerListeners():void {
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
    fauxButton.addEventListener(TouchEvent.TOUCH_TAP, insertDBItem);
    }
    
    
  7. 要创建应用程序数据库,我们首先需要初始化我们的SQLConnection对象,并将一个File.db引用传递给SQLConnection.open()方法以建立连接。如果数据库文件不存在,它将被自动创建。为了编写与数据库交互的 SQL 语法,我们必须初始化一个SQLStatement对象,并将我们建立的SQLConnection分配给SQLStatement.sqlConnection属性。此时,我们可以传入一个包含 SQL 语句的StringSQLStatement.text属性中,并调用SQLConnection.execute()实际执行语句。这个语法将在我们的数据库中创建一个包含两列nametime的表。如果表已经存在,该语句将被忽略:

    protected function createDB():void {
    sqlConnection = new SQLConnection();
    sqlConnection.open(File.applicationStorageDirectory. resolvePath("airandroid.db"));
    var sqlStatement:SQLStatement = new SQLStatement();
    sqlStatement.sqlConnection = sqlConnection;
    sqlStatement.text = "CREATE TABLE IF NOT EXISTS items (name TEXT, time TEXT)";
    sqlStatement.execute();
    getDBItems();
    }
    
    
  8. 为了从数据库中检索现有记录,我们将再次初始化一个SQLStatement,并将已建立的SQLConnection分配给SQLStatement.sqlConnection属性。然后,我们会传入一个包含 SQL 语句的StringSQLStatement.text属性中,并调用SQLConnection.execute()从数据库中检索所有记录。

  9. 要将返回的数据写入TextField,我们只需初始化一个新的Array来包含通过将SQLStatement.getResult()data属性(它本身是一个Array)赋值给Array返回的记录。现在创建一个for循环来解析结果,将每条记录分配的各种属性输出到我们的TextField中。这将在 Android 设备上直观地展示查询结果:

    protected function getDBItems():void {
    traceField.text = "";
    var sqlStatement:SQLStatement = new SQLStatement();
    sqlStatement.sqlConnection = sqlConnection;
    sqlStatement.text = "SELECT * FROM items";
    sqlStatement.execute();
    var sqlArray:Array = new Array();
    var sqlResult:SQLResult = sqlStatement.getResult();
    if(sqlResult.data != null){
    sqlArray = sqlResult.data;
    }
    var itemCount:int = sqlArray.length;
    for(var i:int=0; i<itemCount; i++){
    traceField.appendText("NAME: " + sqlArray[i].name + "\n");
    traceField.appendText("DATE: " + sqlArray[i].time + "\n");
    traceField.appendText("\n");
    }
    }
    
    
  10. 我们需要编写的最后一个方法是允许用户向数据库中插入记录。这大部分与我们之前两个方法中建立和执行SQLStatement对象的方式非常相似。然而,插入操作可能更加复杂和结构化,因此我们使用内置的SQLStatement.parametersArray来为我们的记录赋值。对于name值,我们从用户提供的输入TextField读取。为了生成一个时间戳来填充time的值,我们实例化一个新的Date对象并调用toUTCString()。执行这个完整形成的语句后,我们再次调用getDBItems()以返回新的数据库结果,让用户立即看到记录已被正确插入:

    protected function insertDBItem(e:TouchEvent):void {
    var date:Date = new Date();
    var sqlStatement:SQLStatement = new SQLStatement();
    sqlStatement.sqlConnection = sqlConnection;
    sqlStatement.text = "INSERT into items values(:name, :time)";
    sqlStatement.parameters[":name"] = itemField.text;
    sqlStatement.parameters[":time"] = date.toUTCString();
    sqlStatement.execute();
    getDBItems();
    itemField.text = "";
    }
    
    
  11. 在我们的 Android 设备上运行应用程序,允许我们使用原生的虚拟键盘输入一个名字,并通过触摸点击插入到数据库按钮,这将在我们的数据库中创建一个由输入文本和当前时间戳组成的新条目。如何操作...

  12. 每当我们向应用程序中输入一个新名字时,新条目就会被插入,并且会进行查询,将所有条目以及它们被插入时的时间戳输出到TextField中:如何操作...

工作原理...

SQLite 是一个本地自包含的数据库,可以在 AIR for Android 应用程序中用于执行各种任务,从简单到复杂。为了使用这个功能,我们必须建立一个到设备上本地.db文件的SQLConnection。一旦建立这个连接,我们可以使用一组SQLStatements来执行表创建和管理任务,通过标准的 SQL 语法进行选择、插入和删除查询。在这个例子中,用户可以在应用程序存储目录下的数据库文件中插入记录并执行一般的选择查询。

在这个演示中,我们使用flash.data.SQLStatement来执行INSERTSELECT操作。要进一步探索这一点以及相关类,我们请您参考 Adobe LiveDocs:

help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/data/SQLStatement.html

提供默认的应用程序数据库

允许用户直接或间接地向应用程序数据库中添加或删除项目,在多种场景下都非常有用。或许,我们希望用户从一个标准数据集开始操作,或者为用户提供一些默认设置以供日后操作?这些场景要求应用程序能够提供默认的数据库。在本教程中,我们将展示如何通过文件系统智能地处理这个问题。

准备工作...

在本教程中,我们将在应用程序目录中捆绑一个已经建立的 SQLite 数据库文件。如果您还没有可用的 SQLite 数据库文件,您可以使用本章中的其他教程来生成一个,或者使用任何一种可免费获得的机制来创建这些便携式的小型数据库文件。

如何操作...

我们将把一个默认的 SQLite 数据库与我们的应用程序打包在一起,检查是否存在用户定义的数据库,并在需要时向用户提供我们的默认数据库:

  1. 首先,导入本示例所需以下类:

    import flash.data.SQLConnection;
    import flash.data.SQLStatement;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.filesystem.File;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  2. 在这个应用程序中,我们将需要声明几个对象。一个SQLConnection将允许我们与本地 SQLite 数据库交互,而TextFieldTextFormat组合将把文本信息传递到设备显示屏上:

    private var sqlConnection:SQLConnection;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 现在,我们将设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList中,同时进行一些风格上的增强。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTraceField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "left";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 20;
    traceField.width = stage.stageWidth-40;
    traceField.height = stage.stageHeight-40;
    addChild(traceField);
    }
    
    
  4. 这个方法将在TextField建立后立即触发,因为我们将把复制过程中每一步完成的消息输出到这个视觉元素中。

  5. 首先要确定应用程序数据库是否存在,这将决定我们是否需要复制默认数据库。为此,我们将实例化一个新的File对象,并引用应用程序安装目录中的名为products.db的文件。如果此文件不存在,我们必须创建另一个File对象,引用我们要复制文件到的文件名和位置。

  6. 建立连接后,在源File上使用File.copyTo()方法,并传入目标File。如果一切顺利,现在应该在应用程序存储目录中有一个默认数据库的精确副本:

    protected function checkDefaultDB():void {
    traceField.appendText("Checking if DB exists...\n\n");
    var dbFile:File = File.applicationStorageDirectory;
    dbFile = dbFile.resolvePath("products.db");
    if(dbFile.exists){
    traceField.appendText("Application DB Okay!\n\n");
    }else{
    traceField.appendText("Application DB Missing!\n\n");
    traceField.appendText("Copying Default DB...\n\n");
    var sourceFile:File = File.applicationDirectory;
    sourceFile = sourceFile.resolvePath("default.db");
    var destination:File = File.applicationStorageDirectory;
    destination = destination.resolvePath("products.db");
    sourceFile.copyTo(destination, true);
    traceField.appendText("Database Copy Completed!\n\n");
    }
    connectDB();
    }
    
    
  7. 要打开应用程序数据库,我们首先必须初始化我们的SQLConnection对象,并将File.db引用传递给SQLConnection.open()方法以建立连接。现在我们有了与新建复制的数据库的连接,我们调用getDBItems()方法来检索记录以供显示:

    protected function connectDB():void {
    sqlConnection = new SQLConnection();
    sqlConnection.open(File.applicationStorageDirectory. resolvePath("products.db"));
    getDBItems();
    }
    
    
  8. 要从复制的数据库中检索所有记录,我们将初始化一个SQLStatement,并将建立的SQLConnection分配给SQLStatement.sqlConnection属性。然后,我们将 SQL 语句的String传递给SQLStatement.text属性,并调用SQLConnection.execute()从数据库中检索所有记录。

  9. 要将返回的数据输出到TextField,我们只需初始化一个新的Array来包含返回的记录,通过将SQLStatement.getResult()data属性(它本身是一个Array)分配给Array。现在创建一个for循环来解析结果,将每条记录分配的各种属性输出到我们的TextField。这将在 Android 设备上直观地显示查询结果。

    protected function getDBItems():void {
    traceField.appendText("Gathering items from application DB...\ n\n");
    var sqlStatement:SQLStatement = new SQLStatement();
    sqlStatement.sqlConnection = sqlConnection;
    sqlStatement.text = "SELECT * FROM Products";
    sqlStatement.execute();
    var sqlArray:Array = sqlStatement.getResult().data;
    var itemCount:int = sqlArray.length;
    traceField.appendText("Database Contains:\n");
    for(var i:int=0; i<itemCount; i++){
    traceField.appendText("PRODUCT: " + sqlArray[i].ProductName + "\n");
    }
    }
    
    
  10. 应用程序首次运行时,在应用程序存储目录中找不到数据库。然后将默认数据库复制到预期位置,然后检索记录并显示给用户查看:如何操作...

  11. 如果用户后续运行此应用程序,数据库现在在预期位置,应用程序只需执行查询并显示记录,无需从一处位置复制文件到另一位置:如何操作...

工作原理...

在此食谱中,我们使用FileSQLConnection/SQLStatement对象的组合来确定数据库是否存在,然后进行简单的查询和记录显示,或者使用File.copyTo()将文件从应用程序安装目录复制到应用程序存储目录的更复杂操作。此方法将复制作为初始参数传入的文件引用到指定位置。还有许多其他类似的文件操作方法。以下列出了一些这些方法:

  • File.copyTo():将文件或目录复制到新位置

  • File.moveTo():将文件或目录移动到新位置

  • File.deleteFile()XE"default application database:File.deleteFile()方法":删除指定的文件

  • File.createDirectory():创建目录以及所需的所有父目录

  • File.deleteDirectory():删除指定的目录

要全面了解File类,请参考 Adobe LiveDocs:

help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/filesystem/File.html

数据库文件,仅仅是一个常规文件,可以通过 ActionScript 像其他任何文件一样轻松操作。但是,在这种情况下,了解应用程序是否有权限写入哪些目录是很重要的。例如,File.applicationDirectory是只读的。我们不能将文件写入此目录。

如果你需要一个工具来创建或管理 SQLite 数据库文件,你可能会对像 SQLite Database browser 这样的软件项目感兴趣,可以免费从sqlitebrowser.sourceforge.net/下载。

使用 FlexORM 自动化数据库任务

尽管我们确实可以通过支持的 SQLite 语法完全控制应用程序数据库,但有一些代码库可以使事情变得更容易。这样一个库叫做FlexORM,顾名思义,它只能在 Flex 项目中使用,因此纯 ActionScript 是不行的。

FlexORM 是一个对象关系映射框架,它避免了开发者在项目中编写任何数据库代码或 SQL。对象是持久的,任何数据库转换都由框架本身在幕后处理。

准备工作...

在准备这个应用程序示例时,你还需要采取一些额外的步骤来准备,因为涉及到获取 FlexORM 库并在项目中设置它的一些设置:

  1. 首先,我们必须打开一个网络浏览器并前往FlexORM 的项目页面

  2. 通过屏幕底部的ZIP包或通过 SVN 仓库下载文件。

  3. 文件一旦在你的系统上,我们将想要导航到trunk | flexorm | src目录,并获取src下的所有内容。这是我们为了使用 FlexORM 而必须导入到 Flash Builder 中的包。

  4. 创建一个新的移动 Flex 项目,并将文件从 Flex 项目src文件夹下的src拖拽过来。我们现在可以在我们的应用程序中使用FlexORM

  5. 你的项目将与下面截图显示的项目非常相似:准备工作...

如何操作...

使用FlexORM框架,我们将定义一个持久对象结构,并通过一个简单的 Flex 移动项目管理对象条目的创建和删除:

  1. 我们首先要在一个名为vo [值对象] 的包中创建一个名为Product的类。这将作为我们可绑定对象的声明,并且反映了我们将要从数据库中插入和读取的内容。使用特定于FlexORM的元数据,我们声明一个名为Products的表,其中有一个名为id的 ID 列和一个名为ProductName的附加列。这些对象作为我们实际表结构的接口,允许我们通过熟悉的面向对象范例来管理 SQL 命令:

    package vo {
    [Bindable]
    [Table(name="Products")]
    public class Product {
    [Id]public var id:int;
    [Column]public var ProductName:String;
    }
    }
    
    
  2. 下一步将编写一个ViewNavigatorApplication MXML 文件作为我们的主应用程序文件。我们可以包含一个指向特定ViewfirstView属性,以及一个applicationComplete属性,它将调用一个初始化函数:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication xmlns:fx= "http://ns.adobe.com/mxml/2009"
     firstView="views.FlexORMHomeView" applicationComplete="init()">
    </s:ViewNavigatorApplication>
    
    
  3. 现在我们将声明一个Script块并执行一系列导入,这对于我们应用程序的这部分是必要的。我们从FlexORM只需要EntityManager。这是用于读取和写入我们数据库的内容。我们还必须导入与FlexORM一起使用的vo对象类以及用于保存产生的任何记录的ArrayCollection

    <fx:Script>
    <![
    CDATA[
    import nz.co.codec.flexorm.EntityManager;
    import vo.Product;
    import mx.collections.ArrayCollection;
    ]]>
    </fx:Script>
    
    
  4. 在这里,我们将实例化EntityManagerArrayCollection以供应用程序使用。调用EntityManager.getInstance()将允许我们开始使用FlexORM

    protected var entityManager:EntityManager = EntityManager.getInstance();
    [Bindable] public var productArrayCollection:ArrayCollection;
    
    
  5. 我们必须定义在ViewNavigatorApplication标签中提到的初始化方法。在此方法内,使用File类引用要在应用程序存储目录中创建的数据库文件。创建一个新的SQLConnection,并用它打开先前定义的File引用。现在,可以将SQLConnection绑定到EntityManagersqlConnection属性上,使我们能够使用FlexORM与数据库交互:

    protected function init():void {
    var databaseFile:File =
    File.applicationStorageDirectory.resolvePath("products.db");
    var connection:SQLConnection = new SQLConnection();
    connection.open(databaseFile);
    entityManager.sqlConnection = connection;
    loadProducts();
    }
    
    
  6. 我们可以在任何时候调用此方法从数据库刷新我们的集合。只需在EntityManager上调用findAll(),并传入我们想要检索的类名,就可以从绑定到该类的表中返回所有记录:

    protected function loadProducts():void {
    productArrayCollection = entityManager.findAll(Product);
    productArrayCollection.refresh();
    }
    
    
  7. 我们需要设置方法以插入和删除应用程序数据库中的记录。为了保存记录,我们会根据希望保存到的表创建一个基于类的对象。现在,我们将根据要为此插入写入值的字段为此类分配属性。在传入此对象的同时调用EntityManager.save(),将指示FlexORM在数据库中插入新记录:

    public function saveProduct(e:String):void {
    var ProductEntry:Product = new Product();
    ProductEntry.ProductName = e;
    entityManager.save(ProductEntry);
    loadProducts();
    }
    
    
  8. 从数据库中删除记录同样简单。在传入集合中的对象时调用EntityManager.remove(),该对象与要从数据库中删除的特定记录相对应,这将确保FlexORM为我们删除真正的记录:

    public function deleteProduct(index:int):void {
    entityManager.remove(productArrayCollection.getItemAt(index));
    loadProducts();
    }
    
    
  9. 现在构建我们的应用程序视图。创建一个新的View MXML 文件,并为其分配适合您特定项目视图的属性。在这种情况下,我们将其分配给带有一些宽大填充的VerticalLayout

    <?xml version="1.0" encoding="utf-8"?>
    <s:View 
     title="Product Catalog">
    <s:layout>
    <s:VerticalLayout gap="20" paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
    </s:layout>
    </s:View>
    
    
  10. 我们应用程序中用户可以与之交互的控件将包括一个用于输入的TextInput,一个用于提交的Button,以及一个用于显示我们所有数据库记录的List。我们将在按钮点击时调用一个名为addProduct()的函数,以及另一个名为removeProduct()的函数,该函数与我们的列表更改事件相关联。最后的修改是将我们的ListdataProvider绑定到主 MXML 文件中定义的productArrayCollection

    注意

    在此示例中,我们使用parentApplication作为方便。根据应用程序的结构,您可能不想这样做,因为它会在应用程序及其各个模块之间创建通常不希望的关系。

    <s:TextInput id="entry" width="100%"/>
    <s:Button click="addProduct(event)" width="100%" label="Insert New Product"/>
    <s:List id="productList" change="removeProduct(event)" dataProvider="{this.parentApplication.productArrayCollection}" labelField="ProductName" width="100%" height="100%"></s:List>
    
    
  11. 创建一个Script块并导入我们的List变更事件正常触发所需的IndexChangeEvent类:

    <fx:Script>
    <![
    CDATA[
    import spark.events.IndexChangeEvent;
    ]]>
    </fx:Script>
    
    
  12. 现在要做的只剩下创建一些本地函数,以便将信息传递给我们的主 MXML 文件,并执行本地清理任务。首先,我们为Button点击事件创建方法,该方法将数据传递给之前创建的saveProduct()方法。我们将输入的文本传递过去,然后清空TextInput以允许定义更多的记录:

    protected function addProduct(e:MouseEvent):void {
    this.parentApplication.saveProduct(entry.text);
    entry.text = "";
    }
    
    
  13. 最后,编写一个函数来处理基于从List生成的变更事件删除记录。在List上检测到的任何索引变化都会将索引数据传递给之前创建的deleteProduct()方法。然后我们将ListselectedIndex设置为-1,表示没有选择任何项目:

    protected function removeProduct(e:IndexChangeEvent):void {
    this.parentApplication.deleteProduct(e.newIndex);
    productList.selectedIndex = -1;
    }
    
    
  14. 当用户在设备上运行我们的应用程序时,他们能够通过原生的 Android 虚拟键盘输入数据。点击插入新产品按钮将把他们的信息添加到数据库中:如何操作...

  15. 用户将能够向数据库添加多条记录,并且它们会立即出现在List控件中。点击List中的某个项目将触发一个变更事件,从而相应地从应用程序数据库中删除对应的记录:如何操作...

它的工作原理...

FlexORM 需要一些初始设置,以便在开发应用程序时以对我们有利的方式运行该框架,但一旦一切就绪,对于不那么复杂的数据库,它可以节省大量的时间。而 SQL 在语法或使用上与 ActionScript 完全不同。FlexORM 提供了一个接口,通过这个接口我们可以以面向对象的方式管理数据库记录,使用的语言与我们的应用程序其余部分使用的 ActionScript 相同!

还有更多...

FlexORM 对于简单的交易非常适用,但它并不完全支持 SQLite 提供的所有功能。例如,我们不能使用 FlexORM 创建和管理加密的数据库。对于这类特定活动,最好手写查询语句。

第九章:清单保障:安全与 Android 权限

本章节将涵盖以下内容:

  • 使用 Android Manifest 文件设置应用程序权限

  • 防止设备屏幕变暗

  • 建立 Android 自定义 URI 方案

  • 预期 Android 兼容性筛选

  • 指导应用程序安装到设备 SD 卡

  • 加密本地 SQLite 数据库

引言

Android 有一个非常特定的权限和安全系统,基于清单文件声明,允许或限制应用程序访问各种设备功能。本章将详细介绍如何为你的 Flash 平台应用程序正确识别所需权限,以便利用 Android 市场筛选,应用本地应用程序数据库加密,以及其他有用的技巧!

使用 Android Manifest 文件设置应用程序权限

当用户选择在 Android 上安装应用程序时,他们总会收到关于应用程序将在其系统中拥有哪些权限的警告。从互联网访问到完整的地理位置、相机或外部存储权限;用户会明确知道应用程序在他们的系统上会拥有哪些权利。如果看起来应用程序请求的权限比实际需要的多,用户通常会拒绝安装并寻找其他可以完成所需任务的应用程序。只请求应用程序真正需要的权限非常重要,否则用户可能会对你和你提供的应用程序产生怀疑。

如何操作...

我们可以通过三种方式修改Android Manifest文件,为使用 Adobe AIR 编译应用程序时设置应用权限。

使用 Flash Professional:

在 AIR for Android 项目中,打开属性面板,点击播放器选择旁边的扳手图标:

使用 Flash Professional:

将会出现Android 的 AIR 设置对话框窗口。你将看到一个权限列表,可以选择为你的应用程序启用或禁用。只选中你的应用程序需要的权限,完成后点击确定

使用 Flash Professional:

使用 Flash Builder:

  1. 在 Flash Builder 中首次设置你的 AIR for Android 项目时,在项目位置区域定义所需的一切,然后点击下一步

  2. 你现在处于新建 Flex 移动项目对话框的移动设置区域。点击权限标签,确保已选择Google Android作为平台。你将看到一个权限列表,可以选择为你的应用程序启用或禁用。只选中你的应用程序需要的权限,然后继续你的项目设置:使用 Flash Builder:

  3. 在开始开发应用程序后,若要修改这些权限,只需打开 AIR 描述文件,按照下面几节的详细说明进行编辑。

使用简单的文本编辑器:

  1. 在你的项目中找到 AIR 描述文件。它通常被命名为类似{MyProject}-app.xml的名称,位于项目根目录。

  2. 浏览文件,寻找名为<android>的节点,在这个节点内会有一个名为<manifestAdditions>的节点,它包含一个名为<manifest>的子节点。本文档的这一部分包含了我们为 Android 应用程序设置权限所需的一切。

  3. 我们需要做的就是注释掉或移除那些应用程序不需要的特定权限。例如,这个应用程序需要互联网、外部存储和相机访问权限。其他所有权限节点都使用标准的 XML 注释语法<!-- {在此处注释} -->进行注释。

    <uses-permission name="android.permission.INTERNET"/>
    <uses-permission name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!--<uses-permission name="android.permission.READ_PHONE_STATE"/>-->
    <!--<uses-permission name="android.permission.ACCESS_FINE_LOCATION"/>-->
    <!--<uses-permission name="android.permission.DISABLE_KEYGUARD"/>-->
    <!--<uses-permission name="android.permission.WAKE_LOCK"/>-->
    <uses-permission name="android.permission.CAMERA"/>
    <!--<uses-permission name="android.permission.RECORD_AUDIO"/>-->
    <!--<uses-permission name="android.permission.ACCESS_NETWORK_STATE"/>-->
    <!--<uses-permission name="android.permission.ACCESS_WIFI_STATE"/>-->
    
    

工作原理如下...

在 AIR 描述文件中定义的权限将被用于创建一个 Android 清单文件,该文件将被打包进编译项目时产生的.apk文件中。这些权限限制并启用了应用程序,一旦安装在用户设备上,也会在安装前告知用户应用程序将获得哪些活动和资源的访问权限。为应用程序提供仅完成预期任务所需的权限非常重要。

以下是 Android 清单文档可能包含的权限列表:

  • ACCESS_COARSE_LOCATION: 允许Geoloctaion类访问 WIFI 和三角定位的基站位置数据。

  • ACCESS_FINE_LOCATION: 允许Geolocation类使用设备的 GPS 传感器。

  • ACCESS_NETWORK_STATE: 允许应用程序通过NetworkInfo类访问网络状态。

  • ACCESS_WIFI_STATE: 允许应用程序通过NetworkInfo类访问 WIFI 状态。

  • CAMERA: 允许应用程序访问设备摄像头。

  • INTERNET: 允许应用程序访问互联网并执行数据传输请求。

  • READ_PHONE_STATE: 允许应用程序在电话通话过程中静音音频。

  • RECORD_AUDIO: 允许应用程序访问麦克风以录制或监控音频数据。

  • WAKE_LOCK: 允许应用程序使用SystemIdleMode类防止设备进入休眠状态。(必须与DISABLE_KEYGUARD一起使用)

  • DISABLE_KEYGUARD: 允许应用程序使用SystemIdleMode类防止设备进入休眠状态。(必须与WAKE_LOCK一起使用)

  • WRITE_EXTERNAL_STORAGE: 允许应用程序写入外部存储。这部分存储通常是指设备的 SD 卡。

防止设备屏幕变暗

安卓操作系统会在经过一定时间后,降低亮度并最终关闭设备屏幕。这样做是为了节省电池寿命,因为显示屏幕是设备上的主要耗电项。对于大多数应用程序,如果用户正在与界面互动,那么这种互动将阻止屏幕变暗。然而,如果你的应用程序在长时间内不涉及用户互动,但用户正在观看或阅读屏幕上的内容,那么阻止屏幕变暗是合理的。

如何操作...

AIR 描述文件中有两个设置可以更改,以确保屏幕不会变暗。我们还将修改应用程序的属性来完成这个配方:

  1. 在你的项目中找到 AIR 描述文件。它通常像{MyProject}-app.xml这样命名,位于项目根目录。

  2. 浏览文件,寻找名为<android>的节点,在这个节点内会有一个名为<manifestAdditions>的节点,它包含一个名为<manifest>的子节点。本文档的这一部分包含了我们为 Android 应用程序设置权限所需的一切。

  3. 我们需要确保以下两个节点存在于描述文件的这一部分中。请注意,启用这两个权限是允许应用程序通过SystemIdleMode类控制系统的必要条件。如有必要,请取消注释它们。

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
    
    
  4. 在我们的应用程序中,我们将导入以下类:

    import flash.desktop.NativeApplication;
    import flash.desktop.SystemIdleMode;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  5. 声明一个TextFieldTextFormat对,以向用户输出跟踪信息:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  6. 现在,我们将通过将SystemIdleMode.KEEP_AWAKE常量赋值给NativeApplication.nativeApplication.systemIdleMode属性,为我们的应用程序设置系统空闲模式:

    protected function setIdleMode():void {
    NativeApplication.nativeApplication.systemIdleMode = SystemIdleMode.KEEP_AWAKE;
    }
    
    
  7. 在这一点上,我们继续设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTraceField():void {
    device screenpreventing, from dimmingtraceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "left";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 20
    traceField.width = stage.stageWidth-40;
    traceField.height = stage.stageHeight - traceField.y;
    addChild(traceField);
    }
    
    
  8. 在这里,我们简单地将当前分配的系统空闲模式字符串输出到我们的TextField中,让用户知道设备不会进入休眠状态:

    protected function checkIdleMode():void {
    traceField.text = "System Idle Mode: " + NativeApplication. nativeApplication.systemIdleMode;
    }
    
    
  9. 当应用程序在设备上运行时,系统空闲模式将被设置,结果将输出到我们的显示屏幕。用户可以将设备留置不管,只要需要,屏幕就不会变暗或锁定。在以下示例中,这个应用程序在五分钟内没有用户干预的情况下被允许运行:如何操作...

它是如何工作的...

为了使此功能正确工作,必须完成两件事,这两件事都是绝对必要的。首先,我们必须确保应用程序通过 Android 清单文件具有正确的权限。在 AIR 描述符文件中允许应用程序具有 WAKE_LOCKDISABLE_KEYGUARD 权限将为我们完成此操作。第二部分涉及将 NativeApplication.systemIdleMode 属性设置为 keepAwake。最好使用 SystemIdleMode.KEEP_AWAKE 常量来完成此操作。确保满足这些条件将使应用程序能够保持设备显示屏亮起,并防止 Android 在设备空闲后锁定设备。

另请参阅...

在此食谱中,我们通过基本文本编辑器编辑了 AIR 描述符文件。有关在其他环境中设置这些权限的其他方法,请参阅之前的食谱。

建立 Android 自定义 URI 方案

Android 向 AIR 公开了许多有用的 URI 协议,用于标准操作,如映射、短信和电话。为我们应用程序定义自定义 URI 允许它从系统的任何地方调用:通过网页浏览器、电子邮件,甚至本地应用程序。自定义 URI 提供了调用 AIR 应用程序的一种替代方法。

如何操作...

我们将创建一个可以从设备网页浏览器使用自定义 URI 打开的应用程序。我们通过修改 AIR 描述符文件来定义 URI 意图设置:

  1. 在您的项目中找到 AIR 描述符文件。它通常像 {MyProject}-app.xml 这样命名,位于项目根目录。

  2. 浏览文件以查找名为 <android> 的节点;在此节点内将有一个名为 <manifestAdditions> 的节点,其中包含一个名为 <manifest> 的子节点。本文档的此部分包含设置我们 Android 应用程序权限所需的一切。

  3. 我们现在将突出显示的 <intent-filter> 节点添加到我们的描述符文件中。定义我们 URI 的意图部分是 <data android:scheme="fvm"/>。这将使我们的应用程序能够使用 fvm:// URI。请注意,本例中使用了 "fvm";在根据此类示例编写应用程序时,我们可以自由地将此值更改为适合特定应用程序的任何值:

    <application android:enabled="true">
    <activity android:excludeFromRecents="false">
    <intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="fvm"/>
    </intent-filter>
    </activity>
    </application>
    
    
  4. 在我们的应用程序中,我们将导入以下类:

    import flash.desktop.NativeApplication;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.InvokeEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    
  5. 声明一个 TextFieldTextFormat 对,以向用户输出消息:

    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  6. 在这一点上,我们将继续设置我们的 TextField,应用 TextFormat,并将其添加到 DisplayList 中。在这里,我们创建一个方法来执行所有这些操作:

    protected function setupTraceField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "left";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 40;
    traceField.width = stage.stageWidth-40;
    traceField.height =stage.stageHeight - traceField.y;
    addChild(traceField);
    }
    
    
  7. NativeApplication 上注册一个类型为 InvokeEvent.INVOKE 的事件监听器。这将检测用户使用我们定义的 URI 发起的任何应用程序调用事件:

    protected function registerListeners():void {
    NativeApplication.nativeApplication. addEventListener(InvokeEvent.INVOKE, onInvoke);
    }
    
    
  8. 当应用程序从我们的 URI 打开时,将处理以下方法。我们可以从我们的调用事件中收集一定量的信息,比如 reason 属性。这个属性将具有 "login""standard" 的值。如果应用程序在系统登录时自动启动,该值将显示为 "login"。在 URI 调用的情况下,它将显示为 "standard"。我们还可以访问 currentDirectory。应用程序可能从文件系统内部调用,或者访问通过 URI 传递的任何 arguments。请注意,在从网络浏览器进行 URI 调用的这种情况下,arguments 属性将只包含所选链接的完整 URL。这是我们可以在启动时向应用程序传递数据的一种方式。

    protected function onInvoke(e:InvokeEvent):void {
    traceField.text = "";
    traceField.text = "Invoke Reason: " + e.reason + "\n"; traceField.appendText("Directory URL: " + e.currentDirectory. url + "\n\n");
    var args:Array = e.arguments;
    if (arguments.length > 0) {
    traceField.appendText("Message: " + args.toString() + "\n");
    }
    }
    
    
  9. 对于这个例子,让我们设置一个简单的网页,其中包含一个使用我们定义的 fvm:// URI:<a href="fvm://arg1=Hello&arg2=AIRAndroid">打开 AIR Android 应用!</a> 的链接。如果用户已经安装了该应用程序并点击了这个链接,应用程序应该会打开,因为我们的 URI 意图在设备上已经注册了:如何操作...

  10. 一旦用户点击了使用我们定义的 URI 的链接,AIR 应用程序将会打开并检测到一个 InvokeEvent,在设备显示屏上显示以下信息。我们可以看到这里目录 URL 是空的,因为应用程序不是从设备文件系统中调用的:如何操作...

它的工作原理...

当我们在应用程序描述符文件中定义 URI 意图时,这会被编译到 Android 清单文件中,与应用程序一起安装到设备上。在设备上安装此应用程序会告知操作系统我们定义的 URI 意图。这使得操作系统知道特定的 URI,并指示在遇到该 URI 时打开应用程序。我们可以将 URI 放置在多个不同的位置,包括系统上的本地 Android 应用程序。这使得本地应用程序能够打开 AIR for Android 应用程序。在之前的示例中,我们将 URI 嵌入 HTML 中,并使用 Android 网络浏览器打开我们的应用程序。

另请参阅...

想要了解更多关于在 AIR for Android 中使用 URI 协议的信息,请查看 第七章,本地交互:StageWebView 和 URI 处理器。

预期 Android 兼容性筛选

根据特定应用程序中使用的 API,一些 Android 设备可能无法提供对预期传感器或硬件钩子的访问。如果用户下载了一个无法按预期工作的应用程序,这个用户将会感到沮丧,很可能会给我们一个差评,甚至可能是一条恶评。幸运的是,Android 市场可以代表我们进行一些筛选,以确保只有支持我们应用程序的设备才有下载和安装的选项。

如何操作...

修改 Android Manifest 文件,以指定我们的应用程序需要哪些特定特性:

  1. 在你的项目中找到 AIR 描述文件。它通常被命名为类似{MyProject}-app.xml的名称,因为它位于项目根目录。

  2. 浏览文件,查找名为<android>的节点;在这个节点内,会有一个名为<manifestAdditions>的节点,它包含一个名为<manifest>的子节点。文档的这一部分将包含我们需要声明 Android 应用程序兼容性的所有内容。

  3. 我们将根据需求添加某些标签。查看以下信息布局,以确定你应该在 manifest 节点中为特定的特性依赖添加什么内容。设置android:required="false"可以使一个特性成为可选。

当使用 Android 摄像头的特性时:

<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>

当使用 Android 麦克风的特性时:

<uses-feature android:name="android.hardware.microphone" android:required="false"/>

当使用 Geolocation 传感器时:

<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>

当使用加速度传感器时:

<uses-feature android:name="android.hardware.accelerometer" android:required="false"/>

工作原理...

通过指定摄像头和麦克风的一些必需或可选特性,我们可以确保只有设备满足这些具体要求的用户才会被提供下载和安装我们应用程序的选项。我们通过修改 Android manifest 文件,通过向我们的 AIR 描述文件中添加内容来公开这些规范,如本食谱所示。使用这些修改编译我们的应用程序将确保这些规范与我们的.APK一起编码,并在我们的应用程序发布后通过 Android Market 公开。

另请参阅...

有关在 AIR for Android 中使用摄像头和麦克风的内容,请查看第四章,视觉和音频输入:摄像头和麦克风访问

指示应用程序安装到设备 SD 卡

通过稍微修改我们 AIR 应用程序描述文件中的 Android manifest 指令,我们可以通知设备操作系统,如果可能,我们的应用程序应该安装在 SD 卡上,而不是内部存储。这将有助于将内部设备存储保留给操作系统和相关文件。

如何操作...

修改 Android Manifest 文件以确定安装位置选项:

  1. 在你的项目中找到 AIR 描述文件。它通常被命名为类似{MyProject}-app.xml的名称,并且位于项目根目录。

  2. 浏览文件,查找名为<android>的节点;在这个节点内,会有一个名为<manifestAdditions>的节点,其中包含一个名为<manifest>的子节点。

  3. 我们将在<manifest>节点中添加installLocation属性。要设置应用程序由 Android 自行决定安装位置:

    <manifest android:installLocation="auto"/>
    
    
  4. 要设置应用程序优先选择设备 SD 卡:

    <manifest android:installLocation="preferExternal"/>
    
    

    注意

    不能保证设置installLocation="preferExternal"实际上会将应用程序安装到设备 SD 卡。

用户还可以通过以下步骤,如果允许的话,移动应用程序:

  1. 首先,在设备上导航到安装了我们的 AIR 应用程序的应用程序管理屏幕。在大多数 Android 设备上,这个屏幕的位置是设置 | 应用程序 | 管理应用程序。现在从这个屏幕上选择你创建的 AIR 应用程序。

  2. 要将应用程序移动到设备 SD 卡,只需点击标记为移动到 SD 卡的按钮:如何操作...

工作原理...

让用户在一定程度上选择应用程序的安装位置是一个好主意。在 Android 上,只有两个选项:设备存储区域或外部 SD 卡。考虑到大多数设备在外部 SD 卡上的存储空间比内部存储要多,最好是在 AIR 描述符文件中的清单节点上设置android:installLocation="preferExternal",优先选择 SD 卡。尽管不能保证 Android 在安装我们的应用程序时会使用外部 SD 卡,但这至少会让系统知道该位置是首选的。Android 是否能够将应用程序安装到外部存储,主要取决于操作系统版本。一般来说,如果设备能够安装和运行适用于 Android 的 AIR 运行时,它应该具备这个功能。

正如我们之前所见,如果用户愿意,他们总是可以将应用程序从内部存储移动到外部存储,然后再移回来。同样值得注意的是:即使应用程序安装在设备的 SD 卡上,应用程序存储目录、本地共享对象和任何临时文件仍然会被写入内部存储。如果我们打算与我们的应用程序一起保存大量数据,那么我们将使用File.documents目录或File.user目录将这些数据存储在外部 SD 卡上。

参见...

有关本地文件系统的更多信息,请查看第八章,丰富访问:文件系统和本地数据库

加密本地 SQLite 数据库

通常,本地 SQLite 数据库不需要任何安全或加密。然而,如果我们的应用程序包含存储在本地应用程序数据库文件中的敏感数据,我们会希望确保入侵者或小偷无法访问这些信息。幸运的是,我们可以加密在 Android 上可用的数据库,以确保即使用户的设备丢失或被盗,他们的私人信息仍然保持安全。

准备就绪...

为了正确加密数据库文件,我们需要使用一个加密库。在这个例子中,我们将使用位于code.google.com/p/as3crypto/的 as3crypto 包。下载.SWC文件,跟随这个例子操作。

我们需要使.SWC在我们的项目中可用。根据所使用的工具不同,操作过程也会有所不同。

关于将.SWC 包包含到 Flash Builder 项目的说明

  1. 在您的项目中,选择文件菜单,然后选择属性

  2. 在左侧列中,点击ActionScript 构建路径并选择库路径标签页。在这个屏幕中找到标记为添加 SWC的按钮并点击它。

  3. 将会出现一个对话框窗口。选择浏览到 SWC选项,找到包含我们加密库的.SWC文件,然后点击确定

  4. 加密库现在将显示在此屏幕的构建路径库部分。确认这是正确的,并退出属性窗口。加密库现在可以在我们的移动 Android 项目中使用了:将.SWC 包包含到 Flash Builder 项目的操作指南

将.SWC 包包含到 Flash Professional 项目的操作指南

  1. 在您的 Flash 项目中,导航到属性面板,点击脚本选择框旁边的扳手图标:将.SWC 包包含到 Flash Professional 项目的操作指南

  2. 这将打开高级 ActionScript 3.0 设置对话框窗口。选择库路径标签页。在这个屏幕中找到浏览到 SWC 文件的图标并点击它。它显示为一个白色和红色的盒子,是此屏幕上唯一不是灰度的图标:将.SWC 包包含到 Flash Professional 项目的操作指南

  3. 将会出现一个文件浏览对话框窗口。找到包含我们加密库的.SWC文件,然后点击确定

  4. 加密库现在将显示在此屏幕的库路径部分。确认这是正确的,并退出高级 ActionScript 3.0 设置窗口。加密库现在可以在我们的移动 Android 项目中使用了:将.SWC 包包含到 Flash Professional 项目的操作指南

如何操作...

为了加密应用程序数据库,我们将声明一个密码,并使用外部加密库对其进行加密。这将用于创建和打开我们的数据库连接:

  1. 在我们的应用程序中,我们将导入以下类。确保导入MD5类或等效类以进行正确的密钥加密:

    import com.hurlant.crypto.hash.MD5;
    import flash.data.SQLConnection;
    import flash.data.SQLMode;
    import flash.data.SQLStatement;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.SQLEvent;
    import flash.filesystem.File;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.utils.ByteArray;
    
    
  2. 我们现在必须声明一些在此应用程序中使用的对象。一个String常量将保存我们的纯文本密码以供稍后加密。通常,这会由用户提供,这里为了简单起见而硬编码。我们将需要一个SQLConnection来创建或打开我们的数据库文件,以及一组ByteArray对象和一个MD5对象来执行实际的加密。最后,我们声明一个TextFieldTextFormat对,以向用户输出跟踪消息:

    private const pass:String = "AIR@ndr0idIsKo0l";
    private var sqlConnection:SQLConnection;
    private var encryptionPass:ByteArray;
    private var encryptionKey:ByteArray;
    private var md5:MD5;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 在这一点上,我们将继续设置我们的TextField,应用TextFormat,并将其添加到DisplayList以进行文本输出。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTraceField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "left";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 40;
    traceField.width = stage.stageWidth-40;
    traceField.height =stage.stageHeight - traceField.y;
    addChild(traceField);
    }
    
    
  4. 要执行我们数据库的加密,我们首先会实例化一个ByteArray并调用writeUTFBytes()方法,传递我们预定义的密码常量。这将把我们的String写入字节流。

  5. 现在,实例化一个新的MD5对象以及另一个ByteArray,将ByteArray分配给MD5.hash()方法的结果,传递包含密码字节的先前的ByteArray

  6. 实例化一个SQLConnection并注册一个类型为SQLEvent.OPEN的事件监听器。这将在数据库成功创建或打开后触发一个事件。

  7. 最后,调用SQLConnection.open()方法,传递数据库路径作为File对象,SQLMode.CREATE的打开模式常量,自动压缩Boolean,默认页面大小为 1024,以及对于此示例最重要的是,我们的 MD5 加密ByteArray

    protected function encryptDB():void {
    encryptionPass = new ByteArray();
    encryptionPass.writeUTFBytes(pass);
    md5 = new MD5();
    encryptionKey = new ByteArray();
    encryptionKey = md5.hash(encryptionPass);
    sqlConnection = new SQLConnection();
    sqlConnection.addEventListener(SQLEvent.OPEN, dbOpened);
    sqlConnection.open(File.applicationStorageDirectory. resolvePath("encrypted.db"), SQLMode.CREATE, false, 1024, encryptionKey);
    }
    
    
  8. 只要数据库成功创建(或打开)并验证加密有效,以下方法就会触发,将有关加密数据库的信息输出到我们的显示界面:

    protected function dbOpened(e:SQLEvent):void {
    traceField.appendText("Encrypted DB Created!\n\n");
    traceField.appendText("Pass: " + pass + "\n\n");
    traceField.appendText("Key: " + encryptionKey.toString());
    }
    
    
  9. 当应用程序在我们的 Android 设备上运行时,它将如下所示。由于密钥是一个真正 MD5 加密的ByteArray,它在TextField中显示为乱码字符,因为它不再是明文String如何操作...

工作原理...

如果一个应用程序需要在数据库上进行加密,那么在创建我们的数据库时必须应用加密密钥。实现SQLConnection.open()SQLConnection.openAsync()方法需要我们传递一个使用as3Crypto或类似加密库创建的加密ByteArray密钥。如果我们需要修改加密密钥,可以使用SQLConnection.reencrypt()来进行修改,以本食谱中演示的方式生成密钥。请注意,有效的加密密钥长度必须为 16 字节。

另请参阅...

要了解有关在 AIR for Android 中使用本地数据库的更多信息,请查看第八章,丰富的访问:文件系统和本地数据库

第十章:避免问题:调试和资源考虑

本章将涵盖以下内容:

  • 使用 Flash Professional 调试应用程序

  • 使用 Flash Builder 调试应用程序

  • 使用设备 GPU 渲染应用程序元素

  • 在设备中断事件发生时自动化应用程序关闭

  • 使用设备后退按钮退出应用程序

  • 监控应用程序中的内存使用和帧率

引言

由于 Android 是一个移动操作系统,因此在性能和用户体验优化方面提出了新的特定挑战。Flash 平台开发者在开发 AIR for Android 和移动 Flash Player 应用程序时必须考虑这一点。本章将提供调试和优化技术以及用户体验调整的概览,以使我们的 AIR for Android 应用程序尽可能良好地运行。

使用 Flash Professional 调试应用程序

使用 Flash Professional 调试 AIR for Android 应用程序与调试桌面 AIR 或 Flash 项目非常相似,但有一些值得注意的区别。

准备就绪…

请确保您的 AIR for Android 项目在 Flash Professional 中已打开,并且您的播放器是 AIR for Android。这可以通过属性面板进行验证:

准备就绪…

如何操作…

使用移动调试启动器或在通过 USB 连接的设备上进行调试:

  1. 在应用程序菜单中,选择调试,并将鼠标悬停在标有调试影片的选项上。这将导致出现一个调试选项的子菜单:如何操作…

  2. 当选择在AIR 调试启动器(移动)中调试时,Flash Professional 将切换到完整的调试控制台,并在设备调试器中启动应用程序。这对于在涉及多触控、加速度计或其他设备特定输入和传感器时快速调试应用程序非常有用。断点、跟踪语句和其他调试工具将完全与普通桌面项目中的功能一样。如何操作…

  3. 一旦我们在调试播放器中完成了初步测试,并准备好通过 USB 在设备上进行调试,我们可以在调试菜单中切换到该选项。如果我们从未为此项目配置过 AIR for Android 设置,将出现一个对话框窗口,允许我们进行配置。在随后的调试会话中不应出现此窗口。确保在Android 部署类型下选择调试选项,并在发布后部分选择安装并启动选项。

  4. 在此阶段,你会注意到有用于确定证书来签署你的应用程序的字段。要了解更多关于代码签名过程的信息,请参考第十一章,最后的考虑:应用程序编译和分发如何操作...

  5. 在启动调试会话以在我们的设备上部署后,Flash Professional 将需要几秒钟来编译和部署应用程序。当应用程序开始在设备上加载时,AIR 将会启动一个小对话框,告诉我们它正在尝试连接到我们计算机上的调试器。一旦建立连接,窗口将消失,我们的完整应用程序将启动,使我们能够像平常一样进行测试和调试。如何操作...

工作原理...

通过断点和变量检查来调试应用程序与使用任何 Flash 平台技术开发应用程序的课程相同。在使用 AIR for Android 时,我们需要处理外部硬件,并采取一些额外的步骤以确保我们能够在正常环境中进行调试,同时与在真实设备上运行的应用程序进行交互。本食谱展示了在我们当前工作流程中实现这一切所需步骤。

另请参阅...

若想了解更多关于使用 Flash Professional 进行项目设置的信息,你可以参考第一章,准备使用 Android:开发环境和项目设置

使用 Flash Builder 调试应用程序

在 Flash Builder 中定义调试配置的能力是一个优秀的工作流程改进,我们在设置新的移动项目或准备测试我们工作了一段时间的项目时应该利用这一点。我们可以使用 Flash Builder 的调试配置面板为同一个项目设置多个配置。

如何操作…

我们将要探索调试配置面板,为我们的移动项目配置一组自定义的启动设置:

  1. 选择一个移动项目,点击 Flash Builder 工具栏中调试按钮旁边的箭头。从菜单中选择调试配置选项。调试配置对话框窗口将会打开:如何操作…

  2. 双击左侧菜单中标记为MobileApp的条目,以编辑此选定项目的特定设置。从这个窗口,我们可以选择另一个要配置的项目,为项目指定默认的Application文件,设置一个Target平台(在我们的情况下是 Google Android),并配置一个Launch方法。如果在桌面上调试,我们还可以从各种设备配置文件中选择,甚至可以配置我们自己的。在下一个截图中,我们选择使用摩托罗拉 Droid 上的尺寸和分辨率进行调试:如何操作…

  3. 如果有必要定义其他设备,我们可以点击配置按钮以启动设备配置屏幕,该屏幕允许我们导入设备配置文件,甚至添加我们自己的配置文件:如何操作…

  4. 添加自定义设备配置文件时,我们可以指定显示的宽度和高度以及支持的每英寸像素。Google Android 有一个标准平台 UI,根据制造商对标准显示元素进行的定制程度,在不同设备之间可能有所不同。例如,通知栏除非设备处于全屏模式,否则始终会出现。如果特定设备上的通知栏更高或更短,我们可以在这一步进行相应调整。

    注意

    尽管这里可以模拟分辨率和 PPI,但除非开发机器具有多点触控界面,否则我们仍需要在实际设备上测试任何触摸或手势输入。当然,设备性能也不是模拟的一部分。

    如何操作…

  5. 当选择在实际物理硬件上调试时,我们可以选择通过 USB 或无线网络在设备上进行调试。USB 调试通常更为直接,大多数情况下推荐使用。在以下屏幕截图中,您可以看到我们已经为桌面调试定义了一个配置,以及一个用于通过 USB 连接的设备调试的配置:如何操作…

  6. 完成后,点击应用然后关闭。我们现在可以通过 Flash Builder 调试图标或项目上下文菜单访问任何已定义的配置:如何操作…

  7. 一旦我们选择为项目启动调试会话,那么在桌面调试时,它将在 Flash Builder 移动调试播放器中打开;如果是 USB 设备调试,它将被编译、推送到设备并安装。对于设备调试会话,AIR 将启动一个小对话框,告知我们它正在尝试连接到计算机上的调试器。一旦建立连接,窗口将消失,我们的完整应用程序将启动,使我们能够正常测试和调试。如何操作…

工作原理…

如果你选择在桌面上启动,你将能够在 Flash Builder 中进行本地调试。你也可以通过选择一系列配置文件来模拟各种 Android 设备。如果你想创建自己的配置文件,可以点击配置按钮。

当选择在设备上启动时,你也可以通过 Flash Builder 在设备上进行调试。这是迄今为止调试移动应用程序的最佳方式,因为它是在真正的 Android 硬件上进行测试的。

另请参阅...

有关使用 Flash Builder 进行项目设置的信息,你可以参考第一章,准备使用 Android:开发环境和项目设置。

使用设备 GPU 渲染应用程序元素

虽然较旧的 Android 设备必须依赖 CPU 来渲染移动 Adobe AIR 项目中的所有内容,但市场上许多较新的设备完全支持图形处理单元(GPU)渲染并提供必要的钩子让我们的应用程序利用这一点。本食谱将展示我们必须采取的必要步骤,以启用应用程序元素的 GPU 加速。

如何操作...

我们将修改 AIR 描述符文件中的设置,并使DisplayObject实例能够利用这些修改:

  1. 在你的项目中找到 AIR 描述符文件。它通常被命名为类似{MyProject}-app.xml,并位于项目根目录。

  2. 浏览文件,找到名为<initialWindow>的节点,位于本文档的开头部分。这个节点包含了许多关于我们应用程序窗口视觉方面的默认设置。

  3. 我们现在必须找到名为<renderMode>的子节点。如果此节点不存在,我们可以轻松地在这里添加它。renderMode的值决定了应用是使用 CPU 还是 GPU 来渲染内容。应用程序renderMode有三个可能的值:

    • AUTO: 应用将尝试使用设备 GPU 来渲染视觉显示对象:

      <renderMode>auto</renderMode>
      
      
    • GPU: 应用将被锁定在 GPU 模式下。如果设备不支持 Adobe AIR 中的 GPU 渲染,将会出现问题:

      <renderMode>gpu</renderMode>
      
      
    • CPU: 应用将使用设备 CPU 来渲染所有视觉显示对象。这是最安全的设置,但提供的优势最少:

      <renderMode>cpu</renderMode>
      
      
  4. 现在,每当我们想在应用程序中的DisplayObject实例利用这一点时,我们必须将DisplayObject实例的cacheAsBitmap属性设置为true,并将cacheAsBitmapMatrix属性分配给一个新的 Matrix 对象。这将使这些单个对象通过设备 GPU 进行 2D 内容渲染。当在 2.5D 空间中使用对象时,它们将自动使用 GPU 进行渲染,不需要这些额外的设置。

    displayObject.cacheAsBitmap = true;
    displayObject.cacheAsBitmapMatrix =new Matrix();
    
    

它的工作原理...

在 AIR 描述符文件中将应用的renderMode设置为gpu将强制应用使用 GPU 渲染视觉对象。然而,不在 2.5D 空间中渲染的个别对象需要将cacheAsBitmap属性设置为true,并将cacheAsBitmapMatix属性分配给一个 Matrix 对象。当设置renderModeauto时,应用将尝试通过 GPU 渲染这些对象,如果特定设备不支持 GPU 加速,则回退到 CPU 渲染。我们还可以将renderMode设置为cpu,这样会完全绕过 GPU 渲染,只通过 CPU 渲染所有内容。

当适当使用时,设置应用的renderMode可以显著提高应用内部视觉对象的渲染速度。重要的是要意识到许多设备可能无法通过 AIR for Android 获得完整的 GPU 支持,在这种情况下,强制使用 GPU 可能会对应用造成问题,甚至可能导致在某些特定设备上无法使用。在使用 GPU 时也存在一些限制,例如:不支持滤镜、PixelBender 混合以及各种标准的混合模式。

还有更多内容...

如果使用 Flash Professional,我们还可以通过 AIR 的Android 设置面板设置渲染模式。这可以通过属性面板访问。点击播放器选择旁边的扳手图标来配置这些设置。

还有更多...

在设备中断事件时自动化应用关闭

当应用在 Android 设备上运行时,用户会话很可能因为电话来电或其他不可预见的事件而中断。当发生此类情况时,我们应该考虑是否应该退出应用并释放系统资源供其他任务使用。

如何操作...

我们将监听一个应用发出的停用事件并响应退出应用:

  1. 首先,我们需要将以下类导入到我们的应用中:

    import flash.desktop.NativeApplication:
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    
    
  2. 我们必须在NativeApplication.nativeApplication对象上注册一个类型为Event.DEACTIVATE的事件监听器。当设备上发生电话来电或其他中断导致应用失去焦点时,将触发此事件:

    protected function registerListeners():void {
    NativeApplication.nativeApplication.addEventListener(Event. DEACTIVATE, appDeactivate);
    }
    
    
  3. 在下面的函数中,我们将在NativeApplication.nativeApplication对象上调用exit()方法,完全关闭应用。这将释放资源供其他设备应用使用:

    protected function appDeactivate(e:Event):void {
    NativeApplication.nativeApplication.exit();
    }
    
    

工作原理...

我们希望在使用用户设备上的资源时,成为我们应用程序运行的良好管理者。有效的方法是确保在应用程序处于非活动状态时释放应用程序使用的任何内存。监听停用事件将允许我们知道何时有其他应用程序获得焦点。在这一点上,我们可以完全退出应用程序,释放用户当前正在使用的资源。

另请参阅…

在实际退出应用程序之前,我们有机会通过本地共享对象或本地数据库保存会话数据。有关如何执行此操作的信息,请查看第八章,丰富的访问:文件系统和本地数据库。

使用设备后退按钮退出应用程序

安卓设备通常在设备的一侧有一组四个软键,这些软键始终对用户可见。其中两个键涉及导航——后退和主页键。当用户激活某个事件,例如按下后退按钮时,我们应该考虑是否完全退出应用程序并释放系统资源以供其他任务使用。

注意

主页按钮将始终使用户返回到 Android 桌面,从而停用我们的应用程序。要了解如何在发生此类事件时关闭应用程序,请参阅之前的食谱。

如何操作...

我们将监听专用安卓后退按钮的按下,并在响应中退出应用程序:

  1. 首先,我们需要将以下类导入到我们的应用程序中。

    import flash.desktop.NativeApplication;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
    
    
  2. 我们必须在NativeApplication.nativeApplication对象上注册一个类型为KeyboardEvent.KEY_DOWN的事件监听器。当用户激活专用的安卓后退键时,此事件将被触发:

    protected function registerListeners():void {
    NativeApplication.nativeApplication. addEventListener(KeyboardEvent.KEY_DOWN, onBackPressed);
    }
    
    

当用户按下后退键时,我们将调用NativeApplication.nativeApplication对象的exit()方法,完全关闭应用程序。这将释放资源供其他设备应用程序使用:

protected function onBackPressed(e:KeyboardEvent):void {
if(e.keyCode == Keyboard.BACK){
NativeApplication.nativeApplication.exit();
}
}

工作原理...

我们希望在使用用户设备上的资源时,成为我们应用程序运行的良好管理者。有效的方法是确保在应用程序处于非活动状态时释放应用程序使用的任何内存。做到这一点的方法之一是监听键盘事件并拦截后退键的按下。在这一点上,我们可以完全退出应用程序,释放用户当前正在使用的资源。

根据我们应用程序的当前状态,我们可以选择是退出应用程序还是仅返回到某个之前的状态。在基于 Flex 的移动项目中执行此类操作时,我们可能只有在当前视图是我们应用程序中的初始视图ViewNavigator时才会退出应用程序。

还有更多…

通过使用KeyboardEvent.preventDefault(),也可以阻止安卓返回按钮执行任何操作:

protected function onBackPressed(e:KeyboardEvent):void {
if(e.keyCode == Keyboard.BACK){
KeyboardEvent.preventDefault();
}
}

另请参阅…

请注意,在实际退出应用程序之前,我们有机会通过本地共享对象或本地数据库保存任何会话数据。有关如何执行此操作的更多信息,请查看第八章,丰富的访问:文件系统和本地数据库

监控应用程序中的内存使用和帧率

安卓设备通常与传统桌面或笔记本电脑相比,内存较少且 CPU 性能较弱。在构建安卓应用时,我们必须非常小心,以免创建出过于耗能的应用,导致帧率降至不可接受的水平或应用无响应。为了帮助我们排查和监控这些问题,我们可以跟踪运行中应用的内存消耗和计算出的帧率,以便相应地作出响应。

如何操作...

我们可以通过使用flash.system包以及flash.utils.getTimer类来监控许多系统属性,以计算当前应用程序的帧率:

  1. 首先,我们需要将以下类导入到我们的应用程序中:

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.system.Capabilities;
    import flash.system.System;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.utils.getTimer;
    
    
  2. 我们需要声明一组Number对象来保存持久的计时值,以便计算应用程序的帧率。同时,声明一个TextFieldTextFormat对,以向用户输出此和其他设备消息:

    private var prevTime:Number;
    private var numFrames:Number;
    private var frameRate:Number;
    private var traceField:TextField;
    private var traceFormat:TextFormat;
    
    
  3. 在这一点上,我们继续设置我们的TextField,应用TextFormat,并将其添加到DisplayList中。这里,我们创建一个方法来执行所有这些操作:

    protected function setupTraceField():void {
    traceFormat = new TextFormat();
    traceFormat.bold = true;
    traceFormat.font = "_sans";
    traceFormat.size = 24;
    traceFormat.align = "left";
    traceFormat.color = 0xCCCCCC;
    traceField = new TextField();
    traceField.defaultTextFormat = traceFormat;
    traceField.selectable = false;
    traceField.multiline = true;
    traceField.wordWrap = true;
    traceField.mouseEnabled = false;
    traceField.x = 20;
    traceField.y = 40;
    traceField.width = stage.stageWidth-40;
    traceField.height = stage.stageHeight - traceField.y;
    addChild(traceField);
    }
    
    
  4. 下一步包括创建处理我们帧率计算的机制。我们将prevTimeNumber设置为应用程序初始化后当前经过的毫秒数。我们还将numFrames变量设置为0。这为我们提供了一组基础数字。最后,我们在应用程序上注册一个Event.ENTER_FRAME类型的事件监听器,以定期为我们执行新的帧率计算:

    protected function registerListeners():void {
    prevTime = getTimer();
    numFrames = 0;
    this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    
    
  5. 这个冗长的方法会在每次进入帧时刷新我们TextField中的所有内容。首先,我们将输出一些关于 CPU 架构、制造商以及应用程序可用的内存的信息。在这一步中,内存是重要的部分。

  6. 为了计算运行帧率,我们首先会递增我们的帧计数器,并再次获取从应用程序初始化开始经过的毫秒数。可以减去上一次读取的值,这样我们就得到了自上次运行此函数以来经过的时间。

  7. 如果经过的时间超过 1000,那么已经过去了一秒钟,然后我们可以进行一些计算来确定我们实际每秒的帧数。我们将通过将本次循环中处理的帧数除以持有我们之前时间的变量乘以 1000,来获取每分钟的帧数。将上一个时间变量设置为当前经过的时间,并将我们的帧数重置为0,将开始一个新的周期:

    protected function onEnterFrame(e:Event):void {
    traceField.text = "CPU Arch: " + Capabilities.cpuArchitecture + "\n";
    traceField.appendText("Manufacturer: " + Capabilities. manufacturer + "\n");
    traceField.appendText("OS: " + Capabilities.os + "\n\n");
    traceField.appendText("Free Memory: " + System.freeMemory + "\n");
    traceField.appendText("Total Memory: " + System.totalMemory + "\n\n");
    numFrames++;
    var timeNow:Number = getTimer();
    var timePast:Number = timeNow - prevTime;
    if(timePast > 1000){
    var fpm:Number = numFrames/timePast;
    frameRate = Math.floor(fpm * 1000);
    prevTime = timeNow;
    numFrames = 0;
    }
    traceField.appendText("Framerate: " + frameRate);
    }
    
    
  8. 当我们在设备上运行应用程序时,我们可以看到 CPU 和操作系统信息,以及内存使用情况和计算出的帧率:如何操作...

它的工作原理...

关于 CPU 和内存使用,可以通过 Capabilities 和 System 类访问大量信息。我们可以通过从getTimer()实用方法获取的数据计算实际 FPS,来获取有关当前帧率的更多信息。将这些信息综合使用,将为我们提供一组合理的数据,以确定应用程序在特定设备上的运行情况。然后,我们可以使用这些数据在应用程序运行时通过修改应用程序属性、改变我们渲染内容的方式,甚至提醒用户可能存在问题。

还有更多...

如果帧率变得太慢,我们可能需要考虑降低帧率,甚至降低应用程序的渲染质量以改善性能。这可以通过使用以下代码片段来完成:

this.stage.frameRate = 10;
this.stage.quality = StageQuality.LOW;

另请参阅...

我们还可以推荐使用像Hi-ReS-Stats这样的包,可以从github.com/mrdoob/Hi-ReS-Stats下载,并在移动 Android 应用程序上使用来监控资源使用情况。使用这个类将在我们的应用程序中生成一个图形覆盖层,以监控应用程序性能。

第十一章:最后考虑:应用程序编译和分发

本章节将涵盖以下内容:

  • 使用 Flash Professional 生成代码签名证书

  • 使用 Flash Builder 生成代码签名证书

  • 使用 FDT 生成代码签名证书

  • 使用 AIR 开发工具生成代码签名证书

  • 准备图标文件以供分发

  • 使用 Flash Professional 编译应用程序

  • 使用 Flash Builder 编译应用程序

  • 使用 FDT 编译应用程序

  • 使用 AIR 开发工具编译应用程序

  • 将应用程序提交到 Android Market

简介

当将移动 Flash 应用程序(.swf)部署到 Web 上时,这个过程与桌面版非常相似;将你的 .swf 嵌入 HTML 容器中,就完成了。然而,将 AIR 应用程序部署到 Android Market 上则完全不同。在本章中,我们将了解如何准备一个应用程序以分发给 Android Market,生成适当的代码签名证书,以及编译和提交过程的相关细节。

使用 Flash Professional 生成代码签名证书

在 Android Market 上分发的应用程序必须使用 25 年有效期的代码签名证书进行数字签名。我们有多种不同的方法来生成 Android 应用程序的代码签名证书。在本食谱中,我们将演示如何使用 Flash Professional 生成此类证书。

如何操作...

在 Flash Professional 中,执行以下操作以创建自签名数字证书:

  1. 打开一个针对 AIR for Android 的项目,打开 属性 面板,并点击 播放器选择 框旁边的扳手图标。这将打开 AIR for Android 设置 对话框:如何操作...

  2. AIR for Android 设置 对话框中,点击 创建 按钮以打开 创建自签名数字证书 对话窗口:如何操作...

  3. 创建自签名数字证书 对话框出现在我们面前时,我们将输入所需的信息并为证书选择名称和位置。正确输入所有信息后,我们将点击 确定 以便 Flash Professional 生成证书。确保在 有效期 输入框中输入 25 年,以适用于 Android:如何操作...

工作原理...

通过生成有效的数字代码签名证书,我们可以正确地为提交到 Android Market 的 Android 应用程序签名。Flash Professional 提供了一个简单的方法来生成适当的证书类型并将其应用于我们的分发应用程序。

使用 Flash Builder 生成代码签名证书

在 Android Market 上分发的应用程序必须使用 25 年有效期的代码签名证书进行数字签名。我们有多种不同的方法可以为 Android 应用程序生成代码签名证书。在本食谱中,我们将演示如何使用 Flash Builder 生成此类证书。

如何操作...

在 Flash Builder 中,执行以下操作以创建自签名数字证书:

  1. 包资源管理器中选择移动项目,进入文件菜单并选择属性。将为此项目显示属性对话框。

  2. 属性对话框中,根据所选项目的类型,向下滚动至Flex 构建打包ActionScript 构建打包项,并选择Google Android。选择数字签名标签后,点击创建按钮以打开创建自签名数字证书对话框:如何操作...

  3. 现在需要做的就是输入所需信息并为证书选择名称和位置。正确输入所有信息后,我们将点击确定,让 Flash Builder 生成证书:如何操作...

工作原理...

通过生成有效的数字代码签名证书,我们可以正确地为提交到 Android Market 的 Android 应用程序签名。Flash Professional 提供了一种简单的方法来生成适当的证书类型,并将其应用于我们分发的应用程序。

使用 FDT 生成代码签名证书

在 Android Market 上分发的应用程序必须使用 25 年有效期的代码签名证书进行数字签名。我们有多种不同的方法可以为 Android 应用程序生成代码签名证书。在本食谱中,我们将演示如何使用 PowerFlasher FDT 生成此类证书。

如何操作...

在 FDT 中,执行以下操作以创建自签名数字证书:

  1. 点击顶部菜单中运行图标旁边的小箭头,并从出现的子菜单中选择运行配置。这将打开运行配置对话框:如何操作...

  2. 打开运行配置对话框窗口,双击FDT AIR 应用程序发布菜单项以创建新配置。选择证书标签,并输入所需信息,为证书选择名称和位置。正确输入所有信息后,我们将点击创建证书,让 FDT 为我们生成证书:如何操作...

工作原理...

通过生成有效的数字代码签名证书,我们可以正确地为提交到 Android Market 的 Android 应用程序签名。FDT 提供了一种简单的方法来生成适当的证书类型,并将其应用于我们分发的应用程序。

使用 AIR 开发者工具生成代码签名证书

Android Market 上发布的应用程序必须使用 25 年代码签名证书进行数字签名。我们有多种方法可以生成 Android 应用程序的代码签名证书。在本食谱中,我们将演示如何使用 ADT 命令行工具生成此类证书。

准备中…

要了解如何在特定环境中配置 ADT,请查看第一章,准备使用 Android:开发环境和项目设置。

如何操作...

使用 ADT 命令行工具,执行以下操作以创建自签名的数字证书:

  1. 对于此示例,我们将假设以下情况:

    Publisher Name: "Joseph Labrecque"
    Validity Period: 25 (years)
    Key Type: 1024-RSA
    PFX File: C:\Users\Joseph\Documents\airandroid.p12
    Password: airAndroidPass
    
    
  2. 打开命令提示符或终端(取决于操作系统),并输入生成我们证书的命令字符串:

    adt -certificate -cn "Joseph Labrecque" -validityPeriod 25 1024-
    RSA C:\Users\Joseph\Documents\airandroid.p12 airAndroidPass
    
    
  3. ADT 实用程序现在将处理命令并完成证书生成过程。如果我们的命令有问题,ADT 将在这里打印错误信息,让我们知道出现了错误:如何操作...

  4. 现在,我们可以浏览到命令字符串中指定的位置来找到我们新创建的证书,并使用它来签署我们的 AIR for Android 应用程序:如何操作...

工作原理...

通过生成有效的数字代码签名证书,我们可以正确签署我们的 Android 应用程序以便提交到 Android Market。使用与 AIR SDK 捆绑的 ADT 工具,我们可以生成适合分发的适当证书类型。

准备分发图标文件

当我们为在 Android Market 上发布的应用程序编译应用程序时,我们必须包括一组标准图标图像以及我们的应用程序。这些图标的位置在我们的 AIR 应用程序描述符文件中定义。Android 期望一组三个图标:36x36、48x48 和 72x72。每个图标用于不同的屏幕密度,并且都应该包括为标准的 PNG 文件。

如何操作...

根据使用的工具不同,这项任务可以有不同的处理方式。我们将演示如何使用 Flash Professional CS5.5 在应用程序中包含这些图标,以及如何通过直接修改 AIR 应用程序描述符文件来实现。

使用 Flash Professional CS5.5

  1. 打开一个针对AIR for Android的项目,打开属性面板,点击播放器选择框旁边的扳手图标。这将打开AIR for Android 设置对话框:使用 Flash Professional CS5.5

  2. AIR for Android 设置 对话框中,点击 图标 选项卡。要为我们的项目指定特定图标,我们只需在列表中选择每个图标条目,并通过使用文件夹和放大镜图标浏览来定位每个图标要使用的文件:

直接修改 AIR 描述符文件

  1. 在您的项目中找到 AIR 描述符文件。它通常命名为类似 {MyProject}-app.xml 的名称,并位于项目根目录下。

  2. 在本文档中浏览名为<icon>的节点。此节点包含许多与我们的应用程序窗口视觉方面相关的默认设置。如果它被注释掉了,我们必须在继续之前取消注释。

  3. 我们现在必须确保在<icon>节点内存在以下三个子节点。确保我们的图标文件路径正确。如果它们不正确,在我们尝试编译此应用程序时编译器会告知我们:

    <image36x36>assets/icon_36.png</image36x36>
    <image48x48>assets/icon_48.png</image48x48>
    <image72x72>assets/icon_72.png</image72x72>
    
    

例如,以下是适用于 Android 应用程序的一组三个图标及其像素测量值:

直接修改 AIR 描述符文件

工作原理...

在 Android 应用程序包中包含一组图标对于通过 Android Market 分发应用程序至关重要。它还能在应用程序安装到设备上后为用户提供一个容易识别的视觉提示。花点时间设计一组真正能反映应用程序代表的图标。

还有更多...

如果应用程序要发布到 Android Market,我们还需要制作多种其他图像来正确地为我们的应用程序品牌。查看 Android Market 以了解当前需要哪些图像的详细信息,请访问market.android.com/

使用 Flash Professional 编译应用程序

将项目编译为 Android 发布版本 .apk 文件是在将应用程序分发到 Android Market 或其他渠道之前的最后一步。根据使用的工具不同,有许多方法可以做到这一点。在本食谱中,我们将使用 Flash Professional 中的工具来编译和打包我们的应用程序。

如何操作...

要从 Flash Professional 编译 .apk,我们将采取以下步骤:

  1. 打开针对 AIR for Android 的项目,打开 属性 面板并点击 发布设置 按钮。这将打开 发布设置 对话框:

  2. 我们可以在这里检查我们的设置,如果我们确定一切配置正确,甚至可以直接点击发布。要验证所有设置是否都已就绪以发布到 Android,请点击我们的播放器选择框的小扳手图标,它应该设置为适用于 Android 的 AIR。这将提供对适用于 Android 的 AIR 设置对话框的访问:如何操作...

  3. 打开适用于 Android 的 AIR 设置对话框后,我们可以验证我们的特定配置选项,然后再决定发布。常规标签包含许多重要输入,包括生成的.apk文件的路径、应用程序名称、版本、ID 和其他必需的配置设置。我们还可以选择包含除了编译的.swf和 AIR 描述文件之外的其他文件,例如外部图像资源。图标标签允许我们使用基本的 GUI 包含图标文件,而权限标签将允许我们设置特定于 Android 的应用程序权限。

    注意

    这些设置都会修改应用程序描述文件,进而生成 Android 清单文档。我们可以将这些设置视为这些文件的图形用户界面。

  4. 作为最后一步,点击部署标签:如何操作...

  5. 部署标签中存在一个部署类型设置,以及使用自签名证书为应用程序签名的机会。这非常重要,因为 Android 市场不接受未签名的应用程序或不符合 Android 市场条款设置要求的应用程序。

  6. 一定要提供应用名称,用于用户在设备上安装后识别应用程序,以及一个唯一的应用 ID。App ID非常重要,因为这是 Android Market 中应用程序的主要标识符。为了使应用程序更新正常工作,它必须是唯一的,建议开发人员特别小心地使用反向域名表示法以保持这种唯一性。

  7. 我们需要确保从选择获取 AIR 运行时的选项指示我们正在定位的具体分发市场。对于一般的 Android 市场,我们选择谷歌 Android 市场。此对话框还通过 Android 部署类型设置为我们提供了编译用于不同目的的应用程序版本的选择:

    • 设备发布:当我们想通过 Android 市场分发我们的应用程序时,需要选择的选项

    • 模拟器发布:生成与 Android SDK 模拟器和 AIR 运行时的模拟器版本兼容的发布版本

    • 调试:此选项生成专门用于调试应用程序的发布版本

  8. 一旦我们对所有配置设置感到满意,我们可以退出到发布设置对话框并点击发布,或者直接在此处点击发布按钮。只要我们之前已经完成这些配置步骤,我们也可以使用 Flash Professional 中提供的传统发布方法。如何操作...

现在我们有一个完全编译、有效签名的.apk文件,准备进行分发。

工作原理...

我们通过 Flash Professional GUI 对话框更改的配置设置实际上是在幕后修改 AIR 描述符文件。一旦我们选择发布应用程序,Flash Professional 将使用此文件来编译并将所有内容打包成一个有效的.apk文件,以便在 Android Market 上分发。

使用 Flash Builder 编译应用程序

将项目编译为 Android 发布版本.apk文件是分发应用程序到 Android Market 或其他渠道之前的最后一步。根据使用的工具不同,有许多方法可以做到这一点。在本教程中,我们将使用 Flash Builder 中的工具来编译和打包我们的应用程序。

如何操作...

要从 Flash Builder 编译.apk,请执行以下步骤:

  1. 在移动 ActionScript 或 Flex 项目中,导航到 Flash Builder 菜单,选择项目菜单项。这将显示一个包含多个选项的子菜单。从该菜单中,选择导出发布构建,打开导出发布构建对话框窗口:如何操作...

  2. 在此窗口中,我们可以选择要执行发布构建的项目以及该项目中的具体应用程序,决定要定位的平台,指定构建的路径和文件名,以及选择要导出哪种类型的应用程序。对于 Android,我们将在每个目标平台上选择签名包。只要我们选择了Google Android作为目标平台,点击下一步后,这将打开打包设置对话框:如何操作...

  3. 现在,我们可以为构建配置一些高级属性。点击包内容标签,以验证构建中是否包含所有必需的文件。如果我们想打包其他文件,甚至排除某些资源,我们可以通过使用每个项目旁边的复选框来完成。点击数字签名标签以继续:如何操作...

  4. 最后的任务是选择一个签名证书,以便在 Android Market 上发布我们的应用程序时进行数字签名。选择一个证书并输入关联的密码。点击完成将执行构建,并将编译的.apk保存到我们之前选择的位置。如果我们愿意,可以通过包内容标签包含外部文件,并通过部署标签选择部署到任何连接的设备:如何操作...

现在我们已经拥有一个完全编译、有效签名的.apk文件,准备进行分发。

工作原理...

Flash Builder 在导出项目的发布版本时提供了目标平台的概念。如果我们选择 Google Android 作为目标平台,我们会得到一些特定于 Android 的附加选项,我们可以根据特定项目的需求进行修改。额外的对话框元素允许我们将所有内容编译并打包成一个有效的.apk,准备在 Android Market 上发布。

使用 FDT 时编译应用程序

将项目编译为 Android 发布版本的.apk文件是在 Android Market 或其他渠道发布应用程序之前的最后一步。根据使用的工具不同,有许多方法可以做到这一点。在这个食谱中,我们将讨论在使用 Powerflasher FDT 编译和打包应用程序时可用的三种流行方法。

如何操作...

在撰写本文时,FDT 不支持直接与 Android 的 AIR 一起工作。然而,FDT 用户可以通过三种主要方法编译他们的项目以进行 Android 分发。

使用移动项目模板

FDT 社区制作了许多支持 Android 的 AIR 移动项目模板。这些模板适用于所有 FDT 项目使用的新模板系统,并为工作流程添加不同级别的功能。其中大多数还包括 ANT 脚本,使用 AIR 开发者工具编译.apk

使用 ANT

迄今为止,这是编译 Android 项目最灵活的方法,因为它实际上与 IDE 无关,任何人都可以使用。ANT 随 FDT 的标准安装一起打包,网上社区有许多启动脚本可以部署 Android 的 AIR。若要开始使用 FDT 中的 ANT,请查看fdt.powerflasher.com/docs/FDT_Ant_Tasks

通过 CLI 使用 ADT

最基本的方法是直接使用 FDT 开发一个移动项目,然后通过命令行界面使用 AIR 开发者工具将其打包成.apk。下一个食谱将详细介绍如何实现这一过程。

工作原理...

无论选择哪种方法,目标都是相同的——编译和打包所有内容到一个有效的 .apk,准备在 Android Market 上分发。FDT 的一个优点是它不限制开发者只能用一种特定的方式做事。在为 Android 生成发布版本时,我们有多种选择。

使用 AIR 开发者工具编译应用程序

将项目编译为 Android 发布版本 .apk 文件是分发应用程序到 Android Market 或其他渠道之前的最后一步。根据使用的工具不同,有许多方法可以做到这一点。在本教程中,我们将使用 AIR 开发者工具 (ADT) 命令行实用程序来编译和打包我们的应用程序。

如何操作...

要使用 ADT 命令行工具从移动 AIR 项目编译 .apk,我们将执行以下步骤:

  1. 在此示例中,我们将假定以下内容:

    • 证书: android.p12

    • 期望的 APK: mobileAIR.apk

    • AIR 描述符: mobileAIR\src\mobileAIR-app.xml

    • SWF 文件: mobileAIR\src\mobileAIR.swf

  2. 打开命令提示符或终端(取决于操作系统),输入命令字符串以生成我们的证书。在这种情况下,我们将目标类型设置为 .apk 以进行发布构建。我们也可以将其设置为 apk-debug 以进行调试构建,或者设置为 apk-emulator 以在模拟器上安装:

    -package -target apk -storetype pkcs12 -keystore android.p12
    mobileAIR.apkmobileAIR\src\mobileAIR-app.xml mobileAIR\src\
    mobileAIR.swf
    
    
  3. 其他文件,如资源或图标,可以在 .swf 条目之后包含,用空白分隔:

    -package -target apk -storetype pkcs12 -keystore android.p12
    mobileAIR.apkmobileAIR\src\mobileAIR-app.xml mobileAIR\src\
    mobileAIR.swf mobileAIR\src\assets\icon_32.pngmobileAIR\src\
    assets\icon_36.pngmobileAIR\src\assets\icon_72.png
    
    
  4. 现在 ADT 实用程序将处理该命令并完成 .apk 编译过程。如果我们的命令有问题,ADT 将在这里打印错误信息,让我们知道出了问题。通常,如果出现问题,可能是 AIR 描述符文件有问题,或者预期输入文件的路径不正确。如何操作...

  5. 现在,我们可以浏览到命令字符串中指定的结果位置,找到我们新创建的 .apk 文件,该文件可以直接安装在 Android 设备上,也可以通过 Android Market 进行分发:如何操作...

现在我们已经拥有一个完全编译、有效签名的 .apk 文件,准备进行分发。

工作原理...

假设我们已经正确配置了我们的应用程序,ADT 将为我们编译、签名并打包所有的项目文件到一个 .apk 中。ADT 有许多不同的实用程序和配置选项,可以对项目执行许多操作。请查看 help.adobe.com/en_US/air/build/ 并在菜单中选择 AIR 开发者工具 (ADT) 以获取完整文档。

另请参阅…

要了解如何在特定环境中配置 ADT,请查看第一章,准备使用 Android:开发环境和项目设置

将应用程序提交到 Android 市场

Google 使注册成为 Android 开发者和在 Android 市场上发布应用程序变得非常容易。这个指南将详细说明在编译完成.apk之后进行这些操作的必要步骤。

准备就绪...

在开发者能够向 Android 市场提交任何内容之前,必须创建一个开发者账户。这个过程可以在几分钟内完成,既简单又实惠。

要注册成为 Android 开发者:

  1. 使用网络浏览器,前往 market.android.com/publish/signup

  2. 使用您的 Google 账户登录(或创建新账户)。

  3. 填写注册表单并支付一次性的 25 美元设置费用。

  4. 恭喜您成为 Android 开发者!

如何操作...

  1. 1 将编译并签名的.apk文件上传到 Android 市场,以供全球分发。

  2. 使用您的 Android 开发者凭据在market.android.com/publish/登录 Android 市场。

  3. 点击右下角标有上传应用程序:的按钮如何操作...

  4. 现在展现给我们的是一个相当长的表单,它允许我们包含有关我们应用程序的各种信息。我们可以对应用程序进行分类,添加描述性和促销文本,更新发行说明,并选择是否向用户收费或允许免费下载。如果我们决定要求付费,我们必须首先通过此页面上提供的链接建立 Google 商家账户。

  5. 除了文本条目和其他输入选择外,我们还有机会上传各种图片,这些图片将代表我们的应用程序在 Android 市场中的形象。具体的图片属性在此表单中有详细说明:如何操作...

  6. 在此页底部有三个按钮。我们可以点击保存以保存我们的应用程序资料以供以后编辑。点击删除按钮,将允许我们从 Android 市场完全移除一个应用程序。要发布我们的应用程序,我们将点击发布按钮。

注意

一旦发布应用程序,此按钮将显示为取消发布,如果用户已安装应用程序,则删除按钮将不再作为选项出现。

如何操作...

应用程序现在已发布到 Android 市场,可供全球数百万用户使用。

工作原理...

将应用程序上传并发布到 Android 市场,将允许用户下载并安装应用程序。我们对应用程序描述、版本信息以及相关图像资产拥有完全控制权。我们还能够从开发者区域跟踪评分和评论,并在必要时管理商家账户。发布到 Android 市场是即时的。没有像其他应用程序市场那样的审批和拒绝过程。

还有更多...

将应用程序更新到新版本比设置一个全新的应用程序简单得多:

  1. 一旦进入安卓市场,点击现有应用程序的名称。这将允许你编辑与其相关的任何图片或文本。

  2. 要实际发布应用程序的新版本,我们必须点击[上传升级]的链接。这将导致出现一组新的表单控件。还有更多...

  3. 点击选择文件并浏览新的.apk文件。现在点击上传将文件提交到谷歌服务器。

  4. 新文件将被解析以获取版本信息并验证内容是否有效。对版本号、应用图标、请求的权限等所做的任何更改都将在草稿中反映出来。

  5. 应用描述文件中定义的版本号必须高于之前提交的版本,以便进行有效的升级。如果需要,我们还可以在此页面对一般应用信息进行额外编辑。点击页面底部的发布,新版本将立即在安卓市场可用。

posted @ 2024-05-23 11:07  绝不原创的飞龙  阅读(6)  评论(0编辑  收藏  举报