安卓-4-入门指南-全-

安卓 4 入门指南(全)

原文:Beginning Android 4

协议:CC BY-NC-SA 4.0

零、前言

欢迎来到这本书!

感谢您对开发 Android 应用的兴趣!越来越多的人将使用所谓的“非传统”手段,如移动设备,来访问基于互联网的服务。我们现在在这一领域做得越多,人们就越会帮助投资这一领域,以便在未来构建更强大的移动应用。Android 是新的——Android 驱动的设备在 2008 年末首次出现——但它已经有了巨大的增长,在短短的三年内成为头号手机操作系统。

最重要的是,感谢你对这本书的兴趣!我真诚地希望你会觉得它有用,至少偶尔会觉得有趣。

先决条件

如果你对 Android 编程感兴趣,你至少需要对如何用 Java 编程有一个基本的了解。Android 编程是使用 Java 语法,加上一个类似于 Java SE 库子集的类库(加上特定于 Android 的扩展)完成的。如果你以前没有用 Java 编程,你可能应该在尝试为 Android 编程之前学习它是如何工作的。博文[commonsware.com/blog/2010/08/02/java-good-parts-version.html](http://commonsware.com/blog/2010/08/02/java-good-parts-version.html)列举了一个 Android 开发者需要知道的各种 Java 编程主题。这个主题在另一本出版社的书中也有涉及,作者是杰夫·弗里森(2010 年出版社),书名是为 Android 开发学习 Java】。

这本书的版本

这本书是由 Apress 和 CommonsWare 合作制作的。您正在阅读的是 Apress edition,它有印刷版和数字版,可从各种数字图书服务(如 Safari)获得。

CommonsWare 不断更新原始资料,并以标题Android 开发繁忙的程序员指南向其 waresignment 程序的成员提供。

CommonsWare 在[commonsware.com/apress](http://commonsware.com/apress)维护了一个关于此合作关系的常见问题解答。

源代码及其许可证

这本书的源代码可以在 www.apress.com 买到。所有的 Android 项目都是在 Apache 2.0 许可下授权的,如果你想重用其中任何一个的话。

一、概览

安卓无处不在。手机。平板电脑。由谷歌电视驱动的电视和机顶盒。很快,Android 就会出现在汽车里,飞机上的机上娱乐系统里,甚至机器人里!

然而,Android 设备的一般主题将是更小的屏幕和/或没有硬件键盘。而且,从数字上看,在可预见的未来,Android 很可能主要与智能手机联系在一起。对于开发人员来说,这既有好处也有缺点,如下所述。本章还描述了 Android 应用中的主要组件以及在开发应用时可以利用的 Android 特性。

智能手机编程的优点和缺点

从好的方面来看,安卓风格的智能手机很性感。通过移动设备提供互联网服务可以追溯到 20 世纪 90 年代中期,以及手持设备标记语言(HDML)。然而,只有在最近几年,能够上网的手机才被取消。现在,由于短信等趋势和苹果 iPhone 等产品,可以作为互联网接入设备的手机正迅速受到欢迎。因此,开发 Android 应用让你在快速发展的细分市场(支持互联网的手机)中体验到一种有趣的技术(Android),这总是一件好事。

当你实际上不得不编程时,问题就来了。

任何有 PDA 或手机编程经验的人都曾感受过手机在各种尺寸上都太小的痛苦:

  • 屏幕很小(你不会得到这样的评论,“这是你口袋里的 24 英寸液晶显示器,还是。。。?").
  • 键盘,如果有的话,也很小。
  • 指点设备,如果存在的话,很烦人(任何丢失过手写笔的人都会告诉你),或者不精确(大手指和“多点触控”液晶显示器有时会很烦人。。。有问题)。
  • CPU 速度和内存总是落后于台式机和服务器。

此外,在手机上运行的应用必须处理这样一个事实:它们是在手机上运行的。

当手机不工作时,有手机的人往往会变得非常烦躁。同样,如果你的程序“破坏”了他们的手机,这些人也会变得恼怒

  • 占用中央处理器,这样电话就收不到了。
  • 当有电话打进来或需要拨电话时,它不会悄悄地消失在背景中,因为这个程序与手机操作系统的其他部分不能正常工作。
  • 手机操作系统崩溃,比如像筛子一样漏内存。

因此,为手机开发程序与开发桌面应用、网站或后端服务器进程是不同的体验。工具看起来不同了,框架的行为也不同了,而且你对你的程序有更多的限制。

Android 试图做的是向你妥协:

  • 你得到了一种常用的编程语言(Java)和一些常用的库(例如,一些 Apache Commons APIs),以及对你可能习惯使用的工具的支持(Eclipse)。
  • 你得到了一个相当严格和不常见的框架,你的程序需要在这个框架中运行,这样它们才能成为电话中的“好公民”,而不会干扰其他程序或电话本身的操作。

如你所料,这本书的大部分内容都是关于这个框架,以及你如何在它的范围内编写程序并利用它的能力。

机器人是由什么组成的

当你写一个桌面应用时,你是“你自己领域的主人”你启动你的主窗口和任何需要的子窗口,比如对话框。从你的角度来看,你是你自己的世界,利用操作系统支持的功能,但很大程度上不知道计算机上可能同时运行的任何其他程序。如果您确实与其他程序交互,通常是通过应用编程接口(API),如 Java 数据库连接(JDBC)或其上的框架,来与 MySQL 或其他数据库通信。

Android 也有类似的概念,但它们的包装和结构不同,以使手机更耐撞:

  • Activities :用户界面的构建模块是活动。你可以把一个活动想象成桌面应用中的窗口或对话框,或者经典 web 应用中的页面。Android 旨在支持许多廉价的活动,所以你可以允许用户不断点击以打开新的活动,并点击后退按钮以后退,就像他们在网络浏览器中一样。
  • 服务:活动是短暂的,可以随时关闭。另一方面,服务被设计为在需要时保持运行,独立于任何活动,类似于其他操作系统上的服务或守护进程的概念。您可以使用服务来检查 RSS 提要的更新或播放音乐,即使控制活动不再运行。您还将使用服务来执行计划任务(“cron 作业”)和向设备上的其他应用公开定制 API,尽管这些都是相对高级的功能。
  • 内容提供者:内容提供者为存储在设备上的任何数据提供了一个可由多个应用访问的抽象层。Android 开发模型鼓励您将自己的数据提供给其他应用,以及您自己的应用。构建一个内容供应器可以让您做到这一点,同时保持对数据访问方式的完全控制。内容提供者可以是任何东西,从 web 提要到本地 SQLite 数据库等等。
  • 意图:意图是在设备内部运行的系统消息,通知应用各种事件,从硬件状态变化(例如,插入 SD 卡),到传入数据(例如,短消息服务[SMS]消息到达),到应用事件(例如,您的活动从设备的主菜单启动)。意图很像其他操作系统上的消息或事件。你不仅可以响应一个Intent,还可以创建自己的来启动其他活动,或者在特定情况出现时让你知道(例如,当用户到达某某位置 100 米以内时,引发某某Intent)。

由你支配的东西

  • 存储:你可以把数据文件和你的应用打包成不变的东西,比如图标或者帮助文件。您还可以在设备本身上划出一小块空间,用于存放包含应用所需的用户输入或检索数据的数据库或文件。而且,如果用户提供大容量存储,比如 SD 卡,你可以根据需要在上面读写文件。
  • 网络:安卓设备一般都可以通过一种或另一种通信媒介上网。从原始的 Java 套接字一直到可以嵌入到应用中的内置的基于 WebKit 的 web 浏览器小部件,您可以在任何级别上利用 Internet 访问。
  • 多媒体 : Android 设备有回放和录制音频和视频的能力。虽然具体细节可能因设备而异,但您可以查询设备以了解其功能,然后利用您认为合适的多媒体功能,无论是播放音乐、使用相机拍照还是使用麦克风进行音频笔记。
  • 位置服务 : Android 设备经常可以访问位置供应器,如 GPS 和蜂窝三角定位,这些服务可以告诉您的应用设备在地球表面的位置。反过来,您可以显示地图或利用位置数据,例如在设备被盗时跟踪设备的移动。
  • 电话服务:因为 Android 设备通常是电话,你的软件可以发起呼叫,发送和接收短信,以及做任何你期望从现代电话技术中得到的事情。

大局...这本书的

现在你已经有了 Android 的大图,这是本书接下来的内容:

  • 接下来的两章旨在通过一系列循序渐进的教程式指导,让您快速熟悉 Android 环境,包括设置您需要的工具、创建您的第一个项目,以及让第一个项目在 Android 模拟器上运行。
  • 接下来的三章对第二章和第三章中发生的事情做了更多的解释。我们检查了我们创建的 Android 项目,更多地讨论了 Eclipse,并讨论了我们可以添加到项目中的一些东西,以帮助它在更多的设备上运行并增强其功能。
  • 这本书的大部分探讨了 Android APIs 的各种功能——如何创建活动等组件,如何访问互联网和本地数据库,如何获取您的位置并在地图上显示,等等。

二、如何开始

事不宜迟,让我们为您提供构建 Android 应用所需的各种组件。

注意:在撰写本文时,此处提供的说明是准确的。然而,工具变化很快,所以当您读到本文时,这些说明可能已经过时了。请参考 Android 开发者网站以获得最新的指导,并以此作为预期的基本指南。

第一步:设置 Java

当您编写 Android 应用时,您通常会用 Java 源代码编写它们。然后,Java 源代码被转换成 Android 实际运行的东西(Android 包[APK]文件中的 Dalvikbytecode)。

因此,您需要做的第一件事是建立一个 Java 开发环境,以便为开始编写 Java 类做好准备。

安装 JDK

您需要获得并安装官方的 Oracle Java SE 开发工具包(JDK)。您可以从适用于 Windows 和 Linux 的 Oracle Java 网站以及适用于 Mac OS X 的 Apple 网站上获得该文件。普通的 JDK(没有任何“捆绑包”)应该足够了。按照 Oracle 或 Apple 提供的说明将其安装到您的机器上。在撰写本文时,Android 支持 Java 5 和 Java 6,而在您阅读本文时,可能会支持 Java 7。

可选的 JAVA 编译器

原则上,你应该使用官方的 JDK 神谕。实际上,OpenJDK 似乎也能工作,至少在 Ubuntu 上是这样。然而,离正式的 Oracle 实现越远,它就越不可能工作。例如,GNU Java 编译器(GCJ)可能无法在 Android 上运行。

学习 Java

像大多数关于 Android 的书籍和文档一样,这本书假设您有基本的 Java 编程经验。如果你缺乏这些,你真的应该考虑在钻研 Android 之前花点时间在 Java 基础上。否则,你可能会觉得这种经历令人沮丧。

如果你需要参加 Java 速成班来参与 Android 开发,以下是你需要学习的概念,排名不分先后:

  • 语言基础(流量控制等。)
  • 类别和对象
  • 方法和数据成员
  • 公共、私有和受保护
  • 静态和实例范围
  • 例外
  • 线程和并发控制
  • 收集
  • 无商标消费品
  • 文件输入输出
  • 反射
  • 接口

获取这些知识最简单的方法之一就是阅读 Jeff Friesen 的Learn Java for Android Development(a press,2010)。

第二步:安装 Android SDK

Android SDK 为您提供了创建和测试 Android 应用所需的所有工具。它由两部分组成:基础工具和特定于版本的 SDK 以及相关的附加组件。

安装基础工具

你可以在 Android 开发者网站上找到 Android 开发者工具,网址是[developer.android.com](http://developer.android.com)。下载适用于您的平台的 ZIP 文件,并将其解压缩到您机器上的一个逻辑位置—不需要特定的路径。Windows 用户还可以选择运行自动安装的 EXE 文件。

安装 SDK 和附加软件

在上一步安装的 Android SDK 的tools/目录中,您将看到一个android批处理文件或 shell 脚本。如果你运行它,你会看到 Android SDK 和 AVD 管理器,如图图 2–1 所示。

images

图 2–1。 Android SDK 和 AVD 管理器

此时,您已经有了一些构建工具,但是缺少编译 Android 应用所必需的 Java 文件。您还缺少一些额外的构建工具,以及运行 Android 模拟器所需的文件。要解决这个问题,单击左侧的可用包选项,打开图 2–2 所示的屏幕。

images

图 2–2。 安卓 SDK 和 AVD 管理器可用包

打开树的 Android 存储库分支。短暂停顿后,您将看到类似于图 2–3 的屏幕。

images

图 2–3。 Android SDK 和 AVD 管理器可用 Android 软件包

选中以下项目的复选框:

  • 针对您想要测试的所有 Android SDK 版本的“SDK 平台”
  • 最新 Android SDK 版本的“Android SDK 文档”
  • 最新 Android SDK 版本的“SDK 示例”,如果您愿意,也可以是旧版本

然后,打开树的第三方附加组件分支。短暂停顿后,您将看到类似于图 2–4 的屏幕。

images

图 2–4。??【安卓 SDK 和 AVD 管理器】可用第三方插件

点击“Google Inc. add-ons”分支将其打开,如图 2–5 所示。

images

图 2–5。 安卓 SDK 和 AVD 管理器可用谷歌插件

最有可能的情况是,您需要选中与您在 Android Repository 分支中选择的 SDK 版本相匹配的“Google APIs by Google Inc .”项目的复选框。Google APIs 包括对众所周知的 Google 产品的支持,比如 Google Maps,既来自您的代码,也来自 Android 模拟器。

检查完所有要下载的项目后,单击安装选定项目按钮,这将弹出一个许可确认对话框,如图 2–6 所示。

images

图 2–6。 Android SDK 和 AVD Manger 许可协议屏幕

如果您同意条款,请检查并接受许可,然后单击安装按钮。在这一点上,这是一个很好的时间去吃午饭或晚饭。除非你有一个坚实的互联网连接,否则下载所有这些数据并解压将需要相当多的时间。

下载完成后,如果你愿意,你可以关闭 SDK 和 AVD 管理器,尽管你将在本章的第 5 步中使用它来设置模拟器。

步骤 3:为 Eclipse 安装 ADT

如果您不打算在 Android 开发中使用 Eclipse,可以跳到下一节。如果您将使用 Eclipse,但还没有安装它,那么您需要先安装它。Eclipse 可以从 Eclipse 网站[www.eclipse.org/](http://www.eclipse.org)下载。Eclipse IDE for Java Developers 包可以很好地工作。

接下来,您需要安装 Android 开发者工具(ADT)插件。为此,打开 Eclipse 并选择帮助images images安装新软件。然后,在 Install 对话框中,单击 Add 按钮添加新的插件源。给它起个名字(比如 Android)并提供下面的 URL: [dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/)。这应该会触发 Eclipse 从该站点下载可用的插件列表(参见图 2–7)。

images

图 2–7。 Eclipse ADT 插件安装

选中开发工具复选框,然后单击下一步按钮。按照向导的其余步骤查看要下载的工具,并查看和接受它们各自的许可协议。当 Finish 按钮被激活时,单击它,Eclipse 将下载并安装插件。完成后,Eclipse 会要求重启;让它这样做吧。

然后,您需要向 ADT 展示在哪里可以找到前面小节中的 Android SDK 安装。为此,从 Eclipse 主菜单中选择窗口images images首选项(或 Mac OS X 的等效首选项选项)。在首选项对话框的列表窗格中点击 Android 条目,如图 Figure 2–8 所示。

images

图 2–8。Eclipse ADT 配置

然后,单击 Browse 按钮找到安装 SDK 的目录。选择它之后,在首选项对话框中单击应用,您应该会看到您之前安装的 Android SDK 版本。然后,单击 OK,ADT 就可以使用了。

步骤 4:安装 Apache Ant

如果您将从 Eclipse 进行所有的开发,那么您可以跳到下一节。如果您希望使用命令行构建工具进行开发,您需要安装 Apache Ant。您可能已经在以前的 Java 开发工作中安装了这个,因为它在 Java 项目中相当常见。但是,您需要 Ant 版本 1.8.1 或更高版本,因此请检查您的当前副本(例如,ant -version)。

如果您没有 Ant 或者没有正确的版本,您可以从 Apache Ant 网站[ant.apache.org/](http://ant.apache.org)获得它。Ant 手册中提供了完整的安装说明,但基本步骤如下:

  1. 将 ZIP 存档文件解压到机器上的逻辑位置。
  2. 添加一个JAVA_HOME环境变量,指向你的 JDK 的安装位置,如果你还没有的话。
  3. 添加一个ANT_HOME环境变量,指向您在步骤 1 中解包 Ant 的目录。
  4. $JAVA_HOME/bin$ANT_HOME/bin添加到您的PATH中。
  5. 运行ant -version确认 Ant 安装正确。

第五步:设置仿真器

Android 工具包括一个模拟器,一个伪装成 Android 设备的软件。这对于开发非常有用,因为它不仅使您能够在没有设备的情况下开始 Android 开发,还使您能够为您不拥有的设备测试设备配置。

Android 模拟器可以模拟一个或几个 Android 设备。你想要的每个配置都存储在一个 Android 虚拟设备(AVD)中。您在本章前面用来下载 SDK 组件的 Android SDK 和 AVD 管理器是您创建这些 AVD 的地方。

如果您没有运行 SDK 和 AVD 管理器,您可以通过 SDK 的tools/目录中的android命令运行它,或者通过 Eclipse 中的窗口images运行 SDK 和 AVD 管理器。它会打开一个屏幕,列出您可以使用的 AVDs 最初,列表将为空,如图图 2–9 所示。

images

图 2–9。 Android SDK 和 AVD 管理器 Android 虚拟设备列表

单击“新建”按钮创建新的 AVD 文件。这将打开如图图 2–10 所示的对话框,您可以在其中配置 AVD 的外观和工作方式。

images

图 2–10。增加一个新的 AVD

您需要提供以下信息:

  • AVD 的一个名字:因为这个名字存在于你的开发机器上的文件中,所以你受到你的操作系统的文件名约定的限制(例如,在 Windows 上没有反斜杠)。
  • 您希望仿真器运行的 Android 版本(目标):通过目标下拉列表选择一个您安装的 SDK。请注意,除了“纯”Android 环境,您还可以选择基于您选择的第三方附加组件的选项。例如,您可能有一些选项来设置包含 Google APIs 的 AVD,并且您将需要这样的 AVD 来测试使用 Google Maps 的应用。
  • 仿真器应该仿真的 SD 卡的详细信息:由于 Android 设备总是有某种形式的外部存储,您可能希望通过在相关字段中提供一个大小来设置 SD 卡。然而,由于将在您的开发机器上创建一个您为卡指定的任何大小的文件,您可能不希望创建一个 2GB 的仿真 SD 卡。32MB 是一个不错的起点,不过如果需要的话,你可以更大。
  • 模拟器应该在中运行的“皮肤”或分辨率:您可以使用的皮肤选项取决于您选择的目标。皮肤让你选择一个典型的 Android 屏幕分辨率(例如,800×480 的 WVGA800)。当您想要测试非标准配置时,也可以手动指定分辨率。

您现在可以跳过对话框的硬件部分,因为通常只有高级配置才需要更改这些设置。

产生的对话框看起来可能类似于 Figure 2–11。

images

图 2–11。 添加新的 AVD(续)

单击 Create AVD 按钮,您的 AVD 存根将被创建。

要启动模拟器,请在 Android 虚拟设备列表中选择它,然后单击 start。你现在可以跳过启动选项,只需点击启动。第一次启动新的 AVD 时,需要很长时间才能启动。第二次和以后启动 AVD 时,速度会快一点,通常每天只需要启动一次(例如,当您开始开发时)。在大多数情况下,每次想要测试应用时,不需要停止并重新启动模拟器。

仿真器将经历几个启动阶段,第一个阶段显示一个纯文本 ANDROID 标签,如图 Figure 2–12 所示。

images

图 2–12。 安卓模拟器,初始启动段

第二阶段显示一个图形化的 Android 徽标,如图图 2–13 所示。

images

图 2–13。 安卓模拟器,二次启动段

最后,模拟器到达主屏幕(第一次运行 AVD 参见图 2–14 或键帽(参见图 2–15)。

images

图 2–14。 安卓主屏幕

如果你有键盘守卫,按下菜单按钮或滑动屏幕上的绿色锁到右边,到模拟器的主屏幕。

images

图 2-15。 Android keyguard

第六步:设置设备

有了模拟器,您不需要 Android 设备就可以开始 Android 应用开发。在尝试发布应用(例如,将它上传到 Android Market)之前,拥有一个应用是一个好主意。但是也许你已经有了一台设备——也许这就是激发你开发 Android 的兴趣的原因。

让您的设备准备好用于开发的第一步是进入设备上的设置应用。在那里,选择应用,然后选择开发。这将为您提供一组用于选择发展相关选项的复选框,类似于图 2–16 中所示。

images

图 2–16。 安卓设备开发设置

通常,您会希望启用 USB 调试,这样您就可以使用 Android 构建工具来使用您的设备。如果你愿意,你可以暂时不设置其他设置,尽管你可能会发现保持清醒选项很方便,因为它让你不必在手机插入 USB 时重复解锁。

接下来,您需要设置您的开发机器来与您的设备对话。该过程因您的开发机器的操作系统而异,如以下部分所述。

窗口

当你第一次插入 Android 设备时,Windows 会尝试为它找到一个驱动程序。由于您已经安装了其他软件,驱动程序可能已经可以使用了。如果 Windows 找到了驱动程序,您就可以开始了。

如果 Windows 找不到驱动程序,以下是获取驱动程序的一些选项:

  • Windows Update :某些版本的 Windows(例如 Vista)会提示您在 Windows Update 中搜索驱动程序。这当然值得一试,尽管不是每个设备制造商都会向微软提供其设备的驱动程序。
  • 标准 Android 驱动:在你的 Android SDK 安装中,你会发现一个google-usb_driver目录,包含一个 Android 设备通用的 Windows 驱动。您可以尝试将驱动程序向导指向这个目录,看看它是否认为这个驱动程序适合您的设备。
  • 制造商提供的驱动程序:如果您仍然没有驱动程序,请搜索设备附带的光盘(如果有)或搜索设备制造商的网站。例如,摩托罗拉可以在一个地方下载所有设备的驱动程序。

Mac OS X 和 Linux

很有可能只要插上你的设备就能“正常工作”你可以通过在一个 shell(例如 OS X 终端)中运行adb devices来查看 Android 是否识别你的设备,其中adb位于你的 SDK 的platform-tools/目录中。如果您得到类似如下的输出,Android 检测到您的设备:

List of devices attached HT9CPP809576  device

如果您运行的是 Ubuntu(或者另一个 Linux 版本)并且这个命令不起作用,您可能需要添加一些udev规则。例如,这里有一个51-android.rules文件,它将处理一些制造商的设备:

SUBSYSTEM=="usb", SYSFS{idVendor}=="0bb4", MODE="0666" SUBSYSTEM=="usb", SYSFS{idVendor}=="22b8", MODE="0666" SUBSYSTEM=="usb", SYSFS{idVendor}=="18d1", MODE="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="0c01", MODE="0666",![images](https://gitee.com/OpenDocCN/vkdoc-android-pt2-zh/raw/master/docs/begin-andr4/img/U002.jpg)  OWNER="[me]" SUBSYSTEM=="usb", SYSFS{idVendor}=="19d2", SYSFS{idProduct}=="1354", MODE="0666" SUBSYSTEM=="usb", SYSFS{idVendor}=="04e8", SYSFS{idProduct}=="681c", MODE="0666"

将它放到 Ubuntu 上的/etc/udev/rules.d目录中,然后重启计算机或者重新加载udev规则(例如sudo service udev reload)。然后,拔下设备,再插上,看是否检测到。

三、你的第一个 Android 项目

现在你有了 Android SDK,是时候做你的第一个 Android 项目了。好消息是这不需要任何代码——Android 的工具创建了一个“你好,世界!”作为创建新项目的一部分。您所需要做的就是构建它,安装它,并在您的仿真器或设备上观察它的打开。

第一步:创建新项目

Android 的工具可以为你创建一个完整的骨架项目,拥有一个完整(虽然很琐碎)的 Android 应用所需的一切。根据您使用的是 Eclipse 之类的 IDE 还是命令行,这个过程会有所不同。

月食

从 Eclipse 主菜单中,选择**File** images **New** images images **Project**以打开新建项目对话框,该对话框提供了可供选择的项目类型向导列表。展开 Android 选项,点击 Android 项目,如图 Figure 3–1 所示。

images

图 3–1。 在 Eclipse 新项目对话框中选择向导

点击下一步进入新 Android 项目向导的第一页,如图 3–2 所示。

images

图 3–2。 Eclipse 新建 Android 项目向导,准备填写

填写以下内容,否则保留默认设置(该项目的完整示例见图 3–3):

  • 项目名称:项目名称(如 Now)
  • 构建目标:您希望编译的 Android SDK(例如,Android 2.3.3 的 Google APIs)
  • 应用名称:应用的显示名称,将用于启动器中图标下的标题(如现在)
  • 包名:该项目所属 Java 包的名称(如 com . common sware . Android . skeleton)
  • 创建活动:要创建的初始活动的名称(如 Now)

images

图 3–3。 Eclipse 新建 Android 项目向导,已完成

此时,单击 Finish 创建您的 Eclipse 项目。

命令行

下面是一个从命令行创建 Android 项目的示例命令:

android create project --target "Google Inc.:Google APIs:7" --path Skeleton/Now![images](https://gitee.com/OpenDocCN/vkdoc-android-pt2-zh/raw/master/docs/begin-andr4/img/U002.jpg)  --activity Now --package com.commonsware.android.skeleton

这为您创建了一个应用框架,其中包含了构建您的第一个 Android 应用所需的一切:Java 源代码、构建指令等等。但是,您可能需要对此进行一些定制。这些命令行开关的含义如下:

  • --target:表示你的构建过程针对的是哪个版本的 Android。您需要提供一个安装在您的开发机器上的目标的 ID,它是您通过 Android SDK 和 AVD 管理器下载的。您可以通过android list targets命令找出哪些目标可用。通常情况下,你的构建过程会以你现有的最新版本的 Android 为目标。
  • --path:表示希望项目文件生成的位置。如果您指定的目录不存在,Android 将创建一个目录。例如,在前面的命令中,将在当前工作目录下创建一个Skeleton/Now/目录(或者使用它),项目文件将存储在那里。
  • --activity:表示这个项目的第一个活动的 Java 类名。不要包含包名,并确保该名称符合 Java 类命名约定。
  • --package:表示第一个活动所在的 Java 包。这个包名也可以在你安装它的任何设备上唯一地标识你的项目,如果你计划在 Android Market 上发布你的应用,它必须是唯一的。因此,通常情况下,您应该基于您自己的域名(例如,com.commonsware.android.skeleton)来构建您的包,以减少意外包名与其他人冲突的几率。

对于你的开发机,需要挑一个合适的目标,不妨换个路径。您现在可以忽略活动和包。

步骤 2:在您的模拟器或设备中构建、安装并运行应用

拥有一个项目固然很好,但如果你能构建并运行它,无论是在 Android 模拟器上还是在你的 Android 设备上,那就更好了。同样,根据您使用的是 Eclipse 还是命令行,这个过程会有所不同。

月食

在 Eclipse 的 Package Explorer 面板上选中您的项目后,单击 Eclipse 工具栏中绿色的 play 按钮来运行您的项目。第一次这样做时,您必须经历几个步骤来设置运行配置,因此 Eclipse 知道您想要做什么。

首先,在运行方式对话框中,选择 Android 应用,如图 Figure 3–4 所示。

images

图 3–4。 在 Eclipse 运行方式对话框中选择作为 Android 应用运行

单击确定。如果你有一个以上的模拟器 AVD 或设备可用,你会得到一个选项来选择你想运行的应用。否则,如果您没有插入设备,模拟器将使用您之前创建的 AVD 启动。然后,Eclipse 将在您的设备或模拟器上安装应用并启动它。

命令行

对于没有使用 Eclipse 的开发人员,在您的终端中,切换到Skeleton/Now目录,然后运行以下命令:

ant clean install

基于 Ant 的构建应该发出安装过程中涉及的步骤列表,如下所示:

`Buildfile: /home/some-balding-guy/projects/Skeleton/Now/build.xml
    [setup] Android SDK Tools Revision 10
    [setup] Project Target: Android 1.6
    [setup] API level: 4
    [setup]
    [setup] ------------------
    [setup] Resolving library dependencies:
    [setup] No library dependencies.
    [setup]
    [setup] ------------------
    [setup]
    [setup] WARNING: No minSdkVersion value set. Application will install on all Android
versions.
    [setup]
    [setup] Importing rules file: tools/ant/main_rules.xml

clean: [delete] Deleting directory /home/some-balding-guy/projects/Skeleton/Now/bin
   [delete] Deleting directory /home/some-balding-guy/projects/Skeleton/Now/gen

-debug-obfuscation-check:

-set-debug-mode:

-compile-tested-if-test:

-pre-build:

-dirs:
     [echo] Creating output directories if needed...
    [mkdir] Created dir: /home/some-balding-guy/projects/Skeleton/Now/bin
    [mkdir] Created dir: /home/some-balding-guy/projects/Skeleton/Now/gen
    [mkdir] Created dir: /home/some-balding-guy/projects/Skeleton/Now/bin/classes

-aidl:
     [echo] Compiling aidl files into Java classes...

-renderscript:
     [echo] Compiling RenderScript files into Java classes and RenderScriptbytecode...

-resource-src:
     [echo] Generating R.java / Manifest.java from the resources...

-pre-compile:

compile:
    [javac] /opt/android-sdk-linux/tools/ant/main_rules.xml:384: warning:
'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for
repeatable builds
    [javac] Compiling 2 source files to /home/some-balding-
guy/projects/Skeleton/Now/bin/classes

-post-compile:

-obfuscate:

-dex:
     [echo] Converting compiled files and external libraries into /home/some-balding-
guy/projects/Skeleton/Now/bin/classes.dex...

-package-resources:
     [echo] Packaging resources
     [aapt] Creating full resource package...

-package-debug-sign:
[apkbuilder] Creating Now-debug-unaligned.apk and signing it with a debug key...

debug:
     [echo] Running zip align on final apk...
     [echo] Debug Package: /home/some-balding-guy/projects/Skeleton/Now/bin/Now-debug.apk

install:      [echo] Installing /home/some-balding-guy/projects/Skeleton/Now/bin/Now-debug.apk
onto default emulator or device...
     [exec] 98 KB/s (4626 bytes in 0.045s)
     [exec] pkg: /data/local/tmp/Now-debug.apk
     [exec] Success

BUILD SUCCESSFUL
Total time: 10 seconds`

注意底部的BUILD SUCCESSFUL消息——这就是你如何知道应用编译成功。

当你有了一个干净的构建,在你的仿真器或设备中,打开应用启动器,如 Figure 3–5 所示,它通常位于主屏幕的底部。

images

图 3–5。 安卓模拟器应用启动器

请注意,您的Now应用有一个图标。点按它以打开它,并查看您的第一个活动。要离开应用并返回到启动器,请按 Back 按钮,它位于菜单按钮的右侧,看起来像一个指向左侧的箭头。

四、检查您的第一个项目

上一章指导您创建了一个存根项目。这一章描述了这个项目的内容,这样你就能理解 Android 在一开始给了你什么,以及各种目录和文件的角色是什么。

项目结构

Android 构建系统是围绕您的 Android 项目的特定目录树结构组织的,与任何其他 Java 项目非常相似。不过,具体细节是 Android 独有的——Android 构建工具做了一些额外的事情来准备将在设备或仿真器上运行的实际应用。这里有一个关于项目结构的快速入门,可以帮助你理解它,特别是本书中引用的样本代码。

根内容物

当您创建一个新的 Android 项目时(例如,通过android create project),您会在项目的根目录中获得几个项目,包括以下内容:

  • AndroidManifest.xml:一个 XML 文件,描述正在构建的应用和组件(活动、服务等)。)是由该应用提供的
  • bin/:应用编译后存放的目录
  • libs/:保存应用所需的任何第三方 jar 的目录
  • res/:保存资源的目录,例如图标、GUI 布局等,这些资源与应用中编译的 Java 打包在一起
  • src/:保存应用的 Java 源代码的目录

除了上述文件和目录之外,您还可以在 Android 项目中找到以下内容:

  • assets/:保存其他静态文件的目录,您希望这些文件与应用一起打包,以便部署到设备上
  • gen/:Android 的构建工具放置它们生成的源代码的目录
  • build.xml*.properties:如果您没有使用 Eclipse,这些文件将作为基于 Ant 的命令行构建过程的一部分
  • proguard.cfg:一个用于与 ProGuard 集成的文件,用来混淆你的 Android 代码

你额头上的汗水

当您创建一个 Android 项目时(例如,通过android create project,您为应用提供主活动的全限定类名(例如com.commonsware.android.SomeDemo)。然后你会发现你的项目的src/树已经有了名称空间目录树,加上一个 stub Activity子类代表你的主活动(例如src/com/commonsware/android/SomeDemo.java)。欢迎您修改该文件,并根据需要将其他文件添加到src/树中,以实现您的应用。

第一次编译项目时(例如,通过ant),在主活动的名称空间目录中,Android 构建链将创建R.java。这包含了许多与您放在res/目录树中的各种资源相关的常量。你不应该自己修改R.java,而是让 Android 工具替你处理。你会在整本书中看到许多例子引用了R.java中的东西(例如,通过R.layout.main引用一个布局的标识符)。

现在,接下来的故事

项目中的res/目录树保存了资源——与应用一起打包的静态文件,或者以原始形式,或者偶尔以预处理形式。以下是您将在res/下找到或创建的一些子目录:

  • res/drawable/:针对图像(PNG、JPEG 等。)
  • res/layout/:基于 XML 的 UI 布局规范
  • res/menu/:基于 XML 的菜单规范
  • res/raw/:通用文件(如音频片段或账户信息的 CSV 文件)
  • res/values/:字符串、尺寸等
  • res/xml/:对于您希望发货的其他通用 XML 文件

有些目录名可能有后缀,比如res/drawable-hdpi/。这表明资源目录应该只在特定情况下使用,在这种情况下,可提取的资源应该只在具有高密度屏幕的设备上使用。

我们将在本书后面的章节中介绍所有这些资源,甚至更多。

在您的初始项目中,您会发现以下内容:

  • res/drawable-hdpi/icon.pngres/drawable-ldpi/icon.pngres/drawable-mdpi/icon.png:您的应用的占位符图标的三种呈现,分别用于高、低和中密度屏幕
  • 一个 XML 文件,描述了你的用户界面的简单布局
  • res/values/strings.xml:包含外部化字符串的 XML 文件,特别是应用的占位符名称

你从中获得了什么

当您编译您的项目时(通过ant或 IDE ),结果进入您的项目根目录下的bin/目录,如下所示:

  • bin/classes/:保存编译后的 Java 类
  • bin/classes.dex:保存从那些编译的 Java 类创建的可执行文件
  • bin/*yourapp*.ap_:保存应用的资源,打包成一个 ZIP 文件(其中 yourapp 是应用的名称)
  • bin/*yourapp*-*.apk:实际的 Android 应用(其中*有所不同)

.apk文件是一个 ZIP 存档文件,包含.dex文件、您的资源的编译版本(resources.arsc)、任何未编译的资源(比如您放在res/raw/中的资源)和AndroidManifest.xml文件。如果您构建应用的调试版本(这是默认的),您将拥有yourapp-debug.apkyourapp-debug-aligned.apk作为您的 APK 的两个版本。后者已经用zipalign实用程序进行了优化,使它运行得更快。

在你的货单里

任何 Android 应用的基础都是项目根目录下的清单文件AndroidManifest.xml。这是您声明应用内部内容的地方——活动、服务等等。你还要指出这些部分是如何连接到整个 Android 系统的;例如,您可以指定哪个活动(或哪些活动)应该出现在设备的主菜单上(也称为启动器)。

当您创建应用时,会自动为您生成一个 starter 清单。对于一个简单的应用,只提供一个活动,没有其他内容,自动生成的清单可能会工作得很好,或者可能需要一些小的修改。另一方面,Android API 演示套件的清单文件长达 1000 多行。你生产的 Android 应用可能会落在中间。

起初,有根,它是好的

毫不奇怪,所有清单文件的根都是一个manifest元素:

<manifestxmlns:android="http://schemas.android.com/apk/res/android"   package="com.commonsware.android.search"> ... </manifest>

请注意名称空间声明。奇怪的是,生成的清单只将它应用于属性,而不是元素(例如,manifest,而不是android:manifest)。这种模式是可行的,所以,除非 Android 改变,否则你应该坚持使用它。

您需要在manifest元素上提供的最大信息是package属性(奇怪的是也没有命名空间)。在这里,您可以提供将被视为应用“基础”的 Java 包的名称。然后,在清单文件中任何需要类名的地方,您可以用一个前导点代替包的简写。例如,如果您需要引用前面清单中的com.commonsware.android.search.Snicklefritz,您可以只使用.Snicklefritz,因为com.commonsware.android.search被定义为应用的包。

如前一章所述,您的包也是您的应用的唯一标识符。一个设备只能有一个安装了给定包的应用,Android Market 只会列出一个给定包的项目。

您的清单还指定了android:versionNameandroid:versionCode属性。这些表示您的应用的版本。android:versionName值是用户在设置应用的应用列表中看到的值。此外,如果您以这种方式分发您的应用,Android Market 清单会使用版本名称。版本名称可以是您想要的任何字符串值。另一方面,android:versionCode值必须是一个整数,新版本必须比旧版本具有更高的版本代码。Android 和 Android Market 将比较新 APK 的版本代码和已安装应用的版本代码,以确定新 APK 是否确实是一个更新。典型的方法是在1开始版本代码,并随着应用的每个产品发布而递增,不过如果您愿意,也可以选择另一种约定。

提示:Android Market 将只提供任何 APK 的一个版本(通常是最新版本)。如果您想部署一个不同的版本,而不需要重新编译代码,您可以为任何给定的版本备份您的 APK,并简单地将它加载到您的设备或仿真器上。

为您的应用申请

在您的初始项目清单中,<manifest>元素的唯一子元素是一个<application>元素。<application>元素的子元素代表了清单文件的核心。

在特定情况下可能需要的<application>元素的一个属性是android:debuggable属性。这需要设置为true。如果你在一个实际的设备上安装应用,你正在使用 Eclipse(或者另一个调试器),并且你的设备在没有这个标志的情况下禁止调试。例如,根据一些报道,谷歌/HTC Nexus One 需要android:debuggable = "true"

默认情况下,当您创建一个新的 Android 项目时,您会在<application>元素中获得一个单独的<activity>元素:

<?xml version="1.0"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.commonsware.android.skeleton">     <application>         <activity android:name=".Now" android:label="Now">             <intent-filter>                 <action android:name="android.intent.action.MAIN"/>                 <category android:name="android.intent.category.LAUNCHER"/>             </intent-filter>         </activity>     </application> </manifest>

该元素为实现活动的类提供了android:name,为活动的显示名称提供了android:label,并且(通常)提供了一个<intent-filter>子元素,用于描述在什么条件下显示该活动。stock <activity>元素将您的活动设置为出现在启动器中,因此用户可以选择运行它。正如你将在本书后面看到的,如果你愿意,你可以在一个项目中有几个活动。

五、关于 Eclipse 的一点信息

Eclipse 是一个非常流行的集成开发环境(IDE),尤其是对于 Java 开发。它还被设计成可通过附加系统扩展。最重要的是,Eclipse 是开源的,这要感谢 IBM 多年前决定向全世界发布 Eclipse 的恩惠。这种组合使它成为核心 Android 开发团队的 IDE 的理想选择。

具体来说,为了配合 Android SDK,Google 已经为 Eclipse 环境发布了一些插件。其中最主要的是 Android Developer Tools (ADT)插件,它为 Eclipse it 提供了 Android 的核心意识。

ADT 给了你什么

本质上,ADT 插件接受常规的 Eclipse 操作,并将其扩展到 Android 项目。例如,使用 Eclipse,您可以获得以下特性(以及其他特性):

  • 用于创建常规 Android 项目、Android 测试项目等的新项目向导
  • 能够像运行常规 Java 应用一样运行 Android 项目——通过工具栏中的绿色 run 按钮——尽管这实际上涉及将 Android 应用推送到仿真器或设备上,甚至可能在仿真器不运行时启动它
  • Android 类和方法的工具提示支持

此外,最新版本的 ADT 为您提供了对拖放式 GUI 编辑的初步支持。虽然这本书将重点介绍 Eclipse 生成的 XML 文件,但是 Eclipse 现在允许您通过在屏幕上拖动 GUI 组件来组装这些 XML 文件,并随时调整属性。拖放式 GUI 编辑是相当新的,所以当社区和 Google 发现当前实现的问题和限制时,可能会有一些粗糙的边缘。

应对月食

Eclipse 是一个强大的工具。像许多强大的工具一样,Eclipse 有时令人困惑。决定如何解决一些特定的开发问题可能是一个挑战,Android 本身的新奇性加剧了这一挑战。

这一节提供了一些技巧来处理在 Android 上使用 Eclipse 时的一些常见问题。

如何导入非 Eclipse 项目

并非所有的 Android 项目都附带 Eclipse 项目文件,例如与本书相关的示例项目。但是,如果您愿意,可以很容易地将它们添加到您的 Eclipse 工作区中。下面是怎么做的!

首先,从 Eclipse 主菜单中选择**File** images images **New** images images **Project**,如图图 5–1 所示。

images

图 5–1。Eclipse 中的文件菜单

然后从可用项目类型树中选择**Android** images images **Android Project**,如图图 5–2 所示,点击下一步。

images

图 5–2。Eclipse 中的新建项目向导

注意:如果你没有看到这个选项,说明你没有安装 Android 开发者工具。

然后,在 New Android Project 向导的第一页上,选择“Create Project from existing source”单选按钮,单击 Browse 按钮,打开包含项目的AndroidManifest.xml文件的目录。这将填充向导页面的大部分剩余部分,尽管您可能还需要从表中指定一个构建目标,如 Figure 5–3 所示。

images

图 5–3。Eclipse 中新的 Android 项目向导

然后,单击完成。这将使您返回到 Eclipse,导入的项目在您的工作区中,如 Figure 5–4 所示。

images

图 5–4。Eclipse 中的 Android 项目树

接下来,右键单击项目名称,从上下文菜单中选择**Build Path** images images **Configure Build Path**,如图图 5–5 所示。

images

图 5–5。Eclipse 中的项目上下文菜单

这将打开项目属性窗口的 Java 构建路径部分,如 Figure 5–6 所示。

images

图 5–6。Eclipse 中的项目属性窗口

如果没有选中 Android JAR(图 5–6 中的 Android 2.2 条目),请选中它,然后单击确定关闭属性窗口。此时,您的项目应该可以使用了。

如何去 DDMS

很多时候,您会被告知查看 DDMS 中的一些东西,比如 LogCat 选项卡来检查 Java 堆栈跟踪。在《月蚀》中,DDMS 是一个视角。要在您的工作区中打开这个透视图,从主菜单中选择**Window** images images **Open Perspective** images images **Other**,如图图 5–7 所示。

images

图 5–7。 月食中的透视菜单

然后,在如图 Figure 5–8 所示的透视图列表中,选择 DDMS。

images

图 5–8。 月食中的视角花名册

这将把 DDMS 透视图添加到您的工作区,并在您的 Eclipse IDE 中打开它。

DDMS 将在本书后面的章节中详细介绍。

如何创建仿真器

默认情况下,您的 Eclipse 环境没有设置 Android 模拟器。在成功运行项目之前,您需要一个。

为此,首先从主菜单中选择**Window** images images **Android SDK and AVD Manager**,如图图 5–9 所示。

images

图 5–9。Eclipse 中的 Android SDK 和 AVD 管理器菜单选项

这将打开与从命令行运行android相同的窗口。

现在,您可以按照第二章的“步骤 5:设置仿真器”一节中的说明来定义一个 Android 虚拟设备(AVD)

如何运作一个项目

假设您已经定义了 AVD,或者您已经设置了用于调试的设备并连接到您的开发计算机,您可以在模拟器中运行您的项目。

首先,单击运行工具栏按钮,或者从主菜单中选择**Project** images images **Run**。这将在您第一次运行项目时弹出运行方式对话框,如图图 5–10 所示。

images

图 5–10。Eclipse 中的运行方式对话框

选择 Android 应用,然后单击确定。如果您有多个可用的 AVD 或设备,将会出现一个窗口,您可以在其中选择所需的目标环境。然后,模拟器将启动运行您的应用。请注意,如果模拟器(或设备)上的锁定屏幕被锁定,您需要将其解锁。

如何不运行你的项目

当您运行项目时,请确保 XML 文件不是编辑器中的活动选项卡。试图“运行”这将导致在 XML 文件所在的任何目录中创建一个.out文件(例如,res/layout/main.xml.out)。要恢复,只需删除有问题的.out文件,并尝试再次运行,这一次用一个 Java 文件作为活动标签。

替代 IDEs

如果你真的喜欢 Eclipse 和 ADT,你可能会考虑 MOTODEV Studio for Android。这是 Eclipse 的另一组插件,增强了 ADT,并提供了许多其他与 Android 相关的开发特性,包括以下(以及许多其他特性):

  • 更多帮助你创建 Android 类的向导
  • 集成的 SQLite 浏览,因此您可以直接从 IDE 中操作模拟器中的 SQLite 数据库
  • 更多的验证器来检查常见的错误,以及一个代码片段库来减少一开始的错误
  • 协助将您的申请翻译成多种语言

虽然 MOTODEV Studio for Android 是由摩托罗拉发布的,但你可以用它来为所有 Android 设备构建应用,而不仅仅是摩托罗拉自己制造的设备。随着谷歌即将收购摩托罗拉,MOTODEV 的未来肯定会很有趣。

其他 ide 也在慢慢地获得 ADT 的等价物,尽管谷歌只提供了很少的帮助。比如 IntelliJ 的 IDEA 有一个针对 Android 的模块。它最初是商业性的,但现在从版本 10 开始,它是 IDEA 开源社区版的一部分。

当然,您根本不需要使用 IDE。虽然对一些人来说这听起来可能是亵渎,但是 ide 并不是构建应用的唯一方式。通过 ADT 完成的大部分工作都可以通过等效的命令行来完成,这意味着您真正需要的只是一个 shell 和一个编辑器。例如,本书的作者目前不使用 IDE,也不打算很快采用 Eclipse。

IDEs 和这本书

欢迎您在阅读本书时使用 Eclipse。如果您愿意,欢迎使用另一个 IDE。您甚至可以直接跳过 IDE,直接使用编辑器。

这本书的重点是展示 Android 的能力和利用这些能力的 API。它的目的不是教授任何一个 IDE 的使用。因此,所示的示例代码应该可以在任何 IDE 中工作,特别是如果您按照本章中的说明将非 Eclipse 项目导入 Eclipse 的话。

六、增强您的第一个项目

Android 为您的第一个项目生成的AndroidManifest.xml文件完成了这项工作。但是,对于生产应用,您可能希望考虑添加一些属性和元素,如本章所述。

支持多种屏幕尺寸

Android 设备的屏幕尺寸范围很广,从 2.8 英寸的微型智能手机到 46 英寸的谷歌电视。Android 根据物理屏幕大小和通常观看的距离将它们分为四类:

  • :3 英寸(7.5 厘米)以下,至少 426×320dp 分辨率
  • 普通 : 3 英寸(7.5 厘米)到 4.5 英寸(11.5 厘米)左右,至少 470×320dp 分辨率
  • : 4.5 英寸(11.5 厘米)到 10 英寸(25 厘米)左右,至少 640×480dp 分辨率
  • 超大:10 英寸(25 厘米)以上,至少 960×720dp 分辨率

默认情况下,您的应用将不支持小屏幕,将支持普通屏幕,并可能通过 Android 内置的一些代码支持大屏幕和超大屏幕,这些代码可以自动将应用转换、缩放和调整大小到更大的屏幕上。

为了真正支持您想要的所有屏幕尺寸,您应该考虑在您的清单文件中添加一个<supports-screens>元素。这将枚举您明确支持的屏幕尺寸。例如,如果你想支持小屏幕,你需要包含<supports-screens>元素。类似地,如果您正在为大型或超大屏幕提供定制 UI 支持,您将希望拥有带有适当子元素的<supports-screens>元素。因此,虽然起始清单文件中的默认设置可以工作,但是您应该考虑添加对处理多种屏幕尺寸的支持。

关于为所有屏幕尺寸提供稳固支持的更多信息可以在第二十五章中找到。

指定版本

如前一章所述,您的清单已经包含了一些关于应用版本的信息。然而,您可能希望在您的AndroidManifest.xml文件中添加一个<uses-sdk>元素作为<manifest>元素的子元素,以指定您的应用支持哪个版本的 Android。默认情况下,假设您的应用支持从 1.0 到当前 3.0 以及未来任何版本的所有 Android 版本。很可能,这不是你想要的。

您的<uses-sdk>元素最重要的属性是android:minSdkVersion。这表明你提供支持的 Android 的最老版本是什么。如果您愿意的话,它会与您用来测试应用的最老版本进行通信。属性的值是表示 Android SDK 版本的整数:

  • 1 : Android 1.0
  • 2 : Android 1.1
  • 3 : Android 1.5
  • 4 : Android 1.6
  • 5 : Android 2.0
  • 6 : Android 2.0.1
  • 7 : Android 2.1
  • 8 : Android 2.2
  • 9 : Android 2.3
  • 10 : Android 2.3.3
  • 11 : Android 3.0
  • 12 : Android 3.1
  • 13 : Android 3.2
  • 14 : Android 4.0

因此,如果您只在 Android 2.1 和更高版本的 Android 上测试您的应用,您应该将android:minSdkVersion属性设置为7

从 Android 3.2 开始,提供了一种替代方法来更准确地指定屏幕布局的空间要求。这些属性指定最小宽度sw<N>dp、可用宽度w<N>dp和可用高度h<N>dp(其中N是像素数)。起初,使用这些规定的选项可能看起来更复杂,但是对于许多设计者来说,更自然的是设计布局和一组功能,然后确定最小和最接近像素的最佳尺寸以满足表示要求。

您可能还希望指定一个android:targetSdkVersion属性。这表明当你写代码时,你的目标是哪个版本的 Android。如果你的应用运行在一个新版本的 Android 上,Android 可能会做一些事情来改善你的代码与新版本 Android 的兼容性。因此,举例来说,你可以指定android:targetSdkVersion="10",表明你正在用 Android 2.3.3 编写你的应用;如果有一天你的应用在 Android 3.0 设备上运行,Android 可能会采取一些额外的措施来确保你的 2.3.3 为中心的代码在 3.0 设备上正确运行。特别是,为了获得在 Android 3.0(或更高版本)平板电脑上运行时的平板电脑外观,您需要指定目标 SDK 版本的11或更高版本。这个主题将在第二十六章和第二十七章中详细介绍。

七、重写你的第一个项目

您在第三章中创建的项目仅由 Android 构建工具生成的默认文件组成——您自己没有编写任何 Java 代码。在本章中,您将修改该项目,使其更具交互性。在这个过程中,您将研究组成 Android 活动的基本 Java 代码。

注意:本章中的说明假设您在包和文件的名称方面遵循了第三章中的原始说明。如果您使用了不同的名称,您将需要在以下步骤中调整名称以匹配您的名称。

活动

您的项目的src目录包含基于您创建项目时使用的 Java 包的标准 Java 风格的目录树(例如,com.commonsware.android导致src/com/commonsware/android/)。在最里面的目录中,您应该会找到一个名为Now.java的预生成源文件,这是您的第一个活动所在的位置。

在您的编辑器中打开Now.java并粘贴以下代码(或者,如果您从 Apress 网站下载了源文件,您可以直接使用Skeleton/Now项目):

`packagecom.commonsware.android.skeleton;

importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.Button;
importjava.util.Date; public class Now extends Activity implements View.OnClickListener {
  Button btn;

@Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    btn=new Button(this);
    btn.setOnClickListener(this);
    updateTime();
    setContentView(btn);
  }

public void onClick(View view) {
    updateTime();
  }

private void updateTime() {
    btn.setText(new Date().toString());
  }
}`

解剖活动

让我们一段一段地检查这段 Java 代码,从包声明和导入的类开始:

`packagecom.commonsware.android.skeleton;

importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.Button;
importjava.util.Date;`

包声明需要与创建项目时使用的声明相同。然后,与任何其他 Java 项目一样,您需要导入您引用的任何类。大多数 Android 特有的类都在android包中。

注意:并不是每个 Java SE 类都可供 Android 程序使用。请访问 Android 类参考,了解哪些是可用的,哪些是不可用的。

活动是公共类,继承自android.app.Activity基类。在这种情况下,活动持有一个按钮(btn):

public class Now extends Activity implements View.OnClickListener {   Button btn;

为了简单起见,我们希望将所有的按钮点击都捕获在活动本身中,因此我们也使用了 activity 类 implement OnClickListener

当活动开始时,调用onCreate()方法。您应该做的第一件事是向上链接到超类,这样就可以完成股票 Android 活动初始化:

`@Override
public void onCreate(Bundle icicle) {
  super.onCreate(icicle);

btn=new Button(this);
  btn.setOnClickListener(this);
  updateTime();
  setContentView(btn);
}`

在我们的实现中,然后我们创建按钮实例btn(通过new Button(this)),告诉它将所有的按钮点击发送给活动实例本身(通过setOnClickListener()),调用私有的updateTime()方法,然后将活动的内容视图设置为按钮本身(通过setContentView())。我们将在后面的章节中看看那个神奇的Bundle icicle。目前,将它视为一个不透明的句柄,所有活动在创建时都会收到它。

public void **onClick**(View view) {   **updateTime**(); }

在 Java 传统的 Swing UI 世界中,单击JButton会引发一个ActionEvent,该 ?? 被传递给为按钮配置的ActionListener。在 Android 中,点击一个按钮会导致为按钮配置的OnClickListener实例中的onClick()被调用。侦听器将触发单击的视图(在本例中是按钮)传递给它。我们在这里所做的就是调用私有的updateTime()方法:

private void **updateTime**() {   btn.**setText**(new **Date**().**toString**()); }

当我们打开活动(onCreate())或点击按钮(onClick())时,我们通过setText()将按钮的标签更新为当前时间,其功能与JButton相当。

构建和运行活动

要构建活动,使用 IDE 内置的 Android 打包工具,或者在项目的基础目录中运行ant clean install(如第三章中所述)。然后,运行活动。如果您正在使用 Eclipse,它应该会自动为您启动;否则,在主屏幕启动器中查找活动。您应该会看到类似于图 7–1 中所示的活动。

images

图 7–1。 现在示威活动

点击按钮——换句话说,点击设备屏幕上的任何地方——都会更新按钮标签上显示的时间。

请注意,标签水平和垂直居中,因为这些是应用于按钮标题的默认样式。我们可以控制这种格式,这将在后面的章节中介绍。

在您完成了对高级按钮技术的惊叹之后,您可以单击模拟器上的 Back 按钮返回到启动器。

八、使用基于 XML 的布局

虽然从技术上讲,完全通过 Java 代码创建小部件并将其附加到您的活动是可能的,就像我们在上一章中所做的那样,但是更常见的方法是使用基于 XML 的布局文件。小部件的动态实例化是为更复杂的场景保留的,在这些场景中,小部件在编译时是未知的(例如,基于从互联网检索的数据来填充一列单选按钮)。

考虑到这一点,是时候分解 XML 并学习如何以这种方式展示 Android 活动视图了。

什么是基于 XML 的布局?

顾名思义,基于 XML 的布局是以 XML 格式编码的小部件之间以及与容器之间关系的规范。具体来说,Android 将基于 XML 的布局视为资源,因此,布局文件存储在 Android 项目内的reslayout目录中。

每个 XML 文件包含一个元素树,指定组成一个View的小部件和容器的布局。XML 元素的属性是描述小部件外观或容器行为的属性。例如,如果一个Button元素有一个属性值android:textStyle = "bold",这意味着出现在按钮表面的文本应该以粗体显示。

Android 的 SDK 附带了一个使用这些布局的工具(aapt)。这个工具应该被你的 Android 工具链自动调用(比如 Eclipse 或者 Ant 的build.xml)。作为一名开发人员,对你来说特别重要的是,aapt在你的项目的gen目录中生成R.java源文件,允许你直接从你的 Java 代码中访问布局和布局中的小部件,这将在本章后面演示。

为什么要使用基于 XML 的布局?

使用 XML 布局文件做的几乎所有事情都可以通过 Java 代码实现。例如,您可以使用setTypeface()让按钮以粗体显示文本,而不是使用 XML 布局中的属性。由于 XML 布局是您需要跟踪的另一个文件,我们需要使用这种文件的充分理由。

也许最大的原因是帮助创建视图定义的工具,比如 Eclipse 这样的 IDE 中的 GUI 生成器或 DroidDraw 这样的专用 Android GUI 设计器。原则上,这样的 GUI 生成器可以生成 Java 代码,而不是 XML。挑战是在设计工具中重新读取定义以支持编辑,当数据是像 XML 这样的结构化格式而不是编程语言时,这要简单得多。此外,将生成的位与手写代码分开,使得当生成的位重新生成时,某人定制的源代码不太可能被意外破坏。XML 在工具编写者易于使用的东西和程序员易于根据需要手工操作的东西之间形成了一个很好的中间地带。

此外,XML 作为 GUI 定义格式变得越来越普遍。微软的可扩展应用标记语言(XAML)、Adobe 的 Flex、谷歌的谷歌网络工具包(GWT)和 Mozilla 的 XML 用户界面语言(XUL)都采用了与 Android 类似的方法:将布局细节放在 XML 文件中,将编程智能放在源文件中(例如 XUL 的 JavaScript)。许多不太知名的 GUI 框架,比如 ZK,也使用 XML 进行视图定义。虽然“随大流”不一定是最好的策略,但它确实有助于简化从任何其他以 XML 为中心的视图描述语言到 Android 的过渡。

好的,那它看起来像什么?

下面是来自上一章示例应用的Button,它被转换成一个 XML 布局文件,位于Layouts/NowRedux示例项目中:

<?xml version="1.0" encoding="utf-8"?> <Button xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/button"     android:text=""     android:layout_width="fill_parent"     android:layout_height="fill_parent"/>

小部件的类名Button,构成了 XML 元素的名称。由于Button是 Android 提供的小部件,我们可以只使用裸类名。如果你创建自己的小部件作为android.view.View的子类,你也需要提供一个完整的包声明(例如com.commonsware.android.MyWidget)。

根元素需要声明 Android XML 名称空间:

xmlns:android="http://schemas.android.com/apk/res/android"

所有其他元素都将是根的子元素,并将继承该名称空间声明。

因为我们想从 Java 代码中引用这个按钮,所以我们需要通过android:id属性给它一个标识符。我们将在下一节更详细地介绍这个概念。

其余的属性是这个Button实例的属性:

  • android:text:表示显示在按钮表面的初始文本(本例中为空字符串)
  • android:layout_widthandroid:layout_height:告诉 Android 让按钮的宽度和高度填充父按钮,在本例中是整个屏幕

这些属性将在第十章中详细介绍。

由于这个小部件是活动视图中唯一的内容,所以我们只需要这个元素。复杂的视图需要一个完整的元素树,代表控制其位置的小部件和容器。本书的其余章节将尽可能使用 XML 布局形式,因此还有许多其他更复杂布局的例子供您阅读。

@符号是怎么回事?

许多小部件和容器只需要出现在 XML 布局文件中,不需要在 Java 代码中引用。例如,静态标签(TextView)经常需要出现在布局文件中,只是为了指示它应该出现的位置。XML 文件中这些种类的元素不需要用android:id属性来命名。

然而,你想在你的 Java 源代码中使用的任何事情都需要一个android:id

约定是使用@+id/...作为id值,其中...代表您的小部件在本地的唯一名称,表示给定的id值在布局文件中的第一次出现。在上一节的 XML 布局示例中,@+id/buttonButton小部件的标识符。在同一个布局文件中第二次和随后的出现应该去掉+符号——我们将在第十章中使用这个特性。

Android 提供了一些特殊的android:id值,形式为@android:id/...。你会在本书的各种例子中看到这些价值观。

我们如何将这些附加到 Java 上呢?

假设您已经在存储于res/layout中的名为main.xml的 XML 布局文件中为您的视图精心设置了小部件和容器,那么您所需要的只是活动的onCreate()回调中的一条语句来使用该布局:

**setContentView**(R.layout.main);

这与我们之前使用的setContentView()相同,传递给它一个View子类的实例(在这种情况下,是一个Button)。从我们的布局构建的 Android 构建的View,可以从代码生成的R类访问。所有布局都可以在R.layout下访问,由布局文件的基本名称键入;例如,res/layout/main.xml导致R.layout.main

要访问您标识的小部件,使用findViewById(),向其传递有问题的小部件的数字标识符。这个数字标识符由 Android 在R类中生成为R.id.something(其中something是您正在寻找的特定小部件)。那些小部件只是View的子类,就像我们在前一章中创建的Button实例一样。

故事的其余部分

在最初的Now演示中,按钮的表面将显示当前时间,这将反映按钮最后一次被按下的时间(或者活动第一次显示的时间,如果按钮还没有被按下的话)。即使在这个修改后的演示中,大多数逻辑仍然有效。然而,我们可以引用 XML 布局中的回调函数,而不是在活动的onCreate()回调函数中实例化Button:

`packagecom.commonsware.android.layouts;

importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.Button;
importjava.util.Date;

public class NowRedux extends Activity
  implementsView.OnClickListener {
  Button btn;

@Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);

setContentView(R.layout.main);

btn=(Button)findViewById(R.id.button);
    btn.setOnClickListener(this);
    updateTime();
  }

public void onClick(View view) {
    updateTime();
  }

private void updateTime() {
    btn.setText(new Date().toString());
  }
}`

第一个区别是,我们没有将内容视图设置为用 Java 代码创建的视图,而是将其设置为引用 XML 布局(setContentView(R.layout.main))。当我们重新构建这个项目时,R.java源文件将被更新,以包含对我们布局文件的引用(在我们项目的res/layout目录中存储为main.xml)。

另一个区别是我们需要得到我们的Button实例,为此我们使用了findViewById()调用。因为我们将按钮标识为@+id/button,所以可以将按钮的标识符引用为R.id.button。现在,有了Button实例,我们可以根据需要设置回调和标签。

结果看起来与原始的Now演示相同,如图图 8–1 所示。

images

图 8–1。NowRedux 样本活动

九、使用基本小部件

每个 GUI 工具包都有一些基本的小部件:字段、标签、按钮等等。Android 的工具包在范围上没有什么不同,基本的小部件很好地介绍了小部件如何在 Android 活动中工作。

分配标签

最简单的小部件是标签,在 Android 中称为TextView。和大多数 GUI 工具包一样,标签是用户不能直接编辑的文本。通常,标签用于标识相邻的窗口小部件(例如,用户填写名称的字段旁边的“名称:”标签

在 Java 中,可以通过创建一个TextView实例来创建标签。不过,更常见的是,通过向布局添加一个TextView元素,用一个android:text属性来设置标签本身的值,从而在 XML 布局文件中创建标签。如果您需要基于某些标准交换标签,比如国际化,您可能希望使用 XML 中的字符串资源引用,这将在本书的后面进行描述。

TextView具有许多与标签相关的其他属性,例如

  • android:typeface:设置标签使用的字体(如monospace)
  • android:textStyle:表示字体应该加粗(bold)、斜体(italic)或加粗加斜体(bold_italic)
  • android:textSize:指定字体大小,采用以下几种度量之一:sp(缩放像素)、dip(与密度无关的像素)、px(原始像素)、in(英寸)、mm(毫米)。推荐的做法是使用sp,并且这是追加到大小的,比如12sp
  • android:textColor:以 RGB 十六进制格式设置标签文本的颜色(如#FF0000表示红色)

例如,在Basic/Label项目中,您会发现以下布局文件:

<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="fill_parent"   android:layout_height="wrap_content"   android:text="You were expecting something profound?"   />

仅仅是这个布局,加上 Android 的项目构建器(例如,android create project)提供的存根 Java 源代码,就可以得到如图图 9–1 所示的结果。

images

图 9–1。label demo 示例应用

在我们负责LabelDemo的 XML 中,您会注意到我们使用了两个宽度和高度指令。第一个,fill_parent,表示我们希望我们的 UI 元素完全填充它的父空间,减去任何填充或边框。第二个,wrap_content,确保在父节点中只使用足够的空间来显示我们的内容,而不是更多。随着我们在接下来的章节中逐步学习更多的例子,这些将会变得更加清晰。

按钮,按钮,谁拿了按钮?

在前两章中,您已经看到了Button小部件的使用。事实证明,ButtonTextView的子类,所以上一节讨论的所有内容也适用于格式化按钮的表面。

Android 为你提供了两种方法来处理点击收听者的问题。第一种选择是定义一些对象(比如活动)作为实现View.OnClickListener接口的“经典”方式。比经典的方法更好的是当代机器人简化事物的方式。这个简单的选项有两个步骤:

  1. 在你的Activity上定义一些方法来保存这个按钮,它接受一个View参数,有一个void返回值,并且是public
  2. 在您的布局 XML 中,在Button元素上,包含带有您在上一步中定义的方法名称的android:onClick属性。

例如,我们可能在我们的Activity上有一个如下所示的方法:

public void **someMethod**(View theButton) {  // do something useful here }

然后,我们可以将这个 XML 声明用于Button本身,包括android:onClick:

<Button  android:onClick="someMethod"  ... />

这足以让 Android 将Button与点击处理程序连接在一起。起初你可能不会觉得这比传统方法简单。但是,考虑一下这种方法通过 XML 规范中不同的选项(例如,在不同的语言环境、屏幕大小等条件下)打开选项来改变给定的ButtonActivity是多么容易。我们将在接下来的章节中详细讨论这些选项。

转瞬即逝的影像

Android 有两个小部件可以帮助你在活动中嵌入图片:ImageViewImageButton。顾名思义,它们分别类似于TextViewButton

每个小部件都有一个android:src属性(在 XML 布局中)来指定使用哪张图片。这些属性通常引用一个可提取的资源,在讨论资源的第二十三章中有更详细的描述。

ImageView的子类ImageButton,混合了标准的Button行为,用于响应点击等等。例如,看看来自Basic/ImageView示例项目的main.xml布局:

<?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/icon"     android:layout_width="fill_parent"     android:layout_height="fill_parent"     android:adjustViewBounds="true"     android:src="@drawable/molecule"     />

仅仅使用代码生成的活动,结果就是图像,如图 Figure 9–2 所示。

images

图 9–2。imageview demo 示例应用

绿色或其他颜色的田野

除了按钮和标签,字段是大多数 GUI 工具包的第三个支柱。在 Android 中,它们是通过EditText小部件实现的,它是用于标签的TextView的子类。

除了标准的TextView属性(例如android:textStyle ), EditText还有许多其他属性在构建字段时会很有用,包括:

  • android:autoText:控制字段是否应提供自动拼写帮助
  • android:capitalize:控制字段是否应自动大写输入文本的首字母(例如,在姓名和城市字段中)
  • android:digits:将字段配置为只接受某些数字
  • android:password:将字段配置为在字段中键入字符时显示密码点,隐藏键入的字符
  • android:singleLine:控制该字段是用于单行输入还是多行输入(例如,按 Enter 键是移动到下一个小部件还是添加一个新行?)

大多数前述属性也可以从新的android:inputType属性中获得,该属性是在 Android 1.5 中添加的,作为向 Android 添加“软键盘”的一部分(在第十一章中讨论)。

例如,来自Basic/Field项目的一个 XML 布局文件显示了一个EditText小部件:

<?xml version="1.0" encoding="utf-8"?> <EditText xmlns:android="http://schemas.android.com/apk/res/android"   android:id="@+id/field"   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:singleLine="false"   />

请注意,android:singleLine被设置为"false",因此用户将能够输入几行文本。

对于这个项目,FieldDemo.java文件用一些散文填充输入字段:

`package com.commonsware.android.field;

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

public class FieldDemo extends Activity {
  @Override
  public void onCreate(Bundle icicle) {
      super.onCreate(icicle);
      setContentView(R.layout.main);

EditText fld=(EditText)findViewById(R.id.field);
      fld.setText("Licensed under the Apache License, Version 2.0 " +
              "(the "License"); you may not use this file " +
              "except in compliance with the License. You may " +
              "obtain a copy of the License at " +
              "http://www.apache.org/licenses/LICENSE-2.0");
  }
}`

一旦构建并安装到仿真器中,结果如图 9–3 所示。

field 的另一种风格是提供自动完成功能,帮助用户在不输入整个文本的情况下提供一个值。这在 Android 中作为AutoCompleteTextView小部件提供,在第十二章中有更详细的讨论。

images

图 9–3。field demo 示例应用

只是另一个检查框

经典复选框有两种状态:选中和未选中。单击复选框可在这些状态之间切换,以指示选择(例如,“将紧急交付添加到我的订单”)。

在 Android 中,有一个CheckBox widget 可以满足这个需求。它有TextView作为祖先,所以您可以使用TextView属性如android:textColor来格式化小部件。

在 Java 中,您可以调用以下内容:

  • isChecked():确定复选框是否已被选中
  • setChecked():强制复选框处于选中或未选中状态
  • toggle():切换复选框,就像用户选中它一样

此外,您可以注册一个监听器对象(在本例中是一个OnCheckedChangeListener的实例),以便在复选框的状态改变时得到通知。

例如,在Basic/CheckBox项目中,有一个简单的复选框布局:

<?xml version="1.0" encoding="utf-8"?> <CheckBox xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/check"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:text="This checkbox is: unchecked" />

对应的CheckBoxDemo.java检索并配置复选框的行为:

`public class CheckBoxDemo extends Activity
  implements CompoundButton.OnCheckedChangeListener {
  CheckBox cb;

@Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);

cb=(CheckBox)findViewById(R.id.check);
    cb.setOnCheckedChangeListener(this);
  }

public void onCheckedChanged(CompoundButton buttonView,
                                 boolean isChecked) {
      if (isChecked) {
        cb.setText("This checkbox is: checked");
      }
      else {
        cb.setText("This checkbox is: unchecked");
    }
  }
}`

注意,活动充当复选框状态改变的监听器,因为它实现了OnCheckedChangeListener接口(通过cb.setOnCheckedChangeListener(this))。监听器的回调是onCheckedChanged(),它接收状态已经改变的复选框和新的状态。在这种情况下,我们更新复选框的文本以反映实际框中包含的内容。

结果呢?点击复选框会立即更新其文本,如图图 9–4 和 9–5 所示。

images

图 9–4。??【复选框 Demo 示例应用,复选框未选中

images

图 9–5。 同样的申请,现在用复选框勾选了

扳动开关,伊果

Android 4.0 的新功能(冰激凌三明治)是CheckBox的变体。这是一个双态开关Switch,使用户能够用手指滑动或拖动,就像他们在开关电灯一样。他们还可以像点击CheckBox一样点击Switch小部件来改变其状态。

Switch提供了一个android:text属性来显示与Switch状态相关的文本,通过SwitchsetTextOn()setTextOff()方法来控制。

其他对Switch有用的方法包括:

  • getTextOn():返回Switch打开时使用的文本
  • getTextOff():返回Switch关闭时使用的文本
  • setChecked():将当前Switch状态变为开(如同CheckBox)

例如,在Basic/Switch项目中,这里有一个简单的Switch布局:

<?xml version="1.0" encoding="utf-8"?> <Switch xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/switchdemo"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:text="This switch is: off" />

注意,由于 Java 中的保留字约定,我们不能将小部件称为“switch”。相应的SwitchActivity.java检索并配置开关的行为我们再次配置我们的类来实现OnCheckChangedListener接口,它负责调用我们的onCheckedChanged方法:

`public class SwitchDemo extends Activity
  implements CompoundButton.OnCheckedChangeListener {
  Switch sw;

@Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);

sw=(Switch)findViewById(R.id.switchdemo);
    sw.setOnCheckedChangeListener(this);
  }

public void onCheckedChanged(CompoundButton buttonView,
                                 boolean isChecked) {
    if (isChecked) {
      sw.setTextOn("This switch is: on");
    }
    else {
      sw.setTextOff("This switch is: off");
    }
  } }`

从总体结构、父方法的使用和行为可以看出,Switch的操作方式与CheckBox非常相似。我们的结果显示在图 9–6 和 9–7 中,开关处于每个可能的状态。

images

图 9–6。 开关关闭的 SwitchDemo 示例应用

images

图 9–7。 同样的应用,现在换上了

打开收音机

与其他工具包中单选按钮的其他实现一样,Android 的单选按钮是两种状态的,就像复选框和开关一样,但可以分组,以便在任何时候都只能选中组中的一个单选按钮。

CheckBox一样,RadioButton继承了CompoundButton,后者又继承了TextView。因此,字体、风格、颜色等所有标准的TextView属性都可以用来控制单选按钮的外观。类似地,您可以在RadioButton上调用isChecked()来查看它是否被选中,调用toggle()来选择它,以此类推,就像您可以使用CheckBox一样。

大多数时候,你会想把你的RadioButton部件放在一个RadioGroup里面。RadioGroup表示一组状态为绑定的单选按钮,意味着在任何时候只能选择该组中的一个按钮。如果您在 XML 布局中为您的RadioGroup分配了一个android:id,那么您可以从您的 Java 代码中访问该组并调用以下代码:

  • check():通过 ID 检查特定的单选按钮(如group.check(R.id.radio1))
  • clearCheck():清除所有单选按钮,因此组中没有被选中
  • getCheckedRadioButtonId():获取当前选中的单选按钮的 ID(如果没有选中,则为-1)

注意,RadioGroup的互斥特性只适用于RadioGroup的直接子控件RadioButton。在RadioGroup和它的RadioButton小部件之间不能有其他容器——将在下一章讨论。

例如,来自Basic/RadioButton示例应用的 XML 布局显示了一个RadioGroup包装了一组RadioButton小部件:

`

    


`

使用 Android 为项目生成的 Java 和这个布局,您会得到如图 Figure 9–8 所示的结果。

请注意,单选按钮组最初被设置为完全未选中。要预设要检查的单选按钮之一,请在活动的onCreate()回调中使用RadioButton上的setChecked()RadioGroup上的check()

images

图 9–8。??【单选按钮演示】示例应用

景色真美

所有的部件,包括前面几节中显示的部件,都扩展了View,这为所有的部件提供了一系列已经描述过的有用的属性和方法。

填充

微件有一个最小尺寸,这可能会受到其内部内容的影响。因此,举例来说,Button将扩展以适应其标题的大小。您可以使用填充来控制这个大小。添加填充将增加内容(例如,Button的标题)和小部件边缘之间的空间。

可以在 XML 中为所有四个边(android:padding)或在每个边的基础上(android:paddingLeft等)设置一次填充。).填充也可以通过setPadding()方法在 Java 中设置。

其中任何一个的值都是一个维度,是度量单位和计数的组合。所以,5px是 5 个像素,10dip是 10 个与密度无关的像素,2mm是 2 毫米。我们将在第二十五章的中更详细地检查维度。

其他有用的属性

除了本章和下一章中介绍的属性外,View最有可能使用的其他属性包括:

  • android:visibility:控制小工具最初是否可见
  • android:nextFocusDownandroid:nextFocusLeftandroid:nextFocusRightandroid:nextFocusUp:如果用户使用 D-pad、轨迹球或类似的定点设备,则控制聚焦顺序
  • android:contentDescription:大致相当于 HTML <img>标签上的alt属性,辅助工具使用它来帮助看不到屏幕的人导航应用

有用的方法

您可以通过setEnabled()切换微件是否启用,并通过isEnabled()查看微件是否启用。一种常见的使用模式是基于CheckBoxRadioButton选择禁用一些小部件。

你可以通过requestFocus()给一个小部件焦点,然后通过isFocused()查看它是否被聚焦。您可以将它与禁用小部件配合使用,以确保在禁用操作完成后,正确的小部件获得焦点。

为了帮助导航构成活动总体视图的小部件和容器树,您可以使用:

  • getParent():查找父部件或容器
  • findViewById():查找具有特定 ID 的子部件
  • getRootView():获取树的根(例如,您通过setContentView()提供给活动的内容)

颜色

Android 小部件中有两种颜色属性。有些,像android:background,采用单一颜色(或图形图像作为背景)。其他的,像TextView上的android:textColor(以及子类),可以带一个ColorStateList,包括通过 Java setter(在这里是setTextColor())。

一个ColorStateList允许你为不同的条件指定不同的颜色。例如,TextView在列表中被选中时可以有一种文本颜色,在未被选中时可以有另一种颜色(第十二章介绍了选择部件)。这通过与TextView相关联的默认ColorStateList来处理。

如果您希望在 Java 代码中更改TextView小部件的颜色,您有两个主要选择:

  • 使用ColorStateList.valueOf(),它返回一个ColorStateList,其中所有状态都被认为具有相同的颜色,您将它作为参数提供给valueOf()方法。这是 Java 中相当于android:textColor的方法,使TextView总是一种特定的颜色,不管情况如何。
  • 通过构造函数或者通过一个 XML drawable 资源,为不同的状态创建一个具有不同值的ColorStateList,这个概念在第二十三章中讨论过。

十、使用容器

容器将一组小部件(可能还有子容器)放入您选择的特定结构中。如果你想要一个左边有标签,右边有字段的表单,你需要一个容器。如果您希望 OK 和 Cancel 按钮位于表单的下面,彼此相邻,并与屏幕右侧对齐,您需要一个容器。仅从纯 XML 的角度来看,如果您有多个小部件(在一个RadioGroup中有超过RadioButton个小部件),您需要一个容器来放置小部件。

大多数 GUI 工具包都有一些布局管理的概念,经常被组织到容器中。例如,在 Java/Swing 中,有像BoxLayout这样的布局管理器和使用它们的容器(例如Box)。一些工具包,如 XUL 和 Flex,严格遵循盒子模型,认为任何想要的布局都可以通过嵌套盒子的正确组合来实现。通过LinearLayout,Android 也提供了一个盒子模型,但是除此之外还支持一系列提供不同布局规则的容器。

在这一章中,我们将看看四个常用的容器,LinearLayout(盒子模型)、RelativeLayout(基于规则的模型)、和TableLayout(网格模型),以及与冰淇淋三明治(ICS)一起发布的全新的GridLayout(无限细线模型)。我们还将看看ScrollView,一个用来帮助实现滚动容器的容器。

线性思维

如前所述,LinearLayout是一个盒子模型——小部件或子容器排成一列或一行,一个接一个。这类似于 Java/Swing 中的FlowLayout,Flex 和 XUL 中的vboxhbox,等等。

Flex 和 XUL 使用方框作为布局的主要单位。如果你愿意,你可以用同样的方式使用LinearLayout,避开其他一些容器。获得您想要的可视化表示主要是确定框应该嵌套在哪里以及这些框应该具有哪些属性,例如它们相对于其他框的对齐方式。

LinearLayout 概念和属性

要配置一个LinearLayout,除了容器的内容之外,您还有五个主要的控制区域:方向、填充模型、重量、重力和填充。

方向

方向表示LinearLayout代表一行还是一列。只需将android:orientation属性添加到 XML 布局中的LinearLayout元素,并将值设置为行的horizontal或列的vertical

通过调用LinearLayout上的setOrientation(),提供HORIZONTALVERTICAL,可以在运行时修改方向。

填充模型

想象一排小部件,比如一对单选按钮。这些小部件根据它们的文本有一个“自然”的大小。它们的总尺寸可能与 Android 设备的屏幕宽度不完全匹配——特别是因为屏幕有各种尺寸。然后,我们面临如何处理剩余空间的问题。

一个LinearLayout中的所有小部件必须提供android:layout_widthandroid:layout_height属性来帮助解决这个问题。这些属性值有三种风格:

  • 您可以提供一个特定的维度,比如125dip,来指示小部件应该占据特定的大小。
  • 您可以提供wrap_content,这意味着小部件应该填满它的自然空间,除非它太大,在这种情况下,Android 可以根据需要使用自动换行来使它适合。
  • 您可以提供fill_parent,这意味着在处理完所有其他小部件之后,小部件应该填满其封闭容器中的所有可用空间。

后两种风格是最常见的,因为它们与屏幕大小无关,允许 Android 调整您的视图以适应可用空间。

注:在 API level 8 (Android 2.2)中,fill_parent更名为match_parent,原因不明。您仍然可以使用fill_parent,因为在可预见的未来它将得到支持。然而,在你只支持 API 等级 8 或更高的时候(例如,在你的清单中的android:minSdkVersion="8",你可能应该切换到match_parent

体重

但是,如果我们有两个应该分割可用空间的小部件,会发生什么呢?例如,假设我们在一个列中有两个多行字段,我们希望在所有其他小部件都被分配了空间之后,它们占用列中的剩余空间。

要实现这一点,除了将android:layout_width(对于行)或android:layout_height(对于列)设置为fill_parent,还必须设置android:layout_weight。此属性指示应该归该小部件所有的可用空间的比例。例如,如果您为一对窗口小部件(如1)设置android:layout_weight为相同的非零值,自由空间将在它们之间平均分配。如果您为一个小部件设置为1,为另一个小部件设置为2,第二个小部件将使用第一个小部件两倍的可用空间。等等。默认情况下,小部件的权重是0

使用权重的另一种模式是基于百分比分配大小。例如,要将此技术用于水平布局,请执行以下操作:

  • 对于布局中的小部件,将所有的android:layout_width值设置为0
  • android:layout_weight值设置为布局中每个小部件所需的百分比大小。
  • 确保所有重量加起来等于100
重力

默认情况下,LinearLayout中的所有内容都是左对齐和上对齐的。因此,如果你通过一个水平的LinearLayout创建一行小部件,这一行将从屏幕左侧开始齐平。如果这不是你想要的,你需要指定一个重力值。在小部件上使用android:layout_gravity(或者在运行时在小部件的 Java 对象上调用setGravity(),你可以告诉小部件及其容器如何将屏幕对齐。

对于一列小部件,常见的重力值分别是左对齐、居中和右对齐小部件的leftcenter_horizontalright

对于一行小部件,默认情况下它们是对齐的,因此它们的文本在基线上对齐(字母似乎“坐在”这条看不见的线上)。您可以指定一个重力值center_vertical,使小部件沿着行的垂直中点居中。

利润

默认情况下,小部件紧密地排列在一起。你可以通过使用边距来改变这一点,这是一个类似于填充的概念,在第九章中有描述。

空白和边距之间的区别只有在背景不透明的小部件上才明显。对于具有透明背景的小部件——像TextView的默认外观——填充和边距具有相似的视觉效果,增加了小部件和相邻小部件之间的空间。对于具有不透明背景的小部件,如Button,填充被认为是在背景内,而边距被认为是在背景外。换句话说,添加填充会增加内容(例如,Button的标题)和边缘之间的空间,而添加边距会增加边缘和相邻小部件之间的空白空间。

可以在 XML 中设置边距,可以基于每条边(如android:layout_marginTop)设置,也可以通过android:layout_margin设置所有边的边距。和填充一样,任何一个值都是一个维度——一个度量单位和一个计数的组合,例如 5 像素的5px

线性布局示例

让我们看一个例子(Containers/Linear),它显示了在 XML 布局文件中和运行时设置的LinearLayout属性。布局如下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"   android:orientation="vertical"   android:layout_width="fill_parent"   android:layout_height="fill_parent"   >   <RadioGroup android:id="@+id/orientation"     android:orientation="horizontal"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:padding="5dip">     <RadioButton       android:id="@+id/horizontal"       android:text="horizontal" />     <RadioButton       android:id="@+id/vertical"       android:text="vertical" />   </RadioGroup>   <RadioGroup android:id="@+id/gravity"     android:orientation="vertical"     android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:padding="5dip">     <RadioButton       android:id="@+id/left"       android:text="left" />     <RadioButton       android:id="@+id/center"       android:text="center" />     <RadioButton       android:id="@+id/right"       android:text="right" />   </RadioGroup> </LinearLayout>

注意,我们有一个LinearLayout包装两个RadioGroup集。RadioGroupLinearLayout的子类,所以我们的例子展示了嵌套的盒子,就好像它们都是LinearLayout容器一样。

顶部的RadioGroup设置了一排RadioButton小部件(android:orientation = "horizontal")。RadioGroup的四周都有填充的5dip,将它与另一个RadioGroup分开,其中dip代表与密度无关的像素(现在把它们想象成普通像素——我们将在本书的后面讨论这种区别)。宽度和高度都设置为wrap_content,所以单选按钮将只占据它们需要的空间。

底部的RadioGroup是三个RadioButton小部件的一列(android:orientation = "vertical")。同样,我们在所有的边上都有5dip的衬垫和自然的高度(android:layout_height = "wrap_content")。但是,我们已经将android:layout_width设置为fill_parent,这意味着单选按钮列占据了整个屏幕宽度。

为了在运行时根据用户输入调整这些设置,我们需要一些 Java 代码:

`package com.commonsware.android.linear;

import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.text.TextWatcher;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.EditText;

public class LinearLayoutDemo extends Activity
  implements RadioGroup.OnCheckedChangeListener {
  RadioGroup orientation;
  RadioGroup gravity;

@Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);

orientation=(RadioGroup)findViewById(R.id.orientation);
    orientation.setOnCheckedChangeListener(this);
    gravity=(RadioGroup)findViewById(R.id.gravity);
    gravity.setOnCheckedChangeListener(this);
  }

public void onCheckedChanged(RadioGroup group, int checkedId) {
    switch (checkedId) {
      case R.id.horizontal:
        orientation.setOrientation(LinearLayout.HORIZONTAL);
        break;

case R.id.vertical:
        orientation.setOrientation(LinearLayout.VERTICAL);
        break;       case R.id.left:
        gravity.setGravity(Gravity.LEFT);
        break;

case R.id.center:
        gravity.setGravity(Gravity.CENTER_HORIZONTAL);
        break;

case R.id.right:
        gravity.setGravity(Gravity.RIGHT);
        break;
    }
  }
}`

onCreate()中,我们查找我们的两个RadioGroup容器,并在每个容器上注册一个监听器,所以当单选按钮改变状态时我们会得到通知(setOnCheckedChangeListener(this))。由于活动实现了OnCheckedChangeListener,活动本身就是监听器。

onCheckedChanged()(监听器的回调)中,我们看到哪个RadioButton发生了状态变化。基于点击的项目,我们调整第一个LinearLayout的方向或者第二个LinearLayout的重心。

Figure 10–1 显示了演示首次在仿真器中启动时的结果。

images

图 10–1。 最初启动的 LinearLayoutDemo 示例应用

如果我们打开“垂直”单选按钮,顶部的RadioGroup会相应调整,如图 10–2 中的所示。

images

图 10–2。 同样的应用,用垂直单选按钮选中

如果我们切换“中心”或“右”单选按钮,底部的RadioGroup会调整以匹配,如图图 10–3 和图 10–4 所示。

images

图 10–3。 同一个应用,选择了垂直和居中单选按钮

images

图 10–4。 同样的应用,用垂直和右单选按钮选中

盒子模型

正如本章前面提到的,一些 GUI 框架把所有东西都当作盒子 Android 称之为LinearLayout容器。例如,在 Flex 和 XUL 中,你创建盒子并指出它们应该有多大,占可用空间的百分比,然后你在盒子中放入小部件。类似的模式也存在于 Android 中的LinearLayout,正如在Containers\LinearPercent项目中所展示的。

这里我们有一个布局 XML 文件,它包含一个垂直的LinearLayout包装三个Button小部件:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"   android:orientation="vertical"   android:layout_width="fill_parent"   android:layout_height="fill_parent"   >   <Button     android:text="Fifty Percent"     android:layout_width="fill_parent"     android:layout_height="0dip"     android:layout_weight="50"   />   <Button     android:text="Thirty Percent"     android:layout_width="fill_parent"     android:layout_height="0dip"     android:layout_weight="30"   />   <Button     android:text="Twenty Percent"     android:layout_width="fill_parent"     android:layout_height="0dip"     android:layout_weight="20"   /> </LinearLayout>

三个小部件中的每一个都将为LinearLayout占据一定比例的垂直空间。由于LinearLayout被设置为填充屏幕,这意味着三个小部件将根据它们请求的百分比来划分屏幕。

要请求百分比,每个Button执行以下操作:

  • 将它的android:layout_height设置为0dip(注意,我们在这里使用高度,因为它是我们正在细分的垂直LinearLayout
  • 将其android:layout_weight设置为所需的百分比(如android:layout_weight="50")

只要权重之和为100,就像在这种情况下一样,您将得到您想要的百分比细分,如图图 10–5 所示。

images

图 10–5。 按百分比在三个按钮之间分割的线形布局

一切事物都是相对的

RelativeLayout顾名思义,根据部件与容器和父容器中其他部件的关系来布局部件。您可以将小部件 X 放在小部件 Y 的左下方,让小部件 Z 的下边缘与容器的下边缘对齐,等等。这让人想起了 James Elliot 的用于 Java/Swing 的RelativeLayout

相对布局概念和属性

为了完成所有这些工作,我们需要在 XML 布局文件中引用其他小部件的方法,以及指示这些小部件的相对位置的方法。

相对于容器的位置

最容易设置的关系是那些使用以下属性将小部件的位置与其容器的位置联系起来的关系:

  • android:layout_alignParentTop:将小工具的顶部与容器的顶部对齐
  • android:layout_alignParentBottom:将小工具的底部与容器的底部对齐
  • android:layout_alignParentLeft:将小工具的左侧与容器的左侧对齐
  • android:layout_alignParentRight:将小工具的右侧与容器的右侧对齐
  • android:layout_centerHorizontal:将小工具水平放置在容器的中心
  • android:layout_centerVertical:将小工具垂直放置在容器的中心
  • android:layout_centerInParent:将小工具水平和垂直放置在容器的中心

所有这些属性都采用一个简单的布尔值(truefalse)。

请注意,在执行这些不同的对齐时,会考虑小部件的填充。对齐基于小部件的整个单元格(其自然空间加上填充的组合)。

属性中的相对符号

RelativeLayout相关的其余属性将容器中的小部件的身份作为一个值。为此:

  1. 将标识符(android:id属性)分配给所有需要寻址的元素。
  2. 使用相同的标识符值引用其他小部件。

第一次出现的id值应包含加号(@+id/widget_a);在布局文件中第二次及以后使用id值时,应省略加号(@id/widget_a)。这使得构建工具可以更好地帮助您捕捉小部件id值中的拼写错误——如果您的小部件id值没有加号,这在编译时会被捕捉到。

例如,如果小部件 A 被标识为@+id/widget_a,小部件 B 可以通过标识符@id/widget_a在它自己的一个属性中引用小部件 A。

相对于其他部件的位置

以下四个属性控制小部件相对于其他小部件的位置:

  • android:layout_above:表示小部件应该放在属性中引用的小部件上面
  • android:layout_below:表示小部件应该放在属性中引用的小部件下面
  • android:layout_toLeftOf:表示小部件应该放在属性中引用的小部件的左边
  • android:layout_toRightOf:表示小部件应该放在属性中引用的小部件的右边

除了这四个属性之外,还可以使用另外五个属性来控制一个小部件相对于另一个小部件的对齐:

  • android:layout_alignTop:表示小工具的上边缘应该与属性中引用的小工具的上边缘对齐
  • android:layout_alignBottom:表示小工具的下边缘应该与属性中引用的小工具的下边缘对齐
  • android:layout_alignLeft:表示小工具的左边缘应该与属性中引用的小工具的左边缘对齐
  • android:layout_alignRight:表示小工具的右边缘应该与属性中引用的小工具的右边缘对齐
  • android:layout_alignBaseline:表示两个部件的基线应该对齐(基线是文本所在的不可见线)

属性对于对齐标签和字段很有用,这样文本看起来很自然。因为字段周围有一个框,而标签没有,所以android:layout_alignTop会将字段框的上边缘与标签的上边缘对齐,使标签的文本在屏幕上比输入到字段中的文本高。

因此,如果我们希望小部件 B 位于小部件 A 的右侧,在小部件 B 的 XML 元素中,我们需要包含android:layout_toRightOf = "@id/widget_a"(假设@id/widget_a是小部件 A 的标识)。

评估顺序

Android 以前使用单路处理RelativeLayout定义的规则。这意味着只有在 XML 中声明了小部件之后,才能引用它(例如,通过android:layout_above)。这使得定义一些布局有点复杂。从 Android 1.6 开始,Android 使用两次传递来处理规则,所以现在您可以安全地向前引用尚未定义的小部件。

RelativeLayout 示例

记住所有这些,让我们检查一个带有一个字段、一个标签和一对标记为 OK 和 Cancel 的按钮的典型表单。下面是来自Containers/Relative示例项目的 XML 布局:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout   xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="fill_parent"   android:layout_height="wrap_content">   <TextView android:id="@+id/label"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:text="URL:"     android:layout_alignBaseline="@+id/entry"     android:layout_alignParentLeft="true"/>   <EditText     android:id="@id/entry"     android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:layout_toRightOf="@id/label"     android:layout_alignParentTop="true"/>   <Button     android:id="@+id/ok"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_below="@id/entry"     android:layout_alignRight="@id/entry"     android:text="OK" />   <Button     android:id="@+id/cancel"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_toLeftOf="@id/ok"     android:layout_alignTop="@id/ok"     android:text="Cancel" /> </RelativeLayout>

首先,我们打开RelativeLayout。在这种情况下,我们希望使用屏幕的整个宽度(android:layout_width = "fill_parent")和我们需要的高度(android:layout_height = "wrap_content")。

接下来,我们将标签定义为一个TextView。我们指出,我们希望它的左边缘与RelativeLayout ( android:layout_alignParentLeft="true")的左边缘对齐,它的基线与尚未定义的EditText的基线对齐。由于EditText尚未声明,我们在 ID ( android:layout_alignBaseline="@+id/entry")中使用+符号。

之后,我们添加字段作为EditText。我们希望该字段位于标签的右侧,使该字段与RelativeLayout的顶部对齐,并使该字段占据布局中该“行”的剩余部分。这些要求分别由以下三个属性处理:

  • android:layout_toRightOf = "@id/label"
  • android:layout_alignParentTop = "true"
  • android:layout_width = "fill_parent"

然后,确定按钮被设置在字段的下方(android:layout_below = "@id/entry"),并使其右侧与字段的右侧对齐(android:layout_alignRight = "@id/entry")。取消按钮设置在确定按钮(android:layout_toLeft = "@id/ok")的左侧,其顶部与确定按钮(android:layout_alignTop = "@id/ok")对齐。

在不改变自动生成的 Java 代码的情况下,仿真器给出了如图 Figure 10–6 所示的结果。

images

图 10–6。RelativeLayoutDemo 示例应用

重叠

RelativeLayout还有一个LinearLayout没有的特性——让小部件相互重叠的能力。一个RelativeLayout的后面的子元素比前面的子元素“在 Z 轴上更高”,这意味着如果后面的子元素被设置为占据布局中的相同空间,那么它们将与前面的子元素重叠。

这一点用一个例子会更清楚。这是一个布局,从Containers/RelativeOverlap开始,一个RelativeLayout持有两个Button部件:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout   xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="fill_parent"   android:layout_height="fill_parent"   >   <Button     android:text="I AM BIG"     android:textSize="120dip"     android:textStyle="bold"     android:layout_width="fill_parent"     android:layout_height="fill_parent"   />   <Button     android:text="I am small"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_centerInParent="true"   /> </RelativeLayout>

第一个Button设置为满屏。第二个Button被设置为在父对象内部居中,并且只占据标题所需的空间。因此,第二个Button看起来会浮在第一个Button上方,如图图 10–7 所示。

images

图 10–7。 相对重叠样本应用

两个Button小部件仍然可以被点击,尽管点击较小的Button不会同时点击较大的Button。在这种重叠的情况下,您的点击将由顶部的小部件处理。

白板

如果你喜欢 HTML 表格、电子表格网格和类似的布局选项,你会喜欢 Android 的TableLayout,它允许你根据自己的要求在网格中放置你的小部件。您可以控制行数和列数,哪些列可以收缩或拉伸以容纳它们的内容,等等。

TableLayoutTableRow协同工作。TableLayout控制容器的整体行为,小部件本身被放入一个或多个TableRow容器中,网格中每行一个。

表格布局概念和属性

为了让您的表格布局如您所愿地工作,您需要理解小部件如何处理行和列,以及如何处理位于行之外的小部件。

将单元格成行排列

作为开发人员,您通过将小部件作为一个TableRow的子部件放入整个TableLayout中来声明行。因此,您可以直接控制表中显示的行数。

列数由 Android 决定;您以间接的方式控制列的数量。首先,在最长的一行中,每个小部件至少有一列。因此,如果有三行——一行有两个小部件,一行有三个小部件,一行有四个小部件——那么至少有四列。但是,通过包含android:layout_span属性,您可以让一个小部件占用多列,以指示小部件跨越的列数。这类似于在 HTML 的表格单元格中发现的colspan属性。在这个 XML 布局片段中,该字段跨越三列:

<TableRow>   <TextView android:text="URL:" />   <EditText     android:id="@+id/entry"     android:layout_span="3"/> </TableRow>

通常,小部件被放在第一个可用的列中。在前面的片段中,标签将放在第一列中(列0,因为列是从0开始计数的),字段将放入一个由三列组成的跨区集合中(列13)。但是,您可以通过android:layout_column属性将小部件放入不同的列,指定小部件所属的基于0的列:

<TableRow>   <Button     android:id="@+id/cancel"     android:layout_column="2"     android:text="Cancel" />   <Button android:id="@+id/ok" android:text="OK" /> </TableRow>

在前面的 XML 布局片段中,Cancel 按钮位于第三列(列2)。然后,OK 按钮进入下一个可用列,即第四列。

TableLayout 的非行子级

通常,TableLayout只包含TableRow元素作为直接子元素。但是,也可以在行之间放置其他小部件。对于那些小部件,TableLayout的行为有点像垂直方向的LinearLayout。窗口小部件自动将它们的宽度设置为fill_parent,因此它们将填充与最长一行相同的空间。

一种模式是使用普通的View作为分隔符。例如,您可以使用<View android:layout_height = "2dip" android:background = "#0000FF" />作为横跨表格宽度的两个像素高的蓝色条。

拉伸、收缩和折叠

默认情况下,每列将根据该列中最宽的小部件的自然大小来调整大小(将跨区列考虑在内)。但是,有时这并不太好,您需要对列行为进行更多的控制。

您可以在TableLayout上放置一个android:stretchColumns属性。该值应该是单个列号(同样基于0)或逗号分隔的列号列表。这些列将被拉伸以占据行上的任何可用空间。如果您的内容比可用空间窄,这很有帮助。

相反,您可以在TableLayout上放置一个android:shrinkColumns属性。同样,这应该是单个列号或逗号分隔的列号列表。此属性中列出的列将尝试对其内容进行自动换行,以减少列的有效宽度—默认情况下,小部件不进行自动换行。如果您的列可能包含冗长的内容,这将有助于将一些列从屏幕右侧推开。

您还可以利用TableLayout上的android:collapseColumns属性,同样使用列号或逗号分隔的列号列表。这些列将开始折叠,这意味着它们将是表信息的一部分,但不可见。通过编程,您可以通过调用TableLayout上的setColumnCollapsed()来折叠和取消折叠列。您可以使用它来允许用户控制哪些列对他们很重要,应该显示哪些列,哪些列不太重要,可以隐藏。

您还可以通过setColumnStretchable()setColumnShrinkable()控制运行时的拉伸和收缩。

表格布局示例

前面显示的 XML 布局片段组合在一起,给了我们一个为RelativeLayout创建的表单的TableLayout呈现,在标签/字段和两个按钮之间添加了一条分隔线(在Containers/Table演示中可以找到):

<?xml version="1.0" encoding="utf-8"?> <TableLayout   xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="fill_parent"   android:layout_height="fill_parent"   android:stretchColumns="1">   <TableRow>     <TextView         android:text="URL:" />     <EditText android:id="@+id/entry"       android:layout_span="3"/>   </TableRow>   <View     android:layout_height="2dip"     android:background="#0000FF" />   <TableRow>     <Button android:id="@+id/cancel"       android:layout_column="2"       android:text="Cancel" />     <Button android:id="@+id/ok"       android:text="OK" />   </TableRow> </TableLayout>

当针对生成的 Java 代码进行编译并在模拟器上运行时,我们会得到如图 Figure 10–8 所示的结果。

images

图 10–8。TableLayoutDemo 示例应用

卷轴

手机屏幕往往很小,这就需要开发者使用一些技巧,在有限的可用空间里呈现大量信息。做到这一点的一个技巧是使用滚动,这样一次只能看到部分信息,其余的可以通过上下滚动来查看。

ScrollView是一个提供内容滚动的容器。你可以选择一个对于某些屏幕来说可能太大的布局,将其包装在一个ScrollView中,并且仍然使用你的现有的布局逻辑。用户一次只能看到你布局的一部分,通过滚动可以看到其余部分。

例如,这里有一个在 XML 布局文件中使用的ScrollView(来自Containers/Scroll演示):

<?xml version="1.0" encoding="utf-8"?> <ScrollView   xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="fill_parent"   android:layout_height="wrap_content">   <TableLayout     android:layout_width="fill_parent"     android:layout_height="fill_parent"     android:stretchColumns="0">     <TableRow>       <View         android:layout_height="80dip"         android:background="#000000"/>       <TextView android:text="#000000"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>     <TableRow>       <View         android:layout_height="80dip"         android:background="#440000" />       <TextView android:text="#440000"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>     <TableRow>       <View         android:layout_height="80dip"         android:background="#884400" />     <TextView android:text="#884400"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>     <TableRow>       <View         android:layout_height="80dip"         android:background="#aa8844" />       <TextView android:text="#aa8844"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>     <TableRow>       <View         android:layout_height="80dip"         android:background="#ffaa88" />       <TextView android:text="#ffaa88"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>     <TableRow>       <View         android:layout_height="80dip"         android:background="#ffffaa" />       <TextView android:text="#ffffaa"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>     <TableRow>       <View         android:layout_height="80dip"         android:background="#ffffff" />     <TextView android:text="#ffffff"         android:paddingLeft="4dip"         android:layout_gravity="center_vertical" />     </TableRow>   </TableLayout> </ScrollView>

如果没有ScrollView,表格将占用至少 560 个像素(基于View声明,7 行每行 80 个像素)。一些设备的屏幕能够显示那么多信息,如平板电脑,但许多设备的屏幕会更小。ScrollView让我们保持表格不变,但是一次只显示一部分。

在普通的 Android 模拟器上,当第一次查看活动时,会出现如图 Figure 10–9 所示的内容。

images

图 10–9。ScrollViewDemo 示例应用

请注意,只有五行和第六行的一部分是可见的。通过按下 D-pad 上的向上/向下按钮,您可以上下滚动来查看剩余的行。还要注意滚动条是如何剪切内容的右侧的——一定要在那一侧放置一些填充,或者确保您自己的内容不会以这种方式被剪切。

Android 1.5 推出了HorizontalScrollView,工作方式类似于ScrollView,但是是横向的。这对于可能太宽而不是太高的表单非常有用。注意ScrollViewHorizontalScrollView都不会给你双向滚动,所以你必须选择垂直或水平。

另外,请注意,您不能将可滚动项目放入ScrollView中。例如,一个ListView小部件——我们将在接下来的章节中看到——已经知道如何滚动。如果你把一个ListView放在一个ScrollView里,它不会工作得很好。

带他们去电网

A TableLayout吸引了那些渴望 HTML 或 CSS 风格的像素精度(或缺乏像素精度)的人。通常你会发现你知道你希望你的布局中的元素相对于其他元素如何出现,或者在指定你的布局中的小部件的位置时需要更多的技巧。进入全新的GridLayout,与 Android 4 冰淇淋三明治(ICS)一同发布。

GridLayout是一种布局,它将子元素放在一个由无限细线条组成的网格上,这些线条将区域分隔成单元格。GridLayout精细控制的关键是单元格的数量,或者更准确地说,用于描述单元格的网格线没有限制或阈值——您可以使用rowSpeccolumnSpec属性指定GridLayout应该有多少网格线。这意味着您可以创建一个布局,模拟一个具有几个单元格(即行和列)的简单表格,或者,对于那些需要非常精确的精度的苛刻情况,您可以疯狂地指定数千甚至数百万个单元格。

注意:为了补充GridLayout对 UI 世界的不同看法,它用android:layout_gravity代替了android:layout_weight

例如,这里有一个在 XML 布局文件中使用的GridLayout(来自Containers/Grid演示):

`
<GridLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"

 

Checklist

   
    
            
     

Total: 0         Remaining: 0

  
        
  • Loading…
  •   
 
 
       
`

然而,对于离线应用来说,关键是我们的html元素的manifest属性:

<html lang="en" manifest="checklist.manifest">

这里,我们指定了清单文件的相对路径,指明了离线缓存该应用各个部分的规则。

检查清单

因为清单是所有乐趣所在,所以让我们来看看Checklist的清单:

`CACHE MANIFEST

version 54

styles.css
main.js
splashscreen.png`

html 5 清单格式非常简单。它以一个CACHE MANIFEST行开始,后面是一个应该被缓存的文件列表(技术上来说,是相对 URL)。它还支持注释,注释是以#开头的行。

清单还可以有一个NETWORK:行,后面跟着不应该被缓存的相对 URL。类似地,清单可以有一个FALLBACK:行,后跟成对的相对 URL:试图从网络获取的 URL,后跟网络不可用时使用的缓存资源的 URL。

原则上,清单应该为应用运行所需的所有内容请求缓存,尽管请求缓存的页面(在本例中为index.html)也会被缓存。

网络存储

缓存 HTML5 应用的素材以供离线使用固然很好,但这本身会有相当大的局限性。在离线情况下,应用不能使用 AJAX 技术与 web 服务进行交互。因此,如果应用要能够存储信息,它需要在浏览器本身上这样做。

从 cookies 到 Google Gears 的一切都被用来解决这个问题,后一种工具为 HTML5 应用开辟了一条道路,现在被不同地称为 Web 存储或 DOM 存储。一个 HTML5 应用可以在客户端上持久存储数据,在客户端施加的限制内。这与离线素材缓存相结合,意味着 HTML5 应用在缺少互联网连接时,或者对于存储在“云中”没有意义的数据,可以提供更多的价值

注意:从技术上讲,Web 存储不是 HTML5 的一部分,而是一个相关的规范。然而,在日常对话中,它往往会与 HTML5 混为一谈。

这是什么意思?

在支持 Web 存储的浏览器上,您的 JavaScript 代码将可以访问一个代表应用数据的localStorage对象。更准确地说,每个(即域)在浏览器上将有一个不同的localStorage对象。

localStorage对象是一个关联数组,这意味着您可以通过数字索引或基于字符串的键来使用它。值通常是字符串。您可以使用localStorage执行以下操作:

  • 通过length()找出数组中有多少条目
  • 通过getItem()setItem()按键获取和设置项目
  • 通过key()获取数字索引的密钥
  • 通过removeItem()删除个别条目或通过clear()删除所有项目

这意味着您不具备 SQL 数据库的全部丰富性,就像您在原生 Android 应用中使用 SQLite 一样。但是,对于许多应用来说,这已经足够了。

你如何使用它?

Checklist将列表项作为键存储在关联数组中,常规项的值为0,删除项的值为1。在这里,我们看到了将一个新项目放入清单的代码:

try {   localStorage.**setItem**(strippedString, data); } catch (e) {   if (e == QUOTA_EXCEEDED_ERR) {     **alert**('Quota exceeded!');   } }

下面的代码将这些项目从存储中取出,放入一个数组中进行排序,然后在网页上显示为 DOM 元素:

`/get all items from localStorage and push them one by one into an array./
for (i = 0; i <= listlength; i++) {

var item = localStorage.key(i);
  myArray.push(item);
}

/sort the array into alphabetical order./
myArray.sort();`

当用户选中某个项目旁边的复选框时,存储会更新,以持续切换选中的设置:

`/toggle the check flag./
if (target.previousSibling.checked) {
  data = 0;
}
else {
  data = 1;
}
/save item in localStorage./
try {
  localStorage.setItem(name, data);
} catch (e) {

if (e == QUOTA_EXCEEDED_ERR) {
    alert('Quota exceeded!');
  }
}`

Checklist也有从存储器中删除项目的代码,可以是所有被标记为选中的项目,也可以是所有项目。以下是删除所有选中项目的代码:

`/remove every item from localStorage that has the data flag checked./
while (i <= localStorage.length-1) {

var key = localStorage.key(i);
  if (localStorage.getItem(key) === '1') {
    localStorage.removeItem(key);
  }
  else { i++; }
}`

下面是删除所有项目的代码:

`/deletes all items in the list./
deleteAll: function() {

/ask for user confirmation./
  var answer = confirm("Delete all items?");

/if yes./
  if (answer) {

/remove all items from localStorage./
    localStorage.clear();
    /update view./
    checklistApp.getAllItems();
 }
 /clear up./
 delete checklistApp.deleteAll;
},`

Web SQL 数据库

Android 的内置浏览器还支持 Web SQL 数据库选项,这使您能够从 JavaScript 使用 SQLite 风格的数据库。这比基本的 Web 存储提供了更多的功能,尽管代价很复杂。它也不是现行标准的一部分——致力于该标准的网络超文本应用技术工作组(WHATWG)已经暂时将其搁置一旁。

您可以考虑评估 Lawnchair,它是一个 JavaScript API,允许您存储任意的 JavaScript 对象符号(JSON)编码的对象。它将使用任何可用的存储选项,因此将帮助您处理跨平台的多样性。

即将投入生产

创建一个小的测试应用并不需要什么神奇的东西。不过,假设您对让其他人使用您的应用感兴趣——可能是许多其他人。经典的基于 Java 的 Android 应用必须处理测试,对应用进行数字签名以供生产,通过各种渠道(如 Android Market)分发应用,并通过各种方式为应用提供更新。这些问题不会神奇地消失,因为 HTML5 被用作应用环境。然而,HTML5 确实大大改变了 Java 开发人员必须做的事情。

测试

由于 HTML5 可以在其他浏览器中工作,测试您的业务逻辑可以很容易地利用任何数量的 HTML 和 JavaScript 测试工具,从 Selenium 到 QUnit 再到 Jasmine。

为了在 Android 上进行测试——以确保没有与 Android 浏览器实现相关的问题——您可以使用 Selenium 的 Android 驱动程序或远程控制模式。

签署和分发

与原生 Android 应用不同,您无需担心 HTML5 应用的签名问题。这样做的缺点是不支持通过 Android Market 分发 HTML5 应用,Android Market 目前只支持原生 Android 应用。用户将不得不通过某种方式找到你的应用,在浏览器中访问它,给页面添加书签,可能还会创建一个主屏幕快捷方式。

更新

与默认情况下必须手动更新的原生 Android 应用不同,HTML5 应用将在用户下次在连接到互联网的情况下运行该应用时透明地更新。离线缓存协议将在回退到缓存的副本之前检查 web 服务器上文件的新版本。因此,除了发布最新的 web 应用资源,您没有什么可做的了。

您可能会遇到的问题

不幸的是,没有什么是完美的。虽然 HTML5 可能会使许多事情变得更容易,但它不是解决所有 Android 开发问题的灵丹妙药。

这一节涵盖了一些潜在的关注领域,当你使用 Android 的 HTML5 应用时,你将会考虑到这些领域。

安卓设备版本

并非所有 Android 设备都支持 html 5——只有运行 Android 2.x 或更高版本的设备才支持。因此,理想情况下,你应该在你的网络服务器上做一点用户代理嗅探,把老 Android 用户重定向到其他解释他们设备局限性的页面。

以下是运行 Android 2.1 的 Google/HTC Nexus One 设备的用户代理字符串:

Mozilla/5.0 (Linux; U; Android 2.1-update1; en-us; Nexus One Build/ERE27)![images](https://gitee.com/OpenDocCN/vkdoc-android-pt2-zh/raw/master/docs/begin-andr4/img/U002.jpg)  AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17

如您所见,它的格式类似于典型的现代用户代理字符串,这意味着它相当混乱。它确实表明它正在运行Android 2.1-update1

最终,有人会为不同的设备型号创建一个用户代理字符串数据库,从那里我们可以导出适当的正则表达式或类似的算法来确定给定的设备是否可以支持 HTML5 应用。

屏幕尺寸和密度

HTML5 应用可以在各种屏幕尺寸上运行,从 QVGA Android 设备到 1080p 液晶显示器等等。同样,屏幕密度可能会有很大差异,因此尽管智能手机上的 48×48 像素图像可能是合适的尺寸,但对于 1080p 电视来说可能太大了,更不用说 24 英寸的液晶桌面显示器了。

除了增加低端屏幕尺寸的可能选项,这些都不是 Android 独有的。你需要决定如何最好地设计你的 HTML 和 CSS,以适应不同的大小和密度,即使 Android 不在其中。

有限的平台集成

HTML5 虽然提供了比以往更多的平台集成,但并没有涵盖 Android 应用可能希望能够做到的一切。例如,普通的 HTML5 应用不能执行以下操作:

  • 启动另一个应用
  • 使用联系人数据库
  • 用股票浏览器发出通知(注意,Android 版 Firefox 有一个解决方案)
  • 一定要真正在后台工作(尽管网络工作者有一天可能会减轻这一点)
  • 与蓝牙设备互动
  • 录制音频或视频
  • 使用标准的 Android 偏好系统
  • 使用语音识别或文本到语音转换

当然,许多应用不需要这些功能。其他应用环境,如 PhoneGap(在第四十六章中有所介绍),将很可能演变成 Android 的“HTML5 Plus”。这样,您可以创建一个适用于所有设备的普通应用和一个独立的增强 Android 应用,该应用利用了更好的平台集成,但代价是额外的编程量。

性能和电池

一段时间以来,人们一直担心基于 HTML 的用户界面与原生 Android 用户界面相比,在处理器时间、内存和电池方面效率低下。例如,避免在 Android 主屏幕上使用 BONDI 风格的 web 小部件的原因之一就是性能影响。

当然,设计消耗电池的 HTML5 应用是可能的。例如,如果每秒钟都有一大块 JavaScript 代码无限期地运行,这将消耗相当多的处理器时间。然而,除此之外,一个普通的应用似乎不太可能被如此频繁地使用,以至于严重影响电池寿命。当然,在这方面还需要做更多的测试。

此外,HTML5 应用的启动速度可能会比其他应用慢一点,特别是如果浏览器有一段时间没有使用,或者网络连接存在,但服务器的带宽很小。

外观和感觉

HTML5 应用当然可以看起来非常光滑和专业——毕竟,它们是用 web 技术构建的,web 应用可以看起来非常光滑和专业。

然而,HTML5 应用不一定看起来像标准的 Android 应用,至少最初不是。毫无疑问,一些有事业心的开发人员会创建一些可重用的 CSS、JavaScript 和图像,例如,镜像一个 Android 原生Spinner小部件(一种下拉控件)。类似地,HTML5 应用往往缺少选项菜单、通知或其他原生 Android 应用可能会使用的 UI 功能。

这不一定是坏事。考虑到创建一个看起来非常光滑的 Android 应用的难度,HTML5 应用可能会比 Android 应用看起来更好。毕竟,擅长创建漂亮的网络应用的人比擅长创建漂亮的安卓应用的人多得多。

然而,一些用户可能会抱怨外观和感觉的差距,只是因为它是不同的。

分布

HTML5 应用可以很容易地添加到用户的设备上——浏览、添加书签和添加主屏幕的快捷方式。然而,HTML5 应用不会出现在 Android 市场中,因此受过培训来查看可用应用市场的用户不会找到 HTML5 应用,即使这些应用可能比他们的原生应用更好。

可以想象,有一天,Android 市场将支持 HTML5 应用。还可以想象,有一天,Android 用户将倾向于通过搜索 Android Market 之外的其他方式找到他们的应用,并能够通过这种方式获得他们的 HTML5 应用。然而,在其中一个成为现实之前,HTML5 应用可能不像它们的本地对等物那样“容易被发现”。

浏览器更改贴吧冰淇淋三明治

谷歌已经宣布,他们的 Android 浏览器的未来方向将是 Chrome(或 Android 的 Chrome 衍生版本)。对于 Android 4.0 版本,没有迹象表明这种变化是通过一个渐进的过程慢慢收敛到一个类似 Chrome 的浏览器上,还是通过未来版本中的一个大变革。当这种变化真的发生时,使用 HTML5 的一些细微差别,甚至一些支持的特性,都可能会改变。这将影响所有使用 HTML5 应用的用户,不包括使用其他浏览器的用户。

HTML5 和其他安卓浏览器

虽然内置的 Android 浏览器将是许多 Android 用户的选择,但也有其他浏览器可用。以下是一些更著名的替代方案在 HTML5 支持方面的情况:

  • 火狐移动版:经过一段时间后仍处于测试阶段,支持离线缓存和本地存储。但是,此时无法正确运行Checklist样本。
  • Opera Mobile :稳步改进的产品,最近增加了触觉反馈支持等功能。不支持本地存储,渲染Checklist没有意义。它也不支持离线缓存。
  • 海豚浏览器 HD 4.0 :支持离线缓存和本地存储。虽然在Checklist中有轻微的渲染问题——可能与 CSS 有关——但是应用在其他方面运行良好,即使没有互联网连接。

HTML5:基线

对于传统的应用开发来说,HTML5 可能会变得相当流行。它为 web 开发者提供了一条通向桌面的道路。它可能是谷歌 Chrome OS 的唯一选择。而且,随着对流行移动设备(包括 Android)的支持不断改善,开发者肯定会被另一轮“一次编写,随处运行”的承诺所吸引。

随着时间的推移,HTML5 很有可能成为 Android 应用开发的第二选择,仅次于编写到 Android SDK 的传统 Java 应用。这将使 HTML5 成为比较可选 Android 开发选项的基准——这些选项不仅会与使用 SDK 进行比较,还会与使用 HTML5 进行比较。

四十六、PhoneGap

PhoneGap 可能是 Android 最初的替代应用框架,于 2009 年初出现。PhoneGap ( [www.phonegap.com/](http://www.phonegap.com/))是开源的,由 Nitobi 支持,Nitobi 传统上提供开源和商业产品的混合,以及咨询和培训服务。截至 2011 年 10 月,Nitobi 已同意被 Adobe 收购。为了确保 PhoneGap 代码的持久性,Nitobi 将 PhoneGap 代码库提交给了 Apache 软件基金会,该项目现在被命名为 Apache Callback。大多数人仍然知道这个项目的原始名称,许多文档仍然提到 PhoneGap,所以我们将坚持使用传统的名称。

什么是 PhoneGap?

PhoneGap 是一个围绕 HTML5 构建的平台,使您能够从一个代码库开发应用,并将它们部署到多个平台。使用 PhoneGap 只需遵循以下步骤:

  1. 使用 HTML5 和 JavaScript 等 web 标准语言构建您的应用。
  2. 用 PhoneGap 包装您的应用,以获得对本机 API 的访问。
  3. 将您的应用部署到多个平台。

[www.phonegap.com/about](http://www.phonegap.com/about)阅读更多关于 PhoneGap 及其工作原理的信息。

你在写什么?

PhoneGap 应用由 HTML、CSS 和 JavaScript 组成,与移动网站或 HTML5 应用没有什么不同,只是在 PhoneGap 中,web 素材与应用打包在一起,而不是即时下载。

因此,预装的 PhoneGap 应用可能包含相对较大的素材,如复杂的 JavaScript 库,可能太慢而无法通过较慢的 EDGE 连接下载。然而,PhoneGap 仍然受到移动设备速度以及 WebKit 浏览器加载和处理这些素材的速度的限制。

此外,WebKit for mobile 的开发不同于 WebKit for desktops 的开发,特别是在触摸和鼠标事件方面。在可行的情况下,您可能希望使用 JavaScript 框架的移动层进行开发(例如,jQTouch 与普通 jQuery)。

你有什么特点?

与 HTML5 应用一样,PhoneGap 为您提供了 web 浏览器的基本功能,包括 AJAX 支持。除此之外,PhoneGap 还增加了许多 JavaScript APIs,让你可以了解 Android 平台的底层特性。在撰写本文时,包括以下内容:

  • 加速度计接入,用于检测设备的移动
  • 录音
  • 相机接入,用于拍摄静态照片
  • 指南针访问,用于定向活动
  • 联系人访问,用于使用内置联系人提供程序
  • 数据库访问,既可以访问您创建的数据库(SQLite),也可以访问 Android 内置的其他数据库(例如,联系人)
  • 文件系统访问,例如对 SD 卡或其他外部存储器的访问
  • 地理定位,用于确定设备的位置
  • 通知服务,包括警报和声音效果
  • 存储,设备上和 SD 卡
  • 振动,用于摇动电话(例如,力反馈)

因为其中一些是 HTML5 规范的一部分(例如,地理定位),所以您可以选择 API。此外,这个列表会随着时间的推移而变化,但你会看到前面的列表是大多数当代 Android 设备上的本机功能的一个非常全面的列表。

app 是什么样子的?

PhoneGap 应用看起来像网页,比原生 Android 应用更像,如图图 46–1 所示,这是 PhoneGap 附带的示例应用的截图。您可以使用 CSS 和图像在某种程度上模仿 Android 的外观和感觉,但仅限于那些可以在 Android 和 HTML 中创建的小部件。例如,Android Spinner小部件,类似于一个下拉列表,可能很难在 HTML 中模仿。

images

图 46–1。PhoneGap 附带的示例应用

分销是如何运作的?

分发 PhoneGap 应用与分发任何其他标准 Android 应用非常相似,只是有一些额外的选项。使用独立的 PhoneGap,在测试之后,您使用 Android 构建工具从 PhoneGap 为您生成的 Android 项目中创建一个标准的 APK 文件。这个项目将包含 Java、XML 和其他必要的部分,来包装 HTML、CSS 和 JavaScript,以构成您的应用。然后,您对应用进行数字签名,并将其上传到 Android Market 或您希望使用的任何其他分发机制。Nitobi 还提供了一个托管构建服务,叫做 PhoneGap Build,我们将在本章稍后讨论。

其他平台呢?

PhoneGap 不只是针对 Android。你可以为 iPhone,Blackberry,Symbian,微软 Windows Phone,Samsung Bada 和 Palm 的 WebOS 创建 PhoneGap 应用。至少在理论上,您可以使用 HTML、CSS、JavaScript 和 PhoneGap JavaScript APIs 创建一个应用,并让它在许多设备上运行。

有几个限制会阻碍你实现这个目标:

  • PhoneGap 在所有这些平台上使用的网络浏览组件并不相同。根据 WebKit 集成到给定设备的固件中时可用的版本,即使使用 WebKit 的多个平台也会有不同的 WebKit 版本。因此,您需要进行测试,以确保您的 CSS 能够在尽可能多的设备上正常运行。
  • 由于各种因素,并非所有 PhoneGap JavaScript APIs 都可以在所有设备上使用(例如,没有在平台的本机 API 中公开,缺乏将功能提升到 PhoneGap APIs 中的工程时间,等等)。).PhoneGap wiki 可以让你知道在不同的设备上什么有效,什么无效。您将希望限制您的功能使用以匹配您想要的平台,或者限制您的平台以匹配您想要的功能。

使用 PhoneGap

现在,让我们来看看使用 PhoneGap 的更多技巧。在撰写本文时,PhoneGap 的安装和使用通常需要一名基于 Java 的 Android 开发专家。您需要安装一整套工具,手动编辑配置文件,等等。如果您想做到所有这些,PhoneGap 网站上提供了文档,我们将在下面简单介绍一下。如果你正在读这一章,你很有可能会跳过所有这些。同样,您可以使用 PhoneGap 构建服务([build.phonegap.com/](http://build.phonegap.com/)),我们将很快介绍它。

安装

您可以从 PhoneGap 网站下载最新的 PhoneGap 工具,作为一个 ZIP 存档,它可以重定向到 Apache Software Foundation 的 Apache Callback incubator 存储库。只要对您的开发机器和平台有意义,就将这些工具解包。对于 Android 开发,这就是你需要的所有 PhoneGap 特有的安装。但是,您将需要 Android SDK 和相关工具(例如,如果您希望使用 Eclipse,可以使用 Eclipse)来设置项目。

创建并安装您的项目

PhoneGap Android 项目本质上是一个常规的 Android 项目,您可以按照本书前面概述的说明来创建它。要将标准生成的“Hello,World”应用转换为 PhoneGap 项目,您需要执行以下操作:

  1. 从解压 PhoneGap ZIP 文件的位置的Android/目录,将 PhoneGap JAR 文件复制到项目的libs/目录。如果您正在使用 Eclipse,您还需要将它添加到您的构建路径中。
  2. 在您的项目中创建一个assets/www/目录。然后,从您解压 PhoneGap ZIP 文件的位置的Android/目录中复制 PhoneGap JS 文件。
  3. 将标准的“Hello,World”活动调整为继承自DroidGap而不是Activity。这就需要你导入com.phonegap.DroidGap
  4. 在活动的onCreate()方法中,用super.loadUrl("file:///android_asset/www/index.html");替换setContentView()
  5. 在您的清单中,添加 PhoneGap 请求的所有权限,这些权限将在本章后面列出。
  6. 同样在您的清单中,根据您想要测试和支持的屏幕尺寸添加一个合适的<supports-screens>元素。
  7. 同样在您的清单中,将android:configChanges="orientation|keyboardHidden"添加到您的<activity>元素中,因为DroidGap处理与方向相关的配置更改。

此时,您可以在项目中创建一个assets/www/index.html文件,并开始使用 HTML、CSS 和 JavaScript 创建 PhoneGap 应用。您需要包含对 PhoneGap JavaScript 文件的引用(例如,<script type="text/javascript" charset="utf-8" src="phonegap.1.2.0.js" />)。当您想要测试应用时,您可以像任何其他 Android 应用一样构建和安装它(例如,如果您使用命令行构建过程,可以使用ant clean install)。

对于有 Android SDK 开发经验的人来说,设置这个并不是一个很大的挑战。

PhoneGap 构建

PhoneGap Build 是一种工具即服务(TaaS)托管的创建 PhoneGap 项目的方法。所有 Android 构建过程都由 PhoneGap 提供的服务器为您处理。您只需专注于创建您认为合适的 HTML、CSS 和 JavaScript。

当您登录 PhoneGap Build 时,首先会提示您创建您的初始项目,提供名称和 web 素材以进入应用,如 Figure 46–2 所示。

images

图 46–2。 在 PhoneGap Build 中创建您的第一个项目

你以后可以通过一个新的应用按钮添加新的项目,这给了你同样的选项。

您提供素材的选择是上传一个包含所有素材的 ZIP 文件,或者指定一个公共 GitHub 存储库的 URL,PhoneGap Build 可以从这个存储库中提取素材。如果您习惯于使用 Git 进行版本控制,并且您的项目是开源的(因此有一个公共存储库),后一种方法会更方便。

点击上传按钮后,PhoneGap Build server 立即开始为 Android、Blackberry、Symbian 和其他支持的平台或您选择的平台构建您的应用,如图 Figure 46–3 所示。

images

图 46–3。 在 PhoneGap Build 中构建您的第一个项目

每个目标都有自己的文件扩展名(例如,Android 的apk)。单击该链接将允许您下载该文件。或者,点击项目名称,您将获得快速响应(QR)代码,以便直接下载到您的测试设备,如图 Figure 46–4 所示。

images

图 46–4。 你项目的二维码在 PhoneGap 建立

该页面还提供了从 GitHub 资源库更新应用的链接(如果您选择了该选项)。或者,您可以点击编辑来指定更多选项,如您的应用版本或其启动器图标,如图 Figure 46–5 所示。

images

图 46–5。 您的项目在 PhoneGap Build 中的设置

总而言之,如果您的开发机器上不需要 Android SDK 和相关工具,PhoneGap Build 无疑简化了 PhoneGap 构建过程。

Nitobi 推出了 PhoneGap Build,作为面向非开源应用的商业服务,价格可从 PhoneGap Build 网站获得。

PhoneGap 和清单示例

PhoneGap 的美妙之处在于它包装了 HTML、CSS 和 JavaScript。换句话说,你不需要做太多 PhoneGap 特有的事情,就可以利用 PhoneGap 为你提供一个适合安装在 Android 设备上的 APK。也就是说,PhoneGap 确实向您展示了比您从标准中获得的更多的东西,如果您需要它们并且愿意为它们使用专有的 PhoneGap APIs 的话。

坚持标准

给定一个现有的 HTML5 应用,要使它成为一个可安装的 APK,您需要做的就是将它包装在 PhoneGap 中。例如,要将 HTML5 版本的Checklist(来自第四十五章)转换成一个 APK 文件,你需要做以下事情:

  1. 按照本章前面概述的步骤创建一个空的 PhoneGap 项目。
  2. 将 HTML5 项目中的 HTML、CSS、JavaScript 和图像复制到 PhoneGap 项目的assets/www/目录中(注意,您不需要 HTML5 特有的任何东西,比如缓存清单)。
  3. 确保您的 HTML 入口点文件名与您在活动中使用的loadUrl()调用的路径相匹配(例如,index.html)。
  4. 从 HTML 中添加对 PhoneGap JavaScript 文件的引用。
  5. 构建并安装项目。

这是我们应用的DroidGap活动,来自PhoneGap/Checklist项目:

`package com.commonsware.pg.checklist;

import android.app.Activity;
import android.os.Bundle;
import com.phonegap.DroidGap;

public class Checklist extends DroidGap {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.loadUrl("file:///android_asset/www/index.html");
  }
}`

下面是清单,添加了 PhoneGap 要求的所有设置:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"       package="com.commonsware.pg.checklist"       android:versionCode="1"       android:versionName="1.0">     <application android:label="@string/app_name" android:icon="@drawable/cw">         <activity android:name="Checklist"                   android:configChanges="orientation|keyboardHidden"                   android:label="@string/app_name">             <intent-filter>                 <action android:name="android.intent.action.MAIN" />                 <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>     </application>     <supports-screens         android:largeScreens="true"         android:normalScreens="true"         android:smallScreens="true"         android:resizeable="true"         android:anyDensity="true"     />     <uses-permission android:name="android.permission.CAMERA" />     <uses-permission android:name="android.permission.VIBRATE" />     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />     <uses-permission android:name="android.permission.READ_PHONE_STATE" />     <uses-permission android:name="android.permission.INTERNET" />     <uses-permission android:name="android.permission.RECEIVE_SMS" />     <uses-permission android:name="android.permission.RECORD_AUDIO" />     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />     <uses-permission android:name="android.permission.READ_CONTACTS" />     <uses-permission android:name="android.permission.WRITE_CONTACTS" />     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> </manifest>

这里是 HTML,它几乎与 HTML5 的原始版本相同,但删除了一些 HTML5 离线内容(例如,iPhone 图标),并添加了对 PhoneGap 的 JavaScript 文件的引用:

`

         Checklist                    
        
                     

Checklist

        
        
          
                                          
                         

Total: 0             Remaining: 0

          
                
  • Loading…
  •           
        
        
                              
    
     `

对于许多应用,这就是你所需要的。你只是简单地看着 PhoneGap 给你一些你可以在 Android Market、iOS 应用商店等等上分发的东西。

添加 PhoneGap APIs

如果您想利用更多的设备功能,您可以扩充您的 HTML5 应用来使用 PhoneGap 特定的 API。这些功能涵盖了从告诉你设备的型号到让你获得指南针读数的所有范围。因此,它们的复杂性会有所不同。为了本章的目的,我们将看一些更简单的。

设置设备就绪事件处理程序

由于各种原因,当您的页面被加载时,PhoneGap 不会立即响应它的所有 API。相反,您需要寻找一个deviceready事件来确认使用 PhoneGap 特定的 JavaScript 全局变量是安全的。以下是典型的食谱:

  1. 向您的<body>标签添加一个onload属性,引用一个全局 JavaScript 函数(例如,onLoad())。
  2. onLoad()中,使用addEventListener()deviceready事件注册另一个全局 JavaScript 函数(如onDeviceReady())。
  3. onDeviceReady()中,开始使用 PhoneGap APIs。
使用 PhoneGap 提供的功能

PhoneGap 通过一系列虚拟 JavaScript 对象为您提供了许多方法。这里,“虚拟”意味着您不能检查对象是否存在,但是您可以调用方法并读取它们的属性。例如,有一个device对象有一些有用的属性,比如phonegap返回 PhoneGap 版本,而version返回 OS 版本。这些虚拟对象准备好在deviceready事件中或之后使用。

例如,下面是一个 JavaScript 文件(PhoneGap/ChecklistEx项目中的props.js),它实现了一个onLoad()函数(为deviceready注册)和一个onDeviceReady()函数(使用device对象的属性):

`// PhoneGap's APIs are not immediately ready, so set up an
// event handler to find out when they are ready

function onLoad() {
  document.addEventListener("deviceready", onDeviceReady, false);
}

// Now PhoneGap's APIs are ready

function onDeviceReady() {
  var element=document.getElementById('props');

element.innerHTML='

  • Model: '+device.name+'
  • ' +
                        '
  • OS and Version: '+device.platform +' '+device.version+'
  • ' +
                        '
  • PhoneGap Version: '+device.phonegap+'
  • ';
    }`

    onDeviceReady()函数需要一个idprops的列表元素。这再加上首先加载这个 JavaScript,将需要对我们的 HTML 进行一些小的修改:

    `

             Checklist                           
     
       

    Checklist

     
     
        
                
         

    Total: 0         Remaining: 0

      
          
    • Loading…
    •   
     
     
           
        
            

    Device Properties

            
          
          
             `

      Figure 46–6 显示了最终的应用。

      images

      图 46–6。 带有设备属性的 PhoneGap 清单应用

      显然,读取一些属性远比用设备的摄像头拍照简单。然而,复杂性的差异主要在于 PhoneGap 的虚拟 JavaScript 对象给了你什么以及你如何使用它们,这比 Android 特有的任何东西都要多。

      你可能遇到的问题

      PhoneGap 是创建跨平台应用的绝佳选择。然而,它也不是没有问题。其中一些问题可能会及时得到解决;有些可能是 PhoneGap 特有的性质。

      安全

      Android 应用使用权限系统来请求访问某些系统功能,例如提出互联网请求或读取用户的联系人。应用必须在安装时请求这些权限,因此如果请求的权限可疑,用户可以选择放弃安装。

      一般的经验法则是,您应该请求尽可能少的权限,并确保您可以证明您为什么请求它们。

      PhoneGap,对于一个新项目,请求相当多的权限:

      • CAMERA
      • VIBRATE
      • ACCESS_COARSE_LOCATION
      • ACCESS_FINE_LOCATION
      • ACCESS_LOCATION_EXTRA_COMMANDS
      • READ_PHONE_STATE
      • INTERNET
      • RECEIVE_SMS
      • RECORD_AUDIO
      • MODIFY_AUDIO_SETTINGS
      • READ_CONTACTS
      • WRITE_CONTACTS
      • WRITE_EXTERNAL_STORAGE
      • ACCESS_NETWORK_STATE

      保持这个花名册不变将使您的应用可以使用 PhoneGap 为您的 JavaScript 提供的所有 API...和一个会吓跑很多用户的应用。毕竟,您的应用不太可能能够使用所有这些权限,更不用说证明这些权限的合理性了。

      您当然可以通过修改 PhoneGap 项目根目录下的AndroidManifest.xml文件来缩减这个列表。然而,您将需要彻底测试您的应用,以确保您没有放弃您实际需要的权限。此外,您可能不清楚可以安全删除哪些权限。

      最终,PhoneGap 项目可能会有工具来帮助指导您选择权限,也许是通过静态分析您的 JavaScript 代码来查看您正在使用哪些 PhoneGap APIs。不过,与此同时,获得适当的权限集将涉及大量的尝试和错误。

      屏幕尺寸和密度

      普通的 web 应用主要关注屏幕分辨率和窗口大小作为它们的主要变量。移动网络应用不必担心窗口大小,因为浏览器和应用通常全屏运行。但是,移动 web 应用需要处理物理大小和密度——这些问题与传统 web 开发无关。

      上网本的屏幕可以是 10 英寸或更小,而台式机的屏幕可以是 24 英寸或更大。因此,物理屏幕尺寸似乎是 web 开发人员需要解决的问题。然而,在上网本/笔记本/台式机领域,屏幕分辨率(以像素为单位)通常与物理尺寸相符。这是因为 LCD 的屏幕密度相当一致,而且密度相当低。

      另一方面,智能手机有几种不同的密度,导致分辨率和尺寸之间的联系被打破。一些低端手机,尤其是小尺寸(例如 3 英寸)的 LCD,其密度与漂亮的显示器相当。中档手机的密度是普通手机的两倍(240 dpi 对 120 dpi)。苹果的 iPhone 4 密度更高,很可能很快也会有一些配备所谓视网膜显示屏的 Android 设备。因此,例如,800×480 的分辨率可以在 4 英寸到 7 英寸的屏幕上显示。平板电脑增加了更多可能的尺寸。

      触摸屏带来的问题更是雪上加霜。鼠标点击可以获得像素级的精度。手指就没那么精确了。因此,你可能需要在触摸屏上把你的按钮和类似的东西做得大一点,这样才方便手指操作。这导致了素材缩放的一些问题,尤其是图像。在低密度 3 英寸设备上可能对手指友好的东西对于高密度 4 英寸设备来说可能完全太小了。

      原生 Android 应用具有用于处理该问题的内置逻辑,其形式为多组资源(例如,图像),可以基于设备特征进行交换。最终,PhoneGap 和类似的工具将需要为他们的用户提供相关的建议,告诉他们如何创建同样能适应环境的应用。

      观感

      一个 web 应用看起来永远不会像一个本地应用。这未必是一件坏事。然而,一些用户可能会感到不安,特别是因为他们不明白为什么他们新安装的应用(例如,用 PhoneGap 制作的)与他们已经拥有的任何其他类似应用如此不同。

      随着 HTML5 应用在 Android 上变得更加突出,这个问题的重要性应该会下降。然而,这是需要记住的事情。如果您正在创建自己的图形元素(图标等)。)你几乎肯定会从使用 Android Asset Studio 中受益,这将在第四十三章中介绍,承担一些杂务:制作必要大小的图标,创建与你的图标和其他风格一致的网络图像,等等。

      了解更多信息

      在撰写本文时,还没有专门介绍 PhoneGap 开发的书籍。目前,关于 PhoneGap 的最佳信息可以在 PhoneGap 网站上找到,包括它的 API 文档,也可以在其他网站上的许多其他公开的教程中找到。

      四十七、其他替代环境

      前面章节中描述的备选应用环境只是这些技术不断发展的一小部分。这里,我们将简要地看一下其他几个可供选择的应用环境。

      注意:这个领域正在快速变化,所以当你读到这一章的时候,相对于这些技术已经取得的进步,这些材料可能已经有些过时了。查看每个应用环境的网站,获取最新的更新。

      罗德斯

      Rhodes ( [rhomobile.com/](http://rhomobile.com/))类似于 PhoneGap,因为您开发的 Android 应用的用户界面是通过 HTML、CSS 和 JavaScript 定义的。不同之处在于 Rhodes 在一个完整的 Ruby 环境中使用 Rails 风格的框架。您的 Ruby 代码生成 HTML,并通过一个WebView小部件“提供”给一个活动,就像服务器端的 Ruby web 应用生成 HTML 提供给一个独立的 web 浏览器一样。

      与 PhoneGap 类似,您可以在您的开发机器上构建项目,也可以使用他们的托管构建流程 RhoHub。推荐后一种方法,部分原因是对本地构建的要求高于 PhoneGap——值得注意的是,Rhodes 需要本地开发工具包(NDK)来构建 Ruby 解释器并将其链接到您的应用。

      由于 Ruby 解释器的开销(大约 1.5MB),Rhodes 最终创建了比 PhoneGap 更大的应用。但是,如果你习惯于服务器端的 web 开发,那么对你来说,拿起 Rhodes 可能比拿起 PhoneGap 更容易。

      Rhodes 与众不同的一个方面是提供一组连接器和一种称为 RhoConnect 的服务器端服务,所有这些都是为了在 Rhodes 开发的 Android 应用和各种企业和商业系统之间提供集成。

      闪光、弯曲和空气

      Adobe 长期努力将其 Flash、Flex 和 AIR 技术扩展到移动领域。历史上,你可以使用 Flex(“Hero”版)和 Flash Builder(“Burrito”版)来创建 Android APK 文件,这些文件可以在 Android Market 上分发并部署到 Android 设备上。这些设备需要安装 Adobe Integrated Runtime (AIR ),这是免费的,但下载量很大,只能在采用新款 ARM 处理器的 Android 2.2 或更高版本的设备上运行(具体来说,ARM v7 或更高版本的处理器,因此一些运行 Android 2.2 或 2.3 的早期型号设备就不太走运了,如 LG Optimus V)。

      然后在 2011 年 11 月,Adobe 宣布移动设备(Android 和其他平台)将不再使用 Flash。如果你读了上一章关于 PhoneGap 的内容,你可能会对 Adobe 在移动领域的发展方向得出自己的结论。Adobe 无疑已经在工具领域收集了大量技术和知识来构建 Flash 代码,然后将其转换为 HTML5 和相关的非 Flash 技术。只有时间才能证明这一举措在移动领域是否成功。

      AIR 不像 PhoneGap 那样与平台紧密集成(例如,AIR 不提供对设备联系人的访问),尽管 Adobe 现在可能会在这一领域投入更多资源,可能会利用其通过收购 Nitobi 获得的专业知识。

      JRuby 和 Ruboto

      JRuby ( [www.jruby.org/](http://www.jruby.org/))是设计用于在 JVM 上运行的最流行的语言之一——除了 Java 本身。JRuby 很快被移植到 Android 上运行,但禁用了一些优化,因为 JRuby 实际上运行在 Android 环境底层的 Dalvik 虚拟机上,而不是在经典的 JVM 上。

      然而,仅靠 JRuby 无法创建 Android 应用。作为一种脚本语言,它无法定义一个活动或其他组件——这些需要在应用的清单中注册为常规的 Java 类文件。

      这就是 Ruboto ( [ruboto.org/](http://ruboto.org/))的用武之地。Ruboto 是一个通用 JRuby/Android 应用的框架。它通过代码生成器提供框架活动,并允许 JRuby 脚本为所有生命周期方法定义处理程序(例如,onCreate()),使用 JRuby 代码定义用户界面,等等。可以使用提供的 Rake 脚本将结果打包成 APK 文件。搜索结果可以上传到 Android Market,也可以按照你的意愿进行分发。

      安卓版单声道

      Mono 是 C#和。NET 用于非 Windows 环境。Mono 也有相当多的争议,大部分来自微软,比如微软是否有一天会因为专利考虑而压制 Mono。

      Android 版 Mono 已经开发了一段时间。这将允许 Mono 开发者将 Android 作为他们应用的目标。原则上,人们可以这样为 Android 开发 C#应用。

      虽然 Mono 本身是一个开源项目,但 Mono for Android“是一个商业产品...基于每个开发者的许可”,由 Xamarin ( [android.xamarin.com/](http://android.xamarin.com/))主持。对于期待 Android 上的 Mono 保持开源的开发者来说,这可能有点令人震惊。截至 2011 年底,Xamarin 网站列出的价格从每位开发者 399 美元起。

      应用发明者

      App Inventor ( [appinventor.googlelabs.com/](http://appinventor.googlelabs.com/))是一款 Android 应用开发工具,由谷歌提供,但不在正常的 Android 开发者网站之内。App Inventor 最初是为教育开发的,但谷歌一直在邀请其他人进入他们的封闭测试版。

      App Inventor 理论上是一个基于 web 的开发工具。在这里,“理论上”意味着,在实践中,用户必须在浏览器之外做大量的工作来设置一切:

      • 在浏览器中安装并运行 Java 1.6 或更高版本,能够运行 Java Web Start ( .jnlp)应用
      • 下载并安装大型(~55MB)客户端工具集

      设置完成后,App Inventor 会为您提供一个拖放式 GUI 编辑器,如图 Figure 47–1 所示。

      images

      图 47–1。App 发明者“设计者”查看

      App Inventor 还为您提供了一个块编辑器(参见 Figure 47–2),您可以通过将代表事件、方法和属性的各种“块”捕捉在一起,将行为附加到事件(例如,单击按钮)。

      images

      图 47–2。App Inventor Blocks 编辑

      在 GUI 编辑器中工作时,如果您选择直接在连接的设备上构建您的应用,您可以在连接的设备上实时查看您正在构建的内容,并可以对其进行实时测试。稍后,当您准备好了,您可以将应用打包成一个标准的 APK 文件。

      但是,App Inventor 目前并未真正用于生产应用:

      • 您不能在 Android Market 上分发 App Inventor 应用。谷歌声称这是由于技术上的限制,目前正在积极解决,但这种状况在 2011 年的大部分时间里都没有改变。
      • 它有更多的组件旨在“sizzle”(例如,Twitter 集成),而较少的组件提供典型的现代应用可能需要的功能(例如,关系数据库和列表)。
      • 共享开发是原始的,额外的开发人员上传和下载项目代码的 ZIP 文件。
      • 也许最大的限制是它的单屏幕焦点,这意味着在不同屏幕上进行额外的活动超出了 App Inventor 的范围。

      在未来,App Inventor 可能会成为一个可靠的选择,或者 App Inventor 会引发其他公司为 Android 创建类似的免编程开发选项。

      钛金属手机

      Titanium Mobile 之所以出名,是因为它只使用 JavaScript 来定义用户界面,而完全避开了 HTML。它的 JavaScript 库除了提供对数据库和平台功能的访问之外,还允许您声明用户界面小部件。但是其用于定位所述窗口小部件的布局能力还有待改进。

      在撰写本文时,Titanium Mobile 的创建者 Appcelerator ( www.appcelerator.com/)还没有提供一套基于云的工具。它确实提供了一个名为 Titanium+Plus 的程序,提供额外的 SDK 组件来与 Twitter 和 PayPal 等其他基于云的服务进行对话。它的 Titanium 工具有一个看起来非常光滑的 UI,但它仍然需要 Java SDK 和 Android SDK 才能构建 Android 应用,这使得设置对一些人来说有点令人生畏。

      在撰写本文时,Titanium Mobile 支持 Android 和 iOS 开发。处于私人测试阶段的黑莓支持计划似乎已经暂停。

      其他 JVM 编译语言

      如果你喜欢常规的 Android 开发,但是你不喜欢 Java,任何可以生成兼容的 JVM 字节码的语言都应该可以在 Android 上工作。您必须修改另一种语言的构建链来完成 Android 构建过程的其余部分(例如,从资源中生成R.java并最终创建 APK 文件)。

      Scala ( [www.scala-lang.org/](http://www.scala-lang.org/))和 Clojure ( [clojure.org/](http://clojure.org/))就是两种这样的语言,它们的社区已经将使用它们的语言进行 Android 开发的指令放在了一起。

      四十八、处理设备

      Android 是开源的,设备制造商可以免费获得。因此,当设备制造商将 Android 安装到他们的设备上时,他们可以全权委托 ?? 做他们想做的事情。这意味着设备用户有了更多的选择,他们可以在各种形状、尺寸和颜色的 Android 设备中进行选择。这也意味着开发者需要考虑一些设备差异和特性。

      本章将为您提供一些处理这些设备特定问题的技巧和建议,以配合前面章节中关于屏幕尺寸的内容。

      此应用包含明确的说明

      最初,唯一的 Android 设备是 T-Mobile G1。因此,如果您正在编写一个 Android 应用,您可以假设存在一个硬件 QWERTY 键盘、一个用于导航的轨迹球等等。然而,现在有数百种其他设备存在,其中许多具有不同的硬件功能(例如,无键盘、多屏幕、游戏控制器按钮等等!).

      理想情况下,不管是否存在各种类型的硬件,您的应用都可以工作。但是,如果没有某些硬件特性,一些应用将无法使用。例如,全屏游戏可能依赖硬件键盘或轨迹球来指示玩家的动作,软键盘和触摸屏可能不够用。

      幸运的是,从 Android 1.5 开始,您可以添加明确的指令,告诉 Android 您需要什么,这样您的应用就不会安装在缺乏此类硬件的设备上。我们现在来看看这个,然后继续讨论隐含的特性请求。

      明确的功能要求

      除了使用目标 ID 系统来指示项目所针对的设备级别之外,还可以使用一个AndroidManifest.xml元素来指定应用正常运行所需的硬件。您可以在<manifest>元素中添加一个或多个<uses-configuration>元素。每个<uses-configuration>元素指定您的应用将使用的一个有效的硬件配置。目前,您可以通过这种方式指定五种可能的硬件要求:

      • android:reqFiveWayNav:表示您需要某种形式的五向导航指向设备(如android:reqFiveWayNav = "true")
      • android:reqNavigation:将五向导航定位设备限制为特定类型(如android:reqNavigation = "trackball")
      • android:reqHardKeyboard:指定是否需要硬件(物理)键盘(如android:reqHardKeyboard = "true")
      • android:reqKeyboardType:与android:reqHardKeyboard一起使用,表示需要特定类型的硬件键盘(如android:reqKeyboardType = "qwerty")
      • android:reqTouchScreen:指示需要哪种类型的触摸屏(如有)(如android:reqTouchScreen = "finger")

      从 Android 1.6 开始,有一个类似的 manifest 元素<uses-feature>,用于记录应用对 Android 设备上其他可选功能的需求。例如,以下属性可以放在一个<uses-feature>元素中:

      • android:glEsVersion:表示您的应用需要 OpenGL,其中属性的值表示 OpenGL 支持的级别(例如,0×00010002表示 OpenGL 1.2 或更高版本)
      • android:name = "android.hardware.camera":表示您的应用需要一个摄像头
      • android:name = "android.hardware.camera.autofocus":表示您的应用特别需要自动对焦摄像头

      每个 Android 版本都添加了更多您可能需要的功能。一个很好的例子是最近添加的android:hardware:nfc功能,用于使用近场通信(NFC)。这些请求将导致 Android 市场——以及其他第三方市场,我们希望——过滤掉你的应用,使其不能被加载到不适合它的设备上。

      <uses-feature>元素有一个您可以指定的android:required属性。默认情况下,它被设置为true,这意味着您的应用绝对需要这个特性。如果您将它设置为false,那么您的应用可以利用这个特性,如果它存在的话,但是并不绝对需要它。要在运行时发现该功能是否存在于设备上,您可以使用PackageManager上的hasSystemFeature()方法询问设备。

      隐含特性请求

      如果你请求了类似CALL_PHONESEND_SMS的权限,除非你采取适当的步骤,否则你的应用将无法在摩托罗拉 XOOM 上使用,也无法在其他只有 Wi-Fi 的 Android 平板电脑上使用。

      有些权限意味着您需要某些硬件功能。向下滚动到<uses-feature>页面上的“隐含特性需求的权限”部分,找到列表。1

      Android Market 将类似CALL_PHONE的许可请求视为对以下内容的请求:

      <uses-feature android:name="android.hardware.telephony" />

      XOOM 是第一款没有电话功能的 Android 设备;其他几款设备也紧随其后。虽然 XOOM 可以有数据计划,但它没有语音或短信功能,因此被视为没有android.hardware.telephony。但是,如果你请求类似CALL_PHONE的权限,Android Market 默认会假设你需要 android.hardware.telephony。因此,你将被排除在 XOOM 的 Android 市场之外。

      解决方案很简单:对于权限可能隐含的、但您的应用并不绝对需要的任何硬件特性,使用android:required="false"手动将适当的<uses-feature>元素添加到您的清单中:

      <uses-feature   android:name="android.hardware.telephony"   android:required="false" />

      然后,在你尝试打电话或发送短信或其他东西之前,使用PackageManagergetSystemAvailableFeatures()来确定设备上的android.hardware.telephony是否可用。例如,您可能会提前检查电话,并禁用各种菜单选项,如可能会引导用户拨打电话或发送 SMS 的按钮。

      如果您的应用绝对需要电话,那么隐含的<uses-feature>将会工作,尽管您可能希望考虑显式地加入一个。然而,请记住,这意味着你的应用将无法在 XOOM 或其他缺乏电话功能的平板电脑上运行。

      有保障的市场

      正如本章介绍中提到的,Android 是开源的。具体来说,它主要是在 Apache 软件许可证 2.0 下提供的。该许可证对设备制造商几乎没有限制。因此,坦率地说,设备制造商很有可能制造出一款不能很好地运行 Android 的设备。它可能对设备上的标准应用工作良好,但在处理第三方应用(如您可能编写的应用)方面表现不佳。


      1http://developer . Android . com/guide/topics/manifest/uses-feature-element . html

      为了帮助解决这个问题,谷歌有一些应用,比如 Android Market,它没有作为开源软件发布。虽然这些应用可供设备制造商使用,但运行 Android Market 的设备首先要经过测试,以帮助确保用户对设备的体验是合理的。

      因此,Android Market 在设备上的存在,除了为您的应用提供分发手段之外,还可以作为一种认可,即设备应该支持编写良好的第三方应用。具体来说,任何拥有安卓市场的设备

      • 符合兼容性定义文件(CDD)中概述的标准
      • 已通过兼容性测试套件(CTS)

      其他各不相同的东西

      不同设备之间的其他差异包括:

      • 有哪些定位技术可用(例如,GPS、手机信号发射塔邻近、伽利略)
      • 哪些相机功能可用(例如,闪光灯、自动对焦、深褐色调)
      • 哪些传感器可用(例如,加速度计、陀螺仪、气压计)

      处理这些变量的策略是首先询问系统,找出有哪些可能性,然后决定使用哪一个,这个决定可以由您单独做出,也可以根据用户的输入做出。例如,您可以使用Criteria来确定哪一个是使用LocationManager的最佳位置供应器。

      虫子,虫子,虫子

      不幸的是,设备不可避免地会有 bug。有些 bug 确实是偶然的。有些是设备制造商为实现某些商业目标而做出的改变的副作用。有些实际上是有意的,尽管实现它们的工程师可能没有完全理解它们的后果。

      对于这些错误,除了尝试绕过它们之外,你在战术上没有太多办法。android.os包中的Build类可以告诉你运行你的应用的设备的品牌和型号。这一点,加上您自己在某些问题上得来不易的经验,将帮助您确定需要从哪里绕过固件损坏。

      从战略上来说,如果您发现某个东西明显是一个设备缺陷,您应该提交一个问题,通过 CTS 检测这个缺陷。CTS 应该过滤掉不能忠实运行 Android 应用的设备。然而,CTS 有许多漏洞,设备漏洞会钻过去。通过共同改进 CTS,我们可以帮助防止将来出现问题。您可以在 Android Bugs 的公共问题跟踪器[code.google.com/p/androidbugs/issues/list](http://code.google.com/p/androidbugs/issues/list)上提交问题。

      设备测试

      理想情况下,您应该尝试在各种硬件上测试您的应用。然而,这可能会变得昂贵。以下是一些成本更低的选择:

      • 注册 Keynote DeviceAnywhere 的独立开发人员计划,这是一种能够访问他们的设备群进行远程测试的低成本方式。
      • 一些设备制造商在各种活动中举办设备实验室,例如在 2011 年的 AnDevCon 上举办的摩托罗拉。
      • 一些运营商有永久性的设备实验室,比如 Orange 的开发者中心。
      • 作为 Meetup 或 Google Technology 用户组的一部分,您可以安排与 Android 开发人员进行短期(例如 15 分钟)设备交换。

      四十九、我们将何去何从?

      显然,这本书并没有涵盖一切。虽然您的主要资源(除了书籍之外)是 Android SDK 文档,但您可能需要来自其他来源的信息。

      在网上搜索“android”和一个类名是找到引用给定 Android 类的教程的好方法。然而,请记住,Android 的变化速度意味着几年前编写的教程很可能是为更早的 SDK 版本编写的,因此,将需要相当大的调整才能在当前的 SDK 中正常工作。

      你可以使用本章概述的一些资源来缩小搜索范围,而不是随意搜寻教程。

      问题,有时有答案

      获得安卓帮助的官方渠道是安卓谷歌小组。关于 SDK,有两点需要考虑:

      • Android 开发者([groups.google.com/group/android-developers](http://groups.google.com/group/android-developers)),获取 SDK 问答
      • Android discuse([groups.google.com/group/android-discuss](http://groups.google.com/group/android-discuss)),旨在自由讨论任何与 Android 相关的问题,不一定是编程问题和答案

      您还可以考虑以下资源,尤其是 StackOverflow,它越来越被认为是获得帮助的最佳地方:

      • StackOverflow 的android标签([stackoverflow.com/questions/tagged/android](http://stackoverflow.com/questions/tagged/android))
      • 位于[www.anddev.org](http://www.anddev.org)的 Android 教程和编程论坛
      • 【Android wiki 的 Open Mob(http://andmob.wikidot.com/)
      • freenode 上的#android-dev IRC 通道([freenode.net/](http://freenode.net/))
      • JavaRanch 的 Android 论坛([www.coderanch.com/forums/f-93/Android](http://www.coderanch.com/forums/f-93/Android))

      重要的是,特别是对于 StackOverflow 和 Google 团队来说,要写一些信息性的问题。以下是一些写有效问题的技巧:

      • 包括源代码的相关部分(例如,您遇到异常的方法)。
      • 如果问题是未处理的异常,则包括来自 LogCat 的堆栈跟踪。
      • 在 StackOverflow 上,确保你的源代码和堆栈跟踪被格式化为源代码;在 Google Groups 上,考虑在[gist.github.com](http://gist.github.com)或类似的代码粘贴网站上发布长列表。
      • 彻底解释你正试图做什么,你正试图如何做,以及你为什么这样做(尤其是如果你认为你的目标或方法可能有点与众不同)。做好准备,人们会试图引导你走向一个完全不同的方法,而不是真正试图帮助你解决当前的问题。考虑其他方法,但要小心不要损害你的愿景的完整性。
      • 在 StackOverflow 上,用你自己的评论来回应回答和评论,使用@语法(例如@CommonsWare)称呼对方,以最大化你得到回复的几率。
      • 在 Google Groups 上,不要“ping”或回复您自己的消息来试图获得回应,直到一段合理的时间过去(例如,24 小时)。

      前往源头

      Android 的源代码现在已经可以使用了,尽管比当前的 Android 4.0 版本落后了几个版本。大多数情况下,这是为那些寻求增强、改进或以其他方式对 Android 操作系统内部大惊小怪的人准备的。但是,您可能会在这些代码中找到您想要的答案,特别是如果您想了解一些内置的 Android 组件是如何工作的。

      注:谷歌承诺将在不久的将来发布 Android 4.0 的源代码,冰激凌三明治。这样一个版本变得越来越重要,因为[source.android.com](http://source.android.com)上现有的版本是 2.3,这个版本已经过时了。正如 Google 之前所说,3.x 版本的源代码不太可能发布,因为 4.0 是所有未来版本的首选合并代码基础。

      源代码和相关资源可以在[source.android.com](http://source.android.com)找到,在这里你可以做以下事情:

      • 下载或浏览源代码
      • 针对操作系统本身提交错误报告
      • 提交修补程序并了解如何评估和批准此类修补程序的流程
      • 加入一个独立的谷歌 Android 平台开发小组

      与其下载数千兆字节的 Android 源代码快照,不如使用谷歌代码搜索([www.google.com/codesearch](http://www.google.com/codesearch))来代替。只需在您的搜索查询中添加package:android约束,它将只在 Android 和相关项目中进行搜索。

      获取您的新闻信息

      Ed Burnette 是一个好人,碰巧写了自己的 Android 书籍,他也是 Planet Android ( [www.planetandroid.com](http://www.planetandroid.com))的经理,Planet Android 是许多 Android 相关博客的聚合器。订阅 Planet Android 的 feed 可以让你监控相当多与 Android 相关的博客帖子,尽管这些帖子并不完全与编程相关。

      为了更好地关注与编程相关、与 android 相关的博客文章,你可以在 DZone ( [www.dzone.com](http://www.dzone.com))中搜索“Android ”,并订阅基于该搜索的提要。

      第一部分:核心概念

      第二部分:活动

      第三部分:蜂巢和平板

      第四部分:数据存储、网络服务和 API

      第五部分:服务

      第六部分:其他 Android 功能

      第七部分:替代应用环境

      第八部分:不断发展的安卓系统

      posted @ 2024-10-05 17:10  绝不原创的飞龙  阅读(34)  评论(0编辑  收藏  举报