大家好,欢迎来到IT知识分享网。
原文: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 所示。
图 2–1。 Android SDK 和 AVD 管理器
此时,您已经有了一些构建工具,但是缺少编译 Android 应用所必需的 Java 文件。您还缺少一些额外的构建工具,以及运行 Android 模拟器所需的文件。要解决这个问题,单击左侧的可用包选项,打开图 2–2 所示的屏幕。
图 2–2。 安卓 SDK 和 AVD 管理器可用包
打开树的 Android 存储库分支。短暂停顿后,您将看到类似于图 2–3 的屏幕。
图 2–3。 Android SDK 和 AVD 管理器可用 Android 软件包
选中以下项目的复选框:
- 针对您想要测试的所有 Android SDK 版本的“SDK 平台”
- 最新 Android SDK 版本的“Android SDK 文档”
- 最新 Android SDK 版本的“SDK 示例”,如果您愿意,也可以是旧版本
然后,打开树的第三方附加组件分支。短暂停顿后,您将看到类似于图 2–4 的屏幕。
图 2–4。??【安卓 SDK 和 AVD 管理器】可用第三方插件
点击“Google Inc. add-ons”分支将其打开,如图 2–5 所示。
图 2–5。 安卓 SDK 和 AVD 管理器可用谷歌插件
最有可能的情况是,您需要选中与您在 Android Repository 分支中选择的 SDK 版本相匹配的“Google APIs by Google Inc .”项目的复选框。Google APIs 包括对众所周知的 Google 产品的支持,比如 Google Maps,既来自您的代码,也来自 Android 模拟器。
检查完所有要下载的项目后,单击安装选定项目按钮,这将弹出一个许可确认对话框,如图 2–6 所示。
图 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 并选择帮助

[dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/)。这应该会触发 Eclipse 从该站点下载可用的插件列表(参见图 2–7)。
图 2–7。 Eclipse ADT 插件安装
选中开发工具复选框,然后单击下一步按钮。按照向导的其余步骤查看要下载的工具,并查看和接受它们各自的许可协议。当 Finish 按钮被激活时,单击它,Eclipse 将下载并安装插件。完成后,Eclipse 会要求重启;让它这样做吧。
然后,您需要向 ADT 展示在哪里可以找到前面小节中的 Android SDK 安装。为此,从 Eclipse 主菜单中选择窗口

