Android开发从入门到精通

Android开发从入门到精通

            

                               ——Android 经典教程

目 录

目 录 1

第一章 什么是Android 1

什么是Android - 嵌入式设备编程的历史-第一章(1) 1

开放手机联盟和Android-(2) 4

介绍Android 第一章(3) 5

Android示例 - 第四章(4) 6

Android的几个示例 - 第四章(5) 7

第二章 下载和安装Eclipse总则 8

下载和安装Eclipse总则 - 第二章(1) 8

下载和安装JRE - 第二章(2) 9

下载和安装Eclipse - 第二章(3) 11

第三章 下载和安装Android SDK 12

下载和安装Android SDK - 第三章(1) 12

下载Android SDK - 第三章(2) 13

为Eclipse配置Android Plugin - 第三章(3) 14

第四章 浏览Android SDK 17

浏览Android SDK - 第四章(1) 17

Android SDK是什么 - 第四章(2) 17

Android 文档 - 第四章(3) 18

Android示例 - 第四章(4) 19

Android的几个示例 - 第四章(5) 19

Android工具 - 第四章(6) 20

Android APIs - 第四章(7) 22

应用程序生命周期 - 第四章(8) 22

第五章Android程序:Hello World! 24

Android程序:Hello World! -第五章(1) 24

仔细查看Android创建的文件 - 第五章(2) 26

引用库和目录 - 第五章(3) 27

Hello World!自动产生文件的详解 - 第五章(4) 29

Hellow World! 再来一次 - 第五章(5) 32

Hello World! 使用一个图形 - 第五章(6) 34

Hello World!代码为基的UI-第五章(7) 35

Hello World! XML为基的UI - 第五章(8) 36

第六章 使用命令行工具和Android模拟器 38

使用命令行工具和Android模拟器 - 第六章(1) 38

利用Windows CLI创建一个壳活动 - 第六章(2) 39

运行ActivityCreator.bat - 第六章(3) 39

项目结构 - 第六章(4) 42

在Windows CLI下创建Hello World!活动 - 第六章(5) 47

增加JAVA_HOME 第六章 (6) 47

编译并安装应用程序 第六章(7) 48

如果运行ANT时出错该怎么办? 第六章(8) 48

用adb安装你的应用程序 第六章(9) 52

运行应用程序产生了一个错误怎么办 - 第六章(10) 53

卸载一个较早的活动 - 第六章(11) 53

重新安装并启动应用程序 - 第六章(12) 54

Linux上的Hello World! 第六章(13) 54

在CLI中创建一个图片基础的Hello World! 第六章(14) 56

第七章 使用Intents 和电话拨号盘 57

使用Intents 和电话拨号盘 第七章(1) 57

Intents是什么? 第七章(2) 58

使用拨号盘 第七章(3) 63

从你的活动中打出电话 第七章(4) 66

编辑活动许可 第七章(5) 68

修改AndroidPhoneDialer 第七章(6) 70

执行一个EditText View 第七章(7) 75

试试这个:修改AndoridPhoneDialer项目 第七章(8) 78

第八章 列表,菜单和其它Views 81

列表,菜单和其它Views 第八章(1) 81

修改AndroidManifest.xml文件 第八章(2) 83

使用菜单 第八章(3) 86

为AutoComplete创建一个活动 第八章(4) 90

按钮 第八章(5) 97

CheckBox 第八章(6) 101

EditText 第八章(7) 106

RadioGroup 第八章(8) 110

Spinner 第八章(9) 115

试试这个:修改更多的View属性 第八章(10) 121

第九章 使用手机的GPS功能 121

使用手机的GPS功能 第九章(1) 121

什么是轨迹文件 第九章(2) 124

使用Android位置基础API读取GPS 第九章(3) 125

书写代码来允许活动 第九章(4) 129

传递坐标到Google地图 第九章(5) 131

增加缩放控制 第九章(6) 135

试试这个:在MapView之间转换 第九章(7) 140

第十章 使用Google API的Gtalk 145

使用Google API的GTalk 第十章(1) 145

在Android中执行GTalk 第十章(2) 146

编译并运行GoogleAPI 第十章(3) 154

试试这个:为GoogleAPI活动增加设置特性 第十章(4) 155

第十一章 应用程序:找一个朋友 156

应用程序:找一个朋友 第十一章(1) 156

创建一个SQLite数据库 第十一章(2) 157

创建一个定制的Content Provider 第十一章(3) 158

创建Content Provider 第十一章(4) 161

创建FindAFriend活动 第十一章(5) 171

创建NameEditor活动 第十一章(6) 173

创建LocationEditor活动 第十一章(7) 177

创建FriendsMap活动 第十一章(8) 186

创建FindAFriend活动 第十一章(9) 192

运行FindAFriend活动 第十一章(10) 196

Android SDK 工具参考 第十二章 (完) 196

Android SDK 工具参考 第十二章 (完) 196

Android SDK 1.5 - 包装索引 错误!未定义书签。

第一章 什么是Android 

什么是Android - 嵌入式设备编程的历史-第一章(1

   暂时可以这样说,传统的桌面应用程序开发者已经被惯坏了。这个不是说桌面应用程序开发比其他开发很简单。总之作为桌面应用程序开发者,我们已经有能力按照我们的想法创造出各种应用程序。包括我自己,因为我也是从做桌面程序开始的。一方面,我们已经使得桌面程序更容易的与桌面操作系统来进行交互,并且和任何底部的硬件很自由的交互。这种类型独立自主的程序编制其实对于很小的开发者团体来说是不敢贸然趟手机开发这趟浑水的。 

注意: 

       在本部分讨论中,我提到两种不同的开发者:传统的桌面应用程序开发,他们能使用任何的编程语言,而且最终的产品和程序是用来运行“桌面”操作系统的;还有就是Android的程序开发者,为Android平台开发程序的JAVA程序员。我不是想说谁更好或者其它的意图。区别仅仅在于想说明并比较桌面操作系统环境的开发风格,工具。

    有很长一段时间,手机的开发者由大的著名开发组中的少数人组成,作为嵌入式设备的开发者。相对于桌面开发或者后续的网络开发,被视作更少“魅力”,而且嵌入式设备的开发通常因为硬件和操作系统而处于劣势。因为嵌入式设备的制造商们太小气,他们要保护他们硬件方面的秘密,所以他们给开发者们非常有限的库来运行。

    嵌入设备与桌面系统显著不同的一部分是嵌入设备是个“芯片上的电脑”。例如:说起你的标准电话遥控。这个并不是一个非常强大并且复杂性的技术。当任何的按钮被按下去,一个芯片解释一个信号以一种方式已经被编程进了设备。这个允许设备知道什么是从输入设备(键盘)来的需要。并且如何的响应这些命令(比如,打开电视机)。这个是一个简单的嵌入式设备的编程。总之,不管你相不相信,像这样的简单设备绝对的和早期的手机和开发有着紧密的联系。

    大多数的嵌入式设备运行(有些还在运行)在私有的操作系统。原因是选择并创建一个私有的操作系统而不同定制的系统是产品必然选择。简单的设备不需要非常健全和优化的操作系统。

    作为一个产品的演化,更多复杂的嵌入式设备,如早期的PDA,家庭安全系统和GPS等。5年前某种程度上都转移标准的操作系统平台上。小的操作系统如Linux,或者一个微软的嵌入式平台,已经在嵌入设备上变得普遍了。设备演变的那些时间里,手机已自己的路径开始分支出去。这个分支是显而易见的。

     差不多开始的时候,手机作为一个外围设备并且运行私有软件,而这些软件被制造商们所拥有和控制,而且几乎可以被认为是一个“关闭”的系统。习惯使用私有操作系统主要是制造商自己开发硬件,或者至少定义了开发的目的只是用来运行手机。最终的结果就是使开放成为不可能。现有的软件包或者解决方案会可靠的和他们的硬件交互。而且,制造商想要保护他们硬件的商业秘密。以防允许进入而发现设备软件的水准。所以风尚就是,而且大多数仍然是使用完全私有并且关闭的软件来运行他们的设备。任何人想为手机开发程序必须需要详尽的私有环境来运行软件的知识。而解决方案就是直接从制造商那里购买昂贵的开发工具。这就孤立了很多的“自制软件”的开发者。

注意:

一个关于自制软件开发的文化包含了手机程序的开发。“自制软件”是指开发者通常不是工作在手机开发公司内,通常利用自己的时间在他们的设备上生产小的,一次性的产品。

    另外,使手机开发无法出手的是硬件制造商对于“内存和需要”左右为难的解决方案。直到最近,手机才能执行比打出和接听电话,查找联系人,发送和接收短消息。不是今天“瑞士军刀”的技术。及时在2002年,在消费者的手上,带照相机的手机还是不多见。在1997年,小的应用程序如计算器和游戏爬进了手机内,但是强大的功能仍然是手机的拨号盘本身。手机还不想今天一样是一个多用途,多功能工具。没有人预见互联网浏览的需求,MP3播放,或者更多的是我们今天定制的功能。在1997年,手机制造商们没有预见消费者需要的是一个一体化的设备。但是,即使这个需求展现出来,设备内存和存储容量还是一个需要克服的大的障碍。更多的人可能想要他们的设备是一个多功能一体化的工具,但是制造商们不许跨越他们的障碍。

    让问题变得简单,就要在任何的设备让内存来存储并运行程序,包括手机。手机作为一个设备,直到最近还没有足够多内存来执行“额外”的程序。在最近的两年里,内存的价格已经达到了非常低的水平。设备制造商们有足够的能力压低价格来包含更多的内存。很多的现在的手机标准内存已经超过了90年代中期电脑内存。于是,现在我们有需求,而且有内存。我们可以直接跳到为手机开发酷的应用程序了,对吗?不完全是这样。设备的制造商们仍然紧密的保护他们的操作系统。有一些在手机上开放JAVA为基础的小运行环境。更多的是不允许。即使允许运行JAVA应用程序但还是不允许进入核心的系统。而这些是桌面开发者习惯于拥有的。

开放手机联盟和Android-2)

   这个对于应用程序开发的障碍开始在2007年的11月份被打破,当Google在开放手机联盟下发布Android。开放手机联盟是一个硬件和软件开发者的集合,包括谷歌,NTT DoCoMo,Sprint Nextel和 HTC。他们的目标是创建一个更多的开放手机环境。在开放联盟第一个被发布的产品就是移动设备操作系统Android。(更多关于开放手机联盟的信息,见:www.openhandsetalliance.com)。

    对于这个Android的发布,谷歌使很多开发工具和向导成为可能来帮助在新系统上可能的开发者。帮助系统,平台软件开发包(SDK),甚至一个开发者的论坛,可以在谷歌的Android的网站上找到,http://code.google.com/android. 这个网站应该是你的起点,而且我极度推荐你去访问。

注意:

谷歌为了推动这个新的Android操作系统,甚至为寻找新的Android程序而设立了1000万美元的奖金。

    运行Linux, Windows 或者即使PalmOS的手机是很容易找到,如本文所述,没有硬件平台已经宣告可以来运行Android.HTC,LG电子,摩托罗拉和三星都是开发手机成员,在Android的发布下,我们希望在不久的将来有一些Android为基的设备。在2007年11月发布时,系统自身还仍旧是一个测试版的程序。这是个对开发者的好新闻因为它给了我们一个罕见的提前看到将来的设备和有机会来开始开发应用程序,而当硬件发布时就可以运行。

注意: 

这个策略明确的给了开放手机联盟一个大的优势,超越其它手机操作系统开发者。因为当第一代设备发布时会有数不尽的可用开发程序可以运行。 

介绍Android 第一章(3)

    Android,作为一个系统,是一个运行在Linux2.6核心上的JAVA基础的操作系统。系统是非常轻量型的而且全特性。

图 显示了一个未经修改的Android桌面屏幕。

    Android应用程序用JAVA开发而且很容易被放置到新的平台上。如果你没有下载JAVA或者不确定那一个版本你需要,我在第二章详细列出了开发环境的安装。其他Android的特点包括一个加速3-D图形引擎(基于硬件支持),被SQLite推动的数据库支持,和一个完整的网页浏览器。

    如果你熟悉JAVA编程或者是任何种类的OOP开发者,你可能使用程序用户接口(UI)开发 - 那就是,UI安置是直接在程序代码中有句柄的。Android,识别并许可UI开发,而且支持新生,XML为基础的UI布局。XML UI布局对普通桌面开发者是一个非常新的概念。我会在本书的相关章节里描述XML UI布局和程序化UI开发。

    Android 另一个更令人激动和关注的特点是因为它的样式,第三方应用程序——包括“自制的”——会和系统捆绑的有着同样的优先权。这是和大多数系统不同之处,但是给了嵌入式系统程序一个比由第三方开发者创建的线性优先权大的优先执行权。而且,每一个应用程序在虚拟计算机上以一个非常轻量的方式按照自己的线路执行。

    除了大量的SDK和成型的类库可以用之外,对激动人心的特性对于Android的开发者来说是我们现在可以进入到操作系统可以进入的地方。也就是说,如果你要创建一个应用程序打一个电话,你已经进入到电话的拨号盘。加入你要创建一个应用程序来使用电话内部的GPS(如果安装了),你已经进入了。对于开发者创建动态和令人好奇的程序已经敞开大门。

    和上面这些可用的特点相同,谷歌已经非常迫切的奉送一些特性。Android的开发者可以将自己的应用程序和谷歌提供的如谷歌地图和无所不在的谷歌搜索绑在一起。假设你要写程序在谷歌地图上显示一个来电话者的的位置,或者你要储存一般的搜索结果到你的联系人中。在Android中,这个门已经完全打开。

第二章开始你Android的开发旅程。你会学到如何和为什么使用特定的开发环境或者综合的开发环境(IDE),而且你会下载并且安装JAVA IDE Edipse.

问专家:

Q:谷歌和开放手机联盟的区别在哪里?

A:谷歌是开放手机联盟的一个成员。谷歌在收购了Android的原开发后,在开放手机联盟发布了操作系统。

Q:Android有能力运行任何的Linux软件吗?

A:没必要。我坚信会有一种方式绕开大多数的开源系统和应用程序用Android SDK编译而用于Android。主要原因是Android程序执行特定的文件格式,这会在后续的章节中讨论。

Android示例 第四章(4

Android示例SDK/SAMPLES内,包含了6个示例可以很好的描述Android的一些功能: 
● API Demos 

● Hello, Activity! 

● Lunar Lander 

● Note Pad 

● Skeleton App 

● Snake

      这些示例由谷歌提供来给你一个快速的印象,那就是如何快速的开发Android的应用程序。每一个应用程序描述Android不同功能的一块。你可以用Eclipse打开并且运行这些应用程序。下面是对于每一个示例的简要描述。

API Demos 
这个API示例应用程序说明在一个单独的Activity内如何展示多个API功能的示例。

提示:

一个Activity是一个Android的应用程序。Activities会在后续的章节中深入展开。

如下图(略)所示的,这个API示例应用程序包好了很多的,小的不同的Android功能的例子。这些例子包含3-D图形变换,列表,过程对话框和一个手指-画图示例。

运行API样本示例应用程序

使用Eclipse,装载API示例应用现场作为一个Android项目。要做到这个,在Eclipse菜单选择文件|新建|项目,一个新的Android项目向导会启动。现在不用担心向导页面上的一些选项。只是选择从现有的项目中创建项目就好了,并且浏览到API示例所在的目录,点击这个示例。当项目装载好了,选择运行,在Android模拟器中来查看。用你自己的方式去查看超过40个示例吧,使用每一个示例去熟悉这些术语和功能。

Android的几个示例 第四章(5

Hello, Activity!

Hello, Actoviry 应用程序,是一个简单的Hello World风格的应用程序。虽然设计简单,但是它展示了平台的能力。在下一章,你会创建自己的Hello World!风格的程序。

Lunar Lander月球登陆
Lunar Lander是一个在Android模拟器上玩的游戏。这个游戏是2-D的游戏它在Android上工作是多么的简单。控制非常的简单,而且游戏不是非常的复杂。总之,对游戏开发来说是一个良好的开始。

月球登陆执行一个简单控制方案(上,下,左,右)。游戏同时显示相关的非固定的图形并且对平台来说,令人印象深刻。复杂游戏的理论如冲突检出是以一个简单的方式使用的。虽然本书没有包含Android平台游戏编程的内容,加入你有兴趣来做这个,你或许可以从月球登陆中获得某些启发。

Note Pad写字板
Note Pad, 允许你打开,创建并且编辑小的笔记。写字板不是一个全功能的字符编辑器,所以不要期待是和Windows Mobileword的竞争对手。但是,作为一个演示工具,使用非常少的代码就能实现这个效果已经非常的棒了。

Skeleton App框架应用
Skeleton App这是一个基本的程序,展示了几个不同的应用程序功能。如字体,按钮,图形和表格。如果你想自己运行Skeleton App,真的不应当把它排除在外,参考Skelete App,它会提供不少关于如何执行特定的条款。

Snake 

最有一个在Android SDK的示例就是这个蛇了。这是一个小的SNAFU风格游戏,比月球登陆复杂。

注意:

如果你打开每一个示例应用程序的文件夹,你会看到一个文件夹命名为src。这个是给出示例源代码的文件夹。你可以为其他任何的应用程序来查看,编辑并且重新编译这些代码。利用这些源代码来学一些Android平台技巧和提示。

第二章 下载和安装Eclipse总则

下载和安装Eclipse总则 第二章(1

-关键技能&概念 

-选择一个开发环境 

-下载 Eclipse 

-安装和配置Eclipse 

Android应用程序是在JAVA下开发的。Android自身不是一个语言,但是是一个运行应用程序的环境。这样,理论上你可以使用任何发布或者综合开发环境(IDE)来开始你的开发。事实上,你可以选择非IDE开发。

提示: 

在本章稍后,我会介绍不使用IDE或者“命令行接口”(CLI)来开发Android应用程序。这期间,我不会在书中的每一个例子都使用这种技术,你将会学到如何在CLI里开发的基础知识。 
    假如你对使用JAVA的IDE比较舒服,如Borland的JBuilder或者开源NetBeans,你可以尽管去使用。有了中等的水平的经验,你应当可以适应本书大部分的例子。但是,开放手机联盟和谷歌认同一个JAVA的IDE,那就是:Eclipse. 
注意: 

如果你选择不用Eclipse来跟从本书的例子,你需要看看你的IDE文档关于编译和测试你的Android的程序。书中的例子只给了如何在Eclipse中编译和测试程序的说明,在Eclipse中使用Android的plugin。

    本章简明的描述了如何下载和安装Eclipse以及所要求的JAVA Runtime Environment(JRE)。很多的时候,安装向导和教材趋向于跳过简单的步骤。我已经发现跳过简单的步骤经常忽略重要的条目。因为这个原因,我在本章内包含了从下载到安装的所有步骤。

为什么是Eclipse? 
    为什么Eclipse是推荐的Android程序开发的IDE呢?对这个特定的认同有一些原因: 

    1、为了保持开发手机联盟真正开放移动开发市场的宗旨,Eclipse是有着同样显著特点的,免费的Java IDE可以使用。Eclipse同样容易使用,最少的学习时间。这些特性让Eclipse对于固定的,开放的Java开发成为吸引人的IDE。 

2、开发手机联盟已经为Eclipse发布了一个Android的plugin,允许你来创建Android-定义项目,编译它们,并且使用Android模拟器来运行和调试程序。当你开发你的第一个Android程序时,这些工具和能力将会是非常宝贵的。你还是可以用其它的IDE来创建Android程序,但是Android的plugin为Elipse创建某些元素——如,文件和编译设定。这些来自Android-plugin的帮助将缩短你宝贵的开发时间并减少学习的弯路,那就意味着你可以花费更多的时间来创建惊人的应用程序了。 

注意:

Elipse同样也可用于苹果和Linux系统,有着强大的能力,在不同的操作系统,意味着几乎每个人可以在任何的电脑上开发Android的应用程序。不过,本书的例子和电脑截图觉来自与微软Windows版本的Eclipse。记住这一点,如果你使用其他的电脑操作系统。你的界面可能看上去会有轻微的不同,但是总体的功能不会改变。如果在Linux的Eclipse有一些主要的操作不同点的话,我会举例说明。我会举出一些在Linux上的列子。而主要的例子会是Linux/Android的命令行环境(CLE)。

下载和安装JRE - 第二章(2

在你下载和安装Eclipse之前,你必须确保在电脑上下载并安装了Java Runtime Environment(JRE)。因为Eclipse作为一个程序是由Java写成,它要求JRE来运行。如果JRE没有安装或被检测到,如果你试着打开Eclipse,你会看见下面的错误: 

如果你已经是一个Java的开发者并且已经在电脑上安装了Java,你还是要按照提示安装,确保安装了正确版本的JRE。

注意: 

大多数使用过网络或者以网络为基础的应用程序的人,安装过JRE。JRE允许你运行Java基础的应用程序,但是它不允许你去创建它。要创建Java应用程序,你需要下载并安装Java Development Kit(JDK),这个包含了创建Java应用程序所需的所有工具和库。如果你不熟悉Java,记住这一点就行了。对于书中提到的例子,我会下载JDK,因为它也包含了JRE.虽然你不需要JDK来运行Eclipse,但是你还是可以在本书后续章节的开发中使用。 

   导航到Sun公司的下载页面,http://developers.sun.com/downloads/,如下面的插图(略)所示。正常情况你只需要JRE来运行Eclipse,但是对于本书的目的,你应当下载包含了JRE的完整的JDK,下载JDK的原因是在本书的后面,我会提到只使用JDK而非Eclipse来开发Android程序。如果你想跟从教材的话,你会需要完整的JDK。

从SUN的下载页面,导航到适当JDK的下载部分。选择并下载,如下图(略): 
    对于书中例子,我选择使用Java 5 JDK Updata 14,因为在Eclipse文档中明确说明这是个支持的版本。要下载Java 5 JDK,选择你要下载的平台来下载。你可能简单的跟着下载Java 6 JDK。但是,如果你要下载旧的JDK 5,你需要点击前一个发布的链接,如图(略): 

注意:

下载前,你必须同意并接受Sun公司的专利使用权转让协定。 

在Java Se以前一个发布下载页面,点击J2SE 5.0 下载链接,然后点击JDK 5.0 Update x下载按钮,x是最后的升级号码(14是本书写的时候的号码,你下载的时候可能会有所不同)。 

如果你正在下载一个到微软Windows的环境,当你见到如下图(略)所示的通知时,点击Run来开始JDK的安装。 

提醒: 

如果你想要保存一份JDK包的备份,点击Save而并非Run。总之,当你选择保存了JDK,确保注意保存位置。在下载结束后,你需要导航到下载位置并且手动执行安装包。

在安装期间,你会被提醒阅读协议,如下图(略)。同意之后,点击Next,然后就可以选择你的定制安装选项了。 

这里只有一点你需要改变的,除非你是一个成熟使用Java的人并且需要选择特定的选项,在这种情况下,请自由的改变你需要的安装选项。下面是Java JDK安装的定制安装图(略)。 

为了保持过程的简单性,并且完全地标准化,你应当接受软件自身的安装建议——选择缺省的设定——并且点击Next来继续安装。再次强调,如果你想要订制改变,请按照你自己的方式进行。总之,如果在后面的章节你遇到麻烦,你会需要修改你的安装选项。当安装完成的页面出现,如下图:(略),点击Finish,然后你的安装就会完成。 

一旦你完成Java JDK的安装——而且根据缺省,JRE也会安装——你可以开始安装Eclipse了。

下载和安装Eclipse - 第二章(3

导航到www.eclipse.org/downloads 的下载页面,如下图(略)。根据开放段落申明,需要JRE运行环境(推荐Java 5 JRE)来开发Eclipse,而这个我们已经在上节描述过了。在这个站点下载为Java开发者准备的Eclipse的IDE。软件包比较小(79MB)并且应当下载很快。确保你不是下载了Eclipse IDE for Java EE 的开发包,因为这个是有点不同的产品而且我不会介绍它的使用说明。

   在你下载了Elipse以后,是时候来安装它了。导航到软件包下载的位置。写这本书的时候,最新的Eclipse软件包Windows版本的文件是eclipse-java-curopa-fall2-win32.zip.解压缩软件包并且运行Eclipse.exe。Eclipse按照缺省方式安装到以用户目录(微软Windows),但是你或许想安装到你的程序文件目录下。这样会保持你应用程序的有序而且允许你设定不同的目录作为工作空间。下图(略)显示了软件启动的欢迎画面。

注意: 

如果你没有看见欢迎画面,试着重新启动电脑。如果重启后没有帮助的话,只下载并安装Java 5 JRE。 

一旦Eclipse安装开始,你会被提醒来创建一个缺省的工作空间,或者文件夹。和其他大多数开发环境一样,项目被创建,并且保存到这个工作空间内。缺省的工作空间路径是你的用户路径,选择不同路径,点击Browse来导航。如图(略)。 

我建议你同样也选中选择框来定义你所有的项目到一个工作空间。选中这个框,当创建新项目时,你就会少一个需要担心的事情,而且你总是会知道在哪个路径里能找到你的源文件。在本书内,有时你需要导航到项目文件,并且在Android开发环境的外部工作,所以知道你文件的所在位置是非常有帮助的。

选择工作空间之后,点击OK。 在这里,你的开发环境被下载好和安装。虽然Eclipse的安装似乎很快,你仍然需要在创建你的第一个Android项目前配置Eclipse。很多的配置工作都是和Android SDK和Android plugin有关。 

下一步你需要下载并安装Android SDK,并且为Eclipse下载并安装Android plugin。然后配置Eclipse设定。在第三章的结尾,你会有一个可以开发应用程序的完整的开发环境。然后你会浏览Android SDK并且在第五章创建你的第一个Hello World!应用程序。 

问专家 

Q:Eclipse是用来开发Java的,但是Android能运行其他语言所写的程序吗? A:写这本书时,没有SDK或者模拟器可以让Android来运行Java以外的程序。 

Q:能使用Eclipse(和Android SDK)和JRE非5的版本 一起工作吗? 

A:技术上说你可以使用Eclipse和版本5或者更新的版本一起工作,但是最新版本的Eclipse仅仅在Java 5 JRE上进行过测试。

第三章 下载和安装Android SDK

下载和安装Android SDK - 第三章(1

关键技能和概念 

-下载Android SDK 

-使用Eclipse的可升级特性 

-为Eclipse下载,安装并配置Android Plugin 

-检查PATH声明 

在前面的章节中,你下载并安装了主要的开发环境,Eclipse。现在,你的原始开发环境已经建立了,使用Eclipse作为你的Java IDE,你可以用它来开发Java的应用程序。你必须以某种方式来配置它,以减轻Android的开发。 

因为Eclipse是Java开发环境,你可以很简单的创建并编辑Java项目。但是,如果没有可以理解的库,规定Android应用程序应当如何工作,你就无法开发任何应用可以在Android为基础的设备上运行的程序。要开始创建Android项目,你需要下载并安装Android SDK。然后你需要为Eclipse下载相关的Android plugiin来使用SDK。有了这些部件的支撑,你就可以开始开发工作了。 

如果你已经拥有任何的开发经验,很可能你已经熟悉使用SDK的过程。桌面程序的开发者,不管在哪一种的开发平台上开发,使用SDK来创建他们希望运行的系统上的应用程序。Android SDK和其它的SDK相比没有任何的不同,它包含了所有的创建运行在特有的Android平台上应用程序所需的Java代码库。SDK还包括帮助文件,文档和Android模拟器,大量的开发和调试工具。 

注意: 

第四章深入的阐述了Android SDK大多数的功能。 

作为开始,你准备从谷歌Android开发网站上下载Android SDK,网址:http://code.google.com/android 谷歌Android开发的主页上包含为Android平台开发的大量有价值的工具和文档,包括链接到Android开发者论坛。 

提示:

如果你在开发的过程中遇到问题,你第一个找答案的地方应该就是Android开发者论坛。http://code.google.com/android/groups.html.这里有新手,开发者和黑客的讨论组。并且一个常规问题讨论组。考虑到Android是一个全新的平台,Android开发者论坛是较少的能找到综合,可靠信息的地方。

下载Android SDK - 第三章(2

从谷歌的http://code.google.com/android 网页可以很容易的找到Android SDK软件包。从开发的主页,点击下载SDK的链接开始。在你同意了Android SDK的软件许可协议后,你会看见Android SDK的下载页面。Android SDK软件包对于Windows版本是79MB大小,你应当能够很快的下载。根据你的操作系统选择软件包开始下载。 

注意:

软件包的大小根据不同的操作系统可能不一样。 

说到Android SDK,这里没有“setup”或者安装过程。这里,你必须跟着下面一些列的设置,在Eclipse开发环境里配置Android SDK。第一步是获得Android plugin,然后配置它。

为Eclipse下载和安装Android Plugin ,设置Android SDK的第一步就是为Eclipse开发环境下载和安装Android Plugin。Plugin的下载和安装可以同时进行,而且非常的简单。 

1.打开Eclipse应用程序,你将会下载为Eclipse IDE 准备的Android Plugin。 2.选择帮助|软件升级|寻找和安装。 

3.在安装/升级的窗口,会允许你执行安装和下载在Eclipse任何可用的plugin,点击搜索新特性选项,然后点击下一步。 
4.Update sites to Visit 这个窗口会列出所有可获得Eclipse plugin的网站。但是,你所需要的Android for Eclipse 没有列在这里,所以要下载这个Android plugin你必须要告诉Eclipse到哪里去找它。所以点击 New Remote Site这个按钮。 

5.在New Remote Site对话框内,你要提供两个信息:网站的名称和网址。名字只是便于显示并不影响下载。我们可以输入Android Plugin。在URL字段。输入:https://dl-ssl.google.com/android/clipse.点击OK。 

注意: 

这里填写的名字只是帮助你识别。你可以输入任何你想要的名字。 

6.现在新的站点Android Plugin应当在可用的站点列表上了。这时,Eclipse还没有开始寻找plugin,这只是个路径你告诉Eclipse。 

7.选中Android plugin的选择框然后点击完成。Eclipse开始任何可用的plugin。 

8.在搜索结果页面,选择Android Plugin然后点击完成。 

9.在特性安装的许可页面,点击接受许可协议,然后点下一步。 

注意: 

记住所有的plugin都安装在/eclipse/plugins的路径里。这个信息会帮助你假如你需要自己放置Android plugin。 

10.Eclipse下载Android plugin。本书写作时,plugin的版本是0.4.0.200802081635.在最终的plugin的安装页面,是特性核实,点击安装所有来完成Android plugin的安装。 

安装完成后就是必须去配置plugin。

Eclipse配置Android Plugin - 第三章(3

在完成了Android plugin的安装之后,Eclipse应当提示你重新启动应用程序。如果它没有提示你,现在就重新启动Eclipse。重启会确保安装的plugin有机会被初始化。安装下面的方式来配置是非常重要的。 

配置Android plugin的方式是从Eclipse的Preferences 窗口开始的,按照下面的步骤: 

1.从Eclipse的程序主窗口中|Windows|preferences. 

2.再出现的窗口中,在左边选择Android菜单。在窗体的右边点击 Browse,找到Android SDK的在硬盘的存放位置。输入到SDK Location的字段中。Eclipse需要这个信息来进入到Android提供的工具,比如模拟器。选中Automatically Syne Projects to Current SDK选择框,然后点击应用。 
注意: 
Android plugin for windows是以zip文件格式发布的。而且它包含了一个非常长的文件名称。android_m5-rc14-win32.重命名到一个比较容易管理的名字,这会在将来的章节中对你有帮助,特别是到命令行编程。你可能也会解压缩它到程序文件路径里。 
4.Android SDK的最后一个设置是把它放到PATH声明内。如果你用的是微软的Windows,右击我的电脑,选择属性,然后选择高级。 

5.点击环境变量。在这里可以编辑PATH声明。 

6.在系统变量中,找到PATH然后双击它。 

7. 在编辑系统变量的对话框中增加你的Android SDK路径,使用分号来分别现有的系统路径。点击OK。在环境变量的窗口再次点击OK。 

现在,Android SDK, Eclipse和Android plugin被完全的配置好了并且准备被用来开发了。在下一章,你会浏览Android SDK,了解它的特性。Android SDK包含很多工具来帮助你来开发全功能手机应用程序,并且下一章提供一个好的概述。 

问专家: 
Q:Android SDK可以用在非Java的语言上吗? 
A:不行。Android应用程序只能在Java系统上被开发。 
Q:会有更新的Android SDK吗? 
A:是的!在写本书的时候,一个SDK的升级发布了,并且解决了平台上的很多问题。我建议经常检查开发页面的更新。 
Q:如果升级了,我如何更新我的SDK? 
A:更新SDK是非常棘手的。当一个新的SDK发布,必须是plugin也发布。在写本书时,新的SDK和新的plugin都发布了。我试图使用“Provided(提供的)”的升级工具来改变版本。最终无果并留给了我两个的版本,都工作不正常。我最终不得不卸载了它们并且重新安装最新的一个。然后那个最新的SDK工作正常了。我建议任何面对SDK或者plugin升级的人都采用相同的过程。简单的卸载老版本,然后安装新版本。不要升级。

第四章 浏览Android SDK

浏览Android SDK - 第四章(1

关键技能和观念 
—使用Android SDK文档 
—使用Android 工具 
—使用sample应用程序 
—学习Android程序的生命周期 
    现在,你已经建立了开发环境,准备去浏览Android SDK了,它包含了很多的文件和特别的工具,可以帮助你设计并开发运行在Android平台上的应用程序。这些工具设计的非常的好,而且可以帮助你制作一些难以置信的应用程序。在开始编程之前你真的需要熟悉Android SDK和它所带的工具。 

Android SDK还包含了一些可以让应用程序进入Android特性的库,比如和电话功能关联的(呼出和接电话),GPS功能,和短消息。这些库组成了SDK的核心而且会是你经常会使用到的,所以,有一些时间来学习所有关于核心的库。 
    这一章包括了所有这些在Android SDK重要的条款,在本章的结尾,在你自己熟悉了Android SDK内容之后,你会足够舒适的开始写你的应用程序。总之,任何的事物都是这样,在你开始练习之前,你必须熟悉这些内容和指示。 

注意: 
我不会去介绍Android SDK的每一个细节,谷歌已经在SDK内做了非常好的文档。为了避开花费不必要的时间来讨论如何工作,我已经尽量少的做一些简要的说明。我只是会讨论一些重要的话题和条款,然后按照你自己的步伐去探索更深的层次。

Android SDK是什么 第四章(2  

    

Android SDK下载后会是一个简单的ZIP文件压缩包。Android SDK的主体是一些文件,连续性的文档,可编程的API,工具,例子和其它。本部分详细的说明这个Android SDK到底有些什么。

    提示:

第三章建议你解压缩Android SDK到程序文件的文件夹,所以容易被找到。如果你找不到SDK,因为你使用解压缩的缺省设定,应当在下面的文件夹/%downloadfolder%/android-sdk_m5-rc14_windows/android-sdk_m5-rc14_windows.(译者注:根据下载的文件名不同,这个文件夹也会不同哦).

    找到解压后的Android SDK的文件夹,然后可以在文件夹内浏览。在根目录会有几个文件,像android.jar(一个编译过的,包含核心SDK库和api的Java应用程序)并且一些发布笔记,剩下的Android SDK被分成3个主要的文件夹:

● Docs 包括所有的Android文档

注意:

这些文档同样也可以在Android开发网站上找到 http://code.google.com/android .

● Samples 可以在Eclipse内编译和测试的6个应用程序例子

● Tools 包含所有在开发过程中需要的开发和调试工具

下面的部分会讨论更多关于在每一个文件夹内的内容。每一个API示例被编译过并且可插入至Android。在后续学习如何在windows和Linux中使用命令行选项创建和编译应用程序的章节中会讨论更多的工具。

Android 文档 第四章(3

   

   Android文档被放在Android SDK内的Docs的文件夹内。文档内提供了如何下载和安装SDK的每一个步骤,“Getting Started”开发应用程序的快速步骤和软件包定义。文档是HTML格式并且有一个documentation.html在SDK的根目录可以进入整个文档。下面的插图(略)就是Android SDK文档的主页。

    你可以从documentation.html上提供的链接导航到Android SDK内包含的文档。

注意:

当你浏览Android SDK时,你可能想到一些页面是一些错误的链接或者丢失了。因为当你点击某些链接时,屏幕右边可能会显示空白,不过,如果你再往下滚动页面你将会明白页面只是没有被排列好。

    在这个Android SDK内,我已经发现有一些部分比其他的部分更重要。对于我来说最重要的Android SDK文档如下(它们会出现在导航条上): 
● Reference Information 
● Class Index 
● List of Permissions 
● List of Resource Types 
● FAQs 
● Troubleshooting 
    当你开始开发,Troubleshooting 文档的分类部分将会特别有作用。当你深入本书并且开始开发你自己的应用程序,你会发现文档的Reference Information 部分会更有帮组。例如,List of Permissions 分类部分将会非常的有帮助,当你跟着本书创建更复杂的应用程序时。虽然这个现在对你用处不大。花些时间熟悉一下Android文档吧。

Android示例 第四章(4

Android示例在SDK/SAMPLES内,包含了6个示例可以很好的描述Android的一些功能: 
● API Demos 
● Hello, Activity! 
● Lunar Lander 
● Note Pad 
● Skeleton App 
● Snake

    这些示例由谷歌提供来给你一个快速的印象,那就是如何快速的开发Android的应用程序。每一个应用程序描述Android不同功能的一块。你可以用Eclipse打开并且运行这些应用程序。下面是对于每一个示例的简要描述。

API Demos 
这个API示例应用程序说明在一个单独的Activity内如何展示多个API功能的示例。

提示:

一个Activity是一个Android的应用程序。Activities会在后续的章节中深入展开。

如下图(略)所示的,这个API示例应用程序包括了很多的,小的不同的Android功能的例子。这些例子包含3-D图形变换,列表,过程对话框和一个手指-画图示例。

运行API样本示例应用程序

使用Eclipse,装载API示例应用现场作为一个Android项目。要做到这个,在Eclipse菜单选择文件|新建|项目,一个新的Android项目向导会启动。现在不用担心向导页面上的一些选项。只是选择从现有的项目中创建项目就好了,并且浏览到API示例所在的目录,点击这个示例。当项目装载好了,选择运行,在Android模拟器中来查看。用你自己的方式去查看超过40个示例吧,使用每一个示例去熟悉这些术语和功能。

Android的几个示例 第四章(5

Hello, Activity应用程序,是一个简单的Hello World!风格的应用程序。虽然设计简单,但是它展示了平台的能力。在下一章,你会创建自己的Hello World风格的程序。

Lunar Lander月球登陆 
Lunar Lander, 是一个在Android模拟器上玩的游戏。这个游戏一个2-D的游戏在Android上工作是多么的简单。控制非常的简单,而且游戏不是非常的复杂。总之,对游戏开发来说是一个良好的开始。

月球登陆执行一个简单控制方案(上,下,左,右)。游戏同时显示相关的非固定的图形并且对平台来说,令人印象深刻。复杂游戏的理论如冲突检出是以一个简单的方式使用的。虽然本书没有包含Android平台游戏编程的内容,加入你有兴趣来做这个,你或许可以从月球登陆中获得某些启发。

Note Pad写字板 
Note Pad, 允许你打开,创建并且编辑小的笔记。写字板不是一个全功能的字符编辑器,所以不要期待是和Windows Mobile中word的竞争对手。但是,作为一个演示工具,使用非常少的代码就能实现这个效果已经非常的棒了。

Skeleton App框架应用 
Skeleton App, 这是一个基本的程序展示了几个不同的应用程序的功能。如字体,按钮,图形和表格。如果你想自己运行Skeleton App,真的不应当把它排除在外,参考Skelete App,它会提供不少关于如何执行特定的条款。

Snake 蛇

最后一个在Android SDK的示例就是这个蛇了。这是一个小的SNAFU风格游戏,比月球登陆复杂。

注意:

如果你打开每一个示例应用程序的文件夹,你会看到一个文件夹命名为src。这个是给出示例源代码的文件夹。你可以为其他任何的应用程序来查看,编辑并且重新编译这些代码。利用这些源代码来学一些Android平台技巧和提示。

Android工具 第四章(6

Android SDK提供给开发者一系列功能强大并且有用的工具。在本书内,你会直接使用它们。本部分对其中的一些工具做一个快速的查看,而在后续的章节中会更加深入的进行,那就是在命令行开发中。

注意:

对于Android SDK中包含的更多的工具,请查看Android文档。

emulator.exe

Android SDk中一个最重要的工具就是这个emulator.exe。emulator.exe启动Android模拟器。Android模拟器被用来在一个假的Android环境中运行你的应用程序。在本书写作时,还没有发布Android平台可用的硬件,emulator.exe将会是唯一的方法作为测试应用程序的平台。

    你可以从Eclipse或者命令行中来运行emulator.exe。在本书中,通常会使用Eclipse启动Android模拟器环境。总之,为了给你所有信息关于在Eclipse之外用Android SDK编程。在第六章里会介绍emulator.exe的命令行使用来创建Hello World应用程序。

    当使用Android模拟器来测试你的应用程序,有两个选择可以导航到用户界面。第一,带按钮的模拟器。你可以使用这些导航按钮来导航Android和任何的你为这个平台开发的应用程序。

提示:

电源 On/Off,声音的大小按钮被隐藏在虚拟设备的旁边。当你用鼠标移过它们时,会被自动识别。

很多的高端手机现在都包含了触摸屏,第二个输入选项就是这个模拟的触摸屏。使用你的鼠标作为一个尖笔。模拟器屏幕上的对象可以相应鼠标的动作。

adb.exe 
当你使用命令行编辑器时另外一个工具会变得非常的有用,它就是Android 调试桥,或者adb.exe。这个工具允许你发出命令到模拟器工具。当你在命令行环境下工作时,这个adb工具允许你做下列工作。

● 开始并且停止服务 
● 安装和卸载应用程序 
● 移动文件至模拟器或者从那里移动 
MKSDCARD.exe

MKSDCARD.exe 是一个非常有用的工具,当你测试一个应用程序,而这个程序需要读取或者写入文件到一个插入到移动设备的SD储存卡中。MKSDCARD.exe在你的驱动器中创建一个小的驱动并且会保留测试文件。然后模拟器会把这个小的部分当成一个SD储存卡。

DX.exe 
DX.exe 是Android SDK的编译器。当运行你的Java文件,DX.exe将创建一个带有.dex后缀—Dalvik可执行格式的文件。这些会被Android设备正确的理解和运行。

注意:

Android可执行文件是叫做Dalvik可执行文件,Dalvik虚拟机器以自己脉络来运行每一个应用程序,而且程序的优先权和Android核心程序一致。

activityCreator(.bat 或者 .pn)

activityCreator 是一个简单的命令行工具被用来设定基本的开发环境。当从命令行运行时,activityCreator将设置一个需要的基本Android应用程序所需的壳文件。有了这些壳文件是非常有用的,特别是你不使用Eclipse。当你创建一个新项目时,Android plugin for Eclipse 通过呼叫activityCreator来设置这些壳文件。依据你运行的是哪一种环境类型,你会看到不同的activityCreator的脚本文件。如果你使用Windows环境,这个就会是.bat文件,否则就是python(.pn)脚本。简单的执行这些脚本,就会依次的使用正确的参数来呼叫真正的activityCreator过程。

Android APIs - 第四章(7

    APIs或者叫做应用程序编程接口,是Android SDK的核心。一个API是应用程序开发者在特定平台上创建程序的功能,方法,属性,类别和库的集合。Android API包含所有你创建与Android为基础程序交互的特定信息。

    Android SDk同样包含2套api,—谷歌的API和可选的API.后续的章节中将重点放在这些API上,因为你将利用它们写程序。现在,让我们快速的说明一下它们包含哪些你熟悉的使用。

谷歌 api 

    谷歌API含在Android SDK中并且包含编程参考允许你绑定你的程序到现有的谷歌服务中。假如你写一个应用程序允许你的用户通过你的程序进入到谷歌提供的服务中,你需要包含谷歌的API.

    找到android.jar文件,谷歌的API包含在com.google.*包装中。只有很少的包含了谷歌的API.一些包装随着API一起发布包含了图形,移动性,联系人和日历等工具。总之,我们会把本书中把重点放在谷歌地图上。

    使用com.google.android.maps包装,这个包含了谷歌的地图,你可以创建一个应用程序无缝的和熟悉的谷歌地图界面对接。这个包装打开了一个等待着被开发的整个有用的应用程序世界。

    谷歌api还包含了一套有用的包装,来允许你利用由Jabber开放源码社区开发的最新的Extensible Messaging和Presence Protocl(XMPP)。使用XMPP,应用程序可以快速知道户主在场或者是否可用(从信息和通信中)。如果你要利用电话的短信功能来创建一个聊天类的程序,这个处理XMPP的API是非常有用的。

可选的api

    Android SDK包含了一些可选的api,它包括了一些标准Android api未包含的内容。说它们是可选的api意味着这些功能在手持设备上可能出现也可能不出现。也就是说一些为Android平台创建的设备可能包含升级或者一些特性而其他的没有。当利用在你的应用程序中利用这些可选的API时,包含了你的编程选项。

    其中的一个可选特性(本书的后面会使用)就叫做电话基础的GPS.Android LBS(位置基础的服务)api需要接受并利用设备上GPS单元的信息。如果结合Android LBS api和谷歌地图api,你或许有一个非常有用的应用程序会实时的显示你的位置。

其它可选的api包含利用蓝牙,Wi-Fi,播放MP3,进入并激活3-D-OpenGL硬件等。

应用程序生命周期 第四章(8)

    如果你有相当好的编程经验的话,你对应用程序的生命周期这一概念应该熟悉。一个应用程序的生命周期,由一些应用程序由开始执行到终止的步骤组成。每一个应用程序,不管是哪一种语言所写,都有一定的生命周期。Android应用程序也没有例外。本部分会仔细对比ASP应用程序和Android的应用程序的生命周期。

标准ASP程序应用程序生命周期

    标准ASP应用程序的生命周期和一个Android的程序生命周期非常的类似。ASP应用程序从开始到结束有5个步骤。这些步骤对所有的ASP程序是一致的。并且界定了ASP程序是什么。这些步骤按照次序如下:
1. Application_Start(程序开始)
2. Event(事件)
3. HTTPApplication.Init
4. Disposal
5. Application_End
提示:

有些ASP的参考材料考虑Disposal和Application_End在生命周期中成为一个步骤。但是,Disposal呼叫可以到达Application_End之前被打断。这个可以允许程序在真正结束之前执行特定的功能。 

    当应用程序被从服务器要求执行,开始呼叫Application_Start。这个过程依次的通向过程处理。当所有相关的应用程序模块被装载,HTTPApplicaation.Init被呼叫。程序执行事件,并且当用户试图去关闭它,Dispose被呼叫。Dispose然后转移过程到Application_End过程,来关闭程序。

    这是一个相当标准的应用程序生命周期。大多数的程序是这个生命周期:一个应用程序被创建,装载,拥有事件,并且被关闭。下面说明和Android应用程序生命周期的对比。

    Android应用程序生命周期是唯一一个系统控制多的应用程序生命周期。所有的Android应用程序,或者Actiities都运行在自有的过程中。所用的运行过程都被Android观察,并且取决于活动是如何运行的(就是说,一个前台活动,一个后台活动)Android可能选择去结束一个消耗系统资源的活动。

注意:

当决定是否关闭一个活动时,Android会考虑一些因素,如用户输入,内存使用和过程时间。一个Android或者的生命周期以一些特定的方式被称呼:

● onCreate
● onStart
● Process-specific events (for example: launching activities or accessing a database)
● onStop
● onDestroy

与其它程序的逻辑一样,一个Android应用程序被创建,过程开始,事件被执行,过程停止,并且应用程序结束。虽然有一些不同,很多的程序开发者应该不会对这样的生命周期感到别扭。

问专家:
Q: 谷歌会升级Android SDK吗?

A:是的。从我开始写这本书的时候,谷歌已经升级了Android SDK很多次了.谷歌会在Android的网站上发布最新的版本。
Q: 会有任何API试用版出现在最终产品中吗?
A: 或许不会。API试用版创建出来是为了炫耀产品能力的。虽然它们可能是核心解除的包含一些在API试用版里元素的应用程序,我们应该看不到月球登陆这个游戏出现在最终产品中。

第五章Android程序:Hello World! 

Android程序:Hello World! -第五章(1

关键技能和概念

● 创建新的Android项目

● 同Views一起工作

● 使用一个 TextView

● 修改main.xml 文件

● 在Android模拟器上运行应用程序

    为了让你能够对在Android上编程有一个良好的印象,在第六章,你会在Windwos平台和Linux平台上使用Android SDK创建命令行应用程序。或者说,本章包含了在Eclipse创建程序的过程,第六章包含了使用命令行工具的创建过程。因此,在继续之前,你应当检查你的Eclipse的开发环境是否被正确的配置。再次回顾一下第三章关于Adnroid SDK的PATH声明。同时要确保JRE也是在你的PATH声明中。

提示

如果当你运行命令行示例,有任何与配置有关的问题时,请参考第二章和第三章提到的步骤,并且查看Android SDK文档。

在Eclipse中创建你的第一个Android 项目

    要开始你的第一个Android项目,打开Eclipse.当你第一次打开Eclipse,它会打开一个空开发环境,这就是你要开始的地方。你的第一个任务是设置并且命名一个工作空间。选择 文件| 新建 |Android 项目,使你能够创建一个Android特有的应用程序向导。  

注意:

不要从新建菜单上选择Java项目。你的Android应用程序是在Java中写的,并且你在Java项目中进行开发,这个选项会创建一个标准的Java应用程序。选择Android项目来创建一个Android特有的应用程序。

    如果你没有看到啊Android项目这个选项,这就说明在Eclipse中,Android plugin没有被完全或者正确的安装。重新检查第三章中关于Android plugin的安装程序来修正这个问题。

新的Android项目向导为你创建2个东西:

● 一个绑住Android SDK的壳程序。这个将允许你使用所有Android库和包来进行编码工作,并且允许你在合适的环境中调试程序。

● 新程序的第一个壳文件。这些壳文件包含一些必要的支撑你将要编写程序的文件。就如同一个在Visual Studio中,它会在你的文件中产生的一些代码。使用Eclipse中的Android项目向导产生一些初始的程序文件和一些Android创建的代码。

    另外,新的Android项目向导包含一些你必须输入的选项。

    在项目的名称那个字段,只是为了举例,使用HelloWorldText这个名字,这个名字非常的容易把这个Hello World项目从其它你将要在本章中创建的项目分别开。

    在内容那个区域,保持缺省的选择:在工作区中创建一个新的项目这个选项必须被选中。并且使用缺省的位置这个选择框也应当被选中。这个将允许Eclipse在你缺省的工作区路径中创建你的项目。这样做的好处是很容易对你的项目进行排序,管理和查找。例如,如果你在工作在一个Unix基础的开发环境中,这个路径指向Home路径。如果你工作在一个Windows的环境中,工作路径将会是C:/Users//workspace。总之,有一些原因,你可能需要不选中缺省位置的选择框并且选择一个其它的路径。如果是这样的话,不管那个位置的选项,自己选一个好了。

    另外一方面,如果你在Eclipse设定(在第二章的最后一节中)中没有选中“使用这个作为缺省并且不要再询问”,你可能被要求定义一个项目的位置。在Eclipse的设置中选中“所有的新项目使用缺省工作空间路径设定”(并且提供在新Android项目向导位置字段)。如果你在Eclipse设定过程中不选中这个选择框,你需要通过点击浏览按钮并导航来选择一个路径。最后三个选项是在属性区域中。这些属性定义了你的项目是如何被统一到Android环境中。在包装名称字段,你为程序包装定义,例如:android.app.Activity或者 com.google.android.map.MapActivity.

注意:

包装名称遵从了标准的java命名指导方针,这个方针的建立是为了减少同名程序发布的风险。最高层的包装名称是公司的域名(如com,org和net)这个遵从了域名,如google。最后,一个为包装内容的描述性标题被提供。在本章中,Hello Wrold的包装名称将省略com来识别,因为这只是一个文本程序而且不会被发布。所有在本书中将来创建的包装将是可发布的并且是用com标识符

对于这个HelloWorldText应用程序,使用_programmers_guide.HelloWorldText这个名字。这个名字识别了属于这个程序的编码而且区别开你将开发的其他应用程序。

注意:

    如果你注意到你输入的这个屏幕,你会注意到当你输入程序名称,一个错误显示在本向导页面的顶端说你必须正确的填写所有的字段来继续。这个错误信息是提前并且有一些难以理解的因为你还没有填写完其他的字段。如果你看到这样的错误提示信息,忽略它并且继续完成下面两个字段的填写。

    下一个属性字段,活动的名称,这个要求输入是因为它会在程序的主屏幕上被提到。想一下,活动是一个显示你应用程序的窗口。没有活动,你的应用程序将无法做更多的工作。因为Android应用程序可以被一些活动组成,新Android项目向导需要知道哪一个活动回事缺省的。活动名称的字段是要求输入并且没有缺省值的,所以如果你必须提供一个来继续下去,本例使用HelloWorldText。这个保证了程序的简单而且在这里是个描述。

    最后的属性字段,应用程序名称,应用程序名称描述。这个就是安装在设备上用来管理的应用程序名称。再次说明,为了方便起见,使用HelloWorldText作为程序的名称。

提示:

程序名称和活动名称字段不一定要匹配。事实上,很多的编程者习惯于一个老的惯例,那就是程序的开始画面或者叫做主页。使用你感觉舒服的名字,为了说明的目的,本章假定你使用了建议的名字。

    点击结束来结束创建过程。本向导运行一个后台程序自动产生支持一个Android应用程序所需要所要求文件和文件夹。当过程结束时,你会有你的第一个Android应用程序项目,

提示:

如果结束的按钮不可用,你可能字在属性页面的字段内犯了一个错误。确保属性页面填写正确,Eclipse不会允许任何输入错误的可能引起问题发生的信息。返回确保所有的属性字段是正确的。下一节会仔细检查自动产生的Android文件和一些为你应用程序产生的壳条目的目的。

仔细查看Android创建的文件 第五章(2)

    本部分讨论Android刚刚创建的新文件。一个非常全面的结构已经为你创建好了,而且,如果你不知道要看什么的话,你最终或许会在不应该放置代码的地方放上代码。有一些Android提供的文件你需要去修改,并且有一些你不能修改。知道这些信息会避免你不得不去重新创建项目。

    在你打开的项目中,首先看一下Package Explorer,一个或者二个在主要开发区域面板的左边上的制表符。

注意:

如果你的Package Explorer没有打开,你可以通过选择Windows | Show View | Package Explorer.激活它。

    你应当看到一个根目录,本例中叫做HelloWorldText。根目录是你所有项目文件的“家”或者“容器”,你自己和Android创建的文件都会放在这里。从Package Explorer很容易进入。现在会有比较少的一些项目在你的根目录里:一个AndroidManifest.xml文件,在一个参考库里的一个包装,和三个目录(res,assets和src)。我们轮流来讨论这些项目。

AndroidManifest.xml 
AndroidManifest.xml 文件是一个指定全局设定的地方。如果你是一个ASP.NET的开发者,你可以认为AndroidManifest.xml是Web.config和Global.asax的二合一。(如果你不是APS.NET的开发者,AndroidManifest.xml就是意味着是个存放设定的地方)。AndoridManfiest.xml将包括如程序许可,活动,和意向过滤器等的设定。标准的AndroidManifest.xml文件应当包含下面的信息:

 
package="testPackage.HelloWorldText"> 
 
 
 
 
/> 
 
 
 

如果你创建一个应用程序,你将要在这个文件里增加信息。注意,你提供的包装名称已经列在这里了,同样包括你的活动所需的动作。

引用库和目录 第五章(3)

    一个引用库的列表也包含在根目录里了。通常,对于一个新手的项目,你应当只看一个库。扩展引用库的分支并且仔细查看当前你的应用程序项目所引用的库。由于它是一个新的Android项目,你会在项目引用里看到一个库,那就是android.jar,Android SDK(如果你熟悉Java SDK,android.java是同Java的rt.java非常类似的文件。在rt.java里封装了很多java的API)。Android Plugin确保了这个是唯一被你应用程序引用的库。应用程序需要引用SDK来获得进入在SDK库内所有类别,比如你的Views,Controls或者甚至谷歌的API。

注意:

Eclipse可以允许你增加用户定义的外部库。但是除非你确信这些外部的引用将在Android应用程序上工作,所以增加它们之前请三思而后行。

目录(路径)

    有三个目录在项目的根目录——res,assets和src。每一个都有着显著的目的。这些目录在你应用程序的运行中扮演着完整的角色。 
res 目录

    res目录是你项目资源放置并且编译你的应用程序的地方。当你创建一个新的Android项目,res目录包含3个子目录:drawable, layout, 和 values。 你会在很多的项目中使用 drawable 和layout分别放置并显示图形和布局。而values目录放置遍及程序全局的字符串。

注意:

一个引用到res目录和它内容是被包含在R.java文件中,在src目录中。我们会在本章的后面详细的讲解。drawable目录包含你程序可以使用和引用的真实图形。layout目录放置XML文件,当构造它的界面时,main.xml文件被应用程序引用。在本书的绝大多数应用程序中,你会去编辑在layout目录下的main.xml文件。这将允许你插入Views到程序的可视布局并显示它们。一个原始的main.xml文件包含下列代码:

 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Hello World, HelloWorldText" 
/> 

 

最后一个在res目录下的文件夹是values,放置了XML文件命名字符串。strings.xml文件是用来放置程序引用的全局字符串。

assets 目录

assets目录用来放置“原料”文件的。在这个目录中可以包含为流媒体和动画准备的音频文件。因为Android模拟器的beta音频驱动没有优化,我不会在本书的应用程序中使用任何的音频文件。

src目录

src目录包含项目里所有的源文件。当项目一创立,就会包含两个文件R.java和<活动>.Java(本例中是HelloWorldText.java)

注意:

.java 总是根据你的活动来命名。

Hello World!自动产生文件的详解 第五章(4)

R.java是一个由Android plugin自动产生并添加到你的应用程序中的文件。这个文件包含到drawable,layout和values目录的指针(或者目录里其它的项目,本例中是字符串和图标)。你不应当必须直接修改这个文件。在你大多数的程序里会总是提到R.java.为HelloWorldText自动产生的代码如下:

/* AUTO-GENERATED FILE. DO NOT MODIFY. 

* This class was automatically generated by the 
* aapt tool from the resource data it found. It 
* should not be modified by hand. 
*/ 
package testPackage.HelloWorldText; 
public final class R { 
public static final class attr { 

public static final class drawable { 
public static final int icon=0x7f020000; 

public static final class layout { 
public static final int main=0x7f030000; 

public static final class string { 
public static final int app_name=0x7f040000; 

}

注意:

    R.java文件的注释部分提供了关于这个文件起源的解释。它说明文件由aapt工具创建。在第六章,当你创建命令行版本的Hello World时,你将用命令行工具创建所有自动产生的文件。

    .java文件在src目录下,你会花费大多数时间在这个文件上。本例是HelloWorldText.java.这个是你的创建新的Android程序向导时由Android plugin创建并与活动名称匹配来命名的。不像本部分大多数你已经看过的文件,这个文件完全可以编辑。事实上,如果你不修改代码,它会为了做一点点的事情。

Package android_programmers_guide.HelloWorldText; 
import android.app.Activity; 
import android.os.Bundle; 
public class HelloWorldText extends Activity { 
/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main); 

}

    在文件上面的三行是标准预处理器指令——那就是,如大多数的编程语言,在程序处理前声明是指令到编译然后运行。在本例中,你在package android_programmers_guide.HelloWorldText.有了定义和包含。

    下两行通过android.java从Android SDK中导入特别的包装。

import android.app.Activity; 
和 
import android.os.Bundle;

这些行告诉项目去包括所有你程序里面的代码之前包括所有来自导入包装的代码。这两行对于基本的Android程序非常的重要并且不应当被移除。

提示:

如果你在项目里没有看到.android.os.Bundle 的输入声明,在开发窗口展开树形。Eclipse会给出在第一个下面所有输入的声明,所以你必须展开树形结构来看其余的声明。

    现在让我们关注到你的类 HelloWorldText,你会看到它扩展了Activity class.Activity被从前一行导入。所有的程序源于Activity class,并且在Android上运行一个程序会需要这个起源。运行并在屏幕上显示某些东西必须从Activity起源。

    HelloWorldText的类保持了需要创建,显示并且允许程序的代码。在HelloWorldText的类中,现在只有一个方式来定义代码 onCreate( ).

    onCreate()方法把冰柱作为一个束。那就是所有点钱状态的信息被搜集作为一个冰柱对象并且被保存在内存了。在本程序中你不能直接处理冰柱,但是你需要知道它的存在和目的。

    文件中的下一行是真正可感受到的动作:

setContentView(R.layout.main);

    setContentView()方法把Activity的内容设置到指定的源。在本例中,我们通过R.java文件中的指针使用layout目录里的main.xml文件。现在的main.xml文件包含了HelloWorldText的屏幕和一个TextView。TextView起源于View并且被用来在Android环境中显示文本。回头再看main.xml的内容,你会看到它包含了下面的行:

android:text="Hello World, HelloWorldText"

    setContentView()方法被告诉去设置main.xml作为当前的View,并且main.xml包含了一个宣称“Hello World, HelloWorldText”的TextView。现在可能比较安全的去编译并运行HelloWorldText。要测试这个,运行你的HellowWorldText程序。选择 Run|Run to open the Run As dialog box, 选择一个Android 应用程序,然后点击OK。

    你新建立的项目包含创建Hello World应用程序自身的代码。总之,这个并不是太吸引人,而且也没有教你太多参与Android应用程序编程。你需要仔细研究项目本身并且看看项目是如何显示“Hello World!”信息的。

    当你创建一个新的,由Android plugin修改的main.xml程序的Android项目时究竟发生了什么。这是一个完美修改Android中 UI的一个例子。当项目被创建时,下面的代码行由Android SDK增加到main.xml中:

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Hello World, HelloWorldText" 
/>

    在我讨论xml文件中存在的TextView时,我没有说它为什么能没有任何相应的代码就可以工作。在本书的早些时候提到有两种方式来为Android设计UI:通过代码,和通过main.xml文件。在先前的代码例子中,在xml文件中创建了一个TextView并且设定文本为“Hello World, HelloWorldText”。编辑main.xml中的这一行,按照下面的方式:

android:text="This is the text of an Android TextView!"

重新运行项目,并且你的运行结果应该如图所示(略)。利用一些时间并且用xml的TextView做实验。然后你可以转移到用另外一种方式来创建一个Hello World!应用程序了。

Hellow World! 再来一次 第五章(5)

在本部分中,你将创建另外一个Hello World!这次,你会使用编程代码而不是使用xml文件,并且你会自己来做大部分工作。第一步就是把main.xml里面已经有的TextView代码删除。下面就是TextView部分的代码。完全的删除它,使你的应用程序是一个空的壳。

android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello World, HelloWorldText"
/>
在移除了TextView代码以后,你的main.xml文件应该像下面这样:


android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

    现在你有一个干净的main.xml文件了,并且一个干净的应用程序壳,你可以开始增加可以在屏幕上显示“Hello World!”的代码了。从打开HelloWorldText.java并移除下面的代码行开始:
setContentView(R.layout.main);

注意:

你仍旧需要为你新的应用程序来设置一个ContentView;但是你需要执行和现在的这个有一点细微的不同,所以在这里最好把完整的声明移除。

    这条使用setContentView()来把main.xml显示在屏幕上。因为你不会去使用main.xml来定义你的TextView,所以你不会去设置你的view。取而代之,你会用代码来构建TextView。

   下一步是从android.widget中导入TextView包装。这样你可以进入到TextView并且允许你创建自己的实例。把这些代码放置到当前HelloWorldText.java文件靠近顶部,现有导入声明的import android.widget.TextView的地方;

    现在,创建一个TextView的实例。通过创建这个TextView实例,你可以在屏幕上显示文本而不需要直接修改main.xml文件。在onCreate()声明的后面放置下面的代码:
TextView HelloWorldTextView = new TextView(this);
注意

    TextView在当前上下文中取得一个句柄作为一个变量。传递这个到TextView并和当前的上下文相关联。如果你跟从SDK的等级,HelloWorldText扩展至Activity,而Activity扩展至ApplicationContext,而再扩展至Context。这就是你如果传递TextView的。

    先前的代码行创建一个名叫HelloWorldTextView的TextView的实例,然后例示HelloWorldTextView,通过设置它到一个新的TextView。这个被上下文传递的新的TextView被完全的例示。

    现在,这个TextView已经被定义好了,你可以在里面增加文本。下面的代码指定“Hello World!”文本到TextView:
HelloWorldTextView.setText("Hello World!");

    这一行允许你设定你的TextView文本。setText()允许你赋值一个字符串到TextView。

    你的TextView已经被创建而且现在包含了你想要显示的信息。但是,如果简单的指定“Hello World”到TextView中不会在屏幕上显示任何的东西。如前面所讨论的那样,你需要设置ContentView来在屏幕上显示东西。所以,你必须使用下面的代码来设置TextView到上下文并且在屏幕上显示:
setContentView(HelloWorldTextView);

    仔细查看本行代码,你会发现你把setContentView到TextView。前面的三行代码是制作你的Hello World!应用程序。你创建一个Textview,赋值你的文本,然后显示在屏幕上。所有的事情就是这样,根本不复杂。完整的HelloWorldText.java文件应当像下面这样:

package android_programmers_guide.HelloWorldText;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class HelloWorldText extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
/**Hello World JFD */
/**BEGIN */
/**Create TextView */
TextView HelloWorldTextView = new TextView(this);
/**Set text to Hello World */
HelloWorldTextView.setText("Hello World!");
/**Set ContentView to TextView */
setContentView(HelloWorldTextView);
/**END */
}
}
    现在在Android模拟器中那个编译并且允许这个新的Hello World!应用程序。选择 Run|Run或者按下CTRL-F11在Android模拟器中启动这个应用程序。

你刚刚创建了一个完整的Android活动。这个小的项目展示了一个常规Hello World!应用程序的运行。你在Android模拟器中通过设置TextView到Activity's ContentView中并且在手机上显示“Hello World!”信息。下一节中会用一个细微不同的方式执行一个Hello World!,使用一个图形。

Hello World! 使用一个图形 第五章(6)

    在本章,你会使用一个在编程中大家熟知的活动来熟悉Hello World!应用程序:显示图形。现在的电脑如果不显示图形就太过分了。这些图形显示的重点在于如何让它在屏幕上显示出来的能力。

    大概5年以前,在手机上显示图形是非常困难的一件事。和图形一起工作是我们这些现代电脑用户认为理所应当的事情之一了。我们每天看着不同类型的窗口,甚至没有想到这个是影像被发送到屏幕的。这个版本的Hello World!程序将显示一个Hello World!的图片。

    对于这个应用程序,使用New Android Project wizard(新Android项目向导)来创建一个新的项目并且命名为HelloWorldImage。

    程序创建好后,找到main.xml文件并把其中的TextView代码删除,这样你就有一个干净的项目文件了。如果你没有删除这个代码,最终将再次显示文本类型的Hello World!程序。

    在你开始写代码之前,你需要一个需要显示的图片。在你可选的图形程序内创建一个小的图片。为了方便起见,选择 Microsoft Paint,但是任何的程序都可以给你想要的图片。

    为这个图片命名为helloworld.png并且把它保存到 %workspace%/HelloWorldImage/
res/drawable 目录下。

注意:

不要把图片的名称大小写搞混了。图片的名称只应当是小写字母。如果你插入了大写字母,当你试着在Eclipse中用这个文件时,会得到一个错误的提示。

    在复制这个文件到正确的目录之后。helloworld.png这个图片应当显示在项目窗口中,在drawable目录下。

    打开R.java并且看一下它的代码。Eclipse应当增加了一个指针到helloworld.png.你的R.java文件应当同下面的类似:
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package android_programmers_guide.HelloWorldImage;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int helloworld=0x7f020000;
public static final int icon=0x7f020001;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040000;
}
}

有了一个干净的壳作为起点,并且一个可用的句柄到你要显示的图像,你可以开始增加你的代码了。可以以两种观点来看这个应用程序:XML基础的UI和代码为基的UI。

Hello World!代码为基的UI-五章(7

    假定你对HelloWorldText那个部分能够理解了,这个版本的Hello World!会比较的熟悉了。要开始的话,你需要输入显示图片功能的包装。文本显示使用一个TextView,图片显示就要用ImageView了。因此,你必须输入ImageView包装。和TextView一样,ImageView包含在android.widgets里:

import android.widgets.ImageView;

注意

TextView和ImageView都是从View派生的。这样就使得两者结构非常的类似并且容易执行。

    包装导入(输入)后,你可以创建你的ImageView并且在屏幕上显示它了。示例ImageView和示例ImageView是一样的。创建一个ImageVIew示例并且把它传递给上下文使用下面的代码:
ImageView HelloWorldImageView = new ImageView(this);

    下面是能够看到在ImageView和TextView之间的不同之处。这一步是关于设定你想要显示的东西。在TextView例子中,你使用setText()来设定TextView的文本为“Hello World!”,虽然TextView和ImageVIew都是派生于View,但是它们还是不同的并且因此要求不同的方式。显然,你也不会来为ImageView使用setText()。你需要使用setImageResource()来在ImageView中显示图片。并把句柄从R.java(句柄的语法是R.drawable.helloworld)传递到helloworld.png:

HelloWorldImageView.setImageResource (R.drawable.helloworld);

    最后,要把图片传输到屏幕,你必须设定ContentView。正如你在TextView做的一样,把ImageView传递到ContentView中。ContentView的工作就是把设定到对象的东西传递到屏幕上。
SetContentView (HelloWorldImageView);
Your final HelloWorldImage.java file should look like this:
package android_programmers_guide.HelloWorldImage;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ImageView;
public class HelloWorldImage extends Activity {
/** Called when the activity is first created. */
Chapter 5: Application: Hello World! 77
@Override
public void onCreate (Bundle icicle) {
super.onCreate (icicle);
/**Hello World Image JFD*/
/**BEGIN */
/**Create the ImageView */
ImageView HelloWorldImageView = new ImageView(this);
/**Set the ImageView to helloworld.png */
HelloWorldImageView.setImageResource(R.drawable.helloworld);
/**Set the ContentView to the ImageView */
setContentView(HelloWorldImageView);
/**END */
}
}

    编译HelloWorldImage并且在Android模拟器中运行。在下一节,你将再次显示helloworld.png但是这次使用XML而不是代码。

Hello World! XML为基的UI - 第五章(8)

    本章通过比较使用XML为基的UI和代码为基的UI来给你一个比较的例子。正如你将要看到的,使用main.xml要求和代码为基的方式差不多同样多的代码来把图片发送到屏幕上。但是两个过程的句法不同。

    如果在上个例子中所作使用同样的项目,从HelloWorldImage.java文件中移除TextView代码。干净的文件应该看起来像这个一样:
package android_programmers_guide.HelloWorldImage;
import android.app.Activity;
import android.os.Bundle;
public class HelloWorldImage extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
}

    现在你有一个清白的历史可以开始,把上面的移到main.xml中,你需要为一个ImageView增加定义。开始增加一个空的ImageView标签到你的main.xml中:
/>

    你需要编辑ImageView的4个属性:android:id, android:layout_width,android:layout_height, 和 android:src。你会把这些属性添加到标签中,这些控制标签如何在屏幕上显示。

    android:id属性被用来作为ImageView的识别符。android:id属性可以在ImageView代码中被提交处理。可以等一会儿在R.layout.imageview中使用@+id/句法来给ImageView赋值一个识别符:
android:id="@+id/imageview"

本行插入一个以imageview命名,自动产生的ID,@+id到R.java内。

    下两个你必须定义的属性是:android:layout_width和android:layout_height。这些属性控制图片如何填充屏幕。有两个可选择的选项。fill_parent值定义全部显示图片,wrap_content显示定义的图片尺寸,可能会丢失图像清晰度。本例中使用wrap_content:
android:layout_width="wrap_content"
android:layout_height="wrap_content"

    最后一个需要赋值的属性是最重要的变量型的属性:android:src.这个属性指向你要显示的图片。例如,指向属性到drawable/helloworld图片:

android:src="@drawable/helloworld"
Your full ImageView tag should look like this:
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/helloworld"
/>

    最后,在图像显示前,你必须把main.xml通过setContentView传递到HelloWorldImage.java中:
setContentView(R.layout.main);
    编译并运行HelloWorldImage。

    在本章结束前,再试一下另外一件事。回到main.xml中并且把wrap_content改成fill_parent。完成后,你的main.xml文件应当如下:

android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="@drawable/helloworld"
/>

    再次运行程序来检查wrap_content和fill_parent之间的不同之处。

使用TextView和ImageView

    使用一些在本章学到的技巧和技术来创建一个新的Hello World!应用程序。创建一个即用TextView也用ImageView的程序,图片放在屏幕上并且带有一个文本的标题。这个比在一个Activity中使用一个View多一点难度。多用Views看看你能创建什么。

    下一章还会待在Hello World!上,不过还讨论命令行编程。

问专家

Q:Android和大多数的APIs一样有Label或者LabelView可以用吗?

A:没有。所有的文本显示通过TextView显示。你可以,和其他人一样,自定义一个和Label功能的View,并把它命名为LabelView,但是Android本身并没有LabelView这个包装。

Q:有任何的优点使用<应用程序>.java,而不是main.xml来创建Views吗?

A:没有文件说速度或者处理器方面的二者之间的差别,但是一个关键的优点是,使用main.xml,需要为你的Activity预先确定一组Views 。然后,在编码中,你可以从一个View跳到另外一个View,而不需要手动创建它们。 

第六章 使用命令行工具和Android模拟器 

使用命令行工具和Android模拟器 第六章(1)

关键技能和概念
● 使用Android SDK命令行工具 

● 创建一个命令环境

● 用一个壳导航到Android服务

● 在Linux里使用 Android SDK

    到目前为止,本书包含了一些非常宽的科目关于学习如何运行Android平台。就这一点来说,对于使用Eclipse来创建并运行一个小的Adnroid应用程序,你应该非常的舒服。你创建一个新的项目,编辑main.xml文件和.java文件,然后编译R.java文件。这些是创建Android应用程序的一些基本技能。

    在本章中,你会通过用命令行应用程序开发来扩展你的这些技能。Android开发没必要必须限定在Eclipse IDE环境里进行。Android SDK提供了许多命令行工具,可以在没有图形化的IDE的帮助下,开发完整的应用程序。你会使用这些命令行工具首先在Windows,然后再Linux下来创建,编译和运行Hello World!应用程序。

利用Windows CLI创建一个壳活动 第六章(2)

 Android SDK有很多的工具来帮助你创建并编译Android应用程序。这些工具帮助那些不愿意使用,或者没有所支持GUI IDE系统的用户来工作的。总之,如果你一直在用Eclipse在编程,你仍然需要知道Android SDK命令行工具和它的功能。

    当你运行Android相关的功能,如创建一个Android项目或者在Android模拟器内运行一个应用程序,你实际上是在呼叫到命令行工具的连接器。无论是从命令行接口或者GUI IDE运行,这些Android命令行工具是Android SDK的真正核心。

    在下面的章节中,我演示Android工具的功能。ActivityCreator.bat是一个强有力的工具,被用来为你的程序建立一个活动壳(Activty Shell)。

运行ActivityCreator.bat - 第六章(3)

   ActivityCreator.bat文件应当在Android SDK的…/tools/文件目录下。大多数“前向”命令行工具都放置在工具目录的根目录下。“前向”工具是依次呼叫在工具根目录下更深目录的工具。ActivityCreator.bat是工具根目录下一个示例的工具,它运行时会呼叫另外一个工具。使用vi,Notepad或者一个文本编辑器,打开ActivityCreator.bat。它应当包含下面的代码:

注意

ActivityCreator.bat是定义为Microsoft Windows版本的Android SDK。在本章的后面部分,你将会学习ActivityVreator.py。这个是Linux版本的ActivityCreator。

@echo off 
rem Copyright (C) 2007 Google Inc. 
rem 
rem Licensed under the Apache License, Version 2.0 (the "License"); 
rem you may not use this file except in compliance with the License. 
rem You may obtain a copy of the License at 
rem 
rem http://www.apache.org/licenses/LICENSE-2.0 
rem 
rem Unless required by applicable law or agreed to in writing, software 
rem distributed under the License is distributed on an "AS IS" BASIS, 
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
rem See the License for the specific language governing permissions and 
rem limitations under the License. 
rem don't modify the caller's environment 
setlocal 
"%~dp0\lib\activityCreator\activityCreator.exe" %*

    浏览整个的rem声明(批处理文件注释声明),你会看到在文件的底部有一个实用的代码。ActivityCreator.bat被用来呼叫…/tools/lib/activitycreator/目录里的ActivityCreator.exe。这个ActivityCreator.bat是一个工具的示例,它只是放置在SDK其它工具的前端。

    所以,ActivityCreator.bat(或者ActivityCreator.exe)做了什么?ActivityCreator被用来建立指向在哪里需要开始开发你的应用程序初始文件的开发环境。这个路径结构和在第五章第五章(1)程序:Hello World!讨论过的结构一致。ActivityCreator.bat 创建 R.java, AndroidManifest.xml, 和所有你应用程序需要的支持文件。

    让我们现在转到命令行环境并且浏览ActivityCreator。在开始菜单,点击运行,在运行的对话框内输入 CMD或者COMMAND,然后点击确定。

    执行这个命令会启动命令窗口。这个窗口和老版本的DOS操作系统环境相同。命令窗口出现后,在光标>后输入ActivityCreator

提示

Microsoft命令提示符接口没有大小写限制。在缺省情况下,如果你使用了大小写限制的不同的开发环境,本章中显示的屏幕截图会不同。

    运行命令ActivityCreator,实际运行的是ActivityCreator.bat,产生下面的输出: 
Activity Creator Script 
Usage: 
activityCreator [--out outdir] [--ide intellij] yourpackagename.ActivityName 
Creates the structure of a minimal Android application. 
The following will be created: 
- AndroidManifest.xml: The application manifest file. 
- build.xml: An Ant script to build/package the application. 
- Res: The resource directory. 
- Src: The source directory. 
- src/your/package/name/ActivityName.java the Activity java class. packageName 
is a fully qualified java Package in the format .... (With at 
least two components). 
- Bin: The output folder for the build script. 
Options: 
--out : specifies where to create the files/folders. 
--ide intellij: creates project files for IntelliJ

    这个输出简单的指示了你需要提供更多的信息来运行ActivityCreator。更确切的是,你需要传递给命令一个你需要建造的壳应用程序的位置。

注意

从ActivityCreator输出的命令给了你很多不仅仅是你没有提供足够信息的信息。它给了你一个完整的使用工具创建的文件列表。这个文件列表和第五章第五章(1)程序:Hello World!看起来相似。虽然build.xml没有直接展示在Eclipse用户目前。

    回到命令窗口并且使用下面的选项运行ActivityCreator(如果你使用Unix/Linux环境编程,ActivityCreator同样接受unix-风格路径参数):

--out c:\AndroidHelloWorld\android_programmers_guide.HelloWorldCommandLine

--out选项告诉ActivityCreator你想要它输出点东西。这个命令选项使用两个参数,。第一行告诉ActivityCreator 在一个不存在的文件夹里创建壳应用程序。c:\AndroidHelloWorld. 

提示

如果你定义的文件夹或者路径不存在,AcitivityCreator将会在过程中自动创建一个。

--out的第二个参数是包装名称和活动名称。根据前面章节的习俗,这个实例在本项目中使用android_programmers_guide作为包装的名字并且HelloWorldCommandLine作为活动的名称。

注意

成功运行ActivityCreator并设置你的初始环境和运行新的Android Project wizard是一致的。

NOTE 
the parameters needed to successfully run ActivityCreator and set up your initial 
environment are the same as those required by the New Android Project wizard.

    在新命令行选项和参数下运行ActivityCreator。你应当从工具的输出看到下面的内容:(略)。下面的章节涵盖了由ActivityCreator创建的文件,因为和由Eclipse创建的文件有一些不同。

项目结构 第六章(4

   ActivityCreator为你的开发创建了一组文件目录和文件。浏览 c:\AndroidHelloWorld\来看一下它的结构。ActivityCreator创建的结构如下图所示(略)

    因为在Eclipse环境之外工作,你有一个不同的环境。当你在例如Eclipse的IDE内工作时,一些特定的功能在场景之后为你工作。假如你工作时没有任何IDE的帮助,ActivityCreator创建了一个文件来概述编译器如何的工作来创建你的项目。当手动运行ActivityCreator,为你创建build.xml文件。当你使用Eclipse来开始一个Android项目时,这个文件并没有被创建。

    它包含了一个指令组,解释了如果转变你的.java文件到一个功能性的Android项目中。这个build.xml文件告诉编译器它需要怎么做来创建你的应用程序。在这个例子中的编译器是Apache ANT,一个Java基础的工具,被用来使用构造脚本文件到编译的项目。从  http://ant.apache.org/bindownload.cgi. 下载 ANT。

    一旦你下载并安装了ANT,你必须把它增加的PATH声明中。在Windows环境中,右击“我的电脑”,并且选择“属性”来改变PATH声明。build.xml文件被特地为ANT创建用于编译你的Android应用程序。它应该在你项目的根目录,如上图所示。用文本编辑器打开build.xml并看一下它里面有些什么。

    第一个部分的build.xml包含了可以被用户编辑的代码。这部分是其它部分的开始,因为剩下的部分不应当被修改。 
 
 
rc14_windows\android-sdk_m5-rc14_windows" /> 
rc14_windows\android-sdk_m5-rc14_windows\tools" /> 
/> 
 
 

第一部分的build.xml包含下面属性的值: 
● Project name 项目名称 
● Android SDK location Android SDK位置 
● Android tools location  Android工具位置 
● Android framework location Android框架位置 
● Output location 输出位置

    如果你需要为项目改变任何的这些参数,你可以在这个文件做。但是在build.xml接下来的参数,你会立即看到通知你的警告,告诉你不应当去编辑剩下部分的值: 
 
Following this warning in build.xml is a list of parameters and values that are critical to the proper creation of your project. This list includes compiler options, input directories, and tool locations. Take a look at the following output of the core processing information of build.xml:

注意

当Android建议反对改变下面这些参数时,如果你非常熟悉ANT是如何工作的,你可以修改这些选项来符合你特定的需求。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92 Android: A Programmer’s Guide 
 
 
 
 
 
 
 
 
 
Generating R.java... 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
srcdir="." 
destdir="${outdir-classes}" 
bootclasspath="${android-jar}" /> 
 
 
Chapter 6: Using the Command-Line Tools and the Android Emulator 93 
 
 
 
 
 
 
 
 
 
 
 
 
Packaging resources and assets... 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Packaging resources... 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
only when the assets dir exists. --> 
 
property="res-target" value="and-assets" /> 
 
 
 
 
 
Packaging java... 
basedir="${outdir-classes}" 
update="true" /> 
 
 
 
Packaging dex... 
 
 
 
 
 
 
 
 
 
 
Sending package to default emulator... 
 
 
 
 
 

    现在你对于build.xml在人工下,命令行创建的Android项目是如何使用应该有了好的理解,你可以开始来编辑你的项目文件并且创建一个Android活动。第一个你需要看的文件是main.xml。使用Windows资源管理器,在AndroidHelloWorld\res\layout目录下找到main.xml。

Windows CLI下创建Hello World!活动 第六章5     

   在本部分中,你会使用Windows命令行接口来编辑项目文件。在上一章中,项目文件是由ActivityCreator.bat创建的。你将不使用Eclipse来编辑这些文件并增加一些代码。

编辑项目文件

    在一个XML编译器或者(如果你没有一个XML编辑器)记事本打开main.xml文件。这样你就可以编辑文件并且删除里面的定义。保存过后的main.xml文件是一个空壳。这样你就得到一个编辑.java文件的平台了。.java文件在更深层次的文件夹里,AndroidHelloWorld\src\android\programmers\guide。来创建你的Hello World!应用程序,增加下面的代码行来创建,设置并使用一个TextView: 
/**Hello World JFD */ 
/**BEGIN */ 
/**Create TextView */ 
TextView HelloWorldTextView = new TextView(this); 
/**Set text to Hello World */ 
HelloWorldTextView.setText("Hello World!"); 
/**Set ContentView to TextView */ 
setContentView (HelloWorldTextView); 
/**END */

    别忘了增加TextView包装到文件的开始部分:

import android.widget.TextView; 
    完成后的HelloWorldCommandLine.java文件应当看上去和下面一样(略)。你的项目文件现在应当被设置了。你现在可以在Android模拟器内编译并运行你的应用程序了

增加JAVA_HOME 第六章 (6

    在编译你的项目之前,必须增加另外一个环境变量到你的PC-JAVA_HOME,可以指向你的JDK。即使它只是个PATH声明,你也必须创建一个新的名为JAVA_HOME的变量。

注意 
JAVA_HOME变量是必须的仅仅是因为你使用命令行环境。如果你只使用Eclipse,你不用增加它。

1、右击“我的电脑”,并选择“属性”。 
2、在系统属性下,选择高级选项并点击环境变量按钮。然后会打开一个环境变量窗口。 
3、点击新建按钮来增加一个变量名为_HOME,它的值应该是你Java SDK的完整路径。见下图(略)

编译并安装应用程序 第六章(7)

  是时候来做一个真正的测试了。你现在可以编辑你的命令行项目了。要编译项目,使用ANT。一旦项目编译完成,你需要在模拟器中安装它。

用ANT编译项目

如果运行ANT时出错该怎么办? 第六章(8)

    在你设置好JAVA_HOME环境变量并且ANT在你的PATH声明之后,你应当可以导航到含有build.xml文件的文件夹,并且只要简单的运行ant命令。在你的项目路径下运行ant。如下:(略)。

   运行ant的结果就是一个.apk文件会直接安装到手机(模拟器)中,总之,Eclipse在模拟器中直接为你安装。而这里你需要使用Andorid Debug Bridge(adb)工具来安装应用程序,下一节叙述

如果运行ANT时出错该怎么办?

    当你运行ANT时出错该怎么办呢?不用害怕。因为在写本书时,Android还只是一个刚发布的阶段,有些项目可能需要被纠正,当你使用一项新技术时,总会有一些小的更改会发生。当我第一次试着运行ant并且编译我的项目时,我收到了一个错误,如下图(略)。

    一些问题的研究在谷歌的Android开发者论坛上,一个重写的build.xml纠正了一些提供到ANT的命令。在修改过的主要部分已经被加粗。和原来的那个文件比较一下你会注意到明显的不同。

 
 
rc14_windows\android-sdk_m5-rc14_windows" /> 
rc14_windows\android-sdk_m5-rc14_windows\tools" /> 
/> 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Generating R.java... 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
srcdir="." 
destdir="${outdir-classes}" 
bootclasspath="${android-jar}" /> 
 
 
 
Packaging dex... 
 
 
 
 
 
 
 
 
 
 
 
 
Packaging resources and assets... 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Packaging resources... 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
property="res-target" value="and-assets" /> 
 
 
 
 
 
Packaging java... 
basedir="${outdir-classes}" 
update="true" /> 
 
 
 
Packaging dex... 
 
 
 
 
 
 
 
 
 
在修改过build.xml之后,你可以重新运行

adb安装你的应用程序 第六章(9)

第一步是启动你的模拟器。在Android/tools文件夹找到emulator.exe文件并且执行它。这样就会启动Android服务器。那就是启动了模拟器同时在你的电脑上启动了一个虚拟的手机。如下图(略)。然后你就可以使用不同的工具来和服务器交互了,和安装应用程序和呼叫一个壳环境一样。要在Android服务器安装你的命令行应用程序,你需要使用adb。adb是你到服务器的连接,同模拟器一同开启。

    adb包含了很多有用的功能允许你和Android服务器交互;其中一个功能可以让你安装应用程序。

表格6-1列出了adb接受的命令描述。

    要复制你的应用程序到服务器,打开一个Windows命令提示符窗口并且导航到build.xml文件所在的路径。对于adb,syntax命令如下: 
adb install 

如果应用程序正确的安装到手机,你会得到一个命令行关于包装大小的反馈。如下。(略)。

命令

描述

install 

安装应用程序到服务器

pull  

将远程文件拉出服务器

push  

将本地文件推进服务器

shell

在服务器上打开一个壳环境

forward  

从一个端口转递流量到另外一个端口(到或者从服务器上)

start-server

启动服务器

kill-server

停止服务器

ppp  

通过USB使用一个ppp连接

devices

列出可用的模拟器

help

列出adb的命令

version

显示adb的版本

表6-1 adb 命令

转到运行的模拟器,你将会看到应用程序安装到手机上。

运行应用程序产生了一个错误怎么办 第六章(10)

我第一次在使用新的build.xml文件后,运行这个应用程序时,我在Android模拟器上接受到了一个错误。如下图(略)。错误指出一个丢失的类。

注意

你可能会或者不会遇到同样的错误。关键看本书发行时,哪个版本的Android SDK是可用的,你应当跟从这里的问题解决步骤,因为在后续的项目中会对你有所帮助。

    这个错误似乎指出了一个事实,那就是在HelloWorldCommandLine.apk文件中丢失了一个类。我可以简单的自己去纠正这个错误而不用任何的Android SDK命令行工具。

    根据结果,.apk文件就是一个.zip文件。就是说你可以用.zip解压缩文件打开它。下面的插图就是用winrar打开HelloWorldCommandLine.apk文件后的样子。(略)。

    丢失的是classes.dex。这个是我的类的编译过的Dalvik可执行性文件。导航到Android项目下bin文件夹,我可以看到ANT成功的编译并且创建了classes.dex文件。这个文件只是被留在了HelloWorldCommandLine.apk文件之外了。在Winrar打开的HelloWorldCommandLine.apk状态下,我可以把classes.dex文件拖进HelloWorldCommandLine.apk。在classes.dex文件被加入HelloWorldCommandLine.apk后可以保存并关闭文件了。

卸载一个较早的活动 第六章(11)

   在你增加文件到运行的服务器之前,你将要卸载前一个版本的HelloWorldCommandLine。在安装另外一个程序之前,卸载前一个版本的程序不是必须要做的事。但是,为了更好的查看如何的与服务器交互,在开始前,还是卸载前一个版本的程序吧。

    保持Android模拟器在开启状态,返回到命令行提示符环境并且允许adb壳命令,它会打开Android服务器的壳环境。如果你成功了,你的命令提示符会从>变成#。现在你在Android服务器中有一个打开的壳。有很多的功能现在可以用,但是现在只关注一个:移除旧的HelloWorldCommandLine.apk文件。

提示

记住,Android是一个操作环境。你在壳中可以使用的是标准的POSIX命令。

    在Android服务器中,用户安装的程序被保留在/data/app路径下。使用cd,导航到app路径,如下图(略)。运行ls命令来列出这个路径下所有的文件。你将看到一个HelloWorldCommandLine.apk文件。这个文件展示了你活动的安装。

    现在你已经在服务器上定位了应用程序,你可以移除它了。使用命令语法 rm HelloWorldCommandLine.apk 来移除应用程序。下图就是rm命令(略)。如果成功,不会返回任何的信息。随后使用ls命令表明,文件已被移除。

警告

因为技术上你通过壳登入了一个Linux服务器,所有在壳内运行的命令是区分大小写的。

    应用程序移除后,输入exit来退出壳并返回到你的命令提示符。

重新安装并启动应用程序 第六章(12

你现在可以使用adb重新安装应用程序了:

Adb install HelloWorldCommandLine.apk 

一旦应用程序被安装回服务器,转到模拟器。从模拟器中启动应用程序。它应当能正常工作,如下图(略)。

    现在我们已经谈论过如何在Windows内创建和编辑文件的过程,让我们看看在Linux上会怎么样。即使你是一个顽固的Windows用户,你可能需要注意下面的章节。我发现了对编程绝对有利的开源工具。

Linux上的Hello World! 第六章(13

很多的程序员,特别是对开放源代码软件有兴趣的程序员喜欢使用Linux作为平台。谷歌和开放手机联盟已经为这些程序员准备了Android SDK。这个SDK实际上是同样的SDK(因为java是移动性的),但是被创建的工具特定的运行在Linux上。当我开始写这本书的时候,我在使用一个老版本的红帽Linux作为我的Linux平台。我下载并安装了Eclipse和Android SDK。然而,它很快成为可以安全运行Android的Linux的一些限制。因为最低要求,你必须有一个支持libstdc++.so.6的Linux。Android文档列出了Ubuntu Dapper Drake 作为一个Linux的测试版本。

    如果你还没有决定使用哪一个版本,你可以放心的使用。不幸的是,当我试图安装最新版本的Ubuntu的时候,我电脑的硬件有个问题。于是我决定移除推荐的并且试着用一些新的东西。

    当我决定放弃红帽,我决定使用Fedora 8。本书的下面部分所使用的Linux版本的例子都是从Fedora 8 而来。不过,它们应当在你选择的软件版本上工作的没有问题。

注意

如果你选择Fedora 8,会有一个叫做Fedora Eclipse的定制包装。如果你试图为Fedora Linux安装Android Plugin (使用本书早些时候的概述),它会提示一个错误要求plugin org.eclipse.wst.sse.u。你可以要下面两种方式解决:

下载最新Linux版的Eclipse,或者使用Fedora的自动升级程序,这个可以使Linux版的Fedora Eclipse成为最新。然后可以在这个Eclipse里使用Android SDK了。 
配置PATH声明

    第一步就是配置PATH声明。路径就是一个路径清单,当一个命令被执行时,操作系统会在这个路径下寻找该命令。要查看你当前配置的路径,从一个terminal里运行下面的内容:

echo $PATH 

你会得到像下图(略)一样的一些路径声明。使用输出命令来增加Android到PATH声明中:

export PATH=$PATH:

    在Linux中编辑PATH声明会只是在当前的terminal部分改变PATH声明。要永久的改变你的PATH声明。你必须编辑.bash_profile.使用vi来编辑.bash_profile,如下图所示(略)。

    如你所见,PATH声明清晰可见。使用命令:i来使vi成为插入模式,然后增加Android到PATH中。然后按下ESC按钮,使用命令:w来写文件,然后使用:q来退出。

    Linux版本的Android SDK与一个Python脚本一起提供,activityCreator.py,这个被用来创建你的初始项目。不管怎么说,我喜欢手动创建路径来确保它在我需要它在的地方。使用mkdir来为你的项目创建一个目录。

    创建好项目目录后,你可以运行activityCreator.py Python脚本。这个脚本的语法非常接近于Windows.bat文件: 

activityCreator.py --out  package.activityName 

    使用activityCreator.py脚本来设置你的项目。看看下图activityCreator.py输出的脚本(略)。

提示

activityCreator.py命令是由sudo前缀。sudo命令被用来模拟其他用户的许可(本例为根目录),如果你没有足够的许可来运行要求的命令。在我安装的Fedora,我的用户帐号没有权利与根目录交互。

    项目建好后,编辑HelloWorldLinux.java文件并增加TextView。你可以用很多中方法在Linux中编辑.java文件。可以再次使用vi,或者你可以使用一个如下图的标准文本编辑器(略)。

    最后,从main.xml中移除定义的TextView。你现在编译你的Linux版本的Hello World!应用程序有两个小的改变。要编译应用程序,使用ANT(这个在Windows环境一样)。伺服ANT应当被预先安装在你的Linux下,特别是当你使用Fedora 8.如果你没有使用Fedora 8,你需要为伺服ANT下载,安装并设置路径。

    当你运行ant,你应当看到一个如下图的输出(略)。

    最后,你需要启动Android模拟器来安装你的应用程序。保持模拟器开启的状态下,执行下面的命令:

adb install HelloWorldLinux.apk 

    这个将安装应用程序到Linux Android服务器。如果命令运行成功,你应当可以在模拟器运行活动了。下一章,研究如何使用Android SDK来对图片事件作出反应。

CLI中创建一个图片基础的Hello World! 第六章(14

在本章中使用命令行工具来从第五章中重新创建图片为基础的Hello World! 当你创建这个项目时,记住下面的事宜:

● 在res文件夹中放置图片。

● 检查创建R.java所需要的带有指向图片的任何工具。

● 使用ANT编译项目。

● 使用adb命令来安装并推动应用程序到你的模拟器中。

问专家:

Q:当为Android编程时,有一种操作系统比其他的更好吗?

A:在使用过一些操作系统之后,我还没有注意到哪一种操作系统带有明显的优势。真的只是个人喜好。但是,经常发生的情况是,你可能会看到更多非官方的工具为了Linux平台而发布。因为Linux和Android是开放源码,更多的开放源码开发者倾向于为另一个开放源码平台创建工具。这个最终会给Android带来比Linux更多的好处。

Q:还有其它的命令可以从adb壳环境运行吗?

A:是的。例如,一个有趣的命令就是服务命令,可以被用来检查一个过程的状态,如:service check phone

假定手机正在运行,你应当能得到反馈:

Service phone: found

另外使用服务命令的方法是打一个电话。模拟器开启的情况下,输入命令并检查模拟器界面: 
service call phone 2 s16 "15555551212"

题外话:终于完成第六章的翻译工作了。

第七章 使用Intents 和电话拨号盘

使用Intents 和电话拨号盘 第七章(1)

关键技能 & 概念 
● 使用Intents

● 创建和电话硬件交互的代码

● 学习拨号和呼叫的差异

    本书到目前为止已经介绍了Android编程的基础知识。你已经仔细检查了Android应用程序的概要并且安装了你的第一个应用程序到Android服务器中。你已经学习了如何使用Views和SetContentView(),同时知道如何在一个XML中创建UI。这些技能已经帮助你创建一个静态的应用程序。你还没有做的就是使用应用程序接口来和这个平台的硬件——手机来产生交互。

    你不应该忘记一个事实,那就是Android创建的平台仍然是一个手机。这个Android会运行的设备潜在的硬件,是设计为个人与个人通信目的的。如果你揭开Android SDK外在的浮华之物,它最基本的能力必须要能接或者打电话。

    基于这个原因,本章重点放在与手机硬件交互的代码上,你应当有一些与手机基本功能交互的技能。你将能使用拨号盘来接受和打电话。这些工具和技能将会是你在这个灵活平台创建应用程序的关键所在。

    你在读者本书是因为你想要设计运行在一个手机上的应用程序 ,所以,显而易见你应当学习Android如何允许和手机硬件交互——特别是,打出电话和接收电话的过程。

    当我们想到手机,一些基本的功能会出现在我们的脑海里。首先,绝大多数情况,是能打出并接收电话。这是一个不容争辩的手机核心功能。还有一些非核心的特点使得手机易于使用,比如有能力保留并管理联系人,有能力储存没有接到的电话。通过阅读本章的内容,你会进入并熟练操作这些功能的代码。

    本章中,你看到的一个手机功能就是打出一个电话。你会创建一个应用程序,使用一个Intent,它将控制电话拨号盘并促使它呼叫一个号码。作为文章的进展,你将扩展这个应用程序并增加一些装饰到程序中。

注意

在Android平台,拨号和呼叫是不一样的。当你拨一个号码,你输入数字到键盘(或者通过程序)。但是没有呼叫实际发生。这就是,拨号没有包括呼叫按钮。但你呼叫一个号码,你从手机上发送一个信号。那就是在输入号码到拨号盘以后,你按下呼叫按钮——物理上或者程序上。你需要知道两个动作的不同来理解你会在本章中创建程序的应用范围。

Intents是什么?

Intents是什么? 第七章(2)

在你开始与拨号盘交互之前,需要你理解你要使用的代码类型。Andriod使用Intent在应用程序中定义工作。一旦你掌握了Intents的使用,一个全新的应用程序开发世界将会向你敞开。本节定义了Intent是什么和如何使用它。

    一个Intent是Android从一个Activity(活动)传递信息到另外一个活动的方法。你可以认为一个Intent是一个活动间交换的信息。例如,假定你有一个活动需要来打开一个网页浏览器并且在Android设备上显示一个页面。你的活动应当发送一个“在网页浏览器中打开某页的Intent(意图)”,就像一个WEB_SEARCH_ACTION的Intent,一个Android Intent解答器。Intent解答器从语法上分析一个活动的列表并且选择最匹配你的Intent的一个。那就是,网页浏览器的活动。Intent解答器然后传递你的网页到浏览器中并且启动网页浏览器活动。

     Intents被分成两个主要目录

● Activity Action Intents(活动动作意图)Intents用来呼叫应用程序以外的活动。只有一个活动可以处理Intent。例如,对于网页浏览器,你需要打开网页浏览器活动来显示一个页面。

● Broadcast Intents (广播意图)Intents 被送出到多个活动来处理。一个被Android发出的广播意图的例子就是,当前电池的电量。任何活动处理这个意图并适时的反应。——例如,如果电池电量低到一定程度,取消一个活动。

表格 7-1 列出并且描述了通用的,可以使用活动动作意图。正如你注意到的一样,大多数情况下,从Intent名字可以看出这个Intent是做什么的。

Activity Action Intent 

Message 

ADD_SHORTCUT_ACTION

增加一个功能快捷菜单到Android的主屏

ALL_APPS_ACTION 

列出设备上可用的所有应用程序

ANSWER_ACTION

接电话

BUG_REPORT_ACTION

打开调试报告活动

CALL_ACTION 

呼叫一个提供的位置

DELETE_ACTION

删除定义的数据

DIAL_ACTION

打开拨号活动并且拨打一个定义好的号码

EDIT_ACTION 

对有权使用的数据提供编辑

EMERGENCY_DIAL_ACTION 

拨打一个紧急号码

FACTORY_TEST_ACTION

回复工厂测试设定

GET_CONTENT_ACTION

选择并返回定义的数据

INSERT_ACTION

插入一个空的条目

MAIN_ACTION

建立一个活动开始点

PICK_ACTION

挑选一个条目并且返回一个选择

PICK_ACTIVITY_ACTION

挑选一个特定的活动(返回一个类)

RUN_ACTION

执行特定的数据

SEARCH_ACTION 

在系统上启动搜索

SEND_ACTION

发送数据给没有定义的接收者

SENDTO_ACTION

发送数据到指定的接收者

SETTINGS_ACTION

启动系统设定

SYNC_ACTION

和外部的源同步手机

VIEW_ACTION (DEFAULT_ACTION) 

打开一个视图

WALLPAPER_SETTINGS_ACTION 

显示修改Android墙纸的设定

WEB_SEARCH_ACTION

打开谷歌搜索,或者其它定义过的网页

注意

本章中的应用程序会用到列在表7-1中的Intents: 
CALL_ACTION 和 DIAL_ACTION。这些Intents使你有进入手机拨号和呼叫的能力。

表格7-2列出并描述了通用的广播意图。当你需要为一个定义的Intent建立一个接受器时,请参考这个表。

Broadcast Intent

信息

CALL_FORWARDING_STATE_CHANGED_ACTION

电话呼叫转接状态已经改变

CAMERA_BUTTON_ACTION 

照相机的按钮被按下

CONFIGURATION_CHANGED_ACTION

设备配置发生改变

DATA_ACTIVITY_STATE_CHANGED_ACTION

设备的数据活动状态改变

DATA_CONNECTION_STATE_CHANGED_ACTION 

数据连接状态改变

DATE_CHANGED_ACTION

手机系统数据改变

FOTA_CANCEL_ACTION

取消未决的系统更新下载

FOTA_INSTALL_ACTION

升级已经下载必须立即安装(由系统发送)

FOTA_READY_ACTION

升级已经下载可以延迟安装(由系统发送)

FOTA_RESTART_ACTION

重启一个系统升级下载

FOTA_UPDATE_ACTION

开始系统升级下载

GTALK_SERVICES_CONNECTED_ACTION

发送当GTALK已经成功建立

GTALK_SERVICES_DISCONNECTED_ACTION 

发送当GTALK已经断开

MEDIA_BAD_REMOVAL_ACTION

发送当一个SD储存卡移开但是从系统中未成功移除

MEDIA_BUTTON_ACTION

发送当媒体按钮按下

MEDIA_EJECT_ACTION

发送当弹出动作为一个SD储存卡被初始化

MEDIA_MOUNTED_ACTION

发送当一个SD储存卡在系统中成功安装

MEDIA_REMOVED_ACTION

发送当检测到储存卡移出

MEDIA_SCANNER_FINISHED_ACTION

发送当扫描器完成

MEDIA_SHARED_STARTED_ACTION

发送当扫描器开始

MEDIA_UNMOUNTED_ACTION 

发送当SD卡被检测到但是没有被安装

MESSAGE_WAITING_STATE_CHANGED

手机“信息等待”状态发生变化

NETWORK_TICKLE_RECEIVED_ACTION

一个新网络设备通知被接受

PACKAGE_ADDED_ACTION 

当一个新的包装被安装在设备上发送

PACKAGE_CHANGE_ACTION 

发送当现存的包装发生改变

PACKAGE_INSTALL_ACTION

一个包装可以被下载和安装

PACKAGE_REMOVED_ACTION

一个包装已经被移除

PHONE_INTERFACE_ADDED_ACTION 

设备的手机界面已经被建立

PHONE_STATE_CHANGED_ACTION

设备的手机状态已经改变

PROVIDER_CHANGED_ACTION 

设备从一个接收者处接收到通知

PROVISIONING_CHECK_ACTION

从供给服务中检测最新的设定

SCREEN_OFF_ACTION

屏幕被关闭(设备发送)

SCREEN_ON_ACTION

屏幕被打开(设备发送)

SERVICE_STATE_CHANGED_ACTION

服务状态被改变

SIGNAL_STRENGTH_CHANGED_ACTION

信号强度改变

注意

一些广播意图经常被发送,如TIME_TICK_ACTION 
和 SIGNAL_STRENGTH_CHANGED_ACTION。使用时请谨慎处理。你不应当试着去同时接受这样的广播。Intent只是大约三分之一。其实Intent只是做了某些事情,而且它不能自己来做任何事。你需要Intent过滤器和Intent接受器来听,翻译Intents.一个Intent接收器就像一个Activity的邮箱。Intent接收器被用来允许一个活动来接受定义的Intent。使用前一个网页浏览器的例子,网页浏览器活动被设定来接受网页浏览器Intent。一个像这样的系统允许不相关的活动来忽略不能处理的Intent。它同时允许需要其它活动辅助的活动利用这个活动,而不需要知道如何呼叫它。

    有了Intents和Intents接收器,一个活动可以发送一个Intent并且另外一个可以接受。不过,需要一些东西来管理两个活动之间的信息类型。这就是为什么要用Intent过滤器了。

    Intent过滤器被活动用来描述要接受的Intent类型。更重要的是,它们在Intent的内部概括了传递的数据类型。因此,在我们例子的方案中,我们要网页浏览器来打开网页。Intent过滤器将会陈述数据使用 WEB_SEARCH_ACTION Intent应当是URL格式的。

在下一节中,你将开始使用Intent来打开和利用电话的拨号盘。

使用拨号盘 第七章(3)

现在你知道Intent是什么了,是时候来看它如何运转的了。本节向你展示如何使用DIAL_ACTION 这个Intent来打开电话的拨号盘。你将用你的Intent来传递一个电话号码。如果应用程序工作正常,你将会看到由Intent传递,而显示在拨号盘内的号码。

    第一步是为这个活动创建一个项目(具体操作见第五章:Android程序员向导目录)。把项目命名为AndroidPhoneDialer。下面的插图就是这个项目的新Android项目向导(略)。

    在Eclipse内打开的新的应用程序,第一个要做的就是从main.xml中移除包含Hello World 声明的TextView。在删除了TextView后,main.xml文件应当看起来如下:

 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 

 

你需要增加两个新的包装到你的项目中来使用DIAL_ACTION Intent,如下,第一个包装允许你设置Intents并且第二个允许你来分析URIs。

import android.content.Intent; 
import android.net.Uri;

注意

对于DIAL_ACTION这个Intent有一些不同的Intent过滤器可以使用。你正在使用的是允许你把号码作为了一个URI来传递的过滤器。

    下一步就是来创建你的Intent。创建一个Intent的语法如下:

Intent  = new Intent(,)

    对于你的应用程序,把第一个参数用DialIntent替换掉。要获得第二个参数的数值,请参考Activity Action中的列表。(列表在文章中:什么是Intent)。要呼叫拨号盘,你需要使用DIAL_ACTION Intent。要正确的呼叫Intent,使用Intent.DIAL_ACTION这个格式。最后的参数,就是电话号码。DIAL_ACTION intent把号码作为一个URI。因此,你需要使用Uri.parse来分析出电话号码。使用Uri.parse将确保DIAL_ACTION intent能够理解你试图拨打的号码。你传递了一个Uri.parse的字符串来展示你要拨打的号码,在本例中是 "tel:5551212" 。

    为你项目创建的最后一个呼叫应该像这样:

Intent DialIntent = new 
Intent(Intent.DIAL_ACTION,Uri.parse("tel:5551212"));

提示

你使用记号 tel:来呼叫一个指定的电话号码。你还可以使用voicemail来替代tel:呼出一个电话voicemail的快捷方式。

    Intent创建后,你现在必须告诉Android你想要拨号盘在新的活动中被启动。要这样做,你使用setLaunchFlags()的Intent方法。你必须为启动来传递setLaunchFlags()合适的参数。下面是可以设置接受启动旗帜的一组列表:

注意

在其它情况下,可能会有超过一个的旗帜被设置来完成希望的结果。 
● NO_HISTORY_LAUNCH 启动活动,不记录在系统启动历史中 
● SINGLE_TOP_LAUNCH 告诉系统不要启动活动,如果该活动已经在运行 
● NEW_TASK_LAUNCH 启动活动 
● MULTIPLE_TASK_LAUNCH 启动活动,即使它已经在运行了 
● FORWARD_RESULT_LAUNCH 允许新的活动来接受结果,这个结果通常被转递给现存的活动。本例中,你要使用intent.NEW_TASK_LAUNCH,这样可以简单的让你打开一个新的拨号盘活动示例:

DialIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 

   创建拨号盘的最后一步是启动活动。(更精确的说,你告诉Android你有一个作为新任务来启动的拨号盘。最终由Android来启动拨号盘活动)。要告诉Android你要启动拨号盘,你需要使用startActivity():

startActivity(DialIntent);

    请注意到你把intent传递到startActivity()。这个Intent然后传递到Andriod,然后活动被执行。完整的AndroidPhoneDialer.java文件代码应当如下:

package android_programmers_guide.AndroidPhoneDialer; 
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.net.Uri; 
public class AndroidPhoneDialer extends Activity { 
/** Called when the Activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main); 
/** Create our Intent to call the Dialer */ 
/** Pass the Dialer the number 5551212 */ 
Intent DialIntent = new 
Intent(Intent.DIAL_ACTION,Uri.parse("tel:5551212")); 
/** Use NEW_TASK_LAUNCH to launch the Dialer Activity */ 
DialIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
/** Finally start the Activity */ 
startActivity(DialIntent); 

    你现在应当来编译AndroidPhoneDialer并且在模拟器中运行它。处理编译和运行应用程序的过程在前面的章节中描述过了。你应当已经熟悉这些过程了。一旦你运行应用程序,模拟器启动。在漫长的启动过程后,你的活动被启动。

提示

保持模拟器运行是一个好主意,即使你完成了你的活动并且以及返回到代码窗口。大多数人的本能习惯是在他们完成了测试活动后关闭模拟器。但是,我发现使模拟器一直开启会帮助两个主要的问题。第一个就是启动模拟器要花费大量的时间。保持模拟器开启会避开漫长的开启时间。第二,我已经注意到有好几次当我做一些小的修改到一个活动,而且它们没有被复制到模拟器。保持模拟器开启似乎可以缓解这个问题。如果你在模拟器中有问题,在你的电脑中移除userdata-qemu.img文件。这个会让模拟器从一个干净的镜像启动。

    如果你正确的跟从本例中的代码,你应当能看到下面的结果(略):

如你所见,你已经打开了电话的拨号盘。这个拨号盘显示了你传递的号码,5551212。使用模拟器,点击呼叫按钮。现在电话应当虚拟的呼叫555-1212。显示拨号盘是有用的,加入你创建了一个应用程序运行用户来在呼叫前可以编辑号码,或者确认他们真的想要呼叫这个号码。那么你应当怎么做来让应用程序为你打电话呢?答案就在下一节中。

从你的活动中打出电话 第七章(4)

  在本节中你将会学到呼叫拨号盘时增加什么样的Intent。你还会学到在活动代码中的哪一个地方增加选择的Intent。另外,你将学习如何分析一个作为URI的电话号码。从拨号盘活动代码变成呼叫活动你需要更改一些代码。在本节中,你回去编辑AndroidPhoneDialer活动,在打开拨号盘后,来打一个电话。

    在活动中增加一个Intent,你还是需要Intent和Uri包装,所以,在AndroidPhoneDialer.java的文件头部保留这一部分。

import android.content.Intent; 
import android.net.Uri;

    这些包装将确保你不仅需要intent而且同样会传递需要的电话号码数据到Intent中(用Uri包装)。

提示

如果你不按照顺序匆匆看完这个章节,而且没有运作前一节实际的项目,那么就简单的创建一个新的项目,命名为AndroidPhoneDialer,然后增加前面提到的两个包装进去。这样会赶上进度。

    现在看看在本章早些时候表格7-1中可以使用的Activity Action Intents。你真正需要的是CALL_ACTION。很多的时候DIAL_ACTION打开Andriod拨号盘,CALL_ACTION将会启动电话的呼叫过程并且开始呼叫提供的号码。

    要创建Intent,使用和创建拨号盘同样的程序,只是这次使用CALL_ACTION:

Intent CallIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212"));

请注意你使用Uri.parse来传递一个正确的电话号码到活动中。下一步是告诉Android你要把这个活动设为启动,并且启动它。使用下面的两行代码来实现:

CallIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
startActivity(CallIntent);

    在第一行,你发送启动旗帜到NEW_TASK_LAUNCH。这个会启动一个呼叫的新示例。最后,你告诉Android使用你的Intent启动活动。当结束时,你的AndroidPhoneDialer.java文件应当如下:

package android_programmers_guide.AndroidPhoneDialer; 
import android.app.Activity; 
Chapter 7: Using Intents and the Phone Dialer 129 
import android.content.Intent; 
import android.os.Bundle; 
import android.net.Uri; 
public class AndroidPhoneDialer extends Activity { 
/** Called when the Activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main); 
/** Create our Intent to call the device's Call Activity */ 
/** Pass the Call the number 5551212 */ 
Intent CallIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212")); 
/** Use NEW_TASK_LAUNCH to launch the Call Activity */ 
CallIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
/** Finally start the Activity */ 
startActivity(CallIntent); 

编译这个应用程序并且观察结果,你应当看到如下类似的错误信息。我实际上有意的要你看看这个错误,因为它展示了我们还没有发现的Android的另一面,错误的文本如下: 

Application_Error: 
… 
Java.lang.SecurityException: 
Permission Denial: starting Intent 


Android通过要求许可被执行来准许恰当的行动,在下一节叙述。

编辑活动许可

编辑活动许可 第七章(5)

  大多数的Activity Action Intents是在需要许可在Android允许它行动之前的目录内的。和大多数的系统一样,Android只是需要确保有资格的活动来执行在它们之外的活动。这儿是许可可以使用的活动:

● ACCESS_ASSISTED_GPS 

● INTERNAL_SYSTEM_WINDOW 

● ACCESS_CELL_ID 

● RAISED_THREAD_PRIORITY 

● ACCESS_GPS 

● READ_CONTACTS 

● ACCESS_LOCATION 

● READ_FRAME_BUFFER 

● ACCESS_SURFACE_FLINGER 

● RECEIVE_BOOT_COMPLETED 

● ADD_SYSTEM_SERVICE 

● RECEIVE_SMS 

● BROADCAST_PACKAGE_REMOVED 

● RECEIVE_WAP_PUSH 

● BROADCAST_STICKY 

● RUN_INSTRUMENTATION 

● CALL_PHONE 

● SET_ACTIVITY_WATCHER 

● CHANGE_COMPONENT_ENABLED_ 
STATE 

● SET_PREFERRED_ 
APPLICATIONS 

● DELETE_PACKAGES 

● SIGNAL_PERSISTENT_ 
PROCESSES 

● DUMP 

● SYSTEM_ALERT_WINDOW 

● FOTA_UPDATE 

● WRITE_CONTACTS 

● GET_TASKS 

● WRITE_SETTINGS 

● INSTALL_PACKAGES 

 

把这个许可列表和表格7-1做比较你应当发现大多数的Intent可以匹配。CALL_ACTION也不例外。你需要赋值CALL_PHONE活动许可来执行Intent。

    要赋值相关的许可到活动,第一,你需要知道需要赋值哪一种许可。当前的例子是使用拨号盘活动。进入拨号盘活动是由CALL_PHONE许可管理的。通过赋值这个许可到你的活动,Android将允许你的Intent启动拨号盘活动。

    怎么增加许可到活动中呢?你需要编辑活动的Manifest。如果你使用Eclipse,双击AndroidManifest.xml文件,打开Android Manifest窗口,如下图(略)。

    要编辑活动的许可,点击Permission链接。会把你带到Manifest Permissions窗口,如下图(略)。这个窗口列出了当前赋值到你活动的许可。假定你在一个新的项目中,还没有任何的赋值。因此,点击增加按钮来开始进程。在对话框中,选择使用许可并且点击OK。

    回到Android Manifest Permission窗口,在名称的下拉框中,选择android.permission.CALL_PHONE,如下所示(略)。这样就会增加CALL_PHONE许可到你的活动中。现在,你已经增加了CALL_PHONE许可,看看AndroidManifest.xml文件。它应当和下面相类似:

 
package="android_programmers_guide.AndroidPhoneDialer"> 
 
android:label="@string/app_name"> 
 
 
 
 
 
 
 
 

最有意思的一行实在文件的最后:

 
 

这行代码是由Androd plugin for Eclipse增加的。如果你需要,你可以直接编辑AndroidManifefst.xml文件来赋值。但是,如果有多次情况当你不确定需要增加哪一种许可,或者什么语法来增加,你可以使用Manifest的向导。

    现在许可已经到位了,重新编译并且允许你的活动。你的模拟器应当可以呼叫电话号码了,如下图(略)。

    你创建的活动已经使用了一个Intent来启动设备的呼叫活动并且呼叫号码555-1212。这个演示了使用Intent的好处。总而言之,这个应用程序实际的为你做了一些事情。那就是说,启动一个带有电话号码代码的活动,只是打一个电话?在下一节中,你会通过增加一个按钮来启动Call_Action的Intent,增加一个文本框来运行用户输入他们选择的电话号码来更多的制作应用程序。

修改AndroidPhoneDialer

修改AndroidPhoneDialer 第七章(6)

   本节展示如何通过修改AndroidPhoneDialer来增加一些特性,使得它更加的具有实际价值。到本节结束时,你不仅仅对使用intent的得心应手,而且还会使用EditTexts和Buttons。

警告

如果你没有跟从上一节的项目,回去并创建那个活动。本节的教程假定你已经完成了从上个项目的可以自行支配的编码工作。

增加一个按钮

    本节向你展示如何修改你的项目来包含一个按钮。当活动被启动,替代启动Intent的将会是一个按钮。除了文本,按钮是应用程序里最常用的对象。按钮组织用户和程序之间的交互作用。在Android里学会如何创建并利用按钮是必要的,如果要创建一个友好的活动。

    你将要在main.xml里创建按钮了。回想一下第五章,你为Hello World!活动创建了TextView。TextView有一个清晰的结构,就像这样:

注意

记住,当你在main.xml里创建一个View时,你只是告诉Android,你想让这个View看上去是什么样的。你仍旧需要在AndroidPhoneDialer.java内把功能赋值给它。

 
android:layout_width= 
android:layout_height= 
>

这个格式对于所有的views都有用,并且Button也不例外。你需要为你的Button设定的XML属性是 android:id, android:layout_width, 
android:layout_height,和 android:text。这4个XML属性充分的描述了按钮,那样你就可以在活动中使用它了。

1、赋值你的按钮的ID到 callButton:

android:id="@+id/callButton"

2、独自设置 layout_width 和 layout_height 到 fill_parent 和 wrap_content。

android:layout_width="fill_parent" 
android:layout_height="wrap_content"

3、设置按钮的文本为“Show Dialer”,这个是一个清晰的描述来告知按钮的作用:

android:text="Show Dialer" 
The full XML for the Button, with attributes, looks like this: 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Show Dialer" />


现在看看完成后的main.xml文件。按钮在上下文中显示并且等待着你的编码。

 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Show Dialer" /> 
 

    要开始为按钮增加功能,你需要增加其它的包装到AndroidPhoneDialer.java这个文件。包含按钮View的是android.widget.Button;

    现在你已经在项目里输入按钮窗口小部件了(Button widget)。这些给了你必要的信息来开始你项目的编码工作。你项目的最终编码结果应当是有一个按钮在活动中,当点击它,启动呼叫活动。呼叫活动应当和数据“tel:5551212”一起启动。屏幕显示结果应该和初始的AndroidPhoneDialer相匹配。描述的功能围绕一些不同的概念。首先,你必须编程一个按钮,按钮的属性在main.xml文件内建立。下一步,你必须创建一个功能来启动前一个编码项目的CALL_ACTION这个Intent。最后,你的按钮应该能执行这个功能并且启动Intent。

    创建按钮的语法是

final Button  = 


等号左边的部分在代码中创建按钮。右边是从main.xml文件中调用按钮的属性。要调用它的属性,你使用findViewByID()把结果投递为一个按钮。这个听起来比它实际情况复杂。

    记住,当你增加按钮的属性到main.xml文件中,你给了这个按钮一个定义好的android:id,callButton,而这个通过Android plugin for Eclipse在id文件中以R.id.callbutton注册。使用findViewById()来从main.xml文件中通过传递id callButton来找回:

findViewById(R.id.callButton)

别忘了投递为按钮

(Button) findViewById(R.id.callButton) 

这个声明构成了等号右边的内容。创建完整的按钮应该是这样的:

final Button callButton = (Button) findViewById(R.id.callButton); 

现在你有一个可以工作的按钮了,但是你需要它来做点什么。按钮本身在没有代码的情况下无法完成太多的工作。根据本例的目的,你需要让它来执行CALL_ACTION这个Intent。因此,你需要在你现有的Intent呼叫中创建一个小的功能。这样,当按钮被按下时,你可以呼叫了。

    如果你熟悉Java编程,这里就没什么好奇怪的了。你将要设置onClick()方法来从前一个部分呼叫Intent代码。onClick()方法拿去一个View作为一个变量;但是,本例中,并没有在onClick()方法本身内呼叫任何的View:

public void onClick(View v){ 
Intent callIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212")); 
callIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
startActivity(callIntent); 
}

    代码左边是程序中仅有的绑定按钮到onClick的接受器。接收器对于Java编程者是非常熟悉的。对于不熟悉Java或者接收器的人来说。接收器是Java对象可以从其它对象“接受”的方法。同样的概念也适用于Android。你可以在Android中建立接收器,来允许Android的Views从其它的输入中处理呼叫。对于本项目,你需要为你的按钮创建一个接收器来接受活动中的按钮的onclick事件。当用户按下按钮,接收器,接收器会呼叫在onClick()方式中的代码。要建立接收器,你需要使用按钮的setOnClickListener()方法。

   如果你熟悉Java开发,这个结构不应当看起来陌生。这个是Java中典型的onClickListener接口执行。你在这里将会看到的是使用一个Java匿名的类来为按钮执行onClickListener。还有,作为一个匿名类,你可以用作本地变量来使用-本例中是按钮,如果变量被定义为最终的。

    setOnClickListener( ) 方法抓取一对变量。第一个是onClickListener()的实例。第二个是早前建立的onClick。你的setOnClickListener()应当看上去想这样:

callButton.setOnClickListener(new Button.OnClickListener() { 
public void onClick(View v){ 
Intent callIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212")); 
callIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
startActivity(callIntent); 

});

    这部分代码陈述了当callButton被按下,onClickListerner将执行onClick中的代码。onClick中的代码将执行CALL_ACTION Intent并且呼叫电话号码 555-1212。

你完成的AndroidPhoneDialer.java看起来像这样:

package android_programmers_guide.AndroidPhoneDialer; 
import android.app.Activity; 
import android.os.Bundle; 
import android.widget.Button; 
import android.view.View; 
import android.content.Intent; 
import android.net.Uri; 
public class AndroidPhoneDialer extends Activity { 
/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main ); 
/** Create the Button */ 
final Button callButton = (Button) findViewById(R.id.callButton); 
/** Set the onClickListener to call the onClick */ 
callButton.setOnClickListener(new Button.OnClickListener() { 
/** Use the onClick to call the existing Intent code */ 
public void onClick(View v){ 
Intent callIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212")); 
callIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
startActivity(callIntent); 

}); 

   在模拟器中编译并运行这个活动。主要的活动将显示标签为Show Dialer的按钮。点击这个按钮,它应当打开Call活动并拨号555-1212。主要活动应该如下图(略)。

    如你所见,Android是一个非常健全并灵活的平台。有几行相关的代码,比一页少,你已经创建了一个利用设备电话硬件的活动并且由按钮启动。同样,就这一点来说,你应当对Android处理活动,Intent和Views的方式刚到舒适。

    你的AndroidPhoneDialer活动仍然不是很健全。你需要增加一个更多的条目。本章的最后一节展示如何使用EditText View来运行用户输入一个电话号码。这个号码然后会被传递到CALL_ACTION Intent(而不是代码写好的数值:tel:5551212)。

执行一个EditText View 第七章(7)

   你需要增加一个View到活动中来使得用户输入一些文本。然后你会分析那个文本并把它发送到前一节的Intent呼叫中。因为所有的视图是从基本的视图中派生出来的,它们在结构和使用方面非常的相似。你会发现执行一个EditText是一个非常简单的操作。

    首先,在main.xml文件中放置 Views。实际上这里要放两个View:一个TextView来实现作为一个标签并且给出一些指示给用户,另外一个就是EditView来接收用户的输入。这个Views一起将增加深度和实用性到你的活动中。

    因为你组成活动的外观,记住.xml是在视觉上构成的。这个就意味着加入你要TextView在最后的活动中显示在EditText的上面,你应当在main.xml文件中把它放在EditText之前。

    因为你已经用过好几次TextViews了,所以这里不会讲的太多。简单的看一些你设置的TextView的属性:

 

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Enter Number to Dial:" 
/>

   没什么特别的地方。这只是个简单的TextView用文本输入号码来拨号:。这个TextView将会用做EditView的显示标签。这里是你为EditView设置的属性:

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
/> 

注意

你没有必要一定去设置androd:text属性,因为你不需要任何缺省的文本。

    这个id被用来设为phoneNumber,这是个名字,你将要用来在代码中参阅到EditText。再说一次,当设置main.xml时,没什么特别之处。最后的文件应当看起来如下:

 
android:orientation="vertical" 
android:layout_width="fill_parent" 
142 Android: A Programmer’s Guide 
Chapter 7: Using Intents and the Phone Dialer 143 
android:layout_height="fill_parent" 

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Enter Number to Dial:" 
/> 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
/> 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:layout_alignParentRight="true" 
android:text="Show Dialer" /> 
 

    main.xml现在完成了。你可以转移到AndroidPhoneDialer.java来继续工作。假如你不在使用一个现存的AndroidPhoneDialer.java文件——本章中的前一个项目——你可能需要参阅前一部分去看看增加到.java文件中是什么样的代码。这样会确保你从代码中的正确部分开始。

    在.java文件中你第一个增加的条目是包装定义。你不仅需要增加包装到Uri,按钮和Intent,同时还要到EditText:

import android.widget.Button; 
import android.content.Intent; 
import android.net.Uri; 
import android.widget.EditText;

    设置EditText View的语法和设置按钮的语法一致:

final EditText  = 


    再说一次,呼叫你的EditText phoneNumber。创建EditText的代码如下:

final EditText phoneNumber = (EditText) findViewById(R.id.phoneNumber);

    一旦你的phoneNumber 这个EditTexT创建好了,你可以使用它来参阅在设备上输入的文本。现在你要做的就是呼叫phoneNumber.getText()来找回用户的输入。在下面的行里替换代码数值“tel:5551212”

Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212")); 
with the value of getText( ): 
Intent(Intent.CALL_ACTION,Uri.parse("tel:" + phoneNumber.getText()));

    这就是本项目所有你需要更新的新代码。有了这两个新的附加内容,你可以给用户一个可以输入电话号码的对象,并且把号码发送到电话的呼叫活动中。完整的.java文件中的代码如下:

package android_programmers_guide.AndroidPhoneDialer; 
import android.app.Activity; 
import android.os.Bundle; 
import android.widget.Button; 
import android.view.View; 
import android.content.Intent; 
import android.net.Uri; 
import android.widget.EditText; 
public class AndroidPhoneDialer extends Activity { 
/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main ); 
final EditText phoneNumber = (EditText) findViewById(R.id.phoneNumber 
); 
final Button callButton = (Button) findViewById(R.id.callButton); 
callButton.setOnClickListener(new Button.OnClickListener() { 
public void onClick(View v){ 
Intent CallIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:" + phoneNumber.getText())); 
CallIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
startActivity(CallIntent); 

}); 

}


    当你在模拟器中运行应用程序,你应当看到一个类似于下面插图的屏幕(略)。

试试这个:修改AndoridPhoneDialer项目

试试这个:修改AndoridPhoneDialer项目 第七章(8)

    如果你一直在搞最后版本的AndroidPhoneDialer,你或许发现有些东西错过了。不幸的是,项目当前的写作方式总是允许你输入任何类型的数值到EditText View中,并且发送到Call活动中。这个真不是最佳的应用程序开发。研究一下并且增加验证到EditText中。使用下面的参数来修改项目:

● 使用常规的表达式来验证一个号码被输入到EditText中(package java.regex)。 
● 使用showAlert( ) 语法来显示一条信息告诉用户他们输入的内容和你的常规表达式不匹配。当你觉得已经找到解决方案,和下面的代码做个比较:

main.xml

 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 

android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="Enter Number to Dial:" 
/> 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
/> 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:layout_alignParentRight="true" 
android:text="Show Dialer" /> 
 
AndroidPhoneDialer.java 
package android_programmers_guide.AndroidPhoneDialer; 
import android.app.Activity; 
import android.os.Bundle; 
import android.widget.Button; 
import android.view.View; 
import android.content.Intent; 
import android.net.Uri; 
import android.widget.EditText; 
import java.util.regex.*; 
public class AndroidPhoneDialer extends Activity { 
/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main ); 
final EditText phoneNumber = (EditText) 
findViewById(R.id.phoneNumber ); 
final Button callButton = (Button) findViewById(R.id.callButton); 
callButton.setOnClickListener(new Button.OnClickListener() { 
Chapter 7: Using Intents and the Phone Dialer 147 
public void onClick(View v){ 
if (validatePhoneNumber(phoneNumber.getText().toString())){ 
Intent CallIntent = new 
Intent(Intent.CALL_ACTION,Uri.parse("tel:" + phoneNumber.getText())); 
CallIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH ); 
startActivity(CallIntent); 

else{ 
showAlert("Please enter a phone number in the X-XXX-XXX-XXXX 
format.",0, "Format Error", "Re-enter Number",false); 


}); 

public boolean validatePhoneNumber(String number){ 
Pattern phoneNumber = Pattern.compile("(\\d-)?(\\d{3}-)?\\d{3} 
\\d{4}"); 
Matcher matcher = phoneNumber.matcher(number); 
return matcher.matches(); 

当你运行本项目,它应当产生一个信息和下图类似(略)。

在下一章中,你将学习更多的Views。你将创建创建一个多活动应用程序来允许你浏览并创建本书中还没有谈论过的Views。你也会创建并利用一个菜单系统来启动你的活动。

问专家

Q:有办法来建立一个呼叫或者从模拟器中来确保这些活动正在工作吗?

A:当本书写的时候,还没有办法。但是,谷歌内部的讨论是在将来发布的SDK,开发者将有可能打开两个模拟器并且在两个模拟器之间呼叫。

Q:还有其它类型的按钮可用于活动吗?看上去或者感觉不一样的。

A:是的。你可以使用风格属性来创建小按钮,或者上下左右指示的小按钮。

题外话:到现在为止,第七章的翻译工作完成,下面是第八章。时间已过去一个半月。

第八章 列表,菜单和其它Views

列表,菜单和其它Views 第八章(1)

关键技能 & 概念 
● 建造活动 
● 使用Android菜单 
● 使用AutoComplete TextView

    本章会讨论更深层次的Views和Intents,正如被证明这些是作为一个Android新手最好要掌握的特性。这个两个实体将组成早期活动的主体。几乎每一个你创建的活动需要至少一个View,并且其中的大多数将需要呼叫一个到两个Intent。学习这些东西的最好办法就是动手实践。阅读这些话题并且回顾属性清单是另外一个需要做的事情,但是自己执行代码却是另外一件事。那就是如你在上一章所作,你将建造一个使用Views和Intents的活动。通过构造这个应用程序,你将获得对Views和Intents最好的体验。

    前两章通过创建非常简单的,开发Views和Intents的少量基本功能的活动来大致介绍了Views和Intents。在本章中,你会去建造一个稍微复杂一点的使用Intent来呼叫一个新活动的活动,而这个新活动你也要创建。这个新活动会展示在本版本SDK中可用的Views。

    本章解释这些View的功能性,如AutoComplete清单和图表种类,并介绍每个View属性的不同。作为开始,创建一个新的Eclipse项目并命名为AdnrodiViews。创建有下列插图(略)参数的项目:包装的名称为 android_ 
programmers_guide.AndroidViews, 活动的名称为:AndroidViews,并且应用程序的名称为AndroidViews。

    项目创建好以后打开main.xml文件,从里面把Hello World!代码移除。有了一个创建的项目和干净的main.xml文件,你可以开始来增加你的代码了。

建造活动 
    到目前为止,你仅仅创建过单活动应用程序。那就是说,你创建的非常简单的应用程序,只包含了一个屏幕的数据。稍等片刻,想一下你使用的最后几个应用程序。偶尔它们使用了超过一个“窗口”。大多数应用程序使用多重窗口来采集,显示并保存数据。你的Android应用程序应当是一样的。

    虽然你还没有学习如何创建在Android上运行多重活动的应用程序,但是在最后几章中你得到了如何改变多重活动的提示。你使用了一个新的概念——Intents来呼叫并且运行一个核心的Android活动。这个概念在本章中仍旧适用,但是,当你需要来呼叫创建的活动,与呼叫核心的Android活动相反,它执行起来是不同的。

    你一件你要做的事情是构造活动。然后你可以创建呼叫它们的Intents。构造活动时,需要按照下列步骤进行。 
● 为.xml文件准备Intent代码 
● 为.java文件准备Intent 代码 
● 使用一个Intent呼叫活动

一旦你创建你的第一个附加活动,其它的应当非常的简单。

注意

这些步骤并不是必须的。你可以已任何的次序来执行它们。 
NOTE 
为.xml文件准备Intent代码 

    记住Android活动包含三个主要部分:包含代码的.java文件,控制输出的.xml文件和包装的Manifest。到本书的这里,你只用到main.xml来控制单活动的输出。但是,要更好的使用多活动,你必须有multiple.xml输出文件。

    要创建一个新的.xml文件,打开你的Eclipse项目并且找到包装资源管理器。打开res目录,右击layout文件夹,并且选择新建文件。在新建文件对话框,如下所示(略),命名为test.xml。

警告

确保以小写字母输入test.xml。新的.xml文件名必须是小写字母。

    输出文件创建后。要使活动正常的工作,增加下列代码到test.xml文件中。这个代码会为你的输出提供一个基础。如果你需要,你可以简单的复制在main.xml文件现有的代码。

 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 

 

为.java文件准备Intent 代码

    再次使用包装资源浏览器,找到src目录,打开它,并在android_programmers_guide.AndroidViews 这个包装上右击,如下所示(略)。

    再一次,你将在这文件夹中增加新文件。在你右击AndroidViews包装后,从上下文菜单中选择新建文件。这个文件会在本项目中为第二个活动控制所有的代码。把文件命名为test.java。你现在有了一个非常好的,新的(但是是空的).java文件,只需要在其中增加代码来使它工作了:

package testPackage.test; 
import android.app.Activity; 
import android.os.Bundle; 
public class test extends Activity { 
/** Called when the Activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.test); 
/** This is our Test Activity 
All code goes below */ 

}



    注意,使用上下文R.layout.test,你以setContentView方法呼叫test.xml,。这行告诉新的活动为本“页”使用你创建的.xml文件作为输出文件。

修改AndroidManifest.xml文件

修改AndroidManifest.xml文件 第八章(2)

在Eclipse内打开你的Androidmanifest.xml文件。在本书中还没有大量的讨论这个Androidmanifest.xml文件呢。Androidmanifest.xml文件包含项目的全局设置。更重要的是,Androidmanifest.xml还为项目包含了Intent过滤器。

    第七章讨论了Android如何使用过滤器来排列哪种Intent可以被哪种活动所接受。使这个过程方便的信息就保留在Androidmanifest.xml中了。

注意

每个项目只能有一个Androidmanifest.xml文件。

    如果你的Androidmanifest.xml文件是打开的,它应当如下显示:

 
 
 
 
 

你在这里要看的是AndroidView活动——项目创建的主要活动的Intent过滤器。对于这个文件,你可以增加任何其它的Intent过滤器来交给项目处理。本例中,你要增加处理你创建的Test活动的过滤器。下面是你需要为Intent过滤器增加的代码到Androidmanifest.xml文件中。

 
 
 
 
 


    增加代码到AndroidManifest.xml文件中确保Android为Test活动传递Intent到正确的地方。完整的AndroidManifest.xml文件应当如下:

 

package="android_programmers_guide.AndroidViews"> 
 
 
 
 
 
 
 
 
 
 
/> 
 
 
 

    现在你的活动可以为Test活动处理Intent呼叫了。要让你的Intent呼叫Test活动,你将要使用和在第七章呼叫电话拨号盘非常类似的结构。下面的代码会设置你的Intent:

注意

当你启动应用程序,将要打开的活动是你创建项目的AndroidViews活动。因此,放置下面的代码在AndroidViews.java中来启动Test活动。

Intent testActivity = new Intent(this, test.class);

这一行创建一个叫做testActivity的Intent。参数test.class告诉呼叫,你要testActivity这个Intent来展示创建的和本活动相关联的Test活动。

警告

当你使用Intents时,不要忘记输入android.content.intent包装。

    最后,使用startActivity()方法来精确启动Test 活动:

startActivity(autocomplete); 
Your completed AndroidViews.java file should look like this: 
package android_programmers_guide.AndroidViews; 
import android.app.Activity; 
import android.os.Bundle; 
import android.view.Menu; 
import android.content.Intent; 
public class AndroidViews extends Activity { 
/** Called when the Activity is first created. / 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main); 
/**Set up our Intent /

    在模拟器中运行这个应用程序。Android应当启动AndroidViews活动,紧跟着Test活动。

    在下一节中,你将使用这些技巧来创建一个启动多重活动的应用程序。每个活动将在一个View里,这样你可以应用不同的选项。这个将会给你大量的练习显示并熟练掌握Views和使用活动。

注意

要使用本章剩下的例子,移除本节创建的Test活动。你要继续做没有Test活动的AndroidViews项目的作品。

使用菜单

使用菜单 第八章(3)

  在本节中,你将建造一个应用程序来允许用户从一些不同的Views中进行选择。当用户选择一个View,一个新的活动将被启动。你将要使用给用户选择的工具就是Android菜单。看一下这个插图(略)。当用户激活菜单按钮,菜单就会显示。

    如你所见,从Android主屏幕选择菜单按钮产生一个墙纸的设定选项。你将为你的主活动创建一个类似的菜单,该菜单为View保留所有用户可以从中选择的选项。现在,AndroidViews.java文件的编码应当如下:

package android_programmers_guide.AndroidViews; 
import android.app.Activity; 
import android.os.Bundle; 
public class AndroidViews extends Activity { 
/** Called when the Activity is first created. */ 
@Override 
public void onCreate(Bundle icicle) { 
super.onCreate(icicle); 
setContentView(R.layout.main); 

}

    你增加任何东西到活动中,你需要输入包装一个包装来创建你的菜单。输入android.view.Menu到AndroidViews活动中:

Import android.view.Menu;

    要创建菜单,你需要优先活动的onCreateOptionMenu()方法。onCreateOptionMenu()方法是一个以布尔方式被呼叫的当用户第一次选择菜单按钮。你将使用这个方式来建造菜单并增加可选择的条目到其中。增加下面的代码到AndroidViews.java:

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
super.onCreateOptionsMenu(menu); 
}

    你将增加代码在onCreaterOptionMenu()内增加菜单。需要增加的条目是你将要在本项目中创建的Views。下面是将要增加到菜单的清单:

● AutoComplete 自动完成 
● Button 按钮 
● CheckBox 选择框 
● EditText 
● RadioGroup 按钮组 
● Spinner 
    在前面创建的代码优先onCreateOptionsMenu()方法,你在菜单中传递一个菜单变量呼叫菜单。这个变量代表实际的在Android界面中创建的菜单条目。要增加这些条目到菜单中,你要使用menu.add()方法。这个呼叫的语句是:

menu.add(,,) </p> </td> </tr> </tbody> </table> <p>参数组被用来与菜单条目相关联。在本例中,你将不使用组。但是,这个值是非常重要的。参数id被用来检测哪一个菜单条目被选择。最后,参数标题是显示在菜单上的文本。</p> <p>增加下列代码到onCreateOptionsMenu( )方法中:</p> <table> <tbody> <tr> <td valign="top"> <p>menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner");</p> </td> </tr> </tbody> </table> <p>    你完整的AndroidViews.java文件应当看上去像这样:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> }</p> </td> </tr> </tbody> </table> <p>    如果你执行如上面代码所写,你应当看见如下图所示的菜单(略)。</p> <p>    这个就是你想要达成的。试着去点击菜单中的任意一个选项。当用户选择一个菜单项目时,你在处理世间的活动中还什么都没有呢。</p> <p>    你增加的方法优先处理到菜单的呼叫是onOptionsItemSelected( )。再一次,像onCreateOptionsMenu(),当菜单项被选择,onOptionsItemSelected( )是你需要优先一个拥有代码,被执行的布尔方法。优先代码应当如下:</p> <table> <tbody> <tr> <td valign="top"> <p>@Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> }</p> </td> </tr> </tbody> </table> <p>这个代码有个问题:当任何菜单项被选择,onOptionsItemSelected( ) 是个常规被呼叫的方法。你需要给定onOptionsItemSelected( )一条路来识别不同菜单项之间的差别并执行相应的代码。因此,使用一个switch/case 声明来帮助方法从不同的项目中选择。当你创建了菜单项,你定义了一系列的从0到5的数字作为菜单项的值。你可以在case声明中使用一个呼叫来getI()来检测哪一个菜单项被选择:</p> <table> <tbody> <tr> <td valign="top"> <p>switch (item.getId()) { <br> case 0: <br> return true; <br> case 1: <br> return true; <br> case 2: <br> return true; <br> case 3: <br> return true; <br> case 4: <br> return true; <br> case 5: <br> return true; <br> } <br> return true; </p> </td> </tr> </tbody> </table> <p>    在这个case声明中,对于每一个id当前设定的动作是返回true.这个不会做任何的事情但是会保留一个开放的可以增加代码的区域。你的AndroidViews.java文件现在可以被用来创建被新菜单系统启动的活动了。完整的AndroidViews.java文件代码应当看上去如下:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. / <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> /** Add one menu item for each View in our project */ <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> /** Override onOptionsItemSelected to execute code for each <br> menu item */ <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> } <br> /** Select statement to handle calls <br> to specific menu items */ <br> switch (item.getId()) { <br> case 0: <br> return true; <br> case 1: <br> return true; <br> case 2: <br> return true; <br> case 3: <br> return true; <br> case 4: <br> return true; <br> case 5: <br> return true; <br> } <br> return true; <br> } <br> } </p> </td> </tr> </tbody> </table> <p>    完成了AndroidViews.java,你可以重点去创建其它的活动了,在下一节中,你将在项目中为每一个View创建一个活动并增加代码来启动case声明中view的活动。</p> <p>为AutoComplete创建一个活动</p> <h2>为<span style="font-family:Arial">AutoComplete</span><span style="font-family:宋体">创建一个活动 第八章</span><span style="font-family:Arial">(4)</span></h2> <p>  在本节中,你将创建一个突出AutoCompleteTextView的活动。AutoCompleteTextView对你的有应用程序来说可以成为一个非常有力的工具。特别是对于Android主屏幕有限的空间来说。</p> <p>AutoCompleteTextView,正如这个名字所说,是修改后的TextView,而它可以参考到可用的单词或者短语并自动完成输入。这样的Views是在移动应用程序里是非常有用的当你不想花费大量的空间到一个ListView,或者你想要加速你输入文本的过程。</p> <p>    要开始为AutoCompleteTextView创建活动,你需要为布局增加一个新的.xml文件,为代码增加一个.java文件,并且一个Intent过滤器来处理呼叫。</p> <p>提示</p> <p>创建这些条目的过程出现在本章“构造活动”一节中。创建下面项目的部分时可以参考那个部分。</p> <p>创建一个autocomplete.xml文件 <br> 在你的AndroidViews项目中创建一个新的.xml文件,并命名为autocomplete.xml。记住文件名必须用小写。这个文件应当出现在layout文件夹。双击这个文件来编辑它。这个文件会控制AutoCompleteTextView活动的布局,所以你需要在布局中有一个AutoCompleteTextView。增加过AutoCompleteTextView的XML文件应当如下:</p> <table> <tbody> <tr> <td valign="top"> <p><AutoCompleteTextView android:id="@+id/testAutoComplete" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content"/></p> </td> </tr> </tbody> </table> <p>你已经在.xml文件中创建了几个Views了,所以你应当熟悉这个格式。对于AutoCompleteTextView,没什么特别之处。你设定id到testAutoComplete,还有相应的宽度和高度到fill_parent和wrap_content,还应该为两个按钮增加布局。这些按钮将被用于你将改变的属性控制。命名按钮为autoCompleteButton和textColorButton,如下:</p> <p> </p> <table> <tbody> <tr> <td valign="top"> <p><Button android:id="@+id/autoCompleteButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Layout"/> <br> <Button android:id="@+id/textColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Text Color"/></p> </td> </tr> </tbody> </table> <p><br>    有了新增的三个View布局,你完成的autocomplete.xml文件看上去应该像这样:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <AutoCompleteTextView android:id="@+id/testAutoComplete" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content"/> <br> <Button android:id="@+id/autoCompleteButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Layout"/> <br> <Button android:id="@+id/textColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Text Color"/> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>创建一个autocomplete.java文件 <br>     跟从本章“创建一个新的java文件”的介绍。第一件要做的事就是为你的Views输入包装。在这个活动中,使用了两个Views,AutoCompleteTextView 和按钮。你还需要设置颜色和一个ArrayAdapter。因此,输入下面包装到活动中:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.ArrayAdapter; <br> import android.widget.AutoCompleteTextView; <br> import android.widget.Button; <br> import android.graphics.Color;</p> </td> </tr> </tbody> </table> <p>注意</p> <p>现在你可能不知道它们的用途,先加入它们吧,我会解释的。</p> <p>为AutoCOmplete类增加初始的结构到autocomplete.java文件中:</p> <table> <tbody> <tr> <td valign="top"> <p>public class AutoComplete extends Activity { <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>这个类给了你建造活动其它部分的基础。这个活动的所有功能将会被围绕这个类建造。第一件要做的事就是从autocomplete.xml中装载布局:</p> <table> <tbody> <tr> <td valign="top"> <p>setContentView(R.layout.autocomplete);</p> </td> </tr> </tbody> </table> <p>对于本例,将创建AutoComplete TextView,所以它包含一年中的月份。当一个用户在框中输入,它会猜测那个月份用户试图去输入。假定AutoComplete TextView将包含月份的清单,你需要来创建一个可以被赋值到AutoCompleteTextView的清单。</p> <p>创建字符串数组并赋值月份数值到其中:</p> <table> <tbody> <tr> <td valign="top"> <p>static final String[] Months = new String[]{ <br> "January","February","March","April","May","June","July","August", <br> "September","October","November","December" <br> };</p> </td> </tr> </tbody> </table> <p>下一个任务是复制这个字符串到AutoCompleteTextView。到目前为止,你已经创建了一些Views了。所以,创建AutoCompleteTextView的代码看上去应该很熟悉。你之前没看到过的就是把字符串赋值给View:</p> <table> <tbody> <tr> <td valign="top"> <p>ArrayAdapter<String> monthArray = new ArrayAdapter<String>(this, <br> android.R.layout.simple_list_item_1, Months); <br> final AutoCompleteTextView textView = (AutoCompleteTextView) <br> findViewById(R.id.testAutoComplete); <br> textView.setAdapter(monthArray);</p> </td> </tr> </tbody> </table> <p>在第一行,拿去创建的字符串数组并且复制到一个名为monthArray的ArrayAdapter。下一步,你通过在.xml文件中定位来例示AutoCompleteTextView。最后,使用setAdapter()方法来赋值monthArray ArrayAdapter 到AutoCompleteTextView中。</p> <p>下一个零星的代码例示那两个按钮。与上一章的代码相同。唯一和你所写代码不同的是你正在呼叫两个函数,changeOption 和 changeOption2,而这些,你还没有创建呢。</p> <p>注意</p> <p>你在传递AutoCompleteTextView 到函数呼叫。当你创建函数还需要创建参数。</p> <table> <tbody> <tr> <td valign="top"> <p>final Button changeButton = (Button) findViewById(R.id.autoCompleteButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(textView); <br> } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.textColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(textView); <br> } <br> });</p> </td> </tr> </tbody> </table> <p>被这些按钮呼叫的函数将被用于在AutoComplete TextView改变布局属性。这两个我选择来修改的属性(通过两个按钮)是布局的高度和文本的颜色。你将设置一个按钮来改变AutoCompleteTextView的布局高度,从30到100并且返回。另一个按钮将改变View内文本的为红色。</p> <p>函数changeOption()会改变AutoCompleteTextView的布局高度。代码非常的简单:</p> <table> <tbody> <tr> <td valign="top"> <p>public void changeOption(AutoCompleteTextView text){ <br> if (text.getHeight()==100){ <br> text.setHeight(30); <br> } <br> else{ <br> text.setHeight(100); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>在这个函数中你要做的就是检查当前AutoCompleteTextView的高度。如果高度是100,把它设为30,否则设为100。</p> <p>changeOption2()函数也简单:</p> <table> <tbody> <tr> <td valign="top"> <p>public void changeOption2(AutoCompleteTextView text){ <br> text.setTextColor(Color.RED); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>这个函数简单的把AutoCompleteTextView的文本颜色设为Color.RED。</p> <p>Color.RED的数值从android.graphics.Color包装中导入。你可以浏览这个包装并且改变这个颜色到任何的数值。我选择红色是因为它比较突出。</p> <p>完整的autocomplete.java 文件应当看起来像这样:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.ArrayAdapter; <br> import android.widget.AutoCompleteTextView; <br> import android.widget.Button; <br> import android.graphics.Color; <br> public class AutoComplete extends Activity { <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.autocomplete); <br> ArrayAdapter<String> monthArray = new ArrayAdapter<String>(this, <br> android.R.layout.simple_list_item_1, Months); <br> final AutoCompleteTextView textView = (AutoCompleteTextView) <br> findViewById(R.id.testAutoComplete); <br> textView.setAdapter(monthArray); <br> final Button changeButton = (Button) <br> findViewById(R.id.autoCompleteButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(textView); <br> } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.textColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(textView); <br> } <br> }); <br> } <br> static final String[] Months = new String[]{ <br> "January","February","March","April","May","June","July","August", <br> "September","October","November","December" <br> }; <br> public void changeOption(AutoCompleteTextView text){ <br> if (text.getHeight()==100){ <br> text.setHeight(30); <br> } <br> else{ <br> text.setHeight(100); <br> } <br> } <br> public void changeOption2(AutoCompleteTextView text){ <br> text.setTextColor(Color.RED); <br> } <br> } </p> </td> </tr> </tbody> </table> <p>增加一个Intent过滤器 <br> 在运行这个应用程序之前最后一件事就是在AndroidManifest.xml文件中设置intent过滤器。然后你能从菜单中呼叫intent了。Intent过滤器的代码如下:</p> <table> <tbody> <tr> <td valign="top"> <p><activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity></p> </td> </tr> </tbody> </table> <p>这儿是本项目完成的AndroidManifest.xml文件:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidViews"> <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidViews" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" <br> /> <br> </intent-filter> <br> </activity> <br> </application> <br> </manifest> <br> Handling the Intent Call <br> With AndroidManifest.xml complete, add the following function to AndroidViews.java: <br> public void showAutoComplete(){ <br> Intent autocomplete = new Intent(this, AutoComplete.class); <br> startActivity(autocomplete); <br> }</p> </td> </tr> </tbody> </table> <p>当从select/case声明中呼叫,这个函数将打开autocomplete活动,编辑select声明的case 0来让它呼叫新的函数:</p> <table> <tbody> <tr> <td valign="top"> <p>case 0: <br> showAutoComplete(); <br> return true;</p> </td> </tr> </tbody> </table> <p>在Android模拟器中运行这个应用程序。当主活动启动后,点击菜单按钮的AutoComplete菜单项。点击后应当把你带到autocomplete活动。</p> <p>要测试AutoCompleteTextView,开始输入单词January。在你输入几个字母后,你将会看到January出现在文本框中。下一步,点击Change Layout Button按钮,结果会是一个扩展的文本输入框。现在点击chang Text Color按钮并且输入一些文本。</p> <p>下一节会给你项目中剩下5个Views的代码支持。</p> <p>按钮</p> <h2>按钮 第八章<span style="font-family:Arial">(5)</span></h2> <p>看看下面的代码。这段代码代表了四个文件,AndroidManifest.xml, <br> Button.xml, testButton.java, 和 AndroidViews.java。增加代码到现存的AndroidViews活动中。</p> <p>警告</p> <p>如果你没有一开始就跟从本章,你执行代码时可能会遇到麻烦。要确保得到完整的项目,请从本章的开始开始阅读。</p> <p>AndroidManifest.xml</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidViews" <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidViews" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testButton" android:label="TestButton"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> </application> <br> </manifest></p> </td> </tr> </tbody> </table> <p><br> Button.xml</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> Chapter 8: Lists, Menus, and Other Views 173 <br> android:layout_height="fill_parent"> <br> <Button android:id="@+id/testButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="This is the test Button"/> <br> <Button android:id="@+id/layoutButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Layout"/> <br> <Button android:id="@+id/textColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Text Color"/> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p><br> testButton.java</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.Button; <br> import android.graphics.Color; <br> public class testButton extends Activity { <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.Button); <br> final Button Button = (Button) findViewById(R.id.testButton); <br> final Button changeButton = (Button)findViewById(R.id.layoutButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(Button); } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.textColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(Button); <br> } <br> }); <br> } <br> public void changeOption(Button Button){ <br> if (Button.getHeight()==100){ <br> Button.setHeight(30); <br> } <br> 174 Android: A Programmer’s Guide <br> Chapter 8: Lists, Menus, and Other Views 175 <br> else{ <br> Button.setHeight(100); <br> } <br> } <br> public void changeOption2(Button Button){ <br> Button.setTextColor(Color.RED); <br> } <br> }</p> </td> </tr> </tbody> </table> <p><br> AndroidViews.java</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> import android.content.Intent; <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> switch (item.getId()) { <br> case 0: <br> showAutoComplete(); <br> return true; <br> case 1: <br> showButton(); <br> return true; <br> case 2: <br> return true; <br> case 3: <br> return true; <br> case 4: <br> return true; <br> case 5: <br> return true; <br> } <br> return true; <br> } <br> public void showButton() { <br> Intent showButton = new Intent(this, testButton.class); <br> startActivity(showButton); <br> } <br> public void showAutoComplete(){ <br> Intent autocomplete = new Intent(this, AutoComplete.class); <br> startActivity(autocomplete); <br> } <br> } </p> </td> </tr> </tbody> </table> <p><br> 启动你的应用程序并且选择从菜单上选择按钮选项。试着点击Change Layout按钮。再一次,对文本来说,结果是一个较宽的显示区域,点击改变Text Color按钮并且文本变成红色。</p> <p>CheckBox</p> <h2>CheckBox <span style="font-family:宋体">第八章</span><span style="font-family:Arial">(6)</span></h2> <p>在本节中,将为CheckBox View创建一个活动。创建活动的步骤和前面的章节一致。因此,将会为你提供三个主要活动文件——AndroidManifest.xml, <br> checkbox.xml, 和 testCheckBox.java。这些文件在下面的部分提供。</p> <p>AndroidManifest.xml</p> <p>这个部分包含当前AndroidView的完整代码。如果你使用Eclipse,修改你活动的AndroidManifest.xml 文件,来使得它和下面一样:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidViews"> <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidViews" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testButton" android:label="TestButton"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testCheckBox" android:label="TestCheckBox"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> </application> <br> </manifest></p> </td> </tr> </tbody> </table> <p>checkbox.xml</p> <p>这个部分显示了完整的checkbox.xml代码。使用在本章早些时候提到的方法,在项目中创建一个新的XML文件,把它命名为checkbox.xml。使用下面的代码修改你的文件。</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <CheckBox android:id="@+id/testCheckBox" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="This is the test CheckBox"/> <br> <Button android:id="@+id/layoutButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Layout"/> <br> <Button android:id="@+id/textColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Text Color"/> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>testCheckBox.java <br> 本部分涵盖了执行Checkbox活动所需的最后新文件。在项目中创建一个新的.java文件,并把它命名为testCheckBox.java。这个是活动的主文件并且包含可执行代码。在testCheckBox.java文件中使用下列代码:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.CheckBox; <br> import android.widget.Button; <br> import android.graphics.Color; <br> public class testCheckBox extends Activity { <br> 180 Android: A Programmer’s Guide <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.checkbox); <br> final CheckBox checkbox = (CheckBox)findViewById(R.id.testCheckBox); <br> final Button changeButton = (Button)findViewById(R.id.layoutButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(checkbox); } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.textColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(checkbox); <br> } <br> }); <br> } <br> public void changeOption(CheckBox checkbox){ <br> if (checkbox.getHeight()==100){ <br> checkbox.setHeight(30); <br> } <br> else{ <br> checkbox.setHeight(100); <br> } <br> } <br> public void changeOption2(CheckBox checkbox){ <br> checkbox.setTextColor(Color.RED); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>AndroidViews.java <br> 创建这个活动的最后一步就是编辑AndroidViews.java文件。如果你要从AndroidViews主活动中呼叫testCheckBox活动,你必须增加代码到AndroidViews.java中。用下面的代码和你现存在AndroidViews.java中的代码作个比较。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> import android.content.Intent; <br> Chapter 8: Lists, Menus, and Other Views 181 <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> switch (item.getId()) { <br> case 0: <br> showAutoComplete(); <br> return true; <br> case 1: <br> showButton(); <br> return true; <br> case 2: <br> showCheckBox() <br> return true; <br> case 3: <br> return true; <br> case 4: <br> return true; <br> case 5: <br> return true; <br> } <br> return true; <br> } <br> public void showButton() { <br> Intent showButton = new Intent(this, testButton.class); <br> startActivity(showButton); <br> } <br> public void showAutoComplete(){ <br> Intent autocomplete = new Intent(this, AutoComplete.class); <br> startActivity(autocomplete); <br> } <br> public void showCheckBox() { <br> Intent checkbox = new Intent(this, testCheckBox.class); <br> startActivity(checkbox); <br> } <br> } </p> </td> </tr> </tbody> </table> <p>启动应用程序并从菜单中选择CheckBox选项。点击Change Layout和Change Text Color按钮。</p> <p>EditText</p> <h2>EditText <span style="font-family:宋体">第八章</span><span style="font-family:Arial">(7)</span></h2> <p>在本节中,和上一节很类似,你为EditText View创建一个活动。创建活动的步骤和前几节是一样的。因此,你将被提供三个主要活动文件的代码。—AndroidManifest.xml, edittext.xml, 和 testEditText.java。这些在下面提供给你。</p> <p>AndroidManifest.xml <br> 本部分包含当前AndroidView的AndroidManifest.xml完整代码。如果你使用Eclipse,修改你的活动的AndroidManifest.xml文件,使它和下面类似:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidViews"> <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidViews" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testButton" android:label="TestButton"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testCheckBox" android:label="TestCheckBox"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testEditText" android:label="TestEditText"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> </application> <br> </manifest> </p> </td> </tr> </tbody> </table> <p><br> edittext.xml <br> 这个部分展示了edittext.xml文件的完整代码。使用本章前面的指示,在项目中创建一个名为edittext.xml的文件。使用下面的代码来修改你的文件。</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <EditText android:id="@+id/testEditText" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> /> <br> <Button android:id="@+id/layoutButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Layout"/> <br> <Button android:id="@+id/textColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Text Color"/> <br> </LinearLayout> </p> </td> </tr> </tbody> </table> <p>testEditText.java <br> 本节包含了执行EditText活动需要的最后一个文件。在项目中创建一个名为testEditText.java的文件。这是个活动的主要文件并且包含了可执行代码。在testEditText.java文件中使用下面的代码来完成这个活动。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.EditText; <br> import android.widget.Button; <br> import android.graphics.Color; <br> public class testEditText extends Activity { <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.edittext); <br> final EditText edittext = (EditText)findViewById(R.id.testEditText); <br> final Button changeButton = (Button)findViewById(R.id.layoutButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(edittext); } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.textColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(edittext); <br> } <br> }); <br> } <br> public void changeOption(EditText edittext){ <br> if (edittext.getHeight()==100){ <br> edittext.setHeight(30); <br> } <br> else{ <br> edittext.setHeight(100); <br> } <br> } <br> public void changeOption2(EditText edittext){ <br> edittext.setTextColor(Color.RED); <br> } <br> } </p> </td> </tr> </tbody> </table> <p><br> AndroidViews.java <br> 创建这个活动的最后一步是编辑AndroidView.java。如果你要从主要AndroidView活动中呼叫testEditText活动,你必须增加代码到AndroidView.java中。与你当前AndroidViews.java文件中的代码与下面进行比较。增加需求的代码来完成文件。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> import android.content.Intent; <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> switch (item.getId()) { <br> case 0: <br> showAutoComplete(); <br> return true; <br> case 1: <br> showButton(); <br> return true; <br> case 2: <br> showCheckBox(); <br> return true; <br> case 3: <br> showEditText(); <br> return true; <br> case 4: <br> showRadioGroup(); <br> return true; <br> case 5: <br> showSpinner(); <br> return true; <br> } <br> return true; <br> } <br> public void showButton() { <br> Intent showButton = new Intent(this, testButton.class); <br> startActivity(showButton); <br> } <br> public void showAutoComplete(){ <br> Intent autocomplete = new Intent(this, AutoComplete.class); <br> Chapter 8: Lists, Menus, and Other Views 187 <br> startActivity(autocomplete); <br> } <br> public void showCheckBox(){ <br> Intent checkbox = new Intent(this, testCheckBox.class); <br> startActivity(checkbox); <br> } <br> public void showEditText() { <br> Intent edittext = new Intent(this, testEditText.class); <br> startActivity(edittext); <br> } <br> } </p> </td> </tr> </tbody> </table> <p>启动应用程序并从菜单中选择EditText选项。点击Changae Layout和Change Test Color按钮。</p> <p>RadioGroup</p> <h2>RadioGroup <span style="font-family:宋体">第八章</span><span style="font-family:Arial">(8)</span></h2> <p>在本章中将为RadioGroup View创建一个活动。创建活动的步骤和前节一致。因此会为你提供三个主要文件—AndroidManifest.xml,radiogroup.xml,和 testRadioGroup.java。这些文件将在下面提供给你。</p> <p>AndroidManifest.xml <br> 这个部分包含当前AndroidView AndroidManifest.xml的完整代码。如果你使用Eclipse,修改活动的AndnroidManifest.xml文件如下:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidViews"> <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidViews" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testButton" android:label="TestButton"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testCheckBox" android:label="TestCheckBox"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testEditText" android:label="TestEditText"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testRadioGroup" android:label="Test <br> RadioGroup"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> </application> <br> </manifest></p> </td> </tr> </tbody> </table> <p>radiogroup.xml <br> 这个部分展示了完整的radiogroup.xml文件的完整代码。使用本章前节描述的方法,在项目中创建一个名为radiogroup.xml的文件。</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> Chapter 8: Lists, Menus, and Other Views 191 <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <RadioGroup android:id="@+id/testRadioGroup" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" > <br> <RadioButton <br> android:text="Radio 1" <br> android:id="@+id/radio1" <br> /> <br> <RadioButton <br> android:text="Radio 2" <br> android:id="@+id/radio2" /> <br> </RadioGroup> <br> <Button android:id="@+id/enableButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Set isEnabled"/> <br> <Button android:id="@+id/backgroundColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Background Color"/> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>testRadioGroup.java <br> 本部分包含执行RadioGroup活动最后所需的文件。在项目中创建一个名为testRadioGroup.java的文件。这是个活动的主要文件并且包含可执行的代码。在testRadioGroup.java文件中使用下面的代码来完成文件。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.RadioGroup; <br> import android.widget.Button; <br> import android.graphics.Color; <br> public class testRadioGroup extends Activity { <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.radiogroup); <br> final RadioGroup radiogroup = (RadioGroup) <br> findViewById(R.id.testRadioGroup); <br> final Button changeButton = (Button)findViewById(R.id.enableButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(radiogroup); } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.backgroundColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(radiogroup); <br> } <br> }); <br> } <br> public void changeOption(RadioGroup radiogroup){ <br> if (radiogroup.isEnabled()){ <br> radiogroup.setEnabled(false); <br> } <br> else{ <br> radiogroup.setEnabled(true); <br> } } <br> public void changeOption2(RadioGroup radiogroup){ <br> radiogroup.setBackgroundColor(Color.RED); <br> } <br> }</p> </td> </tr> </tbody> </table> <p><br> AndroidViews.java <br> 最后创建活动的部分是编辑AndroidViews.java。如果你要从主要活动中呼叫testRadioGroup活动,你必须在AndroidViews.java文件中增加代码。与你当前AndroidViews.java文件中的代码相比较,增加所需的代码来完成文件。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> import android.content.Intent; <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> switch (item.getId()) { <br> case 0: <br> showAutoComplete(); <br> return true; <br> case 1: <br> showButton(); <br> return true; <br> case 2: <br> showCheckBox(); <br> return true; <br> case 3: <br> showEditText(); <br> return true; <br> case 4: <br> showRadioGroup(); <br> return true; <br> case 5: <br> showSpinner(); <br> return true; <br> } <br> return true; <br> } <br> public void showButton() { <br> Intent showButton = new Intent(this, testButton.class); <br> startActivity(showButton); <br> Chapter 8: Lists, Menus, and Other Views 193 <br> 194 Android: A Programmer’s Guide <br> } <br> public void showAutoComplete(){ <br> Intent autocomplete = new Intent(this, AutoComplete.class); <br> startActivity(autocomplete); <br> } <br> public void showCheckBox(){ <br> Intent checkbox = new Intent(this, testCheckBox.class); <br> startActivity(checkbox); <br> } <br> public void showEditText() { <br> Intent edittext = new Intent(this, testEditText.class); <br> startActivity(edittext); <br> } <br> public void showRadioGroup(){ <br> Intent radiogroup = new Intent(this, testRadioGroup.class); <br> startActivity(radiogroup); <br> } <br> public void showSpinner(){ <br> } <br> }</p> </td> </tr> </tbody> </table> <p>启动应用程序并从菜单中选择RadioGroup选项。</p> <p>试着点击Set isEnabled和Change BackGroud Color按钮。注意Set isEnabled按钮把RadioGroup设为不可用,而Change Backgroud Color按钮改变组的背景色。</p> <p>Spinner</p> <h2>Spinner <span style="font-family:宋体">第八章</span><span style="font-family:Arial">(9)</span></h2> <p>在本节中将为Spinner View创建一个活动。Spinner View和其它编程语言里的ComboBox相类似。创建这个活动的步骤和前面部分的一样。因此,还是会提供给你三个主要活动的代码文件—AndroidManifest.xml, spinner.xml, <br> 和 testSpinner.java。下面就是这些提供的文件。</p> <p>AndroidManifest.xml</p> <p>本节包含当前AndroidViews的AndroidManifest.xml文件的完整代码。如果你使用Eclipse,修改活动的AndroidManifest.xml文件使它和下面一样:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidViews"> <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidViews" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".AutoComplete" android:label="AutoComplete"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testButton" android:label="TestButton"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testCheckBox" android:label="TestCheckBox"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testEditText" android:label="TestEditText"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testRadioGroup" android:label="Test <br> RadioGroup"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER"/> <br> </intent-filter> <br> </activity> <br> <activity android:name=".testSpinner" android:label="Test Spinner"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> </application> <br> </manifest></p> </td> </tr> </tbody> </table> <p>spinner.xml</p> <p>本节展示了spinner.xml文件的完整代码。在项目中创建一个名为spinner.xml的文件。使用下面的代码修改你的文件。</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <Spinner android:id="@+id/testSpinner" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> /> <br> <Button android:id="@+id/enableButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Set isEnabled"/> <br> <Button android:id="@+id/backgroundColorButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Change Background Color"/> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p><br> testSpinner.java</p> <p>本节包含了执行Spinner活动所需要的最后一个文件。在项目中创建一个名为testSpinner.java的新文件。这是个活动的主要文件并且包含可执行代码。在testSpinner.java文件中使用下面的代码来完成这个活动。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.ArrayAdapter; <br> import android.widget.Spinner; <br> import android.widget.Button; <br> import android.graphics.Color; <br> 198 Android: A Programmer’s Guide <br> public class testSpinner extends Activity { <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.spinner); <br> final Spinner spinner = (Spinner) findViewById(R.id.testSpinner); <br> ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, <br> android.R.layout.simple_spinner_item, Months); <br> adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); <br> spinner.setAdapter(adapter); <br> final Button changeButton = (Button)findViewById(R.id.enableButton); <br> changeButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption(spinner); } <br> }); <br> final Button changeButton2 = (Button) <br> findViewById(R.id.backgroundColorButton); <br> changeButton2.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> changeOption2(spinner); <br> } <br> }); <br> } <br> static final String[] Months = new String[]{ <br> "January","February","March","April","May","June","July","August", <br> "September","October","November","December" <br> }; <br> public void changeOption(Spinner spinner){ <br> if (spinner.isEnabled()){ <br> spinner.setEnabled(false); <br> } <br> else{ <br> spinner.setEnabled(true); <br> } <br> } <br> public void changeOption2(Spinner spinner){ <br> spinner.setBackgroundColor(Color.RED); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>AndroidViews.java</p> <p>创建活动的最后一个步骤就是编辑AndroidViews.java。如果你要从主活动AndroidViews中呼叫testSpinner活动,你必须增加代码到AndroidViews.java中。用当前的AndroidViews.java和下面的代码作个比较。增加代码来完成文件。</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidViews; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.view.Menu; <br> import android.content.Intent; <br> public class AndroidViews extends Activity { <br> /** Called when the Activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, 0, "AutoComplete"); <br> menu.add(0, 1, "Button"); <br> menu.add(0, 2, "CheckBox"); <br> menu.add(0, 3, "EditText"); <br> menu.add(0, 4, "RadioGroup"); <br> menu.add(0, 5, "Spinner"); <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item){ <br> switch (item.getId()) { <br> case 0: <br> showAutoComplete(); <br> return true; <br> case 1: <br> showButton(); <br> return true; <br> case 2: <br> showCheckBox(); <br> return true; <br> case 3: <br> showEditText(); <br> return true; <br> case 4: <br> showRadioGroup(); <br> return true; <br> case 5: <br> showSpinner(); <br> return true; <br> } <br> return true; <br> } <br> public void showButton() { <br> Intent showButton = new Intent(this, testButton.class); <br> startActivity(showButton); <br> } <br> public void showAutoComplete(){ <br> Intent autocomplete = new Intent(this, AutoComplete.class); <br> startActivity(autocomplete); <br> } <br> public void showCheckBox(){ <br> Intent checkbox = new Intent(this, testCheckBox.class); <br> startActivity(checkbox); <br> } <br> public void showEditText() { <br> Intent edittext = new Intent(this, testEditText.class); <br> startActivity(edittext); <br> } <br> public void showRadioGroup(){ <br> Intent radiogroup = new Intent(this, testRadioGroup.class); <br> startActivity(radiogroup); <br> } <br> public void showSpinner(){ <br> Intent spinner = new Intent(this, testSpinner.class); <br> startActivity(spinner); <br> } <br> } </p> </td> </tr> </tbody> </table> <p>启动应用程序并从菜单中选择Spinner选项。试着点击Set isEnabled和Change Backgroud Color按钮。</p> <p>试试这个:修改更多的View属性</p> <h2>试试这个:修改更多的<span style="font-family:Arial">View</span><span style="font-family:宋体">属性 第八章</span><span style="font-family:Arial">(10)</span></h2> <p>为活动修改按钮动作在每个View中来改变不同的属性:</p> <p>● 使用Eclipse的特性列表来查看每个View有哪些属性。 <br> ● 在任一个给定的活动中编辑两个按钮的功能来更改按钮和View的交互活动。</p> <p>在下一章中,将用到谷歌的API(Google API)。将创建一个应用程序和GTalk交互。这将会给你更多的知识关于如何构造独特的应用程序。</p> <p>问专家</p> <p>Q:如果我在应用程序中使用多个Views,我可以使用android.widget.*输入整个widget包装吗?</p> <p>A:是的。但是,我会保守的使用呼叫。当你输入整个包装,你增加了包装的所有代码到活动中。这个管理的不好会让活动速度变慢。我会只输入我需要的包装,试图减少活动中的代码。</p> <p>第九章 使用手机的GPS功能</p> <h2>使用手机的<span style="font-family:Arial">GPS</span><span style="font-family:宋体">功能 第九章</span><span style="font-family:Arial">(1)</span></h2> <p>关键技能 & 概念</p> <p>● 使用Android的定位服务APIs</p> <p>● 从GPS硬件获得坐标数据</p> <p>● 改变活动的外观并且和RelativeLayout接触</p> <p>● 使用一个MapView来绘制你的当前位置</p> <p>● 使用谷歌地图来找到你的当前位置</p> <p>在本章中,你将学习关于Android定位的API。本章的作用是非常重要的,如果你想要让Android和GPS硬件一起工作的话。你将使用位置基础的API来收集你当前的位置并把它在屏幕上显示出来。到本章的结尾,你将在手机上使用谷歌地图来显示你的当前位置。</p> <p>你还会学习到一些关于活动的更深,创新的技巧。资源,如RelativeLayouts和小按钮将允许你创建更友好,可视的活动。第一节,你将学习使用设备的GPS硬件来获得当前的位置。但是,在跳到那个部分之前,你需要先创建一个项目。在Eclipse中创建一个项目并命名为AndroidLBS。</p> <p>使用Android位置基础API</p> <p>Android SDK包含了一个API,它被定制为帮助接口活动与设备上任何的GPS硬件。这章假定你的设备包含GPS硬件。</p> <p>警告</p> <p>Android平台的手机不要求包含一个照相机,也不要求包含GPS硬件,虽然很多的型号可能包含照相机和GPS硬件。Android包含了Android位置基础API预期GPS硬件会被包含在很多手机上。</p> <p>因为你工作在一个软件模拟器中,并且不是一个真的设备,GPS硬件没有被模拟。在本例中,Android在adb服务器中提供了一个文件模拟GPS硬件。这个文件放置在data/misc/location/<provider>,<provider>代表位置信息提供者。Android提供的<provider>是data/misc/location/gps</p> <p>提示</p> <p>你可以有多重的提供者来模拟不同的方案。因此,你可以创建一个提供者为test或者gps1;无论你愿意用哪个。在具体的provider的文件夹内可以用任何数量的文件保留你想要Android使用的例子。当你使用Android模拟器,你可以使用下面类型的文件来储存/找回GPS文体的坐标。每一个文件类型有个不同的格式来提供信息给Android位置基础API <br> ● kml <br> ● nmea <br> ● track </p> <p>我们来看看每一个文件都做些什么并且互相之间有什么不同。</p> <p>创建一个kml文件</p> <p>一个.kml文件Keyhole Markup语言文件。这些文件通常被开发用于并且可以被Google Earth(一款Google软件)。Adnroid位置基础API可以分析一个.kml文件来模拟一个GPS。</p> <p>注意</p> <p>假如你没有Google Earth。可以从Google免费下载。如果你想要开发更多的Android位置基础API活动,安装这个软件是值得的。</p> <p> </p> <p>要从Google Earth创建一个.kml文件,打开Google Earth并且导航到一个位置。选择文件|另存为并选择KML。在本例中,它为你所导航到的地点产生一个.kml文件。下面的.kml代码就是来自这个文件。仔细看看<coordinates>标签,那就是Android位置基础API会读取的。</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="UTF-8"?> <br> <kml xmlns="http://earth.google.com/kml/2.2"> <br> <Document> <br> <name>Tampa, FL.kml</name> <br> <Styleid="default+icon=http://maps.google.com/mapfiles/kml/pal3/icon52.png"> <br> <IconStyle> <br> <scale>1.1</scale> <br> <Icon> <br> <href>http://maps.google.com/mapfiles/kml/pal3/icon52.png</href> <br> </Icon> <br> </IconStyle> <br> <LabelStyle> <br> <scale>1.1</scale> <br> </LabelStyle> <br> </Style> <br> <Styleid="default+icon=http://maps.google.com/mapfiles/kml/pal3/icon60.png"> <br> <IconStyle> <br> <Icon> <br> <href>http://maps.google.com/mapfiles/kml/pal3/icon60.png</href> <br> </Icon> <br> </IconStyle> <br> </Style> <br> <StyleMapid="default+nicon=http://maps.google.com/mapfiles/kml/pal3/ <br> icon60.png+hicon=http://maps.google.com/mapfiles/kml/pal3/icon52.png"> <br> <Pair> <br> <key>normal</key> <br> <styleUrl>#default+icon=http://maps.google.com/mapfiles/kml/pal3/ <br> icon60.png</styleUrl> <br> </Pair> <br> <Pair> <br> <key>highlight</key> <br> <styleUrl>#default+icon=http://maps.google.com/mapfiles/kml/pal3/ <br> icon52.png</styleUrl> <br> </Pair> <br> </StyleMap> <br> <Placemark> <br> <name>Tampa, FL</name> <br> <open>1</open> <br> <address>Tampa, FL</address> <br> <LookAt> <br> <longitude>-82.451142</longitude> <br> <latitude>27.98146</latitude> <br> <altitude>0</altitude> <br> <range>38427.828125</range> <br> <tilt>0</tilt> <br> <heading>0</heading> <br> </LookAt> <br> <styleUrl>#default+nicon=http://maps.google.com/mapfiles/kml/pal3/ <br> icon60.png+hicon=http://maps.google.com/mapfiles/kml/pal3/icon52.png</styleUrl> <br> <Point> <br> <coordinates>-82.451142,27.98146,0</coordinates> <br> </Point> <br> </Placemark> <br> </Document> <br> </kml> </p> </td> </tr> </tbody> </table> <p><br> 你可以用Google Earth来创建自己的.kml文件来模拟不同的位置。当你想要制作一个相应用户不同位置的活动时,这个非常有用。创建.kml文件如此简单使得模拟GPS硬件非常的灵活。</p> <p>什么是轨迹文件</p> <h2>什么是轨迹文件 第九章<span style="font-family:Arial">(2)</span></h2> <p>Android提供的在gps文件夹里的文件是一个.nmea文件(国家海事电子协会文件)。一个.nmea文件可以从任何通用的GPS产品中输出。这些文件是常用格式并且可以包含多重坐标和海拔,来表现行程和轨迹。下面的部分讨论并且在Windows和Linux下各自打开这个文件。</p> <p>在Windows中得到nmea文件</p> <p>Android提供的nmea文件展示了一个贯穿旧金山的短的线路。让我们看看nmea文件的内部。使用adb工具把文件从服务器中pull到你的桌面:</p> <p>adb pull<远程文件><本地文件></p> <p> </p> <p>下面的插图描述使用adb工具pull命令来检索文件(略)。如果命令执行成功,你应当看到一条消息指示文件下载的大小。导航到 C:\Android 文件夹,你可以看到adb pull工具放在这里。</p> <p>现在nmea文件在桌面上,把它与Notepad关联。最后打开它来看看它的内容。你会看到很多的坐标数据。</p> <p>在Linux中得到nmea文件</p> <p>如果你在使用Linux开发Android,启动一个终端部分来进入adb服务器。让我们来看看如何在Linux中检索并且编辑nmea文件。</p> <p>注意(和插图有关,略)</p> <p> </p> <p>第一步是打开一个新的终端部分 (Applications | System Tools | Terminal)。</p> <p>下一步,使用adb pull命令来pull nmea文件到Android文件夹:</p> <p>adb pull data/misc/location/gps/nmea Android/</p> <p>如果你读了关于Windows如何得到nmea文件的说明,你会发现语法上的不同。C:\是没有必要的因为路径结构的不同。</p> <p>从终端中执行了命令后,结果应当如下所示:</p> <p>使用Is命令来在Android文件夹中列出文件。如果命令执行正确,nmea文件应当出现。我使用Fedora GUI来导航并且使用系统的Text Editor打开它。</p> <p>提示</p> <p>你也可以使用vi编辑器从命令行来打开,读取并且编辑nmea文件。</p> <p>现在你已经查看了nmea文件并且知道模拟一个GPS设备的不同方式,你可以开始来使用Android位置基础API来创建一个完整特性的活动了。</p> <p>使用Android位置基础API读取GPS</p> <h2>使用<span style="font-family:Arial">Android</span><span style="font-family:宋体">位置基础</span><span style="font-family:Arial">API</span><span style="font-family:宋体">读取</span><span style="font-family:Arial">GPS </span><span style="font-family:宋体">第九章</span><span style="font-family:Arial">(3)</span></h2> <p>本章剩下的部分是致力于建造一个活动,AndroidLBS,它会从服务器中nmea文件中识别用户的位置。本活动的第一个过程非常的简单。</p> <p>你会创建一个简单的过程,该过程会得到用户当前的GPS位置。然后你可以在屏幕上显示这个位置的坐标。在做这个的时候,你会了解到一个对Android位置基础API比较到位的介绍和它的功用。</p> <p>创建AndroidLBS活动</p> <p>下面是创建这个简单活动的步骤:</p> <p>1.调整许可的权限</p> <p>2.创建活动的布局</p> <p>3.书写代码来允许活动。</p> <p>4.运行活动。</p> <p>调整许可的权限</p> <p>使用Android位置基础API是调整认可的权限。使用Android位置基础本身不要求任何特别的许可。但是在GPS使用Android位置基础来存取位置信息需要。</p> <p>从Eclipse中有两种方式可以设置许可。第一个是通过Android Manifest许可向导,这个你在第七章用过。在Eclipse中,双击AndroidManifest.xml来打开Android Manifest 综览窗口。点击许可链接并使用第七章的方法增加ACCESS_GPS 和 ACCESS_LOCATION 使用许可。</p> <p>第二种方法是,你可以手动编辑AndroidManifest.xml文件增加许可值到活动中。你会需要下面的代码行到AndroidManifest.xml中:</p> <table> <tbody> <tr> <td valign="top"> <p><uses-permission android:name="android.permission.ACCESS_GPS"> <br> </uses-permission> <br> <uses-permission android:name="android.permission.ACCESS_LOCATION"> <br> </uses-permission></p> </td> </tr> </tbody> </table> <p><br> 这里的语句是用来在<uses-permission>标签中增加许可名称。</p> <p>当你结束了增加许可,你的AndroidManifest.xml文件应当像下面的代码片段。这样的代码应当非常的熟悉了。你在Intent过滤器内使用了一个活动和一对许可。</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android=http://schemas.android.com/apk/res/android <br> package="android_programmers_guide.AndroidLBS"> <br> <application android:icon="@drawable/icon"> <br> <activity android:name=".AndroidLBS" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> </activity> <br> </application> <br> <uses-permission android:name="android.permission.ACCESS_GPS"> <br> </uses-permission><uses-permission <br> android:name="android.permission.ACCESS_LOCATION"> <br> </uses-permission></manifest></p> </td> </tr> </tbody> </table> <p>创建你的布局 <br> 要开始创建布局,在Eclipse中打开main.xml,你会一共增加一个按钮和4个TextViews到布局中。按钮可以从GPS呼叫信息并显示到TextViews中。</p> <p>按照下面设置按钮,也就是在屏幕的顶部并使用“Where am I”作为显示文本。</p> <table> <tbody> <tr> <td valign="top"> <p><Button <br> android:id="@+id/gpsButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Where Am I" <br> /></p> </td> </tr> </tbody> </table> <p>下一步,设置4个Texviews,你应当在布局中安排它们,2个TextViews会显示在另外2个TextViews之上。这样可以把其中的2个作为另外2个的标签来使用。要完成这个工作,你需要多两个LinearLayouts.</p> <p>请注意在main.xml文件中的所有元素是包含在一个LinearLayout标签中。这个标签用某些规则来绑定内含的元素。对于LinearLayouts,元素被以依次水平或者垂直的方乡堆栈。</p> <p>LinerLayout的方向由android:orientation属性管理。假如属性没有被赋值,初始的设定是水平方式。</p> <p>请注意,有一些槽(或者架子)是垂直放置的。你可以在屏幕上的这些槽上放置元素。总之,如果你要在立式LinearLayout布局的同一个架子上放置少量的条目,你需要先在架子上放置一个水平的LinearLayout。</p> <p>现在你可以上下左右的码放这些元素了。这是个在本活动中需要利用的概念。因此,在按钮的下面,增加一个水平的LinearLayout来放置2个TextViews。</p> <table> <tbody> <tr> <td valign="top"> <p><LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/latLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Latitude: " <br> /> <br> <TextView <br> android:id="@+id/latText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> Figure 9-2 Vertical LinearLayout with embedded horizontal LinearLayout <br> Horizontal LinearLayout <br> Android screen <br> /> <br> </LinearLayout> </p> </td> </tr> </tbody> </table> <p>这两个TextViews保留标签和你将要从GPS中采集的纬度数据。下一个,增加另一个水平LinearLayout来保留剩下的TextViews:</p> <table> <tbody> <tr> <td valign="top"> <p><LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/lngLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Longitude: " <br> /> <br> <TextView <br> android:id="@+id/lngText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> </p> </td> </tr> </tbody> </table> <p>这个为特定的活动提供了比较好的布局。你完成的main.xml文件应当如下:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <Button <br> android:id="@+id/gpsButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Where Am I" <br> /> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/latLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Latitude: " <br> /> <br> <TextView <br> android:id="@+id/latText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/lngLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Longitude: " <br> /> <br> <TextView <br> android:id="@+id/lngText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>书写代码来允许活动</p> <h2>书写代码来允许活动 第九章<span style="font-family:Arial">(4)</span></h2> <p>现在已经创建了布局,你可以开始写代码来允许活动了。你的按钮需要从GPS中来呼叫用户当前的位置。一旦你有了这些信息,你可以发送纬度和经度坐标到TextViews中了。</p> <p>首先,你需要增加输入声明。你需要输入4个包装:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.view.View; <br> import android.widget.TextView; <br> import android.content.Context; <br> import android.widget.Button;</p> </td> </tr> </tbody> </table> <p>和一个Android位置基础API:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.location.LocationManager; </p> </td> </tr> </tbody> </table> <p>下一步,为按钮创建代码。目标是从GPS中检索当前坐标信息。你已经在本书中创建了一些按钮了,而且这个的格式没有不同。你需要设置按钮并且从main.xml中装载布局。然后你可以设置onClick事件来呼叫一个函数,LoadCoords()。</p> <table> <tbody> <tr> <td valign="top"> <p>final Button gpsButton = (Button) findViewById(R.id.gpsButton); <br> gpsButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> LoadCoords(); <br> }});</p> </td> </tr> </tbody> </table> <p>创建活动的最后步骤是填充代码到LoadCoords()函数中。创建TextViews需要接受坐标数据:</p> <table> <tbody> <tr> <td valign="top"> <p>TextView latText = (TextView) findViewById(R.id.latText); <br> TextView lngText = (TextView) findViewById(R.id.lngText);</p> </td> </tr> </tbody> </table> <p>注意</p> <p>你没必要必须创建两个TextViews来作为标签,因为你不会发送任何东西到它们。</p> <p>现在,创建一个可以pull坐标数据的LocationManager。这个示例的重要部分是你必须要传递给LocationManager一个上下文;使用LOCATION_SERVICE:</p> <table> <tbody> <tr> <td valign="top"> <p>LocationManager myManager = <br> (LocationManager)getSystemService(Context.LOCATION_SERVICE);</p> </td> </tr> </tbody> </table> <p>要从myManager中pull坐标,使用getCurrentLocation()方法。这个方法需要一个参数,一个提供者,它们会展示API将要从中pull的坐标。在这种情况下,Android提供了一个本章早些时候讨论过的包含nmea文件的模拟位置GPS:</p> <table> <tbody> <tr> <td valign="top"> <p>Double latPoint = myManager.getCurrentLocation("gps").getLatitude(); <br> Double lngPoint = myManager.getCurrentLocation("gps").getLongitude();</p> </td> </tr> </tbody> </table> <p>最后,拿去双击数据并且把它们传递到TextViews:</p> <table> <tbody> <tr> <td valign="top"> <p>latText.setText(latPoint.toString()); <br> lngText.setText(lngPoint.toString()); <br> Your finished code should look like this: <br> package android_programmers_guide.AndroidLBS; <br> import android.app.Activity; <br> import android.os.Bundle; <br> import android.location.LocationManager; <br> import android.view.View; <br> import android.widget.TextView; <br> import android.content.Context; <br> import android.widget.Button; <br> public class AndroidLBS extends Activity { <br> /** Called when the activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> final Button gpsButton = (Button) findViewById(R.id.gpsButton); <br> gpsButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> LoadCoords(); <br> }}); <br> } <br> public void LoadCoords(){ <br> TextView latText = (TextView) findViewById(R.id.latText); <br> TextView lngText = (TextView) findViewById(R.id.lngText); <br> LocationManager myManager = (LocationManager) <br> getSystemService(Context.LOCATION_SERVICE); <br> Double latPoint = myManager.getCurrentLocation("gps").getLatitude(); <br> Double lngPoint = myManager.getCurrentLocation("gps").getLongitude(); <br> latText.setText(latPoint.toString()); <br> lngText.setText(lngPoint.toString()); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>运行活动</p> <p>在Android模拟器中运行你的活动。该活动将会打开如下的屏幕(略)。点击“Where Am I”按钮。你将会看到图片中显示的坐标。</p> <p>传递坐标到Google地图</p> <h2>传递坐标到<span style="font-family:Arial">Google</span><span style="font-family:宋体">地图 第九章</span><span style="font-family:Arial">(5)</span></h2> <p>在本节中,你将继续在前一节的基础上构造。对AndroidLBS活动的主要修改就是传递坐标到Google地图中。你将使用Google地图来显示用户的当前位置。在main.xml文件中的唯一修改指出就是为MpaView增加一个布局。在目前版本的Android SDK中,MapView被建立为一个类View。可能在将来的版本中MapView会相当于这个布局。</p> <table> <tbody> <tr> <td valign="top"> <p><view class="com.google.android.maps.MapView" <br> android:id="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content"/></p> </td> </tr> </tbody> </table> <p>完成后的main.xml文件应当像这样:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <Button <br> android:id="@+id/gpsButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Where Am I" <br> /> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/latLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Latitude: " <br> /> <br> <TextView <br> android:id="@+id/latText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/lngLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Longitude: " <br> /> <br> <TextView <br> android:id="@+id/lngText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <view class="com.google.android.maps.MapView" <br> android:id="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content"/> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>因为在这个活动中嵌入MapView,你需要改变类的定义。现在,主要类扩展了活动。但是要正确的使用Google MapView,你必须扩展MapActivity。因此,你需要输入MapActivity包装并且替换在头部的Activity 包装。</p> <p>输入下列包装:</p> <table> <tbody> <tr> <td valign="top"> <p>import com.google.android.maps.MapActivity; <br> import com.google.android.maps.MapView; <br> import com.google.android.maps.Point; <br> import com.google.android.maps.MapController</p> </td> </tr> </tbody> </table> <p>Point包装将被用于保留point的值,它就是展示地图坐标的,而MapController将你的point置于地图中央。这两个包装在使用MapView时非常的关键。</p> <p>现在准备增加建立地图并传递坐标的代码。首先,设置一个一个MapView,并且从main.xml文件中把它赋值到布局:</p> <table> <tbody> <tr> <td valign="top"> <p>MapView myMap = (MapView) findViewById(R.id.myMap);</p> </td> </tr> </tbody> </table> <p>下一步,设置一个Point并且把从GPS检索的数值赋值给latPoint和IngPoint:</p> <table> <tbody> <tr> <td valign="top"> <p>Point myLocation = new Point(latPoint.intValue(),lngPoint.intValue());</p> </td> </tr> </tbody> </table> <p>现在,可以创建MapController了,它将被用于移动Google地图来定位你定义的Point。从MapView使用getController()方法在定制的地图中建立一个控制器:</p> <table> <tbody> <tr> <td valign="top"> <p>MapController myMapController = myMap.getController();</p> </td> </tr> </tbody> </table> <p>唯一剩下的工作就是使用控制器来移动地图到你的位置(要让地图更容易辨认,把zoom设定为9):</p> <table> <tbody> <tr> <td valign="top"> <p>myMapController.centerMapTo(myLocation, false); <br> myMapController.zoomTo(9);</p> </td> </tr> </tbody> </table> <p>你刚才所写的所有代码就是从活动中利用Google地图。完整的类应当像这样:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidLBS; <br> import android.os.Bundle; <br> import android.location.LocationManager; <br> import android.view.View; <br> import android.widget.TextView; <br> import android.content.Context; <br> import android.widget.Button; <br> import com.google.android.maps.MapActivity; <br> import com.google.android.maps.MapView; <br> import com.google.android.maps.Point; <br> import com.google.android.maps.MapController; <br> public class AndroidLBS extends MapActivity { <br> /** Called when the activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> final Button gpsButton = (Button) findViewById(R.id.gpsButton); <br> gpsButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> LoadProviders(); <br> }}); <br> } <br> public void LoadProviders(){ <br> TextView latText = (TextView) findViewById(R.id.latText); <br> TextView lngText = (TextView) findViewById(R.id.lngText); <br> LocationManager myManager = (LocationManager) <br> getSystemService(Context.LOCATION_SERVICE); <br> Double latPoint = <br> myManager.getCurrentLocation("gps").getLatitude()*1E6; <br> Double lngPoint = <br> myManager.getCurrentLocation("gps").getLongitude()*1E6; <br> latText.setText(latPoint.toString()); <br> lngText.setText(lngPoint.toString()); <br> MapView myMap = (MapView) findViewById(R.id.myMap); <br> Point myLocation = new Point(latPoint.intValue(),lngPoint.intValue()); <br> MapController myMapController = myMap.getController(); <br> myMapController.centerMapTo(myLocation, false); <br> myMapController.zoomTo(9); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>在模拟器中运行活动。活动应当打开一个空白的地图。点击“Where Am I”按钮,应当会看到地图聚焦并且放大到旧金山。看看下图就会知道地图会如何出现(略)。</p> <p>增加缩放控制</p> <h2>增加缩放控制 第九章<span style="font-family:Arial">(6)</span></h2> <p>本章的最后一个练习是再增加两个按钮到AndroidLBS活动中。这些按钮将控制Google MapView放大和缩小的方法。让这个修改有一点不同的是我将为main.xml文件介绍一个布局的新类型:RelativeLayout。LinearLayouts允许你直接的一个接一个的放置Views,RelativeLayouts允许你在每一个View上放置。</p> <p>对于这个活动,将会放置两个按钮到Google Map上,要实现这个效果,你可以增加在地图上的按钮。</p> <table> <tbody> <tr> <td valign="top"> <p><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <view class="com.google.android.maps.MapView" <br> android:id="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content"/> <br> </RelativeLayout></p> </td> </tr> </tbody> </table> <p>现在可以增加另外的两个按钮了。它们会出现在MapView的左上方和左下方。你需要对Button布局做一个修改。按照默认的方式,RelativeLayout增加Button来和锚视图顶部的边缘排列,本例中,就是这个MapView。因此,在这个布局,使用android:layout_alignBottom属性并赋值MapView的id。这样就排列按钮到地图的底部了。</p> <table> <tbody> <tr> <td valign="top"> <p><Button android:id="@+id/buttonZoomIn" <br> style="?android:attr/buttonStyleSmall" <br> android:text="+" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonZoomOut" <br> style="?android:attr/buttonStyleSmall" <br> android:text="-" <br> android:layout_alignBottom="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /></p> </td> </tr> </tbody> </table> <p>提示</p> <p>仔细看一下按钮的布局属性。我使用一个新的属性,style,来把这个按钮改小。完整的main.xml应当看上去像这样:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <Button <br> android:id="@+id/gpsButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Where Am I" <br> /> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/latLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Latitude: " <br> /> <br> <TextView <br> android:id="@+id/latText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/lngLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Longitude: " <br> /> <br> <TextView <br> android:id="@+id/lngText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <view class="com.google.android.maps.MapView" <br> android:id="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content"/> <br> <Button android:id="@+id/buttonZoomIn" <br> style="?android:attr/buttonStyleSmall" <br> android:text="+" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonZoomOut" <br> style="?android:attr/buttonStyleSmall" <br> android:text="-" <br> android:layout_alignBottom="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> </RelativeLayout> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>你会去适当的修改这个代码。除了为新的views增加代码之外,你需要移除现存的一些代码。为了让活动更灵活,你需要移除MapView的示例和类主要部分的MapController。这样将会允许你传递这些项目到其它所需的函数(比如将要创建的放大和缩小特性)。</p> <table> <tbody> <tr> <td valign="top"> <p>final MapView myMap = (MapView) findViewById(R.id.myMap); <br> final MapController myMapController = myMap.getController();</p> </td> </tr> </tbody> </table> <p>现在可以创建两个新按钮的代码了。和你之前创建的按钮一样,增加呼叫到下一步将要构建的函数:</p> <table> <tbody> <tr> <td valign="top"> <p>final Button zoomIn = (Button) findViewById(R.id.buttonZoomIn); <br> zoomIn.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomIn(myMap,myMapController); <br> }}); <br> final Button zoomOut = (Button) findViewById(R.id.buttonZoomOut); <br> zoomOut.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomOut(myMap,myMapController); <br> }});</p> </td> </tr> </tbody> </table> <p>最后,创建控制放大缩小特性的函数。最大放大位置是21并且最小是1.因此,在函数中,在调整前测试当前的位置。这样将确保不会出现任何的运行问题。</p> <table> <tbody> <tr> <td valign="top"> <p>public void ZoomIn(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=21){ <br> mc.zoomTo(mv.getZoomLevel()+ 1); <br> } <br> } <br> public void ZoomOut(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=1){ <br> mc.zoomTo(mv.getZoomLevel()- 1); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>注意你传递MapView和MapController到函数中,从那里,对缩放位置进行整数处理非常简单。唯一的关键是这个函数是MapController本身移动MapView到希望的缩放位置,而MapView本身保留缩放位置。</p> <p>提示</p> <p>想想这个关系和一个遥控器和电视机相类似。遥控器切换电视到第5频道,但是频道本身是储存在电视上的。</p> <p>你完成的AndroidLBS.java文件应当看上去像这样:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidLBS; <br> import android.os.Bundle; <br> import android.location.LocationManager; <br> import android.view.View; <br> import android.widget.TextView; <br> import android.content.Context; <br> import android.widget.Button; <br> import com.google.android.maps.MapActivity; <br> import com.google.android.maps.MapView; <br> import com.google.android.maps.Point; <br> import com.google.android.maps.MapController; <br> public class AndroidLBS extends MapActivity { <br> /** Called when the activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> final MapView myMap = (MapView) findViewById(R.id.myMap); <br> final MapController myMapController = myMap.getController(); <br> final Button zoomIn = (Button) findViewById(R.id.buttonZoomIn); <br> zoomIn.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomIn(myMap,myMapController); <br> }}); <br> final Button zoomOut = (Button) findViewById(R.id.buttonZoomOut); <br> zoomOut.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomOut(myMap,myMapController); <br> }}); <br> final Button gpsButton = (Button) findViewById(R.id.gpsButton); <br> gpsButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> LoadProviders(myMap,myMapController); <br> }}); <br> } <br> public void LoadProviders(MapView mv, MapController mc){ <br> TextView latText = (TextView) findViewById(R.id.latText); <br> TextView lngText = (TextView) findViewById(R.id.lngText); <br> LocationManager myManager = (LocationManager) <br> getSystemService(Context.LOCATION_SERVICE); <br> Double latPoint = myManager.getCurrentLocation("gps").getLatitude()*1E6; <br> Double lngPoint = <br> myManager.getCurrentLocation("gps").getLongitude()*1E6; <br> latText.setText(latPoint.toString()); <br> lngText.setText(lngPoint.toString()); <br> Point myLocation = new Point(latPoint.intValue(),lngPoint.intValue()); <br> mc.centerMapTo(myLocation, false); <br> mc.zoomTo(9); <br> } <br> public void ZoomIn(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=21){ <br> mc.zoomTo(mv.getZoomLevel()+ 1); <br> } <br> } <br> public void ZoomOut(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=1){ <br> mc.zoomTo(mv.getZoomLevel()- 1); <br> } <br> } <br> }</p> </td> </tr> </tbody> </table> <p>在Android模拟器中运行这个活动。活动会打开一个新的MapView,如图所示放了按钮(略)。</p> <p>试一下放大和缩小按钮。当你放大,你应当看到和下面类似的图形(略)。</p> <p>试试这个:在MapView之间转换</p> <h2>试试这个:在<span style="font-family:Arial">MapView</span><span style="font-family:宋体">之间转换 第九章</span><span style="font-family:Arial">(7)</span></h2> <p>标准视图和卫星视图再编辑AndroidLBS活动一次。再增加两个按钮到RelativeLayout。这些按钮可以转换标准视图和卫星视图。下面是需要考虑的地方:</p> <p>● 使用排列布局属性使这转换按钮在MapView的另外一面。</p> <p>● 研究MapView来找到转换的方式。</p> <p>● 创建一个可以传递并转换MapView的函数。</p> <p>完成的main.xml和AndroidLBS.java文件应当如下:</p> <p>main.xml </p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <Button <br> android:id="@+id/gpsButton" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content" <br> android:text="Where Am I" <br> /> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/latLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Latitude: " <br> /> <br> <TextView <br> android:id="@+id/latText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> > <br> <TextView <br> android:id="@+id/lngLabel" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Longitude: " <br> /> <br> <TextView <br> android:id="@+id/lngText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> /> <br> </LinearLayout> <br> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <view class="com.google.android.maps.MapView" <br> android:id="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content"/> <br> <Button android:id="@+id/buttonZoomIn" <br> style="?android:attr/buttonStyleSmall" <br> android:text="+" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonMapView" <br> style="?android:attr/buttonStyleSmall" <br> android:text="Map" <br> android:layout_alignRight="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonSatView" <br> style="?android:attr/buttonStyleSmall" <br> android:text="Sat" <br> android:layout_alignRight="@+id/myMap" <br> android:layout_alignBottom="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonZoomOut" <br> style="?android:attr/buttonStyleSmall" <br> android:text="-" <br> android:layout_alignBottom="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> </RelativeLayout> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>AndroidLBS.java </p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.AndroidLBS; <br> import android.os.Bundle; <br> import android.location.LocationManager; <br> import android.view.View; <br> import android.widget.TextView; <br> import android.content.Context; <br> import android.widget.Button; <br> import com.google.android.maps.MapActivity; <br> import com.google.android.maps.MapView; <br> import com.google.android.maps.Point; <br> import com.google.android.maps.MapController; <br> public class AndroidLBS extends MapActivity { <br> /** Called when the activity is first created. */ <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> final MapView myMap = (MapView) findViewById(R.id.myMap); <br> final MapController myMapController = myMap.getController(); <br> final Button zoomIn = (Button) findViewById(R.id.buttonZoomIn); <br> zoomIn.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomIn(myMap,myMapController); <br> }}); <br> final Button zoomOut = (Button) findViewById(R.id.buttonZoomOut); <br> zoomOut.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomOut(myMap,myMapController); <br> }}); <br> final Button gpsButton = (Button) findViewById(R.id.gpsButton); <br> gpsButton.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> LoadProviders(myMap,myMapController); <br> }}); <br> final Button viewMap = (Button) findViewById(R.id.buttonMapView); <br> viewMap.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ShowMap(myMap); <br> }}); <br> final Button viewSat = (Button) findViewById(R.id.buttonSatView); <br> viewSat.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ShowSat(myMap); <br> }}); <br> } <br> public void LoadProviders(MapView mv, MapController mc){ <br> TextView latText = (TextView) findViewById(R.id.latText); <br> TextView lngText = (TextView) findViewById(R.id.lngText); <br> LocationManager myManager = (LocationManager) <br> getSystemService(Context.LOCATION_SERVICE); <br> Double latPoint = <br> myManager.getCurrentLocation("gps").getLatitude()*1E6; <br> Double lngPoint = <br> myManager.getCurrentLocation("gps").getLongitude()*1E6; <br> latText.setText(latPoint.toString()); <br> lngText.setText(lngPoint.toString()); <br> Point myLocation = new Point(latPoint.intValue(),lngPoint.intValue()); <br> mc.centerMapTo(myLocation, false); <br> mc.zoomTo(9); <br> } <br> public void ZoomIn(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=21){ <br> mc.zoomTo(mv.getZoomLevel()+ 1); <br> } <br> } <br> public void ZoomOut(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=1){ <br> mc.zoomTo(mv.getZoomLevel()- 1); <br> } <br> } <br> public void ShowMap(MapView mv){ <br> if (mv.isSatellite()){ <br> mv.toggleSatellite(); <br> } <br> } <br> public void ShowSat(MapView mv){ <br> if (!mv.isSatellite()){ <br> mv.toggleSatellite(); <br> } <br> } <br> }</p> </td> </tr> </tbody> </table> <p>当你运行活动时,应当可以启动和关闭卫星视图,如下图(略)。</p> <p>在下一章,你将进入更深层次的Google API。第十章将一步一步学习使用Google API从Android手机发送信息到GTalk。</p> <p>问专家</p> <p>Q:最终版本的Android还会继续使用.kml或者.nmea文件吗?</p> <p>A:本书写的时候,最终的Android还没有发布,可以假定的是,是的,最后版本的Android还会利用.kml和/或者.nmea文件。这将允许应用程序开发者在应用程序内使用包括静态坐标文件。</p> <p>Q:有没有可能来创建有标记的Google Map?</p> <p>A:是的,在第十一章,你将学习如何熟练操作Google 地图 Overlays。这些视图允许在Google地图的上面你绘制文本,标记和其它形状。</p> <p>第十章 使用Google API的Gtalk</p> <h2>使用<span style="font-family:Arial">Google API</span><span style="font-family:宋体">的</span><span style="font-family:Arial">GTalk </span><span style="font-family:宋体">第十章</span><span style="font-family:Arial">(1)</span></h2> <p>关键技能 & 概念</p> <p>● 执行一个Google API包装</p> <p>● 为Google存取配置XMPP开发环境设置</p> <p>● 执行View.OnClickListener( ) 方法</p> <p>第九章为你介绍了Google API。你创建了一个影响Google API和Google地图的活动。因为API的易用和灵活性,可以快速的在Google地图上显示用户的当前位置。同样,你还学习到了如何使用很少量的相关代码来熟练操控地图。</p> <p>Google API包括了不仅是进入Google地图的功能。在上一章中,你使用了很大的API中很小的一个部分。对于Google API的基本包装是com.google。从这个基础中,Google API包含了允许你创建操控GTalk(Google的聊天服务),Google日历,Google文档,Google电子表格和Google服务等等的活动的权力。</p> <p>当我看是写这本书时,这个版本的Android SDK是m3-rc22。写完书时,Google不提倡这些包装中的一些,但是还是留在SDK中。有显示Google 日历,Google电子表格和Google服务仍旧需要升级,遗憾的是,在m5-rc15版本的SDK中还处于未完成的状态。为了避免混乱,Google还移除了任何与这些包装相关联的帮助文件。因此,本章的重点是在最新发布的Android SDK中工作很好的GTalk。</p> <p>在本章中,将会构造一个小的,利用Android SDK的GTalk包装的活动。当活动完成,你将可以利用手机发送并接受信息到/从另外一个GTalk用户。</p> <p>注意</p> <p>在第一个Google API的反复中,处理GTalk的包装是一个非常广泛的XMPP包装。(XMPP是很多聊天平台的基础协议,包括GTalk和Jabber)。使用最新版的SDK,初始的XMPP包装为反映GTalk的特性而加强并重命名。要开始,用Eclipse创建一个新项目,并且命名GoogleAPI。</p> <p>为GTalk配置Android模拟器</p> <p>在开始为本项目写代码之前,你需要在Android模拟器中调整开发环境设定,XMPP设定。</p> <p>在项目打开的状态下,需要离开常规的程序一会儿。如果你熟悉GTalk,你知道只有登录Google帐户以后才可以使用这个产品。因此,你必须要采取特别的步骤来确保你的设备(本例是Android模拟器)可以登录你的Google帐户,这样,可以确保发送和接受信息。</p> <p>导航到AndroidSDK/tools文件夹并且启动模拟器。你可以从Eclipse开发环境中启动它,但是那样会需要同时启动还没有写代码的活动。为了节约时间,手动启动模拟器。</p> <p>模拟器启动后,点击所有的快捷方式(All shortcut)。找到Dev Tools条目并且启动它。你将会看到和下面类似的图(略)。</p> <p>滚动Dev Tools菜单直到看见XMPP设置。选择XMPP设定。</p> <p>注意</p> <p>当你打开XMPP设定,活动的名称是GTalk设定。这个可能是个表象说明Google将在Google API包装中留下这个包装。命名显而易见的断开可能是从不同SDK版本之间改变而剩下的。</p> <p>活动应当读取账户:<None>,如图所示(略)。这表明了设备中没有储存登录信息。你需要为Google帐户增加登录信息来允许活动来有权使用Google的服务器。</p> <p>点击增加帐户来显示一个屏幕。输入用户名和密码之后,点击登录。Android模拟器应当试着去验证你的信息。当模拟器尝试验证信息时,它显示“Authenticating”信息。</p> <p>警告</p> <p>取决于你的连接和你是否有一个调试器和模拟器相连接。你可能会看到“Authenticating”信息一会儿。如果你的帐户几分钟后无法被验证,重新启动模拟器再试试。</p> <p>一旦你的信息被验证,你应当看到下图(略)。注意,这里没有返回按钮,点击模拟器中的Home键返回到主屏幕。</p> <p>现在模拟器配置好了,并且项目也被设置好,可以开始为活动写代码了。</p> <p>在Android中执行GTalk</p> <h2>在<span style="font-family:Arial">Android</span><span style="font-family:宋体">中执行</span><span style="font-family:Arial">GTalk </span><span style="font-family:宋体">第十章</span><span style="font-family:Arial">(2)</span></h2> <p>在本节中将使用Google API来创建一个使用GTalk的活动。这个活动会从GTalk网络发送并接收信息,在屏幕上保存它们,并在通知条中显示。活动可以和其它GTalk用户通信,而不管他们是在使用Android手机或者电脑上的GTalk。</p> <p>下一部分将从如何创建应用程序的布局开始。第一步为活动的编码工作就是增加布局到GoogleAPI.xml中。</p> <p>在GoogleAPI.xml中创建活动的布局</p> <p>本活动由一些Views组成。需要一个ListView来显示需要发送的文本信息。同样需要两个EditText,用于接收者的地址和信息,还有一个发送的Button。</p> <p>首先设置一个id是messageList的ListView,如下所示。在本布局中,将使用一个新的属性,android:scrollbars。设置这个属性为立式会让你可以在信息列表中滚动。</p> <table> <tbody> <tr> <td valign="top"> <p><ListView <br> android:id="@+id/messageList" <br> android:layout_width="fill_parent" <br> android:layout_height="0dip" <br> android:scrollbars="vertical" <br> android:layout_weight="1" <br> android:drawSelectorOnTop="false" /></p> </td> </tr> </tbody> </table> <p>把ListView放到主要布局标签中。在ListView布局的下方,放置一个EditText的布局,如下。这个EditText保留你会发信息的收信人地址。</p> <table> <tbody> <tr> <td valign="top"> <p><EditText <br> android:id="@+id/messageTo" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:textSize="16sp" <br> android:minWidth="250dp" <br> android:scrollHorizontally="true" /></p> </td> </tr> </tbody> </table> <p>这个EditText视图应该没有什么特殊之处。</p> <p>最后,再创建一个水平的布局来保留信息内容——EditText和发送按钮:</p> <table> <tbody> <tr> <td valign="top"> <p><LinearLayout <br> xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="horizontal" <br> android:layout_width="fill_parent" <br> android:layout_height="wrap_content"> <br> <EditText <br> android:id="@+id/messageText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:textSize="16sp" <br> android:minWidth="250dp" <br> android:scrollHorizontally="true" /> <br> <Button <br> android:id="@+id/btnSend" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Send Msg"> <br> </Button> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>这个布局会以线性的方式放置视图,所以它们相互排成一排。把这个LinearLayout放置进主要的LinearLayout。最后,GoogleAPI.xml文件将会如下:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <LinearLayout <br> xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent"> <br> <ListView <br> android:id="@+id/messageList" <br> android:layout_width="fill_parent" <br> android:layout_height="0dip" <br> android:scrollbars="vertical" <br> android:layout_weight="1" <br> android:drawSelectorOnTop="false" /> <br> <EditText <br> android:id="@+id/messageTo" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:textSize="16sp" <br> android:minWidth="250dp" <br> android:scrollHorizontally="true" /> <br> <LinearLayout <br> xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="horizontal" <br> android:layout_width="fill_parent" <br> 246 Android: A Programmer’s Guide <br> android:layout_height="wrap_content"> <br> <EditText <br> android:id="@+id/messageText" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:textSize="16sp" <br> android:minWidth="250dp" <br> android:scrollHorizontally="true" /> <br> <Button <br> android:id="@+id/btnSend" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:text="Send Msg"> <br> </Button> <br> </LinearLayout> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>为GoogleAPI.java增加包装 <br> 布局文件完成后,有一些新的包装需要增加到GoogleAPI.java文件中。第一个必须导入的包装应该和增加到布局中的Views相对应。因此,必须为EditText,ListView,ListAdapter,和按钮导入包装:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.widget.EditText; <br> import android.widget.ListView; <br> import android.widget.ListAdapter; <br> import android.widget.Button;</p> </td> </tr> </tbody> </table> <p>同样需要导入的是Google API和GTalk打交道的包装:</p> <table> <tbody> <tr> <td valign="top"> <p>import com.google.android.gtalkservice.IGTalkSession; <br> import com.google.android.gtalkservice.IGTalkService; <br> import com.google.android.gtalkservice.GTalkServiceConstants; <br> import com.google.android.gtalkservice.IChatSession;</p> </td> </tr> </tbody> </table> <p>其它需要的包装有intent,ServiceConnection, Color, 和 Im. 完整的列表如下:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.content.ComponentName; <br> import android.content.Intent; <br> import android.content.ServiceConnection; <br> import android.database.Cursor; <br> Chapter 10: Using the Google API with GTalk 247 <br> 248 Android: A Programmer’s Guide <br> import android.os.Bundle; <br> import android.os.DeadObjectException; <br> import android.os.IBinder; <br> import android.provider.Im; <br> import android.graphics.Color; <br> import android.view.View; <br> import android.widget.SimpleCursorAdapter;</p> </td> </tr> </tbody> </table> <p>如你所见,本活动所需的包装不多。而且,你会发现,发送和接受信息所需要的代码非常的少。现在,需要执行一个onClickListnerner来允许代码。</p> <p>执行View.OnClickListener <br> 需要修改GoogleAPI的类来执行View.OnClickListener。当任何按钮被点击,这将允许你从活动的主类来呼叫onClick()方法。通常,这种执行onClick()方法是有效的:只有当你在一个活动中有一组按钮,并且以一种方式来出处理所有的onClick呼叫。但是,我感觉你仍旧需要看看这个方式是如何工作的,那样你就可以在将来用于自己的代码中。记住,展示这个方式是因为在很多情况下,它可以是非常有用的工具</p> <table> <tbody> <tr> <td valign="top"> <p>public class GoogleAPI extends Activity implements View.OnClickListener { <br> }</p> </td> </tr> </tbody> </table> <p>在活动中执行常规变量是本书中另外一个未曾谈到的内容。你需要在活动中建立一些常规变量,这样就可以以很多方法来使用:</p> <table> <tbody> <tr> <td valign="top"> <p>EditText messageText; <br> ListView messageList; <br> IGTalkSession myIGTalkSession; <br> EditText messageTo; <br> Button sendButton;</p> </td> </tr> </tbody> </table> <p>在onCreate()方法中,你将执行正常的初始化。应当赋值布局到活动并且设置IGTalkSession为null。同样,在活动中增加点小乐趣,改变ListView的背景色为灰色。</p> <table> <tbody> <tr> <td valign="top"> <p>myIGTalkSession = null; <br> messageText = (EditText) findViewById(R.id.messageText); <br> messageList = (ListView) findViewById(R.id.messageList); <br> messageTo = (EditText) findViewById(R.id.messageTo); <br> sendButton = (Button) findViewById(R.id.btnSend); <br> sendButton.setOnClickListener(this); <br> messageList.setBackgroundColor(Color.GRAY );</p> </td> </tr> </tbody> </table> <p>提示</p> <p>因为你是从类中执行View.OnClickListener的,可以设置Send Button的OnClickListener()方式到其中。最后在onCreate()方法内执行是绑定你的服务。这个过程创建创建需要使用的连接,由Google帐户使用,传递GTalk信息:</p> <table> <tbody> <tr> <td valign="top"> <p>this.bindService(new <br> Intent().setComponent(GTalkServiceConstants.GTALK_SERVICE_COMPONENT), <br> connection, 0);</p> </td> </tr> </tbody> </table> <p>在上面的bindService声明中,传递到setComponent()方法的一个参数就是连接。这个变量表示一个ServiceConnection执行onServiceConnected()和onServiceDisconnected()方法。下面的代码构造约束前面bindService声明的连接:</p> <table> <tbody> <tr> <td valign="top"> <p>private ServiceConnection connection = new ServiceConnection() { <br> public void onServiceConnected(ComponentName name, IBinder service) { <br> try { <br> myIGTalkSession = <br> IGTalkService.Stub.asInterface(service).getDefaultSession(); <br> } catch (DeadObjectException e) { <br> myIGTalkSession = null; <br> } <br> } <br> public void onServiceDisconnected(ComponentName name) { <br> myIGTalkSession = null; <br> } <br> };</p> </td> </tr> </tbody> </table> <p>在onServiceConnected()方法中,你建立了一个使用IGTalkService.Stub的片段。如果这个过程失败,你需要再次把这个片段设为null。现在,可以为类的onClick事件创建代码。在每一个onClick事件中你应当执行一些行动:</p> <p><br> 1. 为任何的信息检查数据库。</p> <p>2. 从查询的结果中创建一个ListAdapter并显示到ListView中。</p> <p>3. 创建一个ChatSession到EditView中的地址并发送你的信息文本。</p> <p>注意</p> <p>Android服务器包括SQLite数据库,你可以使用来保留任何你认为需要放入的活动相关条目和任何的定制数据。这个数据库在第十一章做深入的介绍。</p> <p>下面的代码为你和接受者之间发生的信息查询数据库:</p> <table> <tbody> <tr> <td valign="top"> <p>Cursor cursor = managedQuery(Im.Messages.CONTENT_URI, null, <br> "contact=\'" + messageTo.getText().toString() + "\'", null, null);</p> </td> </tr> </tbody> </table> <p>使用下面的代码来从查询结果创建一个ListAdapter并且赋值接收器到ListView。在前一个活动,你已经使用了一个类似的过程,所以,对你应该不陌生。</p> <table> <tbody> <tr> <td valign="top"> <p>ListAdapter adapter = new SimpleCursorAdapter(this, <br> android.R.layout.simple_list_item_1, cursor, <br> new String[]{Im.MessagesColumns.BODY}, <br> new int[]{android.R.id.text1}); <br> this.messageList.setAdapter(adapter);</p> </td> </tr> </tbody> </table> <p>信息可以显示了,最后的步骤是发送你的信息。下面的代码用定义的messageTO address创建一个IchatSession。这个信息文本然后被从这里传递到接受者。</p> <table> <tbody> <tr> <td valign="top"> <p>try { <br> IChatSession chatSession; <br> chatSession = <br> myIGTalkSession.createChatSession(messageTo.getText().toString();); <br> chatSession.sendTextMessage(messageText.getText().toString()); <br> } catch (DeadObjectException ex) { <br> myIGTalkSession = null; <br> }</p> </td> </tr> </tbody> </table> <p>所有的东西在一起,完成后的GoogleAPI.java文件如下:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.GoogleAPI; <br> import android.app.Activity; <br> import android.content.ComponentName; <br> import android.content.Intent; <br> import android.content.ServiceConnection; <br> import android.database.Cursor; <br> import android.os.Bundle; <br> import android.os.DeadObjectException; <br> import android.os.IBinder; <br> import android.provider.Im; <br> import android.graphics.Color; <br> import android.view.View; <br> import android.widget.EditText; <br> import android.widget.ListView; <br> import android.widget.ListAdapter; <br> import android.widget.Button; <br> import android.widget.SimpleCursorAdapter; <br> import com.google.android.gtalkservice.IGTalkSession; <br> import com.google.android.gtalkservice.IGTalkService; <br> import com.google.android.gtalkservice.GTalkServiceConstants; <br> import com.google.android.gtalkservice.IChatSession; <br> public class GoogleAPI extends Activity implements View.OnClickListener { <br> EditText messageText; <br> ListView messageList; <br> IGTalkSession myIGTalkSession; <br> EditText messageTo; <br> Button mSend; <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.main); <br> myIGTalkSession = null; <br> messageText = (EditText) findViewById(R.id.messageText); <br> messageList = (ListView) findViewById(R.id.messageList); <br> messageTo = (EditText) findViewById(R.id.messageTo); <br> mSend = (Button) findViewById(R.id.btnSend); <br> mSend.setOnClickListener(this); <br> messageList.setBackgroundColor(Color.GRAY ); <br> Chapter 10: Using the Google API with GTalk 251 <br> this.bindService(new <br> Intent().setComponent(GTalkServiceConstants.GTALK_SERVICE_COMPONENT), <br> connection, 0); <br> } <br> private ServiceConnection connection = new ServiceConnection() { <br> public void onServiceConnected(ComponentName name, IBinder service) { <br> try { <br> myIGTalkSession = <br> IGTalkService.Stub.asInterface(service).getDefaultSession(); <br> } catch (DeadObjectException e) { <br> myIGTalkSession = null; <br> } <br> } <br> public void onServiceDisconnected(ComponentName name) { <br> myIGTalkSession = null; <br> } <br> }; <br> public void onClick(View view) { <br> Cursor cursor = managedQuery(Im.Messages.CONTENT_URI, null, <br> "contact=\'" + messageTo.getText().toString() + "\'", <br> null, null); <br> ListAdapter adapter = new SimpleCursorAdapter(this, <br> android.R.layout.simple_list_item_1, cursor, <br> new String[]{Im.MessagesColumns.BODY}, <br> new int[]{android.R.id.text1}); <br> this.messageList.setAdapter(adapter); <br> try { <br> IChatSession chatSession; <br> chatSession = <br> myIGTalkSession.createChatSession(messageTo.getText().toString()); <br> chatSession.sendTextMessage(messageText.getText().toString()); <br> } catch (DeadObjectException ex) { <br> myIGTalkSession = null; <br> } <br> } <br> }</p> </td> </tr> </tbody> </table> <p>编译并运行GoogleAPI</p> <h2>编译并运行<span style="font-family:Arial">GoogleAPI </span><span style="font-family:宋体">第十章</span><span style="font-family:Arial">(3)</span></h2> <p>现在,在模拟器中运行GoogleAPI。如何你的连接成功,你应当能看见下面的屏幕。</p> <p></p> <p>要测试活动,我发送“hello”信息给androidprogrammersguide@gmail.com,显示如下:</p> <p></p> <p>下一个插图,你将看到点击发送按钮后,移动我发送的信息到ListView。</p> <p></p> <p>当我作为androidprogrammersguide登录,我发现信息确实到了预想的收件人,如下:</p> <p></p> <p>我回复“Greetings!”,要查看这个,看下面的两个图。注意活动屏幕顶部的信息条。紧接的图,你可以看到发送者的信息被显示。</p> <p></p> <p>下一章,将创建最后一个应用程序,你会在Google地图上使用SQLite数据库和Google地图Overlays来绘制数据记录。这些是非常有力的技术来提升Android超越其它的移动操作系统。</p> <p>试试这个:为GoogleAPI活动增加设置特性</p> <h2>试试这个:为<span style="font-family:Arial">GoogleAPI</span><span style="font-family:宋体">活动增加设置特性 第十章</span><span style="font-family:Arial">(4)</span></h2> <p>编辑GoogleAPI活动来包括一个设置特性。使用第八章的AndroidViews活动作为向导,增加一个可以改变应用程序布局属性按钮到GoogleAPI活动。这里是一些提示关于如何设置按钮:</p> <p>● 改变信息列表的字体</p> <p>● 改变信息列表的字体颜色,使得发送和接受相反。</p> <p>● 改变信息列表的背景色。</p> <p>问专家</p> <p>Q:GTalk API可用于其它基于XMPP的聊天客户端吗?</p> <p>A: 对于这个的回答还不清晰。m3-rc22版本的SDK包括一个XMPP API而M5-15 SDK只含了GTalk API。有可能这两者在将来发布的Android SDK绑在一起。那样,GTalk API可以被用来与其它基于XMPP聊天客户端的程序通信。</p> <p>第十一章 应用程序:找一个朋友</p> <h2>应用程序:找一个朋友 第十一章<span style="font-family:Arial">(1)</span></h2> <p>关键技能 & 概念 <br> ● 创建一个SQLite数据库</p> <p>● 创建一个定制内容提供者</p> <p>● 从数据库检索条目并且传递到一个Google Maps Overlay</p> <p>这是你将创建应用程序的最后一章,但是会是本书介绍的最大的一个应用程序。我将介绍一些到目前为止没有遇到过的话题,而且你会用到这些话题所谈到的技能创建一个非常健全的应用程序。</p> <p>在本章中,会学习如何在Android模拟器中创建SQLite数据库。我会向你展示如何在定制的数据库中读取,写入并且输出数据。这个过程包括创建并使用你自己的Content Provider来和数据库一同工作。然后,你拿取存储在数据库中的数据并写入到Google Maps Overlay中。当你在前一章用Google地图时,还没有使用Overlay。使用Google Maps Overlays在地图上绘制形状并且写文本,得到一个有信息量的地图。在这个项目中,将创建一个两部分应用程序。应用程序的第一部分将允许用户输入“friends”到移动数据库中。(一个friend由姓名和地理坐标位置组成)。用户将能增加,修改并且删除friends.</p> <p>第二个部分将包括一个菜单项目。当用户选择这个菜单项目,应用程序将显示一个Google地图。这个Google地图和第九章创建的Google地图不同之处就是,这个地图会包含一个Google Maps Overlay,它会允许你在Google地图的标题处写入姓名,给定信息并且绘制项目。</p> <p>要开始,在Eclipse内创建一个新的Android项目,命名为FindFriend,使用下图的设定(略)。</p> <p>现在,你应该对创建一个Android应用程序非常的熟悉了,创建本项目会需要一点小小的帮助。Google在Android SDK里有一个应用程序叫NotePad,非常简单但是允许你储存,修改并且删除数据库里的“notes”。你会去修改这个例子的一些代码来创建Friends数据库。</p> <p>如果你想要知道Google NotePad如何工作,在继续之前,在Eclipse中装载项目并且在模拟器中运行它。不久将会开始修改这个代码,但是首先,在下一节里,将创建你的第一个SQLite数据库。</p> <p>创建一个SQLite数据库</p> <h2>创建一个<span style="font-family:Arial">SQLite</span><span style="font-family:宋体">数据库 第十一章</span><span style="font-family:Arial">(2)</span></h2> <p>Android设备将发布时会有一个内部的SQLite数据库。这个数据库的目的是允许用户和开发者一个可以在活动中储存信息的地方。</p> <p>如果你用过Microsoft SQL服务器或者SQLite,使用Andorid的SQLite数据库的结构和过程对你将不会陌生。不管你有多少的经验,这个部分将涵盖所有需要创建和使用全功能SQlite数据库的技能。你将要在Android模拟器上创建一个数据库。要实现这个,你需要进入Android SDK命令行编辑器工具并使用shell命令来进入Android服务器。</p> <p>提示</p> <p>参考第三章来重拾你的记忆关于路径声明和使用命令行工具。</p> <p>一旦你进入服务器,你需要导航到数据库的位置。所有的Android SQLite数据库的位置是在data/data/<package>/ <br> databases 目录。使用cd命令来从当前的目录改变到data目录,并且再到<package>目录。如果你不确定<package>目录的名称,使用ls来列出文件和当前目录。改变目录至<package>android_programmers_ <br> guide.FindAFriend,如下所示(略)</p> <p>警告</p> <p>如果你没有android_programmers_guide.FindAFriend目录,按照前一部分描述的方式创建你的应用程序并且运行“Hello World!”默认的由项目创建的应用程序,那样会确保你有个正确的目录。</p> <p>找到android_programmers_guide.FindAFriend目录后,运行Is命令。这个命令列出特定文件夹内所有的文件和目录。改命令应当返回空的内容。因为,此时在该目录内没有文件和文件夹。</p> <p>假定SQLite数据库必须在本目录下的一个数据库目录内,是时候来创建一个了。mkdir工具为你创建目录。因此,运行mkdir databases命令。它将创建保留数据库的目录。</p> <p>警告</p> <p>现在,你几乎是在服务器的根目录上。因此你刚刚创建的目录将被作为根目录进入。当你运行活动时,可能会出问题,因为每一个活动有一个不同的用户。出于开发的目的,要解决这个问题,运行chmod 777 databases来准许每个人都能进入到数据库目录。将来,你必须对给予每个人的权力到一些敏感的Android条目非常谨慎才行。只给予特定的用户需要使用特定条目的权力。</p> <p>已经创建了数据库目录了,可以创建数据库了。使用cd命令导航到数据库目录。在数据库目录后,使用sqlite3 工具来创建数据库并命名它为friends.db,如下:</p> <table> <tbody> <tr> <td valign="top"> <p># sqlite friends.db</p> </td> </tr> </tbody> </table> <p>如果执行命令成功,你应当能看到一个SQLite3版本信息,本例是3.5.0,和一个 SQLite3 prompt—sqlite>。这说明数据库已被建立但是是空的。数据库没有包含表格和数据。记住,下一步是为活动数据创建一个表格。</p> <p>你需要创建一个名为friends的表格。这个表将保留id,name,location,created, 和 modified 字段。这些字段将为你的项目提供足够的信息。</p> <p>提示</p> <p>如果你对SQLite不熟悉,一个SQLite命令必须以分号结束。如果你想要跨越一个命令这个会有帮助。没有终止SQLite命令的情况下,按下ENTER键会继续给你一个提示符,…>。你不能在提示符继续输入命令,除非你使用分号。一旦分号被使用,SQLite将把连续的命令作为一个完整的命令。</p> <p>要在数据库内创建friends表格,在sqlite>提示符输入下列命令:</p> <table> <tbody> <tr> <td valign="top"> <p>CREATE TABLE friends (_id INTEGER PRIMARY KEY, name TEXT, location TEXT, <br> created INTEGER, modified INTEGER);</p> </td> </tr> </tbody> </table> <p> </p> <p>如果命令执行成功,将返回到sqlite>提示符,如下图所示(略)。</p> <p>数据库现在可以被使用了,你可以退出SQLite了。使用.exit来退出。然后可以退出shell部分返回到Eclipse.</p> <p>创建数据库是创建应用程序的第一步。现在数据库和相应的表格已经被创建,你需要一个方法来存储数据。受雇Android数据的存储方式是一个Content Provider。下面的部分带你走进如何为新数据库创建一个定制Content Provider并存储数据。</p> <p>创建一个定制的Content Provider</p> <h2>创建一个定制的<span style="font-family:Arial">Content Provider </span><span style="font-family:宋体">第十一章</span><span style="font-family:Arial">(3)</span></h2> <p>Android使用Content Provider来存储数据。你在第九章使用过Content Provider存储并从一个GPS中读取坐标信息。同样的过程应用于数据库。有这样一些Content Provider Contact Lists,IMs,和 Recent <br> Calls。总之,没有为你数据库准备的Content Provider。Android是非常之灵活并且允许你为定制的数据创建定制的Content Provider。在本节,将创建一个和Friends数据工作的Content Provider。这是存取friend数据和最终在屏幕上显示它们的关键所在。</p> <p>下一节,让我们来编辑string.xml文件。这个文件保留全局遍及活动的字符串内容。</p> <p>编辑string.xml文件 <br> 首先,为项目编辑string.xml文件。string.xml文件由每个项目创建但是你还没有使用过它。这个文件保留可以在活动中使用的静态字符串。</p> <p>通常,你不大可能在写这些字符串之前仔细查看所有的部分。那就是,当你构造活动时,你通常增加到string.xml的入口。因为那样会打断本书的流程,所以我预先给你所有string.xml文件的所有内容。编辑string.xml文件使它看上去像这样:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <resources> <br> <string name="app_name">FindAFriend</string> <br> <string name="menu_delete">Delete</string> <br> <string name="menu_insert">Add Friend</string> <br> <string name="find_friends">Find Friends</string> <br> <string name="menu_revert">Revert</string> <br> <string name="menu_discard">Discard</string> <br> <string name="resolve_edit">Edit location</string> <br> <string name="resolve_title">Edit name</string> <br> <string name="title_create">Create Friend</string> <br> <string name="title_edit">Edit Friend</string> <br> <string name="title_notes_list">Friends</string> <br> <string name="title_note">Location</string> <br> <string name="title_edit_title">Friend Name:</string> <br> <string name="button_ok">OK</string> <br> <string name="error_title">Error</string> <br> <string name="error_message">Error loading note</string> <br> </resources> </p> </td> </tr> </tbody> </table> <p>完成string.xml文件后,需要创建一个.java文件来保留你的代码。应该命名这个文件为FriendsProvider.java。还要创建另外一个.java文件来保留数据定义。命名这个文件为Friends.java,因为它会定义一个Friends数据结构像什么样子并且允许你的Content Provider正常进入。(因为provider将会是项目中的一个类,没有必要来构建一个相应的.xml布局文件)。</p> <p>提示</p> <p>技术上,对于应用程序,你定制的Content Provider不需要定居在同一项目或包装内作为代码的剩余部分。为了简单明了,我在FindAFriend项目里做了一个类。假如你计划创建一个可能用户多重项目的Content Provider,在单独的包装中创建它吧。这样,当你只想要使用Content Provider时,会允许你呼叫一个包装。</p> <p>让我们开始Friends.java文件。你只要为相关的类需要输入两个包装:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.net.Uri; <br> import android.provider.BaseColumns;</p> </td> </tr> </tbody> </table> <p>BaseColumns将被一个从主Friends类中的subclass执行。命名这个subclass为Friend,因为它代表Friends数据集中的一个friend。下面的代码显示如何设置类的概要:</p> <table> <tbody> <tr> <td valign="top"> <p>public final class Friends { <br> public static final class Friend implements BaseColumns { <br> } <br> }</p> </td> </tr> </tbody> </table> <p>这个类将保留一下静态变量,它们定义Friends数据库中的每一列,Content URI,和记录的默认排序。</p> <p>提示</p> <p>Content URI被用于识别将要处理的内容。这个数值必须唯一。</p> <p>需要定义的字符串如下:</p> <table> <tbody> <tr> <td valign="top"> <p>public static final Uri CONTENT_URI <br> = <br> Uri.parse("content://android_programmers_guide.FindAFriend.Friends/friend"); <br> public static final String DEFAULT_SORT_ORDER = "modified DESC"; <br> public static final String NAME = "name"; <br> public static final String LOCATION = "location"; <br> public static final String CREATED_DATE = "created"; <br> public static final String MODIFIED_DATE = "modified"; </p> </td> </tr> </tbody> </table> <p>有了变量的设定以后,Friends类放在一起应该是这样的:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.FindAFriend; <br> import android.net.Uri; <br> import android.provider.BaseColumns; <br> public final class Friends { <br> public static final class Friend implements BaseColumns { <br> public static final Uri CONTENT_URI <br> = <br> Uri.parse("content://android_programmers_guide.FindAFriend.Friends/friend"); <br> public static final String DEFAULT_SORT_ORDER = "modified DESC"; <br> public static final String NAME = "name"; <br> public static final String LOCATION = "location"; <br> public static final String CREATED_DATE = "created"; <br> public static final String MODIFIED_DATE = "modified"; <br> } <br> }</p> </td> </tr> </tbody> </table> <p>创建Content Provider</p> <h2>创建<span style="font-family:Arial">Content Provider </span><span style="font-family:宋体">第十一章</span><span style="font-family:Arial">(4)</span></h2> <p>使用Eclipse打开将会成为项目Content Provider的FriendsProvider.java文件。你将要在活动中使用这个定制的Content Provider来从Friends数据库中检索数据。</p> <p>和往常一样。让我们从导入文件开始。你需要输入Friends类和一些其它的类:</p> <table> <tbody> <tr> <td valign="top"> <p>import android_programmers_guide.FindAFriend.Friends; <br> import android.content.*; <br> import android.database.Cursor; <br> import android.database.SQLException; <br> import android.database.sqlite.SQLiteOpenHelper; <br> import android.database.sqlite.SQLiteDatabase; <br> import android.database.sqlite.SQLiteQueryBuilder; <br> import android.net.Uri; <br> import android.text.TextUtils; <br> import android.util.Log; <br> import java.util.HashMap;</p> </td> </tr> </tbody> </table> <p>如你所见,你输入类的大多数和SQL打交道。当你用这些包装的时候,我再向你解释。</p> <p>第一个将要使用的包装是android.content。要成为一个Content Provider,需要利用并优先要求的方法,你的FriendsProvider类需要扩展ContentProvider。看一下下面类的概要,它们包括一些将要使用在Provider的变量定义:</p> <table> <tbody> <tr> <td valign="top"> <p>public class FriendsProvider extends ContentProvider { <br> private SQLiteDatabase mDB; <br> private static final String TAG = "FriendsProvider"; <br> private static final String DATABASE_NAME = "friends"; <br> private static final int DATABASE_VERSION = 2; <br> private static HashMap<String, String> FRIENDS_PROJECTION_MAP; <br> private static final int FRIENDS = 1; <br> private static final int FRIENDS_ID = 2; <br> private static final UriMatcher URL_MATCHER;}</p> </td> </tr> </tbody> </table> <p><br> Content Provider包含一些需要优先的方法,包括onCreate( ), query( ), insert( ), delete( ), 和update( )。因为这些方法将被活动使用Content Provider呼叫,你必须优先使用它们来进入Friends 数据库。</p> <p>你将优先onCreate( ) 方法呼叫一个SQLiteOpenHelper。因此,在能优先ContentProvider的onCreate()之前,你不得不创建一个类扩展SQLiteIpenHelper。</p> <p>代码块跟从的是一个Content Provider的子类。它扩展SQLiteOpenHelper:</p> <table> <tbody> <tr> <td valign="top"> <p>private static class DatabaseHelper extends SQLiteOpenHelper { <br> @Override <br> public void onCreate(SQLiteDatabase db) { <br> db.execSQL("CREATE TABLE friends (_id INTEGER PRIMARY KEY," <br> + "name TEXT," + "location TEXT," + "created INTEGER," <br> + "modified INTEGER" + ");"); <br> } <br> @Override <br> public void onUpgrade(SQLiteDatabase db, int oldVersion, int <br> newVersion) { <br> Log.w(TAG, "Upgrading database from version " + oldVersion + "to " <br> + newVersion + ", which will destroy all old data"); <br> db.execSQL("DROP TABLE IF EXISTS friends"); <br> onCreate(db); <br> } <br> } </p> </td> </tr> </tbody> </table> <p>刚创建的DatabaseHelper类包含两个优先方法:onCreater()和onUpgrade()。onCreate()方法被用于当从代码中创建数据库,或者表格定义不存在的示例中。</p> <p>注意</p> <p>假定你从adb壳中创建数据库结构,你不会依赖于DatabaseHelper的onCreate()方法来建立你的数据库。</p> <p>DatabaseHelper类创建后,你现在可以为Content Provider优先onCreate()方法了:</p> <table> <tbody> <tr> <td valign="top"> <p>@Override <br> public boolean onCreate() { <br> DatabaseHelper dbHelper = new DatabaseHelper(); <br> mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, <br> DATABASE_VERSION); <br> return (mDB == null) ? false : true; <br> }</p> </td> </tr> </tbody> </table> <p><br> 这是个非常之简单的方法,结果就是返回一个布尔值代表你的数据库是否可以被打开。你使用在兄弟类中创建的SQLiteOpenHelper来打开Friends数据库。注意你传递数据库名称到DatabaseHelper类。如果数据库对象——mDB返回的不是null,然后数据库就成功的被打开并可以查询了。</p> <p>下一步,优先ContentProvider类的query()方法。这将是Content Provider的主要部分。query()方法是从活动通过Content Provider被呼叫来从数据库中获得记录。看下优先版本的query()方法代码:</p> <table> <tbody> <tr> <td valign="top"> <p>@Override <br> public Cursor query(Uri url, String[] projection, String selection, <br> String[] selectionArgs, String sort) { <br> SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> qb.setTables("friends"); <br> qb.setProjectionMap(FRIENDS_PROJECTION_MAP); <br> break; <br> case FRIENDS_ID: <br> qb.setTables("friends"); <br> qb.appendWhere("_id=" + url.getPathSegments().get(1)); <br> break; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> String orderBy; <br> if (TextUtils.isEmpty(sort)) { <br> orderBy = Friends.Friend.DEFAULT_SORT_ORDER; <br> } else { <br> orderBy = sort; <br> } <br> Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, <br> null, orderBy); <br> c.setNotificationUri(getContext().getContentResolver(), url); <br> return c; <br> } </p> </td> </tr> </tbody> </table> <p><br> query()方法做了一点家务管理之类的事宜,通过检查传递到其中的数据库URL的有效性和定义一个查询分类序列达到的。URL检查是为了确保你只是要进入Friends数据库。如果你试图从其它活动进入数据库,或者从其它的Content Provider,query()方法投递一个例外。</p> <p>到方法的结尾,你使用SQLiteQueryBuilder来执行一个查询。通过下面的代码,导致的数据集被赋值到一个光标:</p> <table> <tbody> <tr> <td valign="top"> <p>Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, <br> null, orderBy);</p> </td> </tr> </tbody> </table> <p>注意</p> <p>光标是一个设备允许你移动记录并从数据列中返回信息。</p> <p>update( ),delete( ), 和insert( ) 方法同样的简单。看一下这三个方法,应当优先它们:</p> <table> <tbody> <tr> <td valign="top"> <p>@Override <br> public Uri insert(Uri url, ContentValues initialValues) { <br> long rowID; <br> ContentValues values; <br> if (initialValues != null) { <br> values = new ContentValues(initialValues); <br> } else { <br> values = new ContentValues(); <br> } <br> if (URL_MATCHER.match(url) != FRIENDS) { <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> Long now = Long.valueOf(System.currentTimeMillis()); <br> Resources r = Resources.getSystem(); <br> if (values.containsKey(Friends.Friend.CREATED_DATE ) == false) { <br> values.put(Friends.Friend.CREATED_DATE, now); <br> } <br> if (values.containsKey(Friends.Friend.MODIFIED_DATE) == false) { <br> values.put(Friends.Friend.MODIFIED_DATE, now); <br> } <br> if (values.containsKey(Friends.Friend.NAME) == false) { <br> values.put(Friends.Friend.NAME, <br> r.getString(android.R.string.untitled)); <br> } <br> if (values.containsKey(Friends.Friend.LOCATION) == false) { <br> values.put(Friends.Friend.LOCATION , ""); <br> } <br> 270 Android: A Programmer’s Guide <br> rowID = mDB.insert("friends", "friend", values); <br> if (rowID > 0) { <br> Uri uri = ContentUris.withAppendedId(Friends.Friend.CONTENT_URI <br> , rowID); <br> getContext().getContentResolver().notifyChange(uri, null); <br> return uri; <br> } <br> throw new SQLException("Failed to insert row into " + url); <br> } <br> @Override <br> public int delete(Uri url, String where, String[] whereArgs) { <br> int count; <br> long rowId = 0; <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> count = mDB.delete("friends", where, whereArgs); <br> break; <br> case FRIENDS_ID: <br> String segment = url.getPathSegments().get(1); <br> rowId = Long.parseLong(segment); <br> count = mDB <br> .delete("friends", "_id=" <br> + segment <br> + (!TextUtils.isEmpty(where) ? " AND (" + where <br> + ')' : ""), whereArgs); <br> break; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> getContext().getContentResolver().notifyChange(url, null); <br> return count; <br> } <br> @Override <br> public int update(Uri url, ContentValues values, String where, String[] <br> whereArgs) { <br> int count; <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> count = mDB.update("friends", values, where, whereArgs); <br> break; <br> case FRIENDS_ID: <br> String segment = url.getPathSegments().get(1); <br> count = mDB <br> .update("friends", values, "_id=" + segment <br> Chapter 11: Application: Find a Friend 271 <br> + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); <br> break; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> getContext().getContentResolver().notifyChange(url, null); <br> return count; <br> } </p> </td> </tr> </tbody> </table> <p>这些方法的代码应当不需要再加以说明了。如果看过在每个方法的处理,代码的核心就是发出一个数据库声明来执行要求的动作——更新,删除,或者插入。</p> <p>最后的Content Provider的部分就是一个getType()方法,它返回Friends数据类型。当创建自己的类型,应当跟从一下协议:</p> <table> <tbody> <tr> <td valign="top"> <p>vnd.android.cursor.dir/vnd.<package> <br> Take a look at the getType( ) method: <br> @Override <br> public String getType(Uri url) { <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> return <br> "vnd.android.cursor.dir/vnd.android_programmers_guide.friend"; <br> case FRIENDS_ID: <br> return <br> "vnd.android.cursor.item/vnd.android_programmers_guide.friend"; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>这样就完成了新的定制的Content Provider。看一下完成的FriendsProvider代码:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.FindAFriend; <br> import android_programmers_guide.FindAFriend.Friends; <br> import android.content.*; <br> import android.database.Cursor; <br> import android.database.SQLException; <br> import android.database.sqlite.SQLiteOpenHelper; <br> import android.database.sqlite.SQLiteDatabase; <br> import android.database.sqlite.SQLiteQueryBuilder; <br> import android.net.Uri; <br> import android.text.TextUtils; <br> import android.util.Log; <br> import java.util.HashMap; <br> public class FriendsProvider extends ContentProvider { <br> private SQLiteDatabase mDB; <br> private static final String TAG = "FriendsProvider"; <br> private static final String DATABASE_NAME = "friends"; <br> private static final int DATABASE_VERSION = 2; <br> private static HashMap<String, String> FRIENDS_PROJECTION_MAP; <br> private static final int FRIENDS = 1; <br> private static final int FRIENDS_ID = 2; <br> private static final UriMatcher URL_MATCHER; <br> private static class DatabaseHelper extends SQLiteOpenHelper { <br> @Override <br> public void onCreate(SQLiteDatabase db) { <br> db.execSQL("CREATE TABLE friends (_id INTEGER PRIMARY KEY," <br> + "name TEXT," + "location TEXT," + "created INTEGER," <br> + "modified INTEGER" + ");"); <br> } <br> @Override <br> public void onUpgrade(SQLiteDatabase db, int oldVersion, int <br> newVersion) { <br> Log.w(TAG, "Upgrading database from version " + oldVersion + "to " <br> + newVersion + ", which will destroy all old data"); <br> db.execSQL("DROP TABLE IF EXISTS friends"); <br> onCreate(db); <br> } <br> } <br> @Override <br> public boolean onCreate() { <br> DatabaseHelper dbHelper = new DatabaseHelper(); <br> mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, <br> DATABASE_VERSION); <br> return (mDB == null) ? false : true; <br> } <br> @Override <br> public Cursor query(Uri url, String[] projection, String selection, <br> String[] selectionArgs, String sort) { <br> SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> qb.setTables("friends"); <br> qb.setProjectionMap(FRIENDS_PROJECTION_MAP); <br> break; <br> case FRIENDS_ID: <br> qb.setTables("friends"); <br> qb.appendWhere("_id=" + url.getPathSegments().get(1)); <br> break; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> String orderBy; <br> if (TextUtils.isEmpty(sort)) { <br> orderBy = Friends.Friend.DEFAULT_SORT_ORDER; <br> } else { <br> orderBy = sort; <br> } <br> Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, <br> null, orderBy); <br> c.setNotificationUri(getContext().getContentResolver(), url); <br> return c; <br> } <br> @Override <br> public String getType(Uri url) { <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> return <br> "vnd.android.cursor.dir/vnd.android_programmers_guide.friend"; <br> case FRIENDS_ID: <br> return <br> "vnd.android.cursor.item/vnd.android_programmers_guide.friend"; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> } <br> @Override <br> public Uri insert(Uri url, ContentValues initialValues) { <br> long rowID; <br> ContentValues values; <br> if (initialValues != null) { <br> values = new ContentValues(initialValues); <br> } else { <br> values = new ContentValues(); <br> } <br> if (URL_MATCHER.match(url) != FRIENDS) { <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> Long now = Long.valueOf(System.currentTimeMillis()); <br> Resources r = Resources.getSystem(); <br> if (values.containsKey(Friends.Friend.CREATED_DATE ) == false) { <br> values.put(Friends.Friend.CREATED_DATE, now); <br> } <br> if (values.containsKey(Friends.Friend.MODIFIED_DATE) == false) { <br> values.put(Friends.Friend.MODIFIED_DATE, now); <br> } <br> if (values.containsKey(Friends.Friend.NAME) == false) { <br> values.put(Friends.Friend.NAME, <br> r.getString(android.R.string.untitled)); <br> } <br> if (values.containsKey(Friends.Friend.LOCATION) == false) { <br> values.put(Friends.Friend.LOCATION , ""); <br> } <br> rowID = mDB.insert("friends", "friend", values); <br> if (rowID > 0) { <br> Uri uri = ContentUris.withAppendedId(Friends.Friend.CONTENT_URI <br> , rowID); <br> getContext().getContentResolver().notifyChange(uri, null); <br> return uri; <br> } <br> throw new SQLException("Failed to insert row into " + url); <br> } <br> @Override <br> public int delete(Uri url, String where, String[] whereArgs) { <br> int count; <br> long rowId = 0; <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> Chapter 11: Application: Find a Friend 275 <br> count = mDB.delete("friends", where, whereArgs); <br> break; <br> case FRIENDS_ID: <br> String segment = url.getPathSegments().get(1); <br> rowId = Long.parseLong(segment); <br> count = mDB <br> .delete("friends", "_id=" <br> + segment <br> + (!TextUtils.isEmpty(where) ? " AND (" + where <br> + ')' : ""), whereArgs); <br> break; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> getContext().getContentResolver().notifyChange(url, null); <br> return count; <br> } <br> @Override <br> public int update(Uri url, ContentValues values, String where, String[] <br> whereArgs) { <br> int count; <br> switch (URL_MATCHER.match(url)) { <br> case FRIENDS: <br> count = mDB.update("friends", values, where, whereArgs); <br> break; <br> case FRIENDS_ID: <br> String segment = url.getPathSegments().get(1); <br> count = mDB <br> .update("friends", values, "_id=" + segment <br> + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); <br> break; <br> default: <br> throw new IllegalArgumentException("Unknown URL " + url); <br> } <br> getContext().getContentResolver().notifyChange(url, null); <br> return count; <br> } <br> static { <br> URL_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); <br> URL_MATCHER.addURI("android_programmers_guide.FindAFriend.Friends", <br> "friend", FRIENDS); <br> URL_MATCHER.addURI("android_programmers_guide.FindAFriend.Friends", <br> "friend/#", FRIENDS_ID); <br> FRIENDS_PROJECTION_MAP = new HashMap<String, String>(); <br> FRIENDS_PROJECTION_MAP.put(Friends.Friend._ID, "_id"); <br> FRIENDS_PROJECTION_MAP.put(Friends.Friend.NAME, "name"); <br> FRIENDS_PROJECTION_MAP.put(Friends.Friend.LOCATION, "location"); <br> FRIENDS_PROJECTION_MAP.put(Friends.Friend.CREATED_DATE, "created"); <br> FRIENDS_PROJECTION_MAP.put(Friends.Friend.MODIFIED_DATE, <br> "modified"); <br> } <br> }</p> </td> </tr> </tbody> </table> <p><br> 有了最根本的数据素材(数据库,定义和Content Provider),你可以开始来建造周围的活动。记住,活动将使用数据库内的数据,显示到列表,并允许用户启动另一个活动来放置数据库条目到一个Google Maps Overlay。在下一节,将构建活动并完成FindFriend应用程序。</p> <p>创建FindAFriend活动</p> <h2>创建<span style="font-family:Arial">FindAFriend</span><span style="font-family:宋体">活动 第十一章</span><span style="font-family:Arial">(5)</span></h2> <p>如果你花了些时间运行Google的NotePad示例,那么你就对活动的布局非常熟悉了。你将修改NotePad的接口来使用Friends数据库和Google地图。FindAFriend活动将和一些小的活动交互:NameEditor, LocationEditor, 和 FriendsMap。下面的章节中你将构建所有的这些活动。</p> <p>注意</p> <p>除了NotePad,Google提供了一些写的非常好的示例活动,展示了多编程状态的基本技术。</p> <p>如你所做的过去的几个活动,从文件AndroidManifest.xml开始。要想熟悉复杂的应用程序,你需要多次更改AndroidManifest.xml文件。</p> <p>编辑 AndroidManifest.xml <br> 看看下面为FindAFriend项目准备的AndroidManifest.xml文件。需要为新活动增加一些Intent过滤器,包括一个编辑friend的姓名,并且启动你的Google地图。</p> <p>同样,需要仔细注意每个Intent过滤器的动作。这些动作会被传递到每个活动并处理Intent。最后,不要忘记增加Access_Location and Access_GPS 许可,这样就可以增加你的当前闻之了。完整的AndroidManifest.xml文件应当像这样显示:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <manifest xmlns:android="http://schemas.android.com/apk/res/android" <br> package="android_programmers_guide.FindAFriend"> <br> <application android:icon="@drawable/icon"> <br> <provider android:name="FriendsProvider" <br> android:authorities="android_programmers_guide.FindAFriend.Friends" /> <br> <activity android:name=".FindAFriend" <br> android:label="@string/app_name"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" /> <br> </intent-filter> <br> <intent-filter> <br> <action android:name="android.intent.action.VIEW" /> <br> <action android:name="android.intent.action.EDIT" /> <br> <action android:name="android.intent.action.PICK" /> <br> <category android:name="android.intent.category.DEFAULT" /> <br> <dataandroid:mimeType="vnd.android.cursor.dir/ <br> vnd.android_programmers_guide.friend" /> <br> </intent-filter> <br> <intent-filter> <br> <action android:name="android.intent.action.GET_CONTENT" /> <br> <category android:name="android.intent.category.DEFAULT" /> <br> <dataandroid:mimeType="vnd.android.cursor.item/ <br> vnd.android_programmers_guide.friend" /> <br> </intent-filter> <br> </activity> <br> <activity android:name=".FriendsMap" android:label="FriendsMap"> <br> <intent-filter> <br> <action android:name="android.intent.action.MAIN" /> <br> <category android:name="android.intent.category.LAUNCHER" <br> /> <br> </intent-filter> <br> </activity> <br> <activity android:name="LocationEditor" <br> android:label="@string/title_note"> <br> <intent-filter android:label="@string/resolve_edit"> <br> <action android:name="android.intent.action.VIEW" /> <br> <action android:name="android.intent.action.EDIT" /> <br> <action <br> android:name="com.google.android.notepad.action.EDIT_LOCATION" /> <br> <category android:name="android.intent.category.DEFAULT" /> <br> <dataandroid:mimeType="vnd.android.cursor.item/ <br> vnd.android_programmers_guide.friend" /> <br> </intent-filter> <br> <intent-filter> <br> <action android:name="android.intent.action.INSERT" /> <br> <category android:name="android.intent.category.DEFAULT" /> <br> <dataandroid:mimeType="vnd.android.cursor.dir/ <br> vnd.android_programmers_guide.friend" /> <br> </intent-filter> <br> </activity> <br> <activity android:name="NameEditor" <br> android:label="@string/title_edit_title" <br> android:theme="@android:style/Theme.Dialog"> <br> <intent-filter android:label="@string/resolve_title"> <br> <action android:name="com.google.android.notepad.action.EDIT_NAME" <br> /> <br> <category android:name="android.intent.category.DEFAULT" /> <br> <category android:name="android.intent.category.ALTERNATIVE" /> <br> <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> <br> <dataandroid:mimeType="vnd.android.cursor.item/ <br> vnd.android_programmers_guide.friend" /> <br> </intent-filter> <br> </activity> <br> </application> <br> <uses-permission android:name="android.permission.ACCESS_GPS"> <br> </uses-permission><uses-permission <br> android:name="android.permission.ACCESS_LOCATION"> <br> </uses-permission></manifest></p> </td> </tr> </tbody> </table> <p>在下一节中,将为NameEditor项目创建第一个活动。正如名称所示,当用户期望编辑一个friend的名称时,这个活动将被启动。</p> <p>创建NameEditor活动</p> <h2>创建<span style="font-family:Arial">NameEditor</span><span style="font-family:宋体">活动 第十一章</span><span style="font-family:Arial">(6)</span></h2> <p>在本节中,你将为FindAFriend项目创建NameEditor活动。这个活动将被从主活动FindAFriend菜单项目中启动(还没有创建)。NameEditor活动的目的是修改一个Friend记录的姓名字段。</p> <p>增加一个name_editor.xml布局文件并且一个相应的NameEditor.java文件到应用程序中。你将编辑这些文件来创建你的活动。</p> <p>首先,编辑name_editor.xml文件来为活动创建布局文件。活动将有一个EditText和一个Button。EditText将允许你修改名称字段,Button将写入结果并退出。如果你一开始就跟从本书的话,你已经增加了不少的View布局到XML文件中。因此,我可以免去细节,玩战的name_editor.xml文件应当如下所示:</p> <table> <tbody> <tr> <td valign="top"> <p><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:orientation="vertical" <br> android:paddingLeft="6dip" <br> android:paddingRight="6dip" <br> android:paddingBottom="3dip"> <br> <EditText android:id="@+id/name" <br> android:maxLines="1" <br> android:layout_marginTop="2dip" <br> android:layout_width="wrap_content" <br> android:ems="25" <br> android:layout_height="wrap_content" <br> android:autoText="true" <br> android:capitalize="sentences" <br> android:scrollHorizontally="true" /> <br> <Button android:id="@+id/ok" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" <br> android:layout_gravity="right" <br> android:text="@string/button_ok" /> <br> </LinearLayout></p> </td> </tr> </tbody> </table> <p>现在,编辑NameEditor.java并开始写代码。需要导入前一节的Friends类并且导入Cursor包装来帮助你使用数据库记录:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.app.Activity; <br> import android.database.Cursor; <br> import android.net.Uri; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.Button; <br> import android.widget.EditText;</p> </td> </tr> </tbody> </table> <p>应当建立活动来执行View.OnClickListener()。这将允许你在活动中优先OnClickListener()方法。这部分代码显示NameEditor类的概要和一些你需要的变量定义:</p> <table> <tbody> <tr> <td valign="top"> <p>public class NameEditor extends Activity implements View.OnClickListener { <br> public static final String EDIT_NAME_ACTION = <br> "android_programmers_guide.FindAFriend.action.EDIT_NAME"; <br> private static final int NAME_INDEX = 1; <br> private static final String[] PROJECTION = new String[] { <br> Friends.Friend._ID, <br> Friends.Friend.NAME, <br> }; <br> Cursor mCursor; <br> EditText mText; <br> }</p> </td> </tr> </tbody> </table> <p>下一步,需要优先一些方法,从onCreate()开始。已经在其它章节里看过方法被优先。通常,当活动被创建,它保留所有被执行的代码:</p> <table> <tbody> <tr> <td valign="top"> <p>public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.name_editor); <br> Uri uri = getIntent().getData(); <br> mCursor = managedQuery(uri, PROJECTION, null, null); <br> mText = (EditText) this.findViewById(R.id.name); <br> mText.setOnClickListener(this); <br> Button b = (Button) findViewById(R.id.ok); <br> b.setOnClickListener(this); <br> }</p> </td> </tr> </tbody> </table> <p>注意,在前面的代码示例中,你赋值布局到各自的Views并且初始化你的变量。可是,你可能想知道为姓名字段准备的数据在哪里。那就是,你已经创建了一个光标,但是从中还没有检索任何东西。为此,你将使用onResume()方法。</p> <p>下面两个优先的方法是,onResume()和onPause(),作用是独自的读取和写入数据库。在Android生命周期中,当活动被打开并且在焦点之上,onResume()被呼叫。onPause()被呼叫的情况是当活动被打开,但是在焦点被传递到另一个活动之前。</p> <p>优先onResume()方法来读取数据库并检索姓名字段:</p> <table> <tbody> <tr> <td valign="top"> <p>protected void onResume() { <br> super.onResume(); <br> if (mCursor != null) { <br> mCursor.first(); <br> String title = mCursor.getString(NAME_INDEX); <br> mText.setText(title); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>在这种方式下,你移动光标到第一个记录,使用前面赋值的索引从中读取姓名字段,然后设置EditText到姓名字段的内容。下一步,修改onPause()方法来写回EditText内容到数据库:</p> <table> <tbody> <tr> <td valign="top"> <p>protected void onPause() { <br> super.onPause(); <br> if (mCursor != null) { <br> String title = mText.getText().toString(); <br> mCursor.updateString(NAME_INDEX, title); <br> mCursor.commitUpdates(); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>最后,从onClick句柄呼叫活动方法finish()。它将清除并关闭活动。完成后的NameEditor.java文件应当如下:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.FindAFriend; <br> import android_programmers_guide.FindAFriend.Friends; <br> import android.app.Activity; <br> import android.database.Cursor; <br> import android.net.Uri; <br> import android.os.Bundle; <br> import android.view.View; <br> import android.widget.Button; <br> import android.widget.EditText; <br> public class NameEditor extends Activity implements View.OnClickListener { <br> public static final String EDIT_NAME_ACTION = <br> "android_programmers_guide.FindAFriend.action.EDIT_NAME"; <br> private static final int NAME_INDEX = 1; <br> private static final String[] PROJECTION = new String[] { <br> Friends.Friend._ID, <br> Chapter 11: Application: Find a Friend 281 <br> Friends.Friend.NAME, <br> }; <br> Cursor mCursor; <br> EditText mText; <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.name_editor); <br> Uri uri = getIntent().getData(); <br> mCursor = managedQuery(uri, PROJECTION, null, null); <br> mText = (EditText) this.findViewById(R.id.name); <br> mText.setOnClickListener(this); <br> Button b = (Button) findViewById(R.id.ok); <br> b.setOnClickListener(this); <br> } <br> @Override <br> protected void onResume() { <br> super.onResume(); <br> if (mCursor != null) { <br> mCursor.first(); <br> String title = mCursor.getString(NAME_INDEX); <br> mText.setText(title); <br> } <br> } <br> @Override <br> protected void onPause() { <br> super.onPause(); <br> if (mCursor != null) { <br> String title = mText.getText().toString(); <br> mCursor.updateString(NAME_INDEX, title); <br> mCursor.commitUpdates(); <br> } <br> } <br> public void onClick(View v) { <br> finish(); <br> } <br> }</p> </td> </tr> </tbody> </table> <p><br> 这样,你可以在Friends数据库编辑姓名的值。总之,数据库中的两个字段重要,姓名和位置。下一节,将为位置字段创建编辑器。</p> <p>创建LocationEditor活动</p> <h2>创建<span style="font-family:Arial">LocationEditor</span><span style="font-family:宋体">活动 第十一章</span><span style="font-family:Arial">(7)</span></h2> <p>在本节中,你将为Friends数据库的“位置”字段创建一个编辑器。做这个活动,你将会从NameEditor活动做一点小的修改。因此,代码和过程不同。</p> <p>如果你浏览了Google NotePad演示版,你应当注意到“notes”编辑器是一个白色屏幕,带有动态自身重复划线。这是个使用定制View执行的结果。你会用这个相同的View来制作LocationEditor。</p> <p>location_editor.xml <br> 第一步是独自创建location_editor.xml和LocationEditor.java文件的布局和代码。布局文件应当包含一个到定制View布局的呼叫。完整的布局文件如下:</p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <view xmlns:android="http://schemas.android.com/apk/res/android" <br> class="android_programmers_guide.FindAFriend.LocationEditor$MyEditText" <br> android:id="@+id/location" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> android:background="#ffffff" <br> android:padding="10dip" <br> android:scrollbars="vertical" <br> android:fadingEdge="vertical" /></p> </td> </tr> </tbody> </table> <p>LocationEditor还会包含一个菜单系统,允许用户废弃,删除或者恢复他们做的任何改变。这会是一个非常复杂的活动。因此,最好从头开始,那就是LocationEditor.java文件的输入部分。</p> <p>LocationEditor.java <br> 看看这个活动的输入,很多是处理屏幕上View的图样:</p> <table> <tbody> <tr> <td valign="top"> <p>import android.app.Activity; <br> import android.content.ComponentName; <br> import android.content.Context; <br> import android.content.Intent; <br> import android.database.Cursor; <br> import android.graphics.Canvas; <br> import android.graphics.Paint; <br> import android.graphics.Rect; <br> import android.net.Uri; <br> import android.os.Bundle; <br> import android.util.AttributeSet; <br> import android.view.Menu; <br> import android.widget.EditText; <br> import java.util.Map;</p> </td> </tr> </tbody> </table> <p>下一步,设置活动的主类概要。使用LocationEditor期间,这里有不少的变量需要定义:</p> <table> <tbody> <tr> <td valign="top"> <p>public class LocationEditor extends Activity { <br> private static final String TAG = "Friends"; <br> private static final int FRIEND_INDEX = 1; <br> private static final int NAME_INDEX = 2; <br> private static final int MODIFIED_INDEX = 3; <br> private static final String[] PROJECTION = new String[] { <br> Friends.Friend._ID, // 0 <br> Friends.Friend.LOCATION, // 1 <br> Friends.Friend.NAME, // 2 <br> Friends.Friend.MODIFIED_DATE // 3 <br> }; <br> private static final String ORIGINAL_CONTENT = "origContent"; <br> private static final int REVERT_ID = Menu.FIRST; <br> private static final int DISCARD_ID = Menu.FIRST + 1; <br> private static final int DELETE_ID = Menu.FIRST + 2; <br> private static final int STATE_EDIT = 0; <br> private static final int STATE_INSERT = 1; <br> private int mState; <br> private boolean mNoteOnly = false; <br> private Uri mURI; <br> private Cursor mCursor; <br> Chapter 11: Application: Find a Friend 285 <br> private EditText mText; <br> private String mOriginalContent; <br> }</p> </td> </tr> </tbody> </table> <p>所有本章上一节执行的任务,变量的定义是无需加以说明的。</p> <p>下一小块的代码展示需要创建的子类。这个子类将把EditText画在屏幕上。你把它分开,这样可以从活动中按照需要呼叫它。记住,你会在屏幕上根据用户的需要动态的绘制一个新的EditText。特别注意需要优先的onDraw类:</p> <table> <tbody> <tr> <td valign="top"> <p>public static class MyEditText extends EditText { <br> private Rect mRect; <br> private Paint mPaint; <br> public MyEditText(Context context, AttributeSet attrs, Map params) { <br> super(context, attrs, params); <br> mRect = new Rect(); <br> mPaint = new Paint(); <br> mPaint.setStyle(Paint.Style.STROKE); <br> mPaint.setColor(0xFF0000FF); <br> } <br> @Override <br> protected void onDraw(Canvas canvas) { <br> int count = getLineCount(); <br> Rect r = mRect; <br> Paint paint = mPaint; <br> for (int i = 0; i < count; i++) { <br> int baseline = getLineBounds(i, r); <br> canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, <br> paint); <br> } <br> super.onDraw(canvas); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>还有,看上去好像很多的代码,但是都不陌生。这个子类只是绘制一个所需的新EditText到屏幕上。</p> <p>和NameEditor一样,你会使用onResume()和onPause()方法来使用数据库。看看下面每个的代码:</p> <table> <tbody> <tr> <td valign="top"> <p>protected void onResume() { <br> super.onResume(); <br> if (mCursor != null) { <br> mCursor.first(); <br> if (mState == STATE_EDIT) { <br> setTitle(getText(R.string.title_edit)); <br> } else if (mState == STATE_INSERT) { <br> setTitle(getText(R.string.title_create)); <br> } <br> String note = mCursor.getString(FRIEND_INDEX); <br> mText.setTextKeepState(note); <br> if (mOriginalContent == null) { <br> mOriginalContent = note; <br> } <br> } else { <br> setTitle(getText(R.string.error_title)); <br> mText.setText(getText(R.string.error_message)); <br> } <br> } <br> protected void onPause() { <br> super.onPause(); <br> if (mCursor != null) { <br> String text = mText.getText().toString(); <br> int length = text.length(); <br> if (isFinishing() && (length == 0) && !mNoteOnly) { <br> setResult(RESULT_CANCELED); <br> deleteFriend(); <br> } else { <br> if (!mNoteOnly) { <br> mCursor.updateLong(MODIFIED_INDEX, <br> System.currentTimeMillis()); <br> if (mState == STATE_INSERT) { <br> String title = text.substring(0, Math.min(30, <br> length)); <br> Chapter 11: Application: Find a Friend 287 <br> if (length > 30) { <br> int lastSpace = title.lastIndexOf(' '); <br> if (lastSpace > 0) { <br> title = title.substring(0, lastSpace); <br> } <br> } <br> mCursor.updateString(NAME_INDEX, title); <br> } <br> } <br> mCursor.updateString(FRIEND_INDEX, text); <br> managedCommitUpdates(mCursor); <br> } <br> } <br> }</p> </td> </tr> </tbody> </table> <p>和NameEditor很像,在onResume()期间从数据库读取,在onPause()期间写回到数据库。在LocationEditor中增加的一个和NameEditor相反的特性是当你修改时,还写完修改的数据。</p> <p>最后,需要两个方法来取消和删除friends。这些方法将被从菜单系统中呼叫:</p> <table> <tbody> <tr> <td valign="top"> <p>private final void cancelFriend() { <br> if (mCursor != null) { <br> if (mState == STATE_EDIT) { <br> mCursor.updateString(FRIEND_INDEX, mOriginalContent); <br> mCursor.commitUpdates(); <br> mCursor.deactivate(); <br> mCursor = null; <br> } else if (mState == STATE_INSERT) { <br> deleteFriend(); <br> } <br> } <br> setResult(RESULT_CANCELED); <br> finish(); <br> } <br> private final void deleteFriend() { <br> if (mCursor != null) { <br> mText.setText(""); <br> mCursor.deleteRow(); <br> mCursor.deactivate(); <br> mCursor = null; <br> } <br> }</p> </td> </tr> </tbody> </table> <p>假定你已经学会了第八章中的创建菜单系统,简单的检查一下完整的LocationEditor.java文件来所有的这些方法和子类如何一起工作:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.FindAFriend; <br> import android.app.Activity; <br> import android.content.ComponentName; <br> import android.content.Context; <br> import android.content.Intent; <br> import android.database.Cursor; <br> import android.graphics.Canvas; <br> import android.graphics.Paint; <br> import android.graphics.Rect; <br> import android.net.Uri; <br> import android.os.Bundle; <br> import android.util.AttributeSet; <br> import android.view.Menu; <br> import android.widget.EditText; <br> import java.util.Map; <br> public class LocationEditor extends Activity { <br> private static final String TAG = "Friends"; <br> private static final int FRIEND_INDEX = 1; <br> private static final int NAME_INDEX = 2; <br> private static final int MODIFIED_INDEX = 3; <br> private static final String[] PROJECTION = new String[] { <br> Friends.Friend._ID, // 0 <br> Friends.Friend.LOCATION, // 1 <br> Friends.Friend.NAME, // 2 <br> Friends.Friend.MODIFIED_DATE // 3 <br> }; <br> private static final String ORIGINAL_CONTENT = "origContent"; <br> private static final int REVERT_ID = Menu.FIRST; <br> private static final int DISCARD_ID = Menu.FIRST + 1; <br> private static final int DELETE_ID = Menu.FIRST + 2; <br> private static final int STATE_EDIT = 0; <br> private static final int STATE_INSERT = 1; <br> private int mState; <br> 288 Android: A Programmer’s Guide <br> Chapter 11: Application: Find a Friend 289 <br> private boolean mNoteOnly = false; <br> private Uri mURI; <br> private Cursor mCursor; <br> private EditText mText; <br> private String mOriginalContent; <br> public static class MyEditText extends EditText { <br> private Rect mRect; <br> private Paint mPaint; <br> public MyEditText(Context context, AttributeSet attrs, Map params) { <br> super(context, attrs, params); <br> mRect = new Rect(); <br> mPaint = new Paint(); <br> mPaint.setStyle(Paint.Style.STROKE); <br> mPaint.setColor(0xFF0000FF); <br> } <br> @Override <br> protected void onDraw(Canvas canvas) { <br> int count = getLineCount(); <br> Rect r = mRect; <br> Paint paint = mPaint; <br> for (int i = 0; i < count; i++) { <br> int baseline = getLineBounds(i, r); <br> canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, <br> paint); <br> } <br> super.onDraw(canvas); <br> } <br> } <br> @Override <br> protected void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> final Intent intent = getIntent(); <br> final String type = intent.resolveType(this); <br> final String action = intent.getAction(); <br> if (action.equals(Intent.EDIT_ACTION)) { <br> mState = STATE_EDIT; <br> mURI = intent.getData(); <br> } else if (action.equals(Intent.INSERT_ACTION)) { <br> mState = STATE_INSERT; <br> 290 Android: A Programmer’s Guide <br> mURI = getContentResolver().insert(intent.getData(), null); <br> if (mURI == null) { <br> finish(); <br> return; <br> } <br> setResult(RESULT_OK, mURI.toString()); <br> } else { <br> finish(); <br> return; <br> } <br> setContentView(R.layout.location_editor); <br> mText = (EditText) findViewById(R.id.location); <br> mCursor = managedQuery(mURI, PROJECTION, null, null); <br> if (icicle != null) { <br> mOriginalContent = icicle.getString(ORIGINAL_CONTENT); <br> } <br> } <br> @Override <br> protected void onResume() { <br> super.onResume(); <br> if (mCursor != null) { <br> mCursor.first(); <br> if (mState == STATE_EDIT) { <br> setTitle(getText(R.string.title_edit)); <br> } else if (mState == STATE_INSERT) { <br> setTitle(getText(R.string.title_create)); <br> } <br> String note = mCursor.getString(FRIEND_INDEX); <br> mText.setTextKeepState(note); <br> if (mOriginalContent == null) { <br> mOriginalContent = note; <br> } <br> } else { <br> setTitle(getText(R.string.error_title)); <br> mText.setText(getText(R.string.error_message)); <br> } <br> } <br> Chapter 11: Application: Find a Friend 291 <br> @Override <br> protected void onFreeze(Bundle outState) { <br> outState.putString(ORIGINAL_CONTENT, mOriginalContent); <br> } <br> @Override <br> protected void onPause() { <br> super.onPause(); <br> if (mCursor != null) { <br> String text = mText.getText().toString(); <br> int length = text.length(); <br> if (isFinishing() && (length == 0) && !mNoteOnly) { <br> setResult(RESULT_CANCELED); <br> deleteFriend(); <br> } else { <br> if (!mNoteOnly) { <br> mCursor.updateLong(MODIFIED_INDEX, <br> System.currentTimeMillis()); <br> if (mState == STATE_INSERT) { <br> String title = text.substring(0, Math.min(30, <br> length)); <br> if (length > 30) { <br> int lastSpace = title.lastIndexOf(' '); <br> if (lastSpace > 0) { <br> title = title.substring(0, lastSpace); <br> } <br> } <br> mCursor.updateString(NAME_INDEX, title); <br> } <br> } <br> mCursor.updateString(FRIEND_INDEX, text); <br> managedCommitUpdates(mCursor); <br> } <br> } <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> if (mState == STATE_EDIT) { <br> menu.add(0, REVERT_ID, R.string.menu_revert).setShortcut('0', <br> 'r'); <br> if (!mNoteOnly) { <br> menu.add(0, DELETE_ID, <br> R.string.menu_delete).setShortcut('1', 'd'); <br> } <br> } else { <br> menu.add(0, DISCARD_ID, R.string.menu_discard).setShortcut('0', <br> 'd'); <br> } <br> if (!mNoteOnly) { <br> Intent intent = new Intent(null, getIntent().getData()); <br> intent.addCategory(Intent.ALTERNATIVE_CATEGORY); <br> menu.addIntentOptions( <br> Menu.ALTERNATIVE, 0, <br> new ComponentName(this, LocationEditor.class), null, <br> intent, 0, null); <br> } <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item) { <br> switch (item.getId()) { <br> case DELETE_ID: <br> deleteFriend(); <br> finish(); <br> break; <br> case DISCARD_ID: <br> cancelFriend(); <br> break; <br> case REVERT_ID: <br> cancelFriend(); <br> break; <br> } <br> return super.onOptionsItemSelected(item); <br> } <br> private final void cancelFriend() { <br> if (mCursor != null) { <br> if (mState == STATE_EDIT) { <br> mCursor.updateString(FRIEND_INDEX, mOriginalContent); <br> mCursor.commitUpdates(); <br> mCursor.deactivate(); <br> mCursor = null; <br> } else if (mState == STATE_INSERT) { <br> deleteFriend(); <br> } <br> } <br> setResult(RESULT_CANCELED); <br> finish(); <br> } <br> private final void deleteFriend() { <br> if (mCursor != null) { <br> mText.setText(""); <br> 292 Android: A Programmer’s Guide <br> mCursor.deleteRow(); <br> mCursor.deactivate(); <br> mCursor = null; <br> } <br> } <br> }</p> </td> </tr> </tbody> </table> <p>在下一节,会创建绘制Google Maps Overlay的活动,FriendsMap活动将从Friends数据库中读取完整的friends记录集并把每一个写入Overlay。</p> <h2>创建<span style="font-family:Arial">FriendsMap</span><span style="font-family:宋体">活动 第十一章</span><span style="font-family:Arial">(8)</span></h2> <p>FriendsMap<span style="font-family:宋体">是最后一个可以从主程序中呼叫的活动。本活动将从</span><span style="font-family:Arial">Friends</span><span style="font-family:宋体">数据库中呼叫数据集并且在</span><span style="font-family:Arial">Google Map</span><span style="font-family:宋体">上为每一个</span><span style="font-family:Arial">friend</span><span style="font-family:宋体">画一个圆圈。该活动还将为你的当前位置画一个圆圈。</span></p> <p>从增加两个新文件来开始本项目,<span style="font-family:Arial">friendsmap.xml</span><span style="font-family:宋体">和</span><span style="font-family:Arial">FriendsMap.java</span><span style="font-family:宋体">文件。因为你已经在</span>第九章内看过<span style="font-family:Arial">friendsmap.xml</span><span style="font-family:宋体">文件了,所以没有必要再解释一遍。使用的是一个</span><span style="font-family:Arial">RelativeLayout</span><span style="font-family:宋体">来把</span><span style="font-family:Arial">4</span><span style="font-family:宋体">个按钮放在一个</span><span style="font-family:Arial">Google Map</span><span style="font-family:宋体">上的。完整的</span><span style="font-family:Arial">friendsmap.xml</span><span style="font-family:宋体">文件应当看上去如下:</span></p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <br> android:orientation="vertical" <br> android:layout_width="fill_parent" <br> android:layout_height="fill_parent" <br> > <br> <view class="com.google.android.maps.MapView" <br> android:id="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content"/> <br> <Button android:id="@+id/buttonZoomIn" <br> style="?android:attr/buttonStyleSmall" <br> android:text="+" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonMapView" <br> style="?android:attr/buttonStyleSmall" <br> android:text="Map" <br> android:layout_alignRight="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonSatView" <br> style="?android:attr/buttonStyleSmall" <br> android:text="Sat" <br> android:layout_alignRight="@+id/myMap" <br> android:layout_alignBottom="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> <Button android:id="@+id/buttonZoomOut" <br> style="?android:attr/buttonStyleSmall" <br> android:text="-" <br> android:layout_alignBottom="@+id/myMap" <br> android:layout_width="wrap_content" <br> android:layout_height="wrap_content" /> <br> </RelativeLayout></p> </td> </tr> </tbody> </table> <p>因为在第九章已经看过绝大多数的<span style="font-family:Arial">FriendsMap.java</span><span style="font-family:宋体">文件了,我不会再阐述每一个细节。但是,有一个方法需要解释。</span></p> <p>你会创建一个叫做<span style="font-family:Arial">LoadFriends()</span><span style="font-family:宋体">的方法来存取数据库,读取记录,并且绘制</span><span style="font-family:Arial">Overlay</span><span style="font-family:宋体">。看一下</span><span style="font-family:Arial">LoadFriends()</span><span style="font-family:宋体">的代码。注意,你打开数据库,匹配并分析位置字段,从位置字段的纬度和经度创建点,并且绘制点到</span><span style="font-family:Arial">Overlay</span><span style="font-family:宋体">中。这个方法最后所做的事情就是从</span><span style="font-family:Arial">GPS</span><span style="font-family:宋体">上抓取坐标并且在</span><span style="font-family:Arial">Overlay</span><span style="font-family:宋体">上绘制出来,用标签</span><span style="font-family:Arial">“ME”</span><span style="font-family:宋体">表示。</span></p> <table> <tbody> <tr> <td valign="top"> <p>public void LoadFriends(MapView mv, MapController mc, Cursor c){ <br> Point myLocation = null; <br> Double latPoint = null; <br> Double lngPoint = null; <br> c.first(); <br> do{ <br> if (c.getString(c.getColumnIndex("location")) != null) { <br> final String geoPattern = "(geo:[\\-]?[0-9]{1,3}\\.[0 <br> 9]{1,6}\\,[\\-]?[0-9]{1,3}\\.[0-9]{1,6}\\#)"; <br> Pattern pattern = Pattern.compile(geoPattern); <br> CharSequence inputStr = <br> c.getString(c.getColumnIndex("location")); <br> Matcher matcher = Pattern.matcher(inputStr); <br> boolean matchFound = matcher.find(); <br> if (matchFound) { <br> String groupStr = matcher.group(0); <br> latPoint = <br> Double.valueOf(groupStr.substring(groupStr.indexOf(":") + 1, <br> groupStr.indexOf(","))) ; <br> lngPoint = <br> Double.valueOf(groupStr.substring(groupStr.indexOf(",") + 1, <br> groupStr.indexOf("#"))) ; <br> Point friendLocation = new <br> Point(latPoint.intValue(),lngPoint.intValue()); <br> 294 Android: A Programmer’s Guide <br> drawFriendsOverlay.addNewFriend(c.getString(c.getColumnIndex("name")), <br> friendLocation); <br> } <br> } <br> }while(c.next()); <br> LocationManager myManager = (LocationManager) <br> getSystemService(Context.LOCATION_SERVICE); <br> Double myLatPoint = <br> myManager.getCurrentLocation("gps").getLatitude()*1E6; <br> Double myLngPoint = <br> myManager.getCurrentLocation("gps").getLongitude()*1E6; <br> myLocation = new Point(myLatPoint.intValue(),myLngPoint.intValue()); <br> drawFriendsOverlay.addNewFriend("Me", myLocation); <br> mc.centerMapTo(myLocation, false); <br> mc.zoomTo(9); <br> mv = null; <br> }</p> </td> </tr> </tbody> </table> <p>剩余的<span style="font-family:Arial">FriendsMap.java</span><span style="font-family:宋体">文件操控</span>第十章介绍的缩放和棒性按钮:</p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.FindAFriend; <br> import android.os.Bundle; <br> import android.location.LocationManager; <br> import android.view.View; <br> import android.content.Context; <br> import android.content.Intent; <br> import android.database.Cursor; <br> import android.widget.Button; <br> import java.util.regex.Pattern; <br> import java.util.regex.Matcher; <br> import android.graphics.Canvas; <br> import android.graphics.RectF; <br> import android.graphics.Paint; <br> import com.google.android.maps.MapActivity; <br> import com.google.android.maps.MapView; <br> import com.google.android.maps.Point; <br> import com.google.android.maps.MapController; <br> import com.google.android.maps.Overlay; <br> import com.google.android.maps.OverlayController; <br> public class FriendsMap extends MapActivity { <br> private static final String[] PROJECTION = new String[] { <br> Friends.Friend.NAME, Friends.Friend.LOCATION}; <br> public Cursor mCursor; <br> DrawFriendsOverlay drawFriendsOverlay = new DrawFriendsOverlay(); <br> @Override <br> public void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setContentView(R.layout.friendsmap); <br> Intent intent = getIntent(); <br> if (intent.getData() == null) { <br> intent.setData(Friends.Friend.CONTENT_URI); <br> } <br> mCursor = managedQuery(getIntent().getData(), PROJECTION, null,null); <br> final MapView myMap = (MapView) findViewById(R.id.myMap); <br> final MapController myMapController = myMap.getController(); <br> LoadFriends(myMap, myMapController, mCursor); <br> OverlayController myOverlayController = <br> myMap.createOverlayController(); <br> myOverlayController.add(drawFriendsOverlay, true); <br> final Button zoomIn = (Button) findViewById(R.id.buttonZoomIn); <br> zoomIn.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomIn(myMap,myMapController); <br> }}); <br> final Button zoomOut = (Button) findViewById(R.id.buttonZoomOut); <br> zoomOut.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ZoomOut(myMap,myMapController); <br> }}); <br> final Button viewMap = (Button) findViewById(R.id.buttonMapView); <br> viewMap.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ShowMap(myMap,myMapController); <br> }}); <br> final Button viewSat = (Button) findViewById(R.id.buttonSatView); <br> viewSat.setOnClickListener(new Button.OnClickListener() { <br> public void onClick(View v){ <br> ShowSat(myMap,myMapController); <br> }}); <br> } <br> public void LoadFriends(MapView mv, MapController mc, Cursor c){ <br> Point myLocation = null; <br> Double latPoint = null; <br> Double lngPoint = null; <br> c.first(); <br> do{ <br> if (c.getString(c.getColumnIndex("location")) != null) { <br> final String geoPattern = "(geo:[\\-]?[0-9]{1,3}\\.[0 <br> 9]{1,6}\\,[\\-]?[0-9]{1,3}\\.[0-9]{1,6}\\#)"; <br> Pattern pattern = Pattern.compile(geoPattern); <br> CharSequence inputStr = <br> c.getString(c.getColumnIndex("location")); <br> Matcher matcher = pattern.matcher(inputStr); <br> boolean matchFound = matcher.find(); <br> if (matchFound) { <br> String groupStr = matcher.group(0); <br> latPoint = <br> Double.valueOf(groupStr.substring(groupStr.indexOf(":") + 1, <br> groupStr.indexOf(","))) ; <br> lngPoint = <br> Double.valueOf(groupStr.substring(groupStr.indexOf(",") + 1, <br> groupStr.indexOf("#"))) ; <br> Point friendLocation = new <br> Point(latPoint.intValue(),lngPoint.intValue()); <br> drawFriendsOverlay.addNewFriend(c.getString(c.getColumnIndex("name")), <br> friendLocation); <br> } <br> } <br> }while(c.next()); <br> LocationManager myManager = (LocationManager) <br> getSystemService(Context.LOCATION_SERVICE); <br> Double myLatPoint = <br> myManager.getCurrentLocation("gps").getLatitude()*1E6; <br> Double myLngPoint = <br> myManager.getCurrentLocation("gps").getLongitude()*1E6; <br> myLocation = new Point(myLatPoint.intValue(),myLngPoint.intValue()); <br> drawFriendsOverlay.addNewFriend("Me", myLocation); <br> mc.centerMapTo(myLocation, false); <br> mc.zoomTo(9); <br> mv = null; <br> } <br> public void ZoomIn(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=21){ <br> mc.zoomTo(mv.getZoomLevel()+ 1); <br> } <br> } <br> public void ZoomOut(MapView mv, MapController mc){ <br> if(mv.getZoomLevel()!=1){ <br> mc.zoomTo(mv.getZoomLevel()- 1); <br> } <br> } <br> public void ShowMap(MapView mv, MapController mc){ <br> if (mv.isSatellite()){ <br> mv.toggleSatellite(); <br> } <br> } <br> public void ShowSat(MapView mv, MapController mc){ <br> if (!mv.isSatellite()){ <br> mv.toggleSatellite(); <br> } <br> } <br> protected class DrawFriendsOverlay extends Overlay{ <br> public String[] friendName = new String[0]; <br> public Point[] friendPoint = new Point[0]; <br> final Paint paint = new Paint(); <br> @Override <br> public void draw(Canvas canvas, PixelCalculator calculator, Boolean <br> shadow){ <br> for(int x=0;x<friendPoint.length; x++){ <br> int[] coords = new int[2]; <br> calculator.getPointXY(friendPoint[x], coords); <br> RectF oval = new RectF(coords[0] - 7, coords[1] + 7, <br> coords[0] + 7, coords[1] - 7); <br> paint.setTextSize(14); <br> canvas.drawText(friendName[x], <br> coords[0] +9, coords[1], paint); <br> canvas.drawOval(oval, paint); <br> } <br> } <br> public void addNewFriend(String name,Point point ){ <br> int x = friendPoint.length; <br> String[] friendNameB = new String[x + 1]; <br> Point[] friendPointB = new Point[x + 1]; <br> System.arraycopy(friendName, 0, friendNameB, 0, x ); <br> System.arraycopy(friendPoint, 0, friendPointB, 0, x); <br> friendNameB[x] = name; <br> friendPointB[x]= point; <br> friendName = new String[x + 1]; <br> friendPoint = new Point[x + 1]; <br> System.arraycopy(friendNameB, 0, friendName, 0, x + 1 ); <br> System.arraycopy(friendPointB, 0, friendPoint, 0, x + 1 ); <br> } <br> } <br> }</p> </td> </tr> </tbody> </table> <p>完成本项目的最后一个任务是创建主活动,<span style="font-family:Arial">FindAFriend</span><span style="font-family:宋体">。该活动被放置在一个壳内来呼叫本章创建的活动。</span></p> <h2>创建<span style="font-family:Arial">FindAFriend</span><span style="font-family:宋体">活动 第十一章</span><span style="font-family:Arial">(9)</span></h2> <p>要开始本节,创建两个文件,<span style="font-family:Arial">findafriend.xml</span><span style="font-family:宋体">和</span><span style="font-family:Arial">FindAFriend.java</span><span style="font-family:宋体">。再说一次,这些文件将为当前部分独自保留你的布局和代码。布局文件非常的基本并且只有一个</span><span style="font-family:Arial">TextView</span><span style="font-family:宋体">。这个</span><span style="font-family:Arial">TextView</span><span style="font-family:宋体">将被用来写入到</span><span style="font-family:Arial">friends</span><span style="font-family:宋体">的列表中。完整的</span><span style="font-family:Arial">findafriend.xml</span><span style="font-family:宋体">文件应当显示如下:</span></p> <table> <tbody> <tr> <td valign="top"> <p><?xml version="1.0" encoding="utf-8"?> <br> <TextView xmlns:android="http://schemas.android.com/apk/res/android" <br> android:id="@android:id/text1" <br> android:layout_width="fill_parent" <br> android:layout_height="?android:attr/listPreferredItemHeight" <br> android:textAppearance="?android:attr/textAppearanceLargeInverse" <br> android:gravity="center_vertical" <br> android:paddingLeft="27dip" <br> /></p> </td> </tr> </tbody> </table> <p>完整的<span style="font-family:Arial">FindAFriend.java</span><span style="font-family:宋体">文件如下。文件中的所有代码在本章中已经讨论过。首先,读取数据库并且写入结果到一个</span><span style="font-family:Arial">ListView</span><span style="font-family:宋体">。给予用户一个菜单项目来编辑或者删除条目,或者启动</span><span style="font-family:Arial">FriendsMap</span><span style="font-family:宋体">活动。很简单,对不对?</span></p> <table> <tbody> <tr> <td valign="top"> <p>package android_programmers_guide.FindAFriend; <br> import android_programmers_guide.FindAFriend.Friends; <br> import android.app.ListActivity; <br> import android.content.ComponentName; <br> import android.content.Intent; <br> import android.content.ContentUris; <br> import android.database.Cursor; <br> import android.graphics.Color; <br> import android.net.Uri; <br> import android.os.Bundle; <br> import android.view.Menu; <br> import android.view.View; <br> import android.view.View.MeasureSpec; <br> import android.widget.ListAdapter; <br> import android.widget.ListView; <br> import android.widget.SimpleCursorAdapter; <br> import android.widget.TextView; <br> public class FindAFriend extends ListActivity { <br> 300 Android: A Programmer’s Guide <br> public static final int DELETE_ID = Menu.FIRST; <br> public static final int INSERT_ID = Menu.FIRST + 1; <br> public static final int FIND_FRIENDS = Menu.FIRST + 2; <br> private static final String[] PROJECTION = new String[] { <br> Friends.Friend._ID, Friends.Friend.NAME}; <br> private Cursor mCursor; <br> @Override <br> protected void onCreate(Bundle icicle) { <br> super.onCreate(icicle); <br> setDefaultKeyMode(SHORTCUT_DEFAULT_KEYS); <br> Intent intent = getIntent(); <br> if (intent.getData() == null) { <br> intent.setData(Friends.Friend.CONTENT_URI); <br> } <br> setupList(); <br> mCursor = managedQuery(getIntent().getData(), PROJECTION, null, <br> null); <br> ListAdapter adapter = new SimpleCursorAdapter(this, <br> R.layout.findafriend_item, mCursor, <br> new String[] {Friends.Friend.NAME}, new int[] <br> {android.R.id.text1}); <br> setListAdapter(adapter); <br> } <br> private void setupList() { <br> View view = getViewInflate().inflate( <br> android.R.layout.simple_list_item_1, null, null); <br> TextView v = (TextView) view.findViewById(android.R.id.text1); <br> v.setText("X"); <br> getListView().setBackgroundColor(Color.GRAY); <br> v.measure(MeasureSpec.makeMeasureSpec(View.MeasureSpec.EXACTLY, <br> 100), <br> MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, <br> 0)); <br> } <br> @Override <br> public boolean onCreateOptionsMenu(Menu menu) { <br> super.onCreateOptionsMenu(menu); <br> menu.add(0, INSERT_ID, R.string.menu_insert).setShortcut('3', 'a'); <br> Intent intent = new Intent(null, getIntent().getData()); <br> intent.addCategory(Intent.ALTERNATIVE_CATEGORY); <br> menu.addIntentOptions( <br> Menu.ALTERNATIVE, 0, new ComponentName(this, FindAFriend.class), <br> null, intent, 0, null); <br> return true; <br> } <br> @Override <br> public boolean onPrepareOptionsMenu(Menu menu) { <br> super.onPrepareOptionsMenu(menu); <br> final boolean haveItems = mCursor.count() > 0; <br> if (haveItems) { <br> Uri uri = ContentUris.withAppendedId(getIntent().getData(), <br> getSelectedItemId()); <br> Intent[] specifics = new Intent[1]; <br> specifics[0] = new Intent(Intent.EDIT_ACTION, uri); <br> Menu.Item[] items = new Menu.Item[1]; <br> Intent intent = new Intent(null, uri); <br> intent.addCategory(Intent.SELECTED_ALTERNATIVE_CATEGORY); <br> menu.addIntentOptions(Menu.SELECTED_ALTERNATIVE, 0, null, <br> specifics, intent, 0, items); <br> menu.add(Menu.SELECTED_ALTERNATIVE, DELETE_ID, <br> R.string.menu_delete) <br> .setShortcut('2', 'd'); <br> menu.add(Menu.SELECTED_ALTERNATIVE, FIND_FRIENDS, <br> R.string.find_friends).setShortcut('4', 'f'); <br> if (items[0] != null) { <br> items[0].setShortcut('1', 'e'); <br> } <br> } else { <br> menu.removeGroup(Menu.SELECTED_ALTERNATIVE); <br> } <br> menu.setItemShown(DELETE_ID, haveItems); <br> return true; <br> } <br> @Override <br> public boolean onOptionsItemSelected(Menu.Item item) { <br> switch (item.getId()) { <br> case DELETE_ID: <br> deleteItem(); <br> return true; <br> case INSERT_ID: <br> insertItem(); <br> return true; <br> case FIND_FRIENDS: <br> Intent findfriends = new Intent(this, FriendsMap.class); <br> startActivity(findfriends); <br> return true; <br> } <br> return super.onOptionsItemSelected(item); <br> } <br> @Override <br> protected void onListItemClick(ListView l, View v, int position, long <br> id) { <br> Uri url = ContentUris.withAppendedId(getIntent().getData(), id); <br> String action = getIntent().getAction(); <br> if (Intent.PICK_ACTION.equals(action) <br> || Intent.GET_CONTENT_ACTION.equals(action)) { <br> setResult(RESULT_OK, url.toString()); <br> } else { <br> startActivity(new Intent(Intent.EDIT_ACTION, url)); <br> } <br> } <br> private final void deleteItem() { <br> mCursor.moveTo(getSelectedItemPosition()); <br> mCursor.deleteRow(); <br> } <br> private final void insertItem() { <br> startActivity(new Intent(Intent.INSERT_ACTION, <br> getIntent().getData())); <br> } <br> }</p> </td> </tr> </tbody> </table> <p>这个是本书中最长的一个活动,需要注意的是你所做的相关所需的编程工作还是非常的少。下一步,运行这个活动并且查看所有工作的成果。</p> <h2>运行<span style="font-family:Arial">FindAFriend</span><span style="font-family:宋体">活动 第十一章</span><span style="font-family:Arial">(10)</span></h2> <p>在Android模拟器中运行FindAFriend活动。应当从一个空的列表开始,如下图(略)。要增加第一个朋友,点击菜单按钮并选择Add Friend选项。</p> <p>本选项启动你创建的定制View。在提供的行处输入一个朋友的名称,点击返回按钮返回到主活动。</p> <p>现在在ListView中应当有一个朋友的名称。再次点击菜单按钮,显然你拥有更多的选项了。如下图(略)。选择Edit Location选项。会再次来到定制控制。输入坐标信息。</p> <p>最后,返回到主活动并选择Find Friend选项。这样分别会清除在旧金山的位置和你朋友在非洲海岸的位置。</p> <p>试试这个:实时位置更新 <br> 试着修改FindAFriend应用程序,当你移动时,来更新“ME”标记。这个应该很容易通过使用update()方法实现。</p> <p>问专家</p> <p>Q:SQLite数据库可以通过代码的方式创建吗?</p> <p>A:是的。但是,为了更好的了解Android的目的,我选择了手工创建数据库例子。可以在FriendsProvider Content Provider通过代码创建数据库的creation方法自行修改本项目。</p> <p>Q:需要一个分开的类来执行BaseColumns?</p> <p>A:不。可以直接从Friends类中定义条目。如果你创建的一个Content Provider被其它开发者使用,但是他们不知道数据库的底层结构,那么你就需要提供一个定义的类。</p> <p>Android SDK <span style="font-family:宋体">工具参考 第十二章 (完)</span></p> <h2>Android SDK <span style="font-family:宋体">工具参考 第十二章 (完)</span></h2> <p>本章提供了一些有价值的Android SDK工具参考项目,这些工具你已经在本书的课程中使用过了。它们给了你一些命令行选项,可以在Android模拟器和Android调试桥中使用。</p> <p>Android模拟器命令</p> <p>下表包含了大多数常规的Android模拟器命令。这些命令在2008年3月份发布的SDK版本中可用。每个命令提供了简短的描述。</p> <table> <tbody> <tr> <td valign="top"> <p>Emulator Command 模拟器命令</p> </td> <td valign="top"> <p>功能 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -console</p> </td> <td valign="top"> <p>Enables the console shell on the current terminal <br> 在当前终端上激活控制台外壳</p> </td> </tr> <tr> <td valign="top"> <p><br> emulator -data <filename></p> </td> <td valign="top"> <p>Uses a different file as the working user-data disk image <br> 使用一个不同的文件作为工作用户数据磁盘镜像</p> </td> </tr> <tr> <td valign="top"> <p>emulator -debug-kernel</p> </td> <td valign="top"> <p>Sends kernel output to the console <br> 发送核心输出到控制台</p> </td> </tr> <tr> <td valign="top"> <p>emulator -flash-keys</p> </td> <td valign="top"> <p>Flashes keypresses on the device skin <br> 在设备皮肤上闪烁keypress</p> </td> </tr> <tr> <td valign="top"> <p>emulator -help</p> </td> <td valign="top"> <p>Prints a list of all Emulator commands <br> 列出模拟器列表</p> </td> </tr> <tr> <td valign="top"> <p>emulator -http-proxy <proxy></p> </td> <td valign="top"> <p>Makes all TCP connections through a specified HTTP/HTTPS proxy 通过一个定义的HTTP/HTTPS 代理制作所有的TCP连接。 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -image <file> Uses <file></p> </td> <td valign="top"> <p>as the system image 作为系统镜像 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -kernel <file> Uses <file></p> </td> <td valign="top"> <p>as the emulated kernel 作为模拟的核心 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -logcat <logtags></p> </td> <td valign="top"> <p>Enables logcat output with given tags <br> 用指定的标签激活logcat输出</p> </td> </tr> <tr> <td valign="top"> <p>emulator -mic <device or file></p> </td> <td valign="top"> <p>Uses device or WAV file for audio input <br> 使用设备或者WAV文件作为音频输入</p> </td> </tr> <tr> <td valign="top"> <p>emulator -netdelay <delay></p> </td> <td valign="top"> <p>设置网络反应时间模拟到<delay>Sets network latency emulation to <delay>. <br> (The <delay> parameter simulates the delay experienced on specific types of networks.<delay>参数模拟在定义类型的网络) <br> The <delay>s you can use are as follows <br> 可以使用的<delay>如下: <br> ● Gprs <br> ● Edge <br> ● Umts <br> ● None <br> ● <num> <br> ● <min>:<max> </p> </td> </tr> <tr> <td valign="top"> <p>emulator -netfast Shortcut for -netspeed full -netdelay </p> </td> <td valign="top"> <p>none</p> </td> </tr> <tr> <td valign="top"> <p>emulator -netspeed <speed></p> </td> <td valign="top"> <p>设置网络速度模拟到<speed>。Sets network speed emulation to <speed>. (The <br> <speed> parameter simulates the data speed <br> experienced on specific types of networks.) The <br> <speed>s you can use are as follows: <br> ● Gsm <br> ● Hscsd <br> ● Gprs <br> ● Edge <br> ● Umts <br> ● Hsdpa <br> ● Full <br> ● <num> <br> ● <up>:<down> </p> </td> </tr> <tr> <td valign="top"> <p>emulator -noaudio</p> </td> <td valign="top"> <p>Disables Android audio support废除Android音频支持 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -nojni</p> </td> <td valign="top"> <p>Disables JNI checks in the Dalvik virtual machine在Dalvik virtual machine中废除JNI检查 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -noskin</p> </td> <td valign="top"> <p>Specifies not to use any Emulator skin <br> 定义为不使用任何模拟器皮肤 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -onion <image></p> </td> <td valign="top"> <p>Uses overlay image over screen <br> 在屏幕上使用覆盖图像 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -onion-alpha <percent></p> </td> <td valign="top"> <p>Specifies onion skin translucency value (as percent) 定义半透明皮肤值(百分比) </p> </td> </tr> <tr> <td valign="top"> <p>emulator -qemu</p> </td> <td valign="top"> <p>Passes arguments to QEMU <br> 传递参数到QEMU</p> </td> </tr> <tr> <td valign="top"> <p>emulator -qemu -h</p> </td> <td valign="top"> <p>Displays QEMU help <br> 显示QEMU帮助</p> </td> </tr> <tr> <td valign="top"> <p>emulator -radio <device></p> </td> <td valign="top"> <p>Redirects the radio modem interface to a host character device重定向无线调制解调器接口到一个主字符设备 </p> </td> </tr> <tr> <td valign="top"> <p>emulator -ramdisk <file> Uses <file> </p> </td> <td valign="top"> <p>as the ramdisk image作为ramdisk镜像</p> </td> </tr> <tr> <td valign="top"> <p>emulator -raw-keys</p> </td> <td valign="top"> <p>Disables Unicode keyboard reverse mapping禁用Unicode键盘转换映射</p> </td> </tr> <tr> <td valign="top"> <p>emulator -sdcard <file> Uses <file> </p> </td> <td valign="top"> <p>as the SD Memory Card image作为SD内存卡镜像</p> </td> </tr> <tr> <td valign="top"> <p>emulator -skin <skinID></p> </td> <td valign="top"> <p>使用定义的皮肤启动模拟器Starts the Emulator with the specified skin: <br> ● HVGA-L 480x320, landscape <br> ● HVGA-P 320x480, portrait (default) <br> ● QVGA-L 320x240, landscape <br> ● QVGA-P 240x320, portrait </p> </td> </tr> <tr> <td valign="top"> <p>emulator -skindir <dir></p> </td> <td valign="top"> <p>Searches for Emulator skins in <dir> <br> 在<dir>内查找模拟器皮肤</p> </td> </tr> <tr> <td valign="top"> <p><br> emulator -system <dir></p> </td> <td valign="top"> <p>Searches system, ramdisk, and user-data disk images in <dir> <br> 在<dir>内查找系统,ramdisk和用户数据磁盘镜像</p> </td> </tr> <tr> <td valign="top"> <p>emulator -trace <name> </p> </td> <td valign="top"> <p>Enables code profiling (press F9 to start), written to a specified file <br> 启动代码分析(按F9启动),写入一个定义的文件</p> </td> </tr> <tr> <td valign="top"> <p>emulator -useaudio </p> </td> <td valign="top"> <p>Enables Android audio support启动Android音频支持</p> </td> </tr> <tr> <td valign="top"> <p>emulator -verbose </p> </td> <td valign="top"> <p>Enables verbose output激活冗长的输出</p> </td> </tr> <tr> <td valign="top"> <p>emulator -verbose-keys </p> </td> <td valign="top"> <p>Enables verbose keypress messages激活冗长的的按键信息</p> </td> </tr> <tr> <td valign="top"> <p>emulator -verbose-proxy </p> </td> <td valign="top"> <p>Enables verbose proxy debug messages激活冗长的的代理调试信息</p> </td> </tr> <tr> <td valign="top"> <p>emulator -wipe-data</p> </td> <td valign="top"> <p>Deletes all data on the user-data disk image在用户数据磁盘镜像中删除所有数据(开始前参见emulator - data<filename>) <br> (see emulator –data <filename>) before <br> starting</p> </td> </tr> </tbody> </table> <p>Android调试桥命令</p> <p>下面的是gsm命令。通过连接到Android模拟器的终端控制台使用。如果你不了解端口终端控制台,它是一个比调试端口少的端口。执行adb来获得活动的设备列表和相关的端口号。</p> <table> <tbody> <tr> <td valign="top"> <p><br> adb Bugreport</p> </td> <td valign="top"> <p>Prints dumpsys, dumpstate, and logcat data to the screen, for the purposes of bug reporting <br> 为故障报告在屏幕上打印dumpsys,dumpstate和logcat数据 </p> </td> </tr> <tr> <td valign="top"> <p>adb call <phonenumber></p> </td> <td valign="top"> <p>Simulates an inbound phone call from <br> <phonenumber>模拟一个呼入的电话 </p> </td> </tr> <tr> <td valign="top"> <p>adb cancel <phonenumber></p> </td> <td valign="top"> <p>Cancels an inbound phone call from <br> <phonenumber>取消一个呼入的电话 </p> </td> </tr> <tr> <td valign="top"> <p>adb -d {<ID>|<serialNumber>}</p> </td> <td valign="top"> <p>Lets you direct an adb command to a specific Emulator/device instance, referred to by its adb-assigned ID or serial number <br> 允许你指引一个adb命令到一个定义的模拟器/设备示例中,通过它的赋值adb ID或者序列号来引用 </p> </td> </tr> <tr> <td valign="top"> <p>adb data <state></p> </td> <td valign="top"> <p>Changes the state of the GPRS data <br> connection to <state>改变GPRS数据连接状态到<state> </p> </td> </tr> <tr> <td valign="top"> <p>adb Devices</p> </td> <td valign="top"> <p>Prints a list of all attached mulator/device <br> instances <br> 打印所附模拟器列表/设备示例 </p> </td> </tr> <tr> <td valign="top"> <p>adb forward <local> <remote></p> </td> <td valign="top"> <p>Forwards socket connections from a specified local port to a specified remote port on the Emulator/device instance <br> <br> 在模拟器/设备示例中从一个定义的本来端口转递socket连接到一个定义的远程端口</p> </td> </tr> <tr> <td valign="top"> <p>adb get-serialno</p> </td> <td valign="top"> <p>Prints the adb instance identifier string <br> 打印adb示例标识符字符串</p> </td> </tr> <tr> <td valign="top"> <p>adb get-state</p> </td> <td valign="top"> <p>Prints the adb state of an emulator/device instance <br> 打印一个模拟器/设备示例的adb状态</p> </td> </tr> <tr> <td valign="top"> <p>adb help</p> </td> <td valign="top"> <p>Prints a list of supported adb commands <br> 打印支持的adb命令列表</p> </td> </tr> <tr> <td valign="top"> <p>adb install <path-to-apk></p> </td> <td valign="top"> <p>Pushes an Android application (specified as a full path to an .apk file) to the data file of an Emulator/device <br> 推入Android应用程序(作为一个完整路径一个.apk文件)到模拟器/设备示例 </p> </td> </tr> <tr> <td valign="top"> <p>adb jdwp</p> </td> <td valign="top"> <p>Prints a list of available JDWP processes on a given device <br> 在指定的设备上列出可用的JDWP进程</p> </td> </tr> <tr> <td valign="top"> <p>adb kill-server</p> </td> <td valign="top"> <p>Terminates the adb server process <br> 终止adb服务器进程</p> </td> </tr> <tr> <td valign="top"> <p>adb logcat [<option>] [<filter-specs>] </p> </td> <td valign="top"> <p>Prints log data to the screen在屏幕上打印log数据</p> </td> </tr> <tr> <td valign="top"> <p>adb ppp <tty> [parm]...</p> </td> <td valign="top"> <p>Runs PPP over USB通过USB运行PPP: <br> ● <tty> The tty for PPP stream; for <br> example, dev:/dev/omap_csmi_ttyl <br> <tty>ppp流;例如,dev:/dev/omap_csmi_ttyl <br> ● [parm]... Zero or more PPP/PPPD <br> options, such as defaultroute, local, <br> notty, etc. <br> 0或者更多的PPP/PPPD选项,如defaultroute, local,notty等等 <br> Note that you should not automatically start <br> a PDP connection. <br> 注意,你不应当自动启动一个PDP连接 </p> </td> </tr> <tr> <td valign="top"> <p>adb pull <remote> <local></p> </td> <td valign="top"> <p>Copies a specified file from an <br> Emulator/device instance to your <br> development computer <br> 从模拟器/设备复制定义的文件到你的电脑 </p> </td> </tr> <tr> <td valign="top"> <p>adb push <local> <remote> </p> </td> <td valign="top"> <p>Copies a specified file from your development <br> computer to an Emulator/device instance <br> 从电脑中复制文件到模拟器/设备</p> </td> </tr> <tr> <td valign="top"> <p>adb Shell</p> </td> <td valign="top"> <p>Starts a remote shell in the target <br> Emulator/device instance <br> 在目标模拟器/设备上启动远程外壳</p> </td> </tr> <tr> <td valign="top"> <p>adb start-server</p> </td> <td valign="top"> <p>Checks whether the adb server process is <br> running and, if not, starts it <br> 检测adb服务器进程是否启动,如果否,启动它</p> </td> </tr> <tr> <td valign="top"> <p>adb Status</p> </td> <td valign="top"> <p>Reports the current GSM voice/data state <br> 报告当前GSM声音/数据状态</p> </td> </tr> <tr> <td valign="top"> <p>adb unregistered</p> </td> <td valign="top"> <p>Indicates no network is available <br> 指示无网络可用</p> </td> </tr> <tr> <td valign="top"> <p>adb Version</p> </td> <td valign="top"> <p>Prints the adb version number <br> 打印adb版本号</p> </td> </tr> <tr> <td valign="top"> <p>adb voice <state></p> </td> <td valign="top"> <p>Changes the state of the GPRS voice <br> connection to <state>改变GPRS声音状态连接到<state> </p> </td> </tr> <tr> <td valign="top"> <p>adb wait-for-bootloader</p> </td> <td valign="top"> <p>Blocks execution until the bootloader is <br> online—that is, until the instance state is <br> bootloader <br> 阻止执行直到引导装入完成。也就是除非设备完成引导</p> </td> </tr> <tr> <td valign="top"> <p>adb wait-for-device</p> </td> <td valign="top"> <p>Blocks execution until the device is <br> online—that is, until the instance state is <br> device <br> 阻止执行直到设备在线。也就是示例状态是设备</p> </td> </tr> </tbody> </table> <p> </p> <p> </p> <p>注意:为不产生歧义,本表格的解释部分仍保留英文。</p> <p>至此,历经两个多月的时间,本书的翻译工作全部完成,在英文版的书中还有索引部分,这里就没有必要翻译了。</p> </div> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1274632942118780928"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(Android书籍)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1835513699826233344.htm" title="android系统selinux中添加新属性property" target="_blank">android系统selinux中添加新属性property</a> <span class="text-muted">辉色投像</span> <div>1.定位/android/system/sepolicy/private/property_contexts声明属性开头:persist.charge声明属性类型:u:object_r:system_prop:s0图12.定位到android/system/sepolicy/public/domain.te删除neverallow{domain-init}default_prop:property</div> </li> <li><a href="/article/1835508630959517696.htm" title="向内而求" target="_blank">向内而求</a> <span class="text-muted">陈陈_19b4</span> <div>10月27日,阴。阅读书目:《次第花开》。作者:希阿荣博堪布,是当今藏传佛家宁玛派最伟大的上师法王,如意宝晋美彭措仁波切颇具影响力的弟子之一。多年以来,赴海内外各地弘扬佛法,以正式授课、现场开示、发表文章等多种方法指导佛学弟子修行佛法。代表作《寂静之道》、《生命这出戏》、《透过佛法看世界》自出版以来一直是佛教类书籍中的畅销书。图片发自App金句:1.佛陀说,一切痛苦的根源在于我们长期以来对自身及外</div> </li> <li><a href="/article/1835470119195734016.htm" title="2.2.6 通知类控件 Toast、Menu" target="_blank">2.2.6 通知类控件 Toast、Menu</a> <span class="text-muted">常思行</span> <div>本文例程下载:WillFlow_Toast、WillFlowMenu一、什么是Toast?Toast也被叫做吐司,是Android系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,它有如下两个特点:Toast是没有焦点的Toast显示的时间有限过一定的时间就会自动消失所以一般来讲Toast的使用并不会影响我们的正常操作,并且它通常不会占用太大的屏幕空间,有着良好的用户体</div> </li> <li><a href="/article/1835460655675699200.htm" title="张芝华49天共修 - 草稿" target="_blank">张芝华49天共修 - 草稿</a> <span class="text-muted">李娟AINI</span> <div>祈禱、靜心、源代碼編程、觀想發願四根支柱,運用靈性能量的助力,讓夢想和渴望在最大向度中輕鬆實現。共修群指定书籍:1.能断金刚麦克格西2.新世界:灵性的觉醒埃克哈特·托尔3.爱是一切的答案芭芭拉迪安吉莉思4.完美的爱,不完美的关系约翰•威尔伍德5.爱的业力法则麦克格西6.漫画《金刚经》蔡志忠7.蔡志忠典藏国学漫画系列(套装共6册)作业:全部在共修群里完成,并请保存好自己的作业。l一周三次共修觉察作业</div> </li> <li><a href="/article/1835450889452744704.htm" title="mac 备份android 手机通讯录导入iphone,iphone如何导出通讯录(轻松教你iPhone备份通讯录的方法)..." target="_blank">mac 备份android 手机通讯录导入iphone,iphone如何导出通讯录(轻松教你iPhone备份通讯录的方法)...</a> <span class="text-muted">weixin_39762838</span> <a class="tag" taget="_blank" href="/search/mac/1.htm">mac</a><a class="tag" taget="_blank" href="/search/%E5%A4%87%E4%BB%BDandroid/1.htm">备份android</a><a class="tag" taget="_blank" href="/search/%E6%89%8B%E6%9C%BA%E9%80%9A%E8%AE%AF%E5%BD%95%E5%AF%BC%E5%85%A5iphone/1.htm">手机通讯录导入iphone</a> <div>在日新月异的手机更替中,换手机已经成为一个非常稀松平常的事情,但将旧手机上面的通讯录导入到新手机还是让不少小伙伴为难,本篇将给大家详细讲解这方面的知识:“苹果手机通讯录怎么导入到新手机”及“安卓手机通讯录导入到新手机”的方法。一、苹果手机通讯录导入到新手机常用方法(SIM卡导入)在苹果手机主频幕上找到“设置”,单击进入设置菜单,下拉菜单列表,点击“邮件、通讯录、日历”,然后找到“导入SIM卡通讯录</div> </li> <li><a href="/article/1835450511071997952.htm" title="android 更改窗口的层次,浮窗开发之窗口层级" target="_blank">android 更改窗口的层次,浮窗开发之窗口层级</a> <span class="text-muted">Ms.Bu</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E6%9B%B4%E6%94%B9%E7%AA%97%E5%8F%A3%E7%9A%84%E5%B1%82%E6%AC%A1/1.htm">更改窗口的层次</a> <div>最近在项目中遇到了这样的需求:需要在特定的其他应用之上悬浮自己的UI交互(拖动、输入等复杂的UI交互),和九游的浮窗类似,不过我们的比九游的体验更好,我们越过了很多授权的限制。浮窗效果很多人都知道如何去实现一个简单的浮窗,但是却很少有人去深入的研究背后的流程机制,由于项目中浮窗交互比较复杂,遇到了些坑查看了很多资料,故总结浮窗涉及到的知识点:窗口层级关系(浮窗是如何“浮”的)?浮窗有哪些限制,如何</div> </li> <li><a href="/article/1835448619277316096.htm" title="Android应用性能优化" target="_blank">Android应用性能优化</a> <span class="text-muted">轻口味</span> <a class="tag" taget="_blank" href="/search/Android/1.htm">Android</a> <div>Android手机由于其本身的后台机制和硬件特点,性能上一直被诟病,所以软件开发者对软件本身的性能优化就显得尤为重要;本文将对Android开发过程中性能优化的各个方面做一个回顾与总结。Cache优化ListView缓存:ListView中有一个回收器,Item滑出界面的时候View会回收到这里,需要显示新的Item的时候,就尽量重用回收器里面的View;每次在getView函数中inflate新</div> </li> <li><a href="/article/1835420898262347776.htm" title="2023-4-6晨间日记" target="_blank">2023-4-6晨间日记</a> <span class="text-muted">百里清风柏年醉</span> <div>今天是什么日子起床:7:00就寝:10:30天气:阳光明媚心情:沉闷,忧心忡忡纪念日:无任务清单昨日完成的任务,最重要的三件事:看咨询工程师的书锻炼身体记75个单词改进:自己做饭习惯养成:看纸质书籍不刷抖音每天日更周目标·完成进度学习·信息·阅读健康·饮食·锻炼人际·家人·朋友保持与朋友交流,多认识、结交新的朋友工作·思考怎么做好向上管理该学习什么新的技能怎么与同事更好相处,更好地开展工作最美好的</div> </li> <li><a href="/article/1835417586574127104.htm" title="新月|图卡5-8《心》一切始于心,终于心" target="_blank">新月|图卡5-8《心》一切始于心,终于心</a> <span class="text-muted">新月_f578</span> <div>大家好,我是坚持做图卡,不断精进的新月,近期阅读书籍《心。》,持续输出图卡……截止目前已经读完本书,输出卡片9张~借助9张卡片,回顾本书的整体内容,结构上可以分为:始于心-修心-终于心。首先明确:我们为什么要这么做?其次懂得如何去做,落实到具体的方式方法上,就是修心的过程。最后是知道目标在哪,不断自我提升,向目标靠进,使修心贯穿始终。</div> </li> <li><a href="/article/1835415428059459584.htm" title="李克富 | 咨询师推荐阅读书目" target="_blank">李克富 | 咨询师推荐阅读书目</a> <span class="text-muted">李克富</span> <div>最重要的书籍不是别人的推荐,而是自己学过的教材,不论当初使用的是哪个版本,它都是我们专业的底层代码,具有不可替代性。前不久,中国心理咨询师筹委会的一位老师邀请我罗列一个推荐书目清单作为咨询师工具包的内容,并要求“说明一下简单的分类或者作三言两语的说明”。斟酌后,我觉得自己推荐的书目大体可以分为普及类书籍、心理学书籍和心理咨询与治疗专业书籍,第三类又分为适合于咨询师新手的和有经验咨询师的。经过严格筛</div> </li> <li><a href="/article/1835412764722556928.htm" title="Android实现监听事件的方法" target="_blank">Android实现监听事件的方法</a> <span class="text-muted">Amy木婉清</span> <div>1.通过内部类实现2.通过匿名内部类实现3.通过事件源所在类实现4.通过外部类实现5.布局文件中onclick属性(针对点击事件)1.通过内部类实现代码:privateButtonmBtnEvent;//oncreate中mBtnEvent.setOnClickListener(newOnClick());//内部类实现监听classOnClickimplementsView.OnClickLis</div> </li> <li><a href="/article/1835409210855223296.htm" title="2021-12-11" target="_blank">2021-12-11</a> <span class="text-muted">人生导演</span> <div>今天读到佛学书籍的一段话:初学者很难直接体验到无我,但可以经常提醒自己:一切事物都是无我的。不断强化这个观念,也会相当有帮助。比如生病了我们一般会说:“我不舒服!我很痛!我很惨!”这时候如果我们提醒自己:没有我,只是这个肉体的某些部分、某些功能出了问题,不舒服、疼痛也只是一时的感受,而感受随时在变化。仅仅是知道没有一个实存的我在生病、在受苦。然后把“一切事物都是无我的”这句话,记到笔记上,并且朗读</div> </li> <li><a href="/article/1835406164452536320.htm" title="《蛤蟆先生去看心理医生》读后感" target="_blank">《蛤蟆先生去看心理医生》读后感</a> <span class="text-muted">我是八零后</span> <div>《蛤蟆先生去看心理医生》,听书名像是童书,其实是一本专业心理学书籍,一本可以给成年人带来的心灵疗愈的书。走进书本,我们一起跟着蛤蟆先生跟随心理医生的咨询,探寻情绪的根源,进行自我突破,完成个人状态的转变,实现自我的疗愈。一、刷重点1个前提。改变的唯一前提是认识你自己,在这个世界上能帮你的人,只有你自己。2个思维。人人在理性与感性之间徘徊。真正厉害的人,是理性与感性并存。3个状态。每个人都有儿童、父</div> </li> <li><a href="/article/1835400203130204160.htm" title="高级UI<第二十四篇>:Android中用到的矩阵常识" target="_blank">高级UI<第二十四篇>:Android中用到的矩阵常识</a> <span class="text-muted">NoBugException</span> <div>(1)定义在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合。由m×n个数aij排成的m行n列的数表称为m行n列的矩阵,简称m×n矩阵。记作:图片.png这m×n个数称为矩阵A的元素,简称为元,数aij位于矩阵A的第i行第j列,称为矩阵A的(i,j)元,以数aij为(i,j)元的矩阵可记为(aij)或(aij)m×n,m×n矩阵A也记作Amn。元素是实数的矩阵称为实矩阵,元素是复</div> </li> <li><a href="/article/1835398694636187648.htm" title="RK3229_Android9.0_Box 4G模块EC200A调试" target="_blank">RK3229_Android9.0_Box 4G模块EC200A调试</a> <span class="text-muted">suifen_</span> <a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a> <div>0、kernel修改这部分完全可以参考Linux的移植:RK3588EC200A-CN【4G模块】调试_rkec200a-cn-CSDN博客1、修改device/rockchip/rk322xdiff--gita/device.mkb/device.mkindexec6bfaa..e7c32d1100755---a/device.mk+++b/device.mk@@-105,6+105,8@@en</div> </li> <li><a href="/article/1835374864970641408.htm" title="kt文件和java文件_Java与Kotlin之间怎样进行互操作" target="_blank">kt文件和java文件_Java与Kotlin之间怎样进行互操作</a> <span class="text-muted">铭空间</span> <a class="tag" taget="_blank" href="/search/kt%E6%96%87%E4%BB%B6%E5%92%8Cjava%E6%96%87%E4%BB%B6/1.htm">kt文件和java文件</a> <div>Java与Kotlin之间怎样进行互操作发布时间:2021-02-0210:50:43来源:亿速云阅读:98作者:小新这篇文章主要介绍了Java与Kotlin之间怎样进行互操作,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言目前kotlin是谷歌首推的开发Android的语言,但由于历史原因,我们绝大部分项目依旧还是以Java为主</div> </li> <li><a href="/article/1835369316342657024.htm" title="Android shell 常用 debug 命令" target="_blank">Android shell 常用 debug 命令</a> <span class="text-muted">晨春计</span> <a class="tag" taget="_blank" href="/search/Audio/1.htm">Audio</a><a class="tag" taget="_blank" href="/search/debug/1.htm">debug</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>目录1、查看版本2、am命令3、pm命令4、dumpsys命令5、sed命令6、log定位查看APK进程号7、log定位使用场景1、查看版本1.1、Android串口终端执行getpropro.build.version.release#获取Android版本uname-a#查看linux内核版本信息uname-r#单独查看内核版本1.2、linux服务器执行lsb_release-a#查看Lin</div> </li> <li><a href="/article/1835368531940700160.htm" title="家庭教育,先家庭后教育:家庭是硬件,教育是软件" target="_blank">家庭教育,先家庭后教育:家庭是硬件,教育是软件</a> <span class="text-muted">唯唯育家</span> <div>很多家长为孩子付出很多,也学习很多家庭教育课程,看很多家庭教育书籍,为什么还是教育孩子很困难?因为主次颠倒,没有抓住家庭教育的主干!家庭教育,很多家长只行使“教育”功能,忽视了“家庭”功能!家长总想着怎么教孩子,怎么教育孩子!如果单靠教育,就能把孩子教好,学校老师在教育方面比家长在行,孩子应该在学校就被教好了,哪还需要家庭教育?为什么只有学校教育不够,还需要家庭教育?家庭教育的主要功能不在“教育”</div> </li> <li><a href="/article/1835367119223615488.htm" title="这样共读一本书" target="_blank">这样共读一本书</a> <span class="text-muted">eggplant</span> <div>2021年10月6日星期三本期学校阳光管理轮训共读刘铁芳教授的《以教学打开生命——个体成人的教学哲学阐释》,这是继共读刘教授《什么是好的教育》之后的第二本书籍,这两本书籍都是有关教育的哲学书籍,应该说,《以教学打开生命——个体成人的教学哲学阐释》是《什么是好的教育》的延伸、丰富与升华,理论性更强,哲学意味更浓,对于一线教师来说,接触哲学类的书籍较少,在阅读上有些内容的理解有难度,但是,有难度才更值</div> </li> <li><a href="/article/1835361639189278720.htm" title="90天读书分享76——家为什么会伤人" target="_blank">90天读书分享76——家为什么会伤人</a> <span class="text-muted">韧2018</span> <div>图片发自App今天分享的书籍是《家为什么会伤人》,作者是武志红,著名的心理学家,畅销书作家。本书是讲述中国家庭的文化的一些缺陷造成的种种不良后果,我将分享书中家庭和婚姻中的误区和正确的做法。由于我们在家庭和婚姻中没有采取正确做法,走入了误区,会给婚姻和家庭带来巨大的伤害,而且这种伤害会可能会对子女造成影响,会不断延续下去,造成悲剧的反复发生。现在来看看婚姻中存在误区:第一个误区是不以夫妻关系为家庭</div> </li> <li><a href="/article/1835360874869649409.htm" title="每日一书|《亲密关系》(Day5)" target="_blank">每日一书|《亲密关系》(Day5)</a> <span class="text-muted">采臣在等我</span> <div>采臣在等我-广州【书籍名称】《亲密关系》图片发自App【阅读目标】1.了解“亲密关系”的几个阶段及特点2.认识和理解有效沟通的技巧和原则3.思考自己在亲密关系建立中的角色和心理,以及面临的挑战【阅读感受】这本书是克里斯多福研究亲密关系的智慧结晶,阅读的整体感受是:书中文字亲切,有种娓娓道来的感觉。书中的逻辑感较强,也有详细的小结和应用建议,适合应用和反思。1.亲密关系的4个阶段和特点阶段一:月晕A</div> </li> <li><a href="/article/1835352706760404992.htm" title="谈哲学" target="_blank">谈哲学</a> <span class="text-muted">本仙老四</span> <div>我是谁?从哪里来?要到哪里去?最近看了些西方哲学类书籍,忽然就有了这些哲学式的思考。世界真的如我们所看到的这样吗?还是只是我们觉得它是这样?或者它根本就不存在。哲学书是引发人思考的好书籍,即使你觉得读起来枯燥无味也要坚持阅读,之后你会发现受益无穷。大家都说哲学起源于西方,文艺复兴时期的哲学对欧洲的发展起到了重要作用。其实早在中国古代就有一批哲人出现,老子、庄子、孟子、孔子……他们的思想各有独到之处</div> </li> <li><a href="/article/1835341893614006272.htm" title="新媒体运营小白,有哪些书籍可以推荐?" target="_blank">新媒体运营小白,有哪些书籍可以推荐?</a> <span class="text-muted">y耳朵</span> <div>为了转行运营,我曾花了3个月的时间,看了不下百本书,可以说市面上大部分跟运营有关的书籍,我都看过了,因此关于书的推荐也有一些自己的小见解。看书不一定要多,但一定要****精,我根据豆瓣评分、推荐热度和自己的转行经历,挑出了13本值得运营小白看的书,收藏好这份书单,不需要你浪费时间去找书了。先看下统计好的书单:整理不易,看完记得点个赞哦!感谢你的支持。入门篇:1.《运营之光》(豆瓣评分:8.0)推荐</div> </li> <li><a href="/article/1835332133149831168.htm" title="2024年最全Flutter如何和Native通信-Android视角,Electron开发Android界面" target="_blank">2024年最全Flutter如何和Native通信-Android视角,Electron开发Android界面</a> <span class="text-muted">2401_84544531</span> <a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a> <div>总结【Android详细知识点思维脑图(技能树)】其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。虽然Android没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明Android中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪</div> </li> <li><a href="/article/1835319360852422656.htm" title="跟剽悍一只猫学习收获之成为领域专家" target="_blank">跟剽悍一只猫学习收获之成为领域专家</a> <span class="text-muted">财务自由的社群运营人苏宝</span> <div>001找到这个领域内权威的书籍。002按照书的脉络(章节目录)记录书中的重要内容(对自己认知系统造成冲击的,以前没有学过的,觉得有用的,暂时还不太理解的)记录下来。003读完第一遍以后,接着读第二遍。这一遍记录书里对你有用的方法论,并尝试依据这些方法论实战。004再读一遍,这一遍记录尝试梳理整个书的认知框架和内在逻辑。005之后,可以多朗读几遍全书。你会发现,你对这些知识的理解会越来越全面,越有深</div> </li> <li><a href="/article/1835300629283696640.htm" title="钟汉良日记:自媒体写作要向古代拜师学艺的人取经" target="_blank">钟汉良日记:自媒体写作要向古代拜师学艺的人取经</a> <span class="text-muted">钟汉良日记</span> <div>2022年9月8日周四晴朗的天前几天晚上,和实操班的学员谈了很多为什么要写日记的原因。前后两个多小时,推荐了好几本书,隔了一天再回忆竟然不能把所有的书籍都列出来。是自己年龄大了,记忆力衰退了?我觉得不是。而是谈话这种氛围,比起文字写作更容易激发我们的潜能。一些灵光乍现的想法,会在那种特别融洽的时候突然涌现出来。有的想法和总结,你平时就是绞尽脑汁也想不出来,但就是在那种你侃侃而谈的时候,思想会像流水</div> </li> <li><a href="/article/1835292753668435968.htm" title="心情不好时,去做这四件事" target="_blank">心情不好时,去做这四件事</a> <span class="text-muted">绿茵下</span> <div>01读书当一个人的智慧不够时,最容易胡思乱想,将自己困住思维的牢笼里。想要开悟,首先要与智者对话,与高人同行,那么与智者对话最经济实惠的方式,莫过于读书了。于时光的静美中,手捧一本高质量的书籍,在书中窥见先贤们的所思所想,仿佛进入了一个平静的世界,让人思绪越来越清晰明了。或许读书不能够马上帮你解决眼前的难题,可日复一日地坚持下去,总能让你内心更加笃定,心灵更加沉静,生出智慧之花。在书中人物不同的经</div> </li> <li><a href="/article/1835288964056051712.htm" title="分享一个基于python的电子书数据采集与可视化分析 hadoop电子书数据分析与推荐系统 spark大数据毕设项目(源码、调试、LW、开题、PPT)" target="_blank">分享一个基于python的电子书数据采集与可视化分析 hadoop电子书数据分析与推荐系统 spark大数据毕设项目(源码、调试、LW、开题、PPT)</a> <span class="text-muted">计算机源码社</span> <a class="tag" taget="_blank" href="/search/Python%E9%A1%B9%E7%9B%AE/1.htm">Python项目</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%AF%95%E4%B8%9A%E8%AE%BE%E8%AE%A1%E9%80%89%E9%A2%98/1.htm">计算机毕业设计选题</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%AF%95%E4%B8%9A%E8%AE%BE%E8%AE%A1%E6%BA%90%E7%A0%81/1.htm">计算机毕业设计源码</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a><a class="tag" taget="_blank" href="/search/spark%E6%AF%95%E8%AE%BE/1.htm">spark毕设</a> <div>作者:计算机源码社个人简介:本人八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流!学习资料、程序开发、技术解答、文档报告如需要源码,可以扫取文章下方二维码联系咨询Java项目微信小程序项目Android项目Python项目PHP项目ASP.NET项目Node.js项目选题推荐项目实战|p</div> </li> <li><a href="/article/1835279386866184192.htm" title="数据结构 1" target="_blank">数据结构 1</a> <span class="text-muted">五花肉村长</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/visualstudio/1.htm">visualstudio</a> <div>1.什么是数据结构数据结构(DataStructure)是计算机存储和组织数据的方式,是指相互之间存在的一种或多种特定关系的数据元的集合。2.什么是算法算法(Algorithm)就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。3.数据结构和算法的书籍资料学习完数据结构知识,可以去看《剑指offer》和《</div> </li> <li><a href="/article/1835271269306691584.htm" title="小故事:森林书生" target="_blank">小故事:森林书生</a> <span class="text-muted">zero川</span> <div>1文生是一个生活在森林里的书生,他在那里有一个小树屋,屋子里放满了各种书籍。文生住所这里离乡镇闹市有30多公里远,所以平时很少会去市面上跟别人产生任何交集。文生大多数生活时间都是以书为伴、以野外万物为伴…文生靠采摘蘑菇、野菜野果维持生计,由于天性善良,所以不曾捕猎。森林里的动物也因此跟文生特别交好,经常会来文生住所调皮捣蛋。</div> </li> <li><a href="/article/55.htm" title="多线程编程之卫生间" target="_blank">多线程编程之卫生间</a> <span class="text-muted">周凡杨</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%B9%B6%E5%8F%91/1.htm">并发</a><a class="tag" taget="_blank" href="/search/%E5%8D%AB%E7%94%9F%E9%97%B4/1.htm">卫生间</a><a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B/1.htm">线程</a><a class="tag" taget="_blank" href="/search/%E5%8E%95%E6%89%80/1.htm">厕所</a> <div>如大家所知,火车上车厢的卫生间很小,每次只能容纳一个人,一个车厢只有一个卫生间,这个卫生间会被多个人同时使用,在实际使用时,当一个人进入卫生间时则会把卫生间锁上,等出来时打开门,下一个人进去把门锁上,如果有一个人在卫生间内部则别人的人发现门是锁的则只能在外面等待。问题分析:首先问题中有两个实体,一个是人,一个是厕所,所以设计程序时就可以设计两个类。人是多数的,厕所只有一个(暂且模拟的是一个车厢)。</div> </li> <li><a href="/article/182.htm" title="How to Install GUI to Centos Minimal" target="_blank">How to Install GUI to Centos Minimal</a> <span class="text-muted">sunjing</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/Install/1.htm">Install</a><a class="tag" taget="_blank" href="/search/Desktop/1.htm">Desktop</a><a class="tag" taget="_blank" href="/search/GUI/1.htm">GUI</a> <div>http://www.namhuy.net/475/how-to-install-gui-to-centos-minimal.html   I have centos 6.3 minimal running as web server. I’m looking to install gui to my server to vnc to my server. You can insta</div> </li> <li><a href="/article/309.htm" title="Shell 函数" target="_blank">Shell 函数</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a><a class="tag" taget="_blank" href="/search/%E5%87%BD%E6%95%B0/1.htm">函数</a> <div>Shell 函数 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。 shell中函数的定义格式如下: [function] funname [()]{ action; [return int;] } 说明: 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。 2、参数返回</div> </li> <li><a href="/article/436.htm" title="Linux服务器新手操作之一" target="_blank">Linux服务器新手操作之一</a> <span class="text-muted">周凡杨</span> <a class="tag" taget="_blank" href="/search/Linux+%E7%AE%80%E5%8D%95+%E6%93%8D%E4%BD%9C/1.htm">Linux 简单 操作</a> <div>1.whoami      当一个用户登录Linux系统之后,也许他想知道自己是发哪个用户登录的。      此时可以使用whoami命令。      [ecuser@HA5-DZ05 ~]$ whoami       e</div> </li> <li><a href="/article/563.htm" title="浅谈Socket通信(一)" target="_blank">浅谈Socket通信(一)</a> <span class="text-muted">朱辉辉33</span> <a class="tag" taget="_blank" href="/search/socket/1.htm">socket</a> <div>在java中ServerSocket用于服务器端,用来监听端口。通过服务器监听,客户端发送请求,双方建立链接后才能通信。当服务器和客户端建立链接后,两边都会产生一个Socket实例,我们可以通过操作Socket来建立通信。    首先我建立一个ServerSocket对象。当然要导入java.net.ServerSocket包    ServerSock</div> </li> <li><a href="/article/690.htm" title="关于框架的简单认识" target="_blank">关于框架的简单认识</a> <span class="text-muted">西蜀石兰</span> <a class="tag" taget="_blank" href="/search/%E6%A1%86%E6%9E%B6/1.htm">框架</a> <div>入职两个月多,依然是一个不会写代码的小白,每天的工作就是看代码,写wiki。 前端接触CSS、HTML、JS等语言,一直在用的CS模型,自然免不了数据库的链接及使用,真心涉及框架,项目中用到的BootStrap算一个吧,哦,JQuery只能算半个框架吧,我更觉得它是另外一种语言。 后台一直是纯Java代码,涉及的框架是Quzrtz和log4j。 都说学前端的要知道三大框架,目前node.</div> </li> <li><a href="/article/817.htm" title="You have an error in your SQL syntax; check the manual that corresponds to your" target="_blank">You have an error in your SQL syntax; check the manual that corresponds to your</a> <span class="text-muted">林鹤霄</span> <div>You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'option,changed_ids  ) values('0ac91f167f754c8cbac00e9e3dc372</div> </li> <li><a href="/article/944.htm" title="MySQL5.6的my.ini配置" target="_blank">MySQL5.6的my.ini配置</a> <span class="text-muted">aigo</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>注意:以下配置的服务器硬件是:8核16G内存    [client]   port=3306   [mysql]   default-character-set=utf8     [mysqld]   port=3306   basedir=D:/mysql-5.6.21-win</div> </li> <li><a href="/article/1071.htm" title="mysql 全文模糊查找 便捷解决方案" target="_blank">mysql 全文模糊查找 便捷解决方案</a> <span class="text-muted">alxw4616</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>mysql 全文模糊查找 便捷解决方案 2013/6/14 by 半仙 alxw4616@Msn.com 目的: 项目需求实现模糊查找. 原则: 查询不能超过 1秒. 问题: 目标表中有超过1千万条记录. 使用like '%str%' 进行模糊查询无法达到性能需求. 解决方案: 使用mysql全文索引. 1.全文索引 : MySQL支持全文索引和搜索功能。MySQL中的全文索</div> </li> <li><a href="/article/1198.htm" title="自定义数据结构 链表(单项 ,双向,环形)" target="_blank">自定义数据结构 链表(单项 ,双向,环形)</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/%E5%8D%95%E9%A1%B9%E9%93%BE%E8%A1%A8/1.htm">单项链表</a><a class="tag" taget="_blank" href="/search/%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8/1.htm">双向链表</a> <div>     链表与动态数组的实现方式差不多,    数组适合快速删除某个元素    链表则可以快速的保存数组并且可以是不连续的       单项链表;数据从第一个指向最后一个   实现代码:        //定义动态链表 clas</div> </li> <li><a href="/article/1325.htm" title="threadLocal实例" target="_blank">threadLocal实例</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/java%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">java多线程</a><a class="tag" taget="_blank" href="/search/threadLocal/1.htm">threadLocal</a> <div>实例1: package com.bijian.thread; public class MyThread extends Thread { private static ThreadLocal tl = new ThreadLocal() { protected synchronized Object initialValue() { return new Inte</div> </li> <li><a href="/article/1452.htm" title="activemq安全设置—设置admin的用户名和密码" target="_blank">activemq安全设置—设置admin的用户名和密码</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/activemq/1.htm">activemq</a> <div>        ActiveMQ使用的是jetty服务器, 打开conf/jetty.xml文件,找到 <bean id="adminSecurityConstraint" class="org.eclipse.jetty.util.security.Constraint"> <p</div> </li> <li><a href="/article/1579.htm" title="【Java范型一】Java范型详解之范型集合和自定义范型类" target="_blank">【Java范型一】Java范型详解之范型集合和自定义范型类</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>本文详细介绍Java的范型,写一篇关于范型的博客原因有两个,前几天要写个范型方法(返回值根据传入的类型而定),竟然想了半天,最后还是从网上找了个范型方法的写法;再者,前一段时间在看Gson, Gson这个JSON包的精华就在于对范型的优雅简单的处理,看它的源代码就比较迷糊,只其然不知其所以然。所以,还是花点时间系统的整理总结下范型吧。   范型内容 范型集合类 范型类 </div> </li> <li><a href="/article/1706.htm" title="【HBase十二】HFile存储的是一个列族的数据" target="_blank">【HBase十二】HFile存储的是一个列族的数据</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/hbase/1.htm">hbase</a> <div>在HBase中,每个HFile存储的是一个表中一个列族的数据,也就是说,当一个表中有多个列簇时,针对每个列簇插入数据,最后产生的数据是多个HFile,每个对应一个列族,通过如下操作验证   1. 建立一个有两个列族的表   create 'members','colfam1','colfam2'   2. 在members表中的colfam1中插入50*5</div> </li> <li><a href="/article/1833.htm" title="Nginx 官方一个配置实例" target="_blank">Nginx 官方一个配置实例</a> <span class="text-muted">ronin47</span> <a class="tag" taget="_blank" href="/search/nginx+%E9%85%8D%E7%BD%AE%E5%AE%9E%E4%BE%8B/1.htm">nginx 配置实例</a> <div>user www www; worker_processes 5; error_log logs/error.log; pid logs/nginx.pid; worker_rlimit_nofile 8192; events { worker_connections 4096;} http { include conf/mim</div> </li> <li><a href="/article/1960.htm" title="java-15.输入一颗二元查找树,将该树转换为它的镜像, 即在转换后的二元查找树中,左子树的结点都大于右子树的结点。 用递归和循环" target="_blank">java-15.输入一颗二元查找树,将该树转换为它的镜像, 即在转换后的二元查找树中,左子树的结点都大于右子树的结点。 用递归和循环</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> //use recursion public static void mirrorHelp1(Node node){ if(node==null)return; swapChild(node); mirrorHelp1(node.getLeft()); mirrorHelp1(node.getRight()); } //use no recursion bu</div> </li> <li><a href="/article/2087.htm" title="返回null还是empty" target="_blank">返回null还是empty</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/apache/1.htm">apache</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a> <div>第一个问题,函数是应当返回null还是长度为0的数组(或集合)? 第二个问题,函数输入参数不当时,是异常还是返回null? 先看第一个问题 有两个约定我觉得应当遵守: 1.返回零长度的数组或集合而不是null(详见《Effective Java》) 理由就是,如果返回empty,就可以少了很多not-null判断: List<Person> list</div> </li> <li><a href="/article/2214.htm" title="[科技与项目]工作流厂商的战略机遇期" target="_blank">[科技与项目]工作流厂商的战略机遇期</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C%E6%B5%81/1.htm">工作流</a> <div>       在新的战略平衡形成之前,这里有一个短暂的战略机遇期,只有大概最短6年,最长14年的时间,这段时间就好像我们森林里面的小动物,在秋天中,必须抓紧一切时间存储坚果一样,否则无法熬过漫长的冬季。。。。         在微软,甲骨文,谷歌,IBM,SONY</div> </li> <li><a href="/article/2341.htm" title="过度设计-举例" target="_blank">过度设计-举例</a> <span class="text-muted">cuityang</span> <a class="tag" taget="_blank" href="/search/%E8%BF%87%E5%BA%A6%E8%AE%BE%E8%AE%A1/1.htm">过度设计</a> <div>过度设计,需要更多设计时间和测试成本,如无必要,还是尽量简洁一些好。 未来的事情,比如 访问量,比如数据库的容量,比如是否需要改成分布式  都是无法预料的 再举一个例子,对闰年的判断逻辑:   1、 if($Year%4==0) return True; else return Fasle;   2、if (   ($Year%4==0  &am</div> </li> <li><a href="/article/2468.htm" title="java进阶,《Java性能优化权威指南》试读" target="_blank">java进阶,《Java性能优化权威指南》试读</a> <span class="text-muted">darkblue086</span> <a class="tag" taget="_blank" href="/search/java%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/1.htm">java性能优化</a> <div>记得当年随意读了微软出版社的.NET 2.0应用程序调试,才发现调试器如此强大,应用程序开发调试其实真的简单了很多,不仅仅是因为里面介绍了很多调试器工具的使用,更是因为里面寻找问题并重现问题的思想让我震撼,时隔多年,Java已经如日中天,成为许多大型企业应用的首选,而今天,这本《Java性能优化权威指南》让我再次找到了这种感觉,从不经意的开发过程让我刮目相看,原来性能调优不是简单地看看热点在哪里,</div> </li> <li><a href="/article/2595.htm" title="网络学习笔记初识OSI七层模型与TCP协议" target="_blank">网络学习笔记初识OSI七层模型与TCP协议</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">学习笔记</a> <div>  协议:在计算机网络中通信各方面所达成的、共同遵守和执行的一系列约定   计算机网络的体系结构:计算机网络的层次结构和各层协议的集合。   两类服务:   面向连接的服务通信双方在通信之前先建立某种状态,并在通信过程中维持这种状态的变化,同时为服务对象预先分配一定的资源。这种服务叫做面向连接的服务。   面向无连接的服务通信双方在通信前后不建立和维持状态,不为服务对象</div> </li> <li><a href="/article/2722.htm" title="mac中用命令行运行mysql" target="_blank">mac中用命令行运行mysql</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/mac/1.htm">mac</a> <div>参考这篇博客:http://www.cnblogs.com/macro-cheng/archive/2011/10/25/mysql-001.html  感觉workbench不好用(有点先入为主了)。 1,安装mysql 在mysql的官方网站下载 mysql 5.5.23 http://www.mysql.com/downloads/mysql/,根据我的机器的配置情况选择了64</div> </li> <li><a href="/article/2849.htm" title="MongDB查询(1)——基本查询[五]" target="_blank">MongDB查询(1)——基本查询[五]</a> <span class="text-muted">eksliang</span> <a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/mongodb+%E6%9F%A5%E8%AF%A2/1.htm">mongodb 查询</a><a class="tag" taget="_blank" href="/search/mongodb+find/1.htm">mongodb find</a> <div>MongDB查询 转载请出自出处:http://eksliang.iteye.com/blog/2174452 一、find简介 MongoDB中使用find来进行查询。 API:如下 function ( query , fields , limit , skip, batchSize, options ){.....}  参数含义: query:查询参数 fie</div> </li> <li><a href="/article/2976.htm" title="base64,加密解密 经融加密,对接" target="_blank">base64,加密解密 经融加密,对接</a> <span class="text-muted">y806839048</span> <a class="tag" taget="_blank" href="/search/%E7%BB%8F%E8%9E%8D%E5%8A%A0%E5%AF%86/1.htm">经融加密</a><a class="tag" taget="_blank" href="/search/%E5%AF%B9%E6%8E%A5/1.htm">对接</a> <div>String data0 = new String(Base64.encode(bo.getPaymentResult().getBytes(("GBK")))); String data1 = new String(Base64.decode(data0.toCharArray()),"GBK"); // 注意编码格式,注意用于加密,解密的要是同</div> </li> <li><a href="/article/3103.htm" title="JavaWeb之JSP概述" target="_blank">JavaWeb之JSP概述</a> <span class="text-muted">ihuning</span> <a class="tag" taget="_blank" href="/search/javaweb/1.htm">javaweb</a> <div>  什么是JSP?为什么使用JSP? JSP表示Java Server Page,即嵌有Java代码的HTML页面。使用JSP是因为在HTML中嵌入Java代码比在Java代码中拼接字符串更容易、更方便和更高效。   JSP起源    在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。  如果使用Servl</div> </li> <li><a href="/article/3230.htm" title="apple watch 指南" target="_blank">apple watch 指南</a> <span class="text-muted">啸笑天</span> <a class="tag" taget="_blank" href="/search/apple/1.htm">apple</a> <div>1. 文档 WatchKit Programming Guide(中译在线版 By @CocoaChina) 译文 译者 原文 概览 - 开始为 Apple Watch 进行开发 @星夜暮晨 Overview - Developing for Apple Watch 概览 - 配置 Xcode 项目 - Overview - Configuring Yo</div> </li> <li><a href="/article/3357.htm" title="java经典的基础题目" target="_blank">java经典的基础题目</a> <span class="text-muted">macroli</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a> <div>1.列举出 10个JAVA语言的优势 a:免费,开源,跨平台(平台独立性),简单易用,功能完善,面向对象,健壮性,多线程,结构中立,企业应用的成熟平台, 无线应用 2.列举出JAVA中10个面向对象编程的术语 a:包,类,接口,对象,属性,方法,构造器,继承,封装,多态,抽象,范型 3.列举出JAVA中6个比较常用的包 Java.lang;java.util;java.io;java.sql;ja</div> </li> <li><a href="/article/3484.htm" title="你所不知道神奇的js replace正则表达式" target="_blank">你所不知道神奇的js replace正则表达式</a> <span class="text-muted">qiaolevip</span> <a class="tag" taget="_blank" href="/search/%E6%AF%8F%E5%A4%A9%E8%BF%9B%E6%AD%A5%E4%B8%80%E7%82%B9%E7%82%B9/1.htm">每天进步一点点</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E6%B0%B8%E6%97%A0%E6%AD%A2%E5%A2%83/1.htm">学习永无止境</a><a class="tag" taget="_blank" href="/search/%E7%BA%B5%E8%A7%82%E5%8D%83%E8%B1%A1/1.htm">纵观千象</a><a class="tag" taget="_blank" href="/search/regex/1.htm">regex</a> <div>var v = 'C9CFBAA3CAD0'; console.log(v); var arr = v.split(''); for (var i = 0; i < arr.length; i ++) { if (i % 2 == 0) arr[i] = '%' + arr[i]; } console.log(arr.join('')); console.log(v.r</div> </li> <li><a href="/article/3611.htm" title="[一起学Hive]之十五-分析Hive表和分区的统计信息(Statistics)" target="_blank">[一起学Hive]之十五-分析Hive表和分区的统计信息(Statistics)</a> <span class="text-muted">superlxw1234</span> <a class="tag" taget="_blank" href="/search/hive/1.htm">hive</a><a class="tag" taget="_blank" href="/search/hive%E5%88%86%E6%9E%90%E8%A1%A8/1.htm">hive分析表</a><a class="tag" taget="_blank" href="/search/hive%E7%BB%9F%E8%AE%A1%E4%BF%A1%E6%81%AF/1.htm">hive统计信息</a><a class="tag" taget="_blank" href="/search/hive+Statistics/1.htm">hive Statistics</a> <div>关键字:Hive统计信息、分析Hive表、Hive Statistics   类似于Oracle的分析表,Hive中也提供了分析表和分区的功能,通过自动和手动分析Hive表,将Hive表的一些统计信息存储到元数据中。   表和分区的统计信息主要包括:行数、文件数、原始数据大小、所占存储大小、最后一次操作时间等;   14.1 新表的统计信息 对于一个新创建</div> </li> <li><a href="/article/3738.htm" title="Spring Boot 1.2.5 发布" target="_blank">Spring Boot 1.2.5 发布</a> <span class="text-muted">wiselyman</span> <a class="tag" taget="_blank" href="/search/spring+boot/1.htm">spring boot</a> <div>    Spring Boot 1.2.5已在7月2日发布,现在可以从spring的maven库和maven中心库下载。   这个版本是一个维护的发布版,主要是一些修复以及将Spring的依赖提升至4.1.7(包含重要的安全修复)。   官方建议所有的Spring Boot用户升级这个版本。   项目首页 | 源</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>