图 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 手册中提供了完整的安装说明,但基本步骤如下:
- 将 ZIP 存档文件解压到机器上的逻辑位置。
- 添加一个
JAVA_HOME环境变量,指向你的 JDK 的安装位置,如果你还没有的话。 - 添加一个
ANT_HOME环境变量,指向您在步骤 1 中解包 Ant 的目录。 - 将
$JAVA_HOME/bin和$ANT_HOME/bin添加到您的PATH中。 - 运行
ant -version确认 Ant 安装正确。
第五步:设置仿真器
Android 工具包括一个模拟器,一个伪装成 Android 设备的软件。这对于开发非常有用,因为它不仅使您能够在没有设备的情况下开始 Android 开发,还使您能够为您不拥有的设备测试设备配置。
Android 模拟器可以模拟一个或几个 Android 设备。你想要的每个配置都存储在一个 Android 虚拟设备(AVD)中。您在本章前面用来下载 SDK 组件的 Android SDK 和 AVD 管理器是您创建这些 AVD 的地方。
如果您没有运行 SDK 和 AVD 管理器,您可以通过 SDK 的tools/目录中的android命令运行它,或者通过 Eclipse 中的窗口
图 2–9。 Android SDK 和 AVD 管理器 Android 虚拟设备列表
单击“新建”按钮创建新的 AVD 文件。这将打开如图图 2–10 所示的对话框,您可以在其中配置 AVD 的外观和工作方式。
图 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。
图 2–11。 添加新的 AVD(续)
单击 Create AVD 按钮,您的 AVD 存根将被创建。
要启动模拟器,请在 Android 虚拟设备列表中选择它,然后单击 start。你现在可以跳过启动选项,只需点击启动。第一次启动新的 AVD 时,需要很长时间才能启动。第二次和以后启动 AVD 时,速度会快一点,通常每天只需要启动一次(例如,当您开始开发时)。在大多数情况下,每次想要测试应用时,不需要停止并重新启动模拟器。
仿真器将经历几个启动阶段,第一个阶段显示一个纯文本 ANDROID 标签,如图 Figure 2–12 所示。
图 2–12。 安卓模拟器,初始启动段
第二阶段显示一个图形化的 Android 徽标,如图图 2–13 所示。
图 2–13。 安卓模拟器,二次启动段
最后,模拟器到达主屏幕(第一次运行 AVD 参见图 2–14 或键帽(参见图 2–15)。
图 2–14。 安卓主屏幕
如果你有键盘守卫,按下菜单按钮或滑动屏幕上的绿色锁到右边,到模拟器的主屏幕。
图 2-15。 Android keyguard
第六步:设置设备
有了模拟器,您不需要 Android 设备就可以开始 Android 应用开发。在尝试发布应用(例如,将它上传到 Android Market)之前,拥有一个应用是一个好主意。但是也许你已经有了一台设备——也许这就是激发你开发 Android 的兴趣的原因。
让您的设备准备好用于开发的第一步是进入设备上的设置应用。在那里,选择应用,然后选择开发。这将为您提供一组用于选择发展相关选项的复选框,类似于图 2–16 中所示。
图 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 HT9CPP 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", 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 
New 

Project以打开新建项目对话框,该对话框提供了可供选择的项目类型向导列表。展开 Android 选项,点击 Android 项目,如图 Figure 3–1 所示。
图 3–1。 在 Eclipse 新项目对话框中选择向导
点击下一步进入新 Android 项目向导的第一页,如图 3–2 所示。
图 3–2。 Eclipse 新建 Android 项目向导,准备填写
填写以下内容,否则保留默认设置(该项目的完整示例见图 3–3):
- 项目名称:项目名称(如 Now)
- 构建目标:您希望编译的 Android SDK(例如,Android 2.3.3 的 Google APIs)
- 应用名称:应用的显示名称,将用于启动器中图标下的标题(如现在)
- 包名:该项目所属 Java 包的名称(如 com . common sware . Android . skeleton)
- 创建活动:要创建的初始活动的名称(如 Now)
图 3–3。 Eclipse 新建 Android 项目向导,已完成
此时,单击 Finish 创建您的 Eclipse 项目。
命令行
下面是一个从命令行创建 Android 项目的示例命令:
android create project --target "Google Inc.:Google APIs:7" --path Skeleton/Now --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 所示。
图 3–4。 在 Eclipse 运行方式对话框中选择作为 Android 应用运行
单击确定。如果你有一个以上的模拟器 AVD 或设备可用,你会得到一个选项来选择你想运行的应用。否则,如果您没有插入设备,模拟器将使用您之前创建的 AVD 启动。然后,Eclipse 将在您的设备或模拟器上安装应用并启动它。
命令行
对于没有使用 Eclipse 的开发人员,在您的终端中,切换到Skeleton/Now目录,然后运行以下命令:
ant clean install
基于 Ant 的构建应该发出安装过程中涉及的步骤列表,如下所示:
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:
-pre-compile:
-post-compile:
-obfuscate:
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消息——这就是你如何知道应用编译成功。
当你有了一个干净的构建,在你的仿真器或设备中,打开应用启动器,如 Figure 3–5 所示,它通常位于主屏幕的底部。
图 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.png、res/drawable-ldpi/icon.png和res/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.apk和yourapp-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:versionName和android: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 

New 

Project,如图图 5–1 所示。
图 5–1。Eclipse 中的文件菜单
然后从可用项目类型树中选择Android 

Android Project,如图图 5–2 所示,点击下一步。
图 5–2。Eclipse 中的新建项目向导
注意:如果你没有看到这个选项,说明你没有安装 Android 开发者工具。
然后,在 New Android Project 向导的第一页上,选择“Create Project from existing source”单选按钮,单击 Browse 按钮,打开包含项目的AndroidManifest.xml文件的目录。这将填充向导页面的大部分剩余部分,尽管您可能还需要从表中指定一个构建目标,如 Figure 5–3 所示。
图 5–3。Eclipse 中新的 Android 项目向导
然后,单击完成。这将使您返回到 Eclipse,导入的项目在您的工作区中,如 Figure 5–4 所示。
图 5–4。Eclipse 中的 Android 项目树
接下来,右键单击项目名称,从上下文菜单中选择Build Path 

Configure Build Path,如图图 5–5 所示。
图 5–5。Eclipse 中的项目上下文菜单
这将打开项目属性窗口的 Java 构建路径部分,如 Figure 5–6 所示。
图 5–6。Eclipse 中的项目属性窗口
如果没有选中 Android JAR(图 5–6 中的 Android 2.2 条目),请选中它,然后单击确定关闭属性窗口。此时,您的项目应该可以使用了。
如何去 DDMS
很多时候,您会被告知查看 DDMS 中的一些东西,比如 LogCat 选项卡来检查 Java 堆栈跟踪。在《月蚀》中,DDMS 是一个视角。要在您的工作区中打开这个透视图,从主菜单中选择Window 

Open Perspective 

Other,如图图 5–7 所示。
图 5–7。 月食中的透视菜单
然后,在如图 Figure 5–8 所示的透视图列表中,选择 DDMS。
图 5–8。 月食中的视角花名册
这将把 DDMS 透视图添加到您的工作区,并在您的 Eclipse IDE 中打开它。
DDMS 将在本书后面的章节中详细介绍。
如何创建仿真器
默认情况下,您的 Eclipse 环境没有设置 Android 模拟器。在成功运行项目之前,您需要一个。
为此,首先从主菜单中选择Window 

Android SDK and AVD Manager,如图图 5–9 所示。
图 5–9。Eclipse 中的 Android SDK 和 AVD 管理器菜单选项
这将打开与从命令行运行android相同的窗口。
现在,您可以按照第二章的“步骤 5:设置仿真器”一节中的说明来定义一个 Android 虚拟设备(AVD)
如何运作一个项目
假设您已经定义了 AVD,或者您已经设置了用于调试的设备并连接到您的开发计算机,您可以在模拟器中运行您的项目。
首先,单击运行工具栏按钮,或者从主菜单中选择Project 

Run。这将在您第一次运行项目时弹出运行方式对话框,如图图 5–10 所示。
图 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.02: Android 1.13: Android 1.54: Android 1.65: Android 2.06: Android 2.0.17: Android 2.18: Android 2.29: Android 2.310: Android 2.3.311: Android 3.012: Android 3.113: Android 3.214: 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;
public void onClick(View view) {
updateTime();
}
private void updateTime() {
btn.setText(new Date().toString());
}
}`
解剖活动
让我们一段一段地检查这段 Java 代码,从包声明和导入的类开始:
`packagecom.commonsware.android.skeleton;
包声明需要与创建项目时使用的声明相同。然后,与任何其他 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 活动初始化:
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 中所示的活动。
图 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_width和android:layout_height:告诉 Android 让按钮的宽度和高度填充父按钮,在本例中是整个屏幕
这些属性将在第十章中详细介绍。
由于这个小部件是活动视图中唯一的内容,所以我们只需要这个元素。复杂的视图需要一个完整的元素树,代表控制其位置的小部件和容器。本书的其余章节将尽可能使用 XML 布局形式,因此还有许多其他更复杂布局的例子供您阅读。
@符号是怎么回事?
许多小部件和容器只需要出现在 XML 布局文件中,不需要在 Java 代码中引用。例如,静态标签(TextView)经常需要出现在布局文件中,只是为了指示它应该出现的位置。XML 文件中这些种类的元素不需要用android:id属性来命名。
然而,你做想在你的 Java 源代码中使用的任何事情都需要一个android:id。
约定是使用@+id/...作为id值,其中...代表您的小部件在本地的唯一名称,表示给定的id值在布局文件中的第一次出现。在上一节的 XML 布局示例中,@+id/button是Button小部件的标识符。在同一个布局文件中第二次和随后的出现应该去掉+符号——我们将在第十章中使用这个特性。
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;
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 所示。
图 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 所示的结果。
图 9–1。label demo 示例应用
在我们负责LabelDemo的 XML 中,您会注意到我们使用了两个宽度和高度指令。第一个,fill_parent,表示我们希望我们的 UI 元素完全填充它的父空间,减去任何填充或边框。第二个,wrap_content,确保在父节点中只使用足够的空间来显示我们的内容,而不是更多。随着我们在接下来的章节中逐步学习更多的例子,这些将会变得更加清晰。
按钮,按钮,谁拿了按钮?
在前两章中,您已经看到了Button小部件的使用。事实证明,Button是TextView的子类,所以上一节讨论的所有内容也适用于格式化按钮的表面。
Android 为你提供了两种方法来处理点击收听者的问题。第一种选择是定义一些对象(比如活动)作为实现View.OnClickListener接口的“经典”方式。比经典的方法更好的是当代机器人简化事物的方式。这个简单的选项有两个步骤:
- 在你的
Activity上定义一些方法来保存这个按钮,它接受一个View参数,有一个void返回值,并且是public。 - 在您的布局 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 规范中不同的选项(例如,在不同的语言环境、屏幕大小等条件下)打开选项来改变给定的Button的Activity是多么容易。我们将在接下来的章节中详细讨论这些选项。
转瞬即逝的影像
Android 有两个小部件可以帮助你在活动中嵌入图片:ImageView和ImageButton。顾名思义,它们分别类似于TextView和Button。
每个小部件都有一个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 所示。
图 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;
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小部件提供,在第十二章中有更详细的讨论。
图 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检索并配置复选框的行为:
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 所示。
图 9–4。??【复选框 Demo 示例应用,复选框未选中
图 9–5。 同样的申请,现在用复选框勾选了
扳动开关,伊果
Android 4.0 的新功能(冰激凌三明治)是CheckBox的变体。这是一个双态开关Switch,使用户能够用手指滑动或拖动,就像他们在开关电灯一样。他们还可以像点击CheckBox一样点击Switch小部件来改变其状态。
Switch提供了一个android:text属性来显示与Switch状态相关的文本,通过Switch的setTextOn()和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方法:
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 中,开关处于每个可能的状态。
图 9–6。 开关关闭的 SwitchDemo 示例应用
图 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()。
图 9–8。??【单选按钮演示】示例应用
景色真美
所有的部件,包括前面几节中显示的部件,都扩展了View,这为所有的部件提供了一系列已经描述过的有用的属性和方法。
填充
微件有一个最小尺寸,这可能会受到其内部内容的影响。因此,举例来说,Button将扩展以适应其标题的大小。您可以使用填充来控制这个大小。添加填充将增加内容(例如,Button的标题)和小部件边缘之间的空间。
可以在 XML 中为所有四个边(android:padding)或在每个边的基础上(android:paddingLeft等)设置一次填充。).填充也可以通过setPadding()方法在 Java 中设置。
其中任何一个的值都是一个维度,是度量单位和计数的组合。所以,5px是 5 个像素,10dip是 10 个与密度无关的像素,2mm是 2 毫米。我们将在第二十五章的中更详细地检查维度。
其他有用的属性
除了本章和下一章中介绍的属性外,View最有可能使用的其他属性包括:
android:visibility:控制小工具最初是否可见android:nextFocusDown、android:nextFocusLeft、android:nextFocusRight和android:nextFocusUp:如果用户使用 D-pad、轨迹球或类似的定点设备,则控制聚焦顺序android:contentDescription:大致相当于 HTML<img>标签上的alt属性,辅助工具使用它来帮助看不到屏幕的人导航应用
有用的方法
您可以通过setEnabled()切换微件是否启用,并通过isEnabled()查看微件是否启用。一种常见的使用模式是基于CheckBox或RadioButton选择禁用一些小部件。
你可以通过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 中的vbox和hbox,等等。
Flex 和 XUL 使用方框作为布局的主要单位。如果你愿意,你可以用同样的方式使用LinearLayout,避开其他一些容器。获得您想要的可视化表示主要是确定框应该嵌套在哪里以及这些框应该具有哪些属性,例如它们相对于其他框的对齐方式。
LinearLayout 概念和属性
要配置一个LinearLayout,除了容器的内容之外,您还有五个主要的控制区域:方向、填充模型、重量、重力和填充。
方向
方向表示LinearLayout代表一行还是一列。只需将android:orientation属性添加到 XML 布局中的LinearLayout元素,并将值设置为行的horizontal或列的vertical。
通过调用LinearLayout上的setOrientation(),提供HORIZONTAL或VERTICAL,可以在运行时修改方向。
填充模型
想象一排小部件,比如一对单选按钮。这些小部件根据它们的文本有一个“自然”的大小。它们的总尺寸可能与 Android 设备的屏幕宽度不完全匹配——特别是因为屏幕有各种尺寸。然后,我们面临如何处理剩余空间的问题。
一个LinearLayout中的所有小部件必须提供android:layout_width和android: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(),你可以告诉小部件及其容器如何将与屏幕对齐。
对于一列小部件,常见的重力值分别是左对齐、居中和右对齐小部件的left、center_horizontal和right。
对于一行小部件,默认情况下它们是对齐的,因此它们的文本在基线上对齐(字母似乎“坐在”这条看不见的线上)。您可以指定一个重力值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集。RadioGroup是LinearLayout的子类,所以我们的例子展示了嵌套的盒子,就好像它们都是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;
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;
在onCreate()中,我们查找我们的两个RadioGroup容器,并在每个容器上注册一个监听器,所以当单选按钮改变状态时我们会得到通知(setOnCheckedChangeListener(this))。由于活动实现了OnCheckedChangeListener,活动本身就是监听器。
在onCheckedChanged()(监听器的回调)中,我们看到哪个RadioButton发生了状态变化。基于点击的项目,我们调整第一个LinearLayout的方向或者第二个LinearLayout的重心。
Figure 10–1 显示了演示首次在仿真器中启动时的结果。
图 10–1。 最初启动的 LinearLayoutDemo 示例应用
如果我们打开“垂直”单选按钮,顶部的RadioGroup会相应调整,如图 10–2 中的所示。
图 10–2。 同样的应用,用垂直单选按钮选中
如果我们切换“中心”或“右”单选按钮,底部的RadioGroup会调整以匹配,如图图 10–3 和图 10–4 所示。
图 10–3。 同一个应用,选择了垂直和居中单选按钮
图 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 所示。
图 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:将小工具水平和垂直放置在容器的中心
所有这些属性都采用一个简单的布尔值(true或false)。
请注意,在执行这些不同的对齐时,会考虑小部件的填充。对齐基于小部件的整个单元格(其自然空间加上填充的组合)。
属性中的相对符号
与RelativeLayout相关的其余属性将容器中的小部件的身份作为一个值。为此:
- 将标识符(
android:id属性)分配给所有需要寻址的元素。 - 使用相同的标识符值引用其他小部件。
第一次出现的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 所示的结果。
图 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 所示。
图 10–7。 相对重叠样本应用
两个Button小部件仍然可以被点击,尽管点击较小的Button不会同时点击较大的Button。在这种重叠的情况下,您的点击将由顶部的小部件处理。
白板
如果你喜欢 HTML 表格、电子表格网格和类似的布局选项,你会喜欢 Android 的TableLayout,它允许你根据自己的要求在网格中放置你的小部件。您可以控制行数和列数,哪些列可以收缩或拉伸以容纳它们的内容,等等。
TableLayout与TableRow协同工作。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开始计数的),字段将放入一个由三列组成的跨区集合中(列1到3)。但是,您可以通过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 所示的结果。
图 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="#" /> <TextView android:text="#" android:paddingLeft="4dip" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80dip" android:background="#" /> <TextView android:text="#" 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 所示的内容。
图 10–9。ScrollViewDemo 示例应用
请注意,只有五行和第六行的一部分是可见的。通过按下 D-pad 上的向上/向下按钮,您可以上下滚动来查看剩余的行。还要注意滚动条是如何剪切内容的右侧的——一定要在那一侧放置一些填充,或者确保您自己的内容不会以这种方式被剪切。
Android 1.5 推出了HorizontalScrollView,工作方式类似于ScrollView,但是是横向的。这对于可能太宽而不是太高的表单非常有用。注意ScrollView和HorizontalScrollView都不会给你双向滚动,所以你必须选择垂直或水平。
另外,请注意,您不能将可滚动项目放入ScrollView中。例如,一个ListView小部件——我们将在接下来的章节中看到——已经知道如何滚动。如果你把一个ListView放在一个ScrollView里,它不会工作得很好。
带他们去电网
A TableLayout吸引了那些渴望 HTML 或 CSS 风格的像素精度(或缺乏像素精度)的人。通常你会发现你知道你希望你的布局中的元素相对于其他元素如何出现,或者在指定你的布局中的小部件的位置时需要更多的技巧。进入全新的GridLayout,与 Android 4 冰淇淋三明治(ICS)一同发布。
GridLayout是一种布局,它将子元素放在一个由无限细线条组成的网格上,这些线条将区域分隔成单元格。GridLayout精细控制的关键是单元格的数量,或者更准确地说,用于描述单元格的网格线没有限制或阈值——您可以使用rowSpec和columnSpec属性指定GridLayout应该有多少网格线。这意味着您可以创建一个布局,模拟一个具有几个单元格(即行和列)的简单表格,或者,对于那些需要非常精确的精度的苛刻情况,您可以疯狂地指定数千甚至数百万个单元格。
注意:为了补充GridLayout对 UI 世界的不同看法,它用android:layout_gravity代替了android:layout_weight。
例如,这里有一个在 XML 布局文件中使用的GridLayout(来自Containers/Grid演示):
在 ICS Android 模拟器中,我们使用GridLayout查看活动,如图图 10–10 所示。
图 10–10。GridDemo 示例应用
我们的按钮按照它们不同的重力方向将自己放置在GridLayout上,使用默认的rowSpec和columnSpec计数。通过在main.xml的声明中添加另一个按钮,我们可以观察到GridLayout的效用,它不需要TableLayout有些乏味的静态布局指令
... <Button android:text="Defying gravity!" android:layout_gravity="top" /> <Button android:text="Floating middle right" android:layout_gravity="right|center_vertical" /> <Button android:text="Falling like an apple" android:layout_gravity="bottom" /> ...
图 10–11 显示了我们的GridLayout如何适应显示其子节点。
图 10–11。??【GridDemo】修订版
十一、输入法框架
Android 1.5 推出了输入法框架(IMF),也就是通常所说的软键盘。然而,这一术语不一定准确,因为 IMF 可用于手写识别或通过屏幕接受文本输入的其他手段。
键盘,硬键盘和软键盘
一些 Android 设备有一个有时可见的硬件键盘(当它滑出时)。一些 Android 设备有一个总是可见的硬件键盘(所谓的“条形”或“平板”手机)。然而,大多数安卓设备根本没有硬件键盘。国际货币基金组织处理所有这些情况。
简而言之,如果没有硬件键盘,当用户点击一个启用的EditText小部件时,一个输入法编辑器(IME)将对用户可用。如果您想要提供 IME 的默认功能,则无需对应用进行任何代码更改。幸运的是,Android 在猜测您想要什么方面相当聪明,所以您可能只需要用 IME 进行测试,而不需要进行特定的代码更改。
但是 IME 可能不会像您希望的那样运行。例如,在Basic/Field示例项目中,FieldDemo活动的 IME 覆盖了多行EditText,如图图 11–1 所示。如果能对这种情况有更多的控制,并且能够控制 IME 的其他行为,那就太好了。幸运的是,正如本章所述,国际货币基金组织作为一个整体给了你很多选择。
图 11–1。 输入法编辑器,参见 FieldDemo 示例应用
根据您的需求量身定制
Android 1.1 和更早的版本在EditText小工具上提供了许多属性来控制它们的输入风格,例如android:password指示一个字段应该用于密码输入(遮住密码键盘以免被窥探)。从 Android 1.5 开始,有了 IMF,这些属性中的许多被合并成一个单一的android:inputType属性。
android:inputType属性接受一个类加修饰符,在一个管道分隔的列表中(其中|是管道字符)。该类通常描述允许用户输入的内容,这决定了软键盘上可用的基本键集。可用的类别如下:
text(默认)numberphonedatetimedatetime
这些类中有许多提供了一个或多个修饰符来进一步细化允许用户输入的内容。为了更好地理解这些修改器是如何工作的,请看一下来自InputMethod/IMEDemo1项目的res/layout/main.xml文件:
<?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="No special rules:" /> <EditText /> </TableRow> <TableRow> <TextView android:text="Email address:" /> <EditText android:inputType="text|textEmailAddress" /> </TableRow> <TableRow> <TextView android:text="Signed decimal number:" /> <EditText android:inputType="number|numberSigned|numberDecimal" /> </TableRow> <TableRow> <TextView android:text="Date:" /> <EditText android:inputType="date" /> </TableRow> <TableRow> <TextView android:text="Multi-line text:" /> <EditText android:inputType="text|textMultiLine|textAutoCorrect" android:minLines="3" android:gravity="top" /> </TableRow> </TableLayout>
这显示了一个包含 5 行的TableLayout,每一行展示了一个略有不同的EditText风格:
- 第一行在
EditText上没有任何属性,这意味着您得到的是一个纯文本输入字段。 - 第二行有
android:inputType = "text|textEmailAddress",这意味着它是一个专门查找电子邮件地址的文本输入字段。 - 第三行允许通过
android:inputType = "number|numberSigned|numberDecimal"输入带符号的十进制数字。 - 第四行设置为允许数据输入日期(
android:inputType = "date")。 - 最后一行允许多行输入,自动纠正可能的拼写错误(
android:inputType = "text|textMultiLine|textAutoCorrect")。
类和修饰符定制键盘。例如,一个纯文本输入字段会产生一个纯软键盘,如图 Figure 11–2 所示。
图 11–2。 标准输入法编辑器(又称软键盘)
一个电子邮件地址域可能会把@符号放在软键盘上,代价是一个更小的空格键,如图 Figure 11–3 所示。
图 11–3。 电子邮件地址的输入法编辑器
请注意,此行为特定于 IME。有些编辑可能会在主键盘上为电子邮件字段放置@符号。有些人可能会在主键盘上放一个.com按钮。有些人可能根本没有反应。这取决于 IME 的实现——您所能做的就是提供提示。
数字和日期字段将键限制为数字键,加上一组在给定字段上可能有效也可能无效的符号,如图 Figure 11–4 所示。
图 11–4。??【有符号十进制数字输入法编辑器】??
这些只是可能的 ime 的几个例子。通过选择合适的android:inputType,您可以为用户提供一个最适合他们应该输入的数据类型的软键盘。
告诉安卓它能去哪里
你可能已经注意到图 11–2 中所示的 IME 和图 11–3 中所示的 IME 之间的细微差别,除了增加了@键。图 11–3 中软键盘的右下角有一个 Next 按钮,而图 11–2 中的软键盘有一个换行按钮。这指出了两件事:
- 如果不指定
android:inputType,默认情况下EditText小部件是多行的。 - 你可以控制右下角的按钮,称为附件按钮。
默认情况下,在您指定了android:inputType的EditText微件上,附件按钮将是下一个,按顺序将您移动到下一个EditText微件,或者如果您在屏幕上的最后一个EditText微件上,则完成。您可以通过android:imeOptions属性手动指定附件按钮的标签。例如,在来自InputMethod/IMEDemo2的res/layout/main.xml文件中,您将看到上一个示例的增强版本,其中两个输入字段指定了它们的附件按钮应该是什么样子:
在这里,我们为电子邮件地址的附件按钮(android:imeOptions = "actionSend")附加了一个发送动作,并为中间字段(android:imeOptions = "actionDone")附加了一个完成动作。
默认情况下,Next 将焦点移动到下一个EditText,Done 关闭 IME。然而,对于那些动作,或者任何其他类似 Send 的动作,您可以使用EditText上的setOnEditorActionListener()(技术上来说,是在TextView超类上)来获得当附件按钮被点击或者用户按下回车键时的控制权。为您提供了一个指示所需操作的标志(例如,IME_ACTION_SEND),然后您可以做一些事情来处理该请求(例如,向所提供的电子邮件地址发送一封电子邮件)。
融入
请注意,上一节中显示的IMEDemo2布局与其IMEDemo1前身有另一个不同之处:使用ScrollView集装箱包裹TableLayout。这与您对《时代》的另一个控制层次有关:当 IME 出现时,您的活动本身的布局会发生什么。根据具体情况,有三种可能性:
- Android 可以“平移”你的活动,有效地向上滑动整个布局以适应 IME,或者覆盖你的布局,这取决于正在编辑的
EditText是在顶部还是底部。这实际上隐藏了用户界面的一部分。 - Android 可以调整你的活动大小,有效地使其缩小到更小的屏幕尺寸,允许 IME 位于活动本身的下方。当布局可以很容易地缩小时,这是很好的(例如,它由一个列表或多行输入字段控制,不需要整个屏幕都起作用)。
- Android 可能会全屏显示 IME,遮住你的整个活动。这允许更大的键盘和更容易的数据输入。
Android 使用其历史默认设置控制全屏选项。默认情况下,Android 会根据你的布局选择平移和调整大小模式。如果你想在平移和调整大小之间做出选择,你可以通过AndroidManifest.xml文件中<activity>元素的android:windowSoftInputMode属性来实现。例如,下面是来自IMEDemo2的清单:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.commonsware.android.imf.two" android:versionCode="1" android:versionName="1.0"> <application android:label="@string/app_name" android:icon="@drawable/cw"> <activity android:name=".IMEDemo2" android:label="@string/app_name" android:windowSoftInputMode="adjustResize"> <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:anyDensity="true"/> </manifest>
因为我们指定了 resize,Android 将缩小我们的布局以适应 IME。当ScrollView就位时,这意味着滚动条将根据需要出现,如图 11–5 所示。
图 11–5。 缩小的、可滚动的布局
您可以通过使用 Honeycomb 中引入的附加方法来控制 Android 的行为,以最大化屏幕空间,并在 Ice Cream Sandwich 中进行完善。使用 Java 方法setSystemUiVisibility()和STATUS_BAR_HIDDEN选项来隐藏系统栏,允许更大的全屏模式,或者使用方法setDimAmount()来调整主页按钮的亮度,以消除对您定期调整大小的全屏布局的干扰。
简,停止这疯狂的事情!
有时候,你需要 IME 走开。例如,如果您将附件按钮设置为搜索按钮,当用户点击该按钮时,IME 不会自动隐藏,而您可能希望隐藏它。要隐藏 IME,您需要调用InputMethodManager,这是一个控制这些 ime 的系统服务:
`InputMethodManager mgr=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mgr.hideSoftInputFromWindow(fld.getWindowToken(), 0);`
(在前一行中,fld是您要隐藏其 IME 的EditText。)
这将始终关闭指定的 IME。但是,请记住,用户首先可以通过两种方式打开 IME:
- 如果用户的设备没有暴露硬件键盘,并且用户点击
EditText,IME 应该会出现。 - 如果用户之前关闭了 IME,或者正在使用通常不会弹出窗口的小部件的 IME(例如
ListView),并且用户按下了菜单按钮,则 IME 应该会出现。
如果您只想在第一种情况下关闭 IME,而不想在第二种情况下关闭,那么使用InputMethodManager.HIDE_IMPLICIT_ONLY作为调用hideSoftInputFromWindow()的第二个参数的标志,而不是前面例子中的0。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118278.html
























































