使用手机的 GPS 功能

关键技能  &  概念
●  使用 Android 的定位服务 APIs
●  从 GPS 硬件获得坐标数据
●  改变活动的外观并且和 RelativeLayout 接触
●  使用一个 MapView 来绘制你的当前位置
●  使用谷歌地图来找到你的当前位置
在本章中,你将学习关于 Android 定位的 API。本章的作用是非常重要的,如果
你想要让 Android 和 GPS 硬件一起工作的话。你将使用位置基础的 API 来收集你
当前的位置并把它在屏幕上显示出来。到本章的结尾,你将在手机上使用谷歌地
图来显示你的当前位置。
你还会学习到一些关于活动的更深,创新的技巧。资源,如 RelativeLayouts
和小按钮将允许你创建更友好,可视的活动。第一节,你将学习使用设备的 GPS
硬件来获得当前的位置。但是,在跳到那个部分之前,你需要先创建一个项目。
在 Eclipse 中创建一个项目并命名为 AndroidLBS。
使用 Android 位置基础 API
Android  SDK 包含了一个 API,它被定制为帮助接口活动与设备上任何的 GPS 硬
件。这章假定你的设备包含 GPS 硬件。
警告
Android 平台的手机不要求包含一个照相机,也不要求包含 GPS 硬件,虽然很多
的型号可能包含照相机和 GPS 硬件。Android 包含了 Android 位置基础 API 预期
GPS 硬件会被包含在很多手机上。
因为你工作在一个软件模拟器中,并且不是一个真的设备,GPS 硬件没有被模拟 。
在本例中,Android 在 adb 服务器中提供了一个文件模拟 GPS 硬件。这个文件放
置在 data/misc/location/<provider> , <provider> 代表 位置 信 息提 供 者。
Android 提供的<provider>是 data/misc/location/gps
提示
你可以有多重的提供者来模拟不同的方案。因此,你可以创建一个提供者为 test
或者 gps1;无论你愿意用哪个。在具体的 provider 的文件夹内可以用任何数量
的文件保留你想要 Android 使用的例子。当你使用 Android 模拟器,你可以使用
下面类型的文件来储存/找回 GPS 文体的坐标。每一个文件类型有个不同的格式
来提供信息给 Android 位置基础 API
●  kml
●  nmea
●  track
我们来看看每一个文件都做些什么并且互相之间有什么不同。
创建一个 kml 文件114
一个.kml 文件 Keyhole  Markup 语言文件。这些文件通常被开发用于并且可以被
Google  Earth(一款 Google 软件)。Adnroid 位置基础 API 可以分析一个.kml
文件来模拟一个 GPS。
注意
假如你没有 Google  Earth。可以从 Google 免费下载。如果你想要开发更多的
Android 位置基础 API 活动,安装这个软件是值得的。
要从 Google  Earth 创建一个.kml 文件,打开 Google  Earth 并且导航到一个位
置。选择文件|另存为并选择 KML。在本例中,它为你所导航到的地点产生一
个.kml 文件。下面的.kml 代码就是来自这个文件。仔细看看<coordinates>标签 ,
那就是 Android 位置基础 API 会读取的。

<?xml  version="1.0"  encoding="UTF-8"?>
 <kml  xmlns="http://earth.google.com/kml/2.2">
 <Document>
 <name>Tampa,  FL.kml</name>
 <Styleid="default+icon=http://maps.google.com/mapfiles/kml/pal3/icon52.png">
 <IconStyle>
 <scale>1.1</scale>
 <Icon>
 <href>http://maps.google.com/mapfiles/kml/pal3/icon52.png</href>
 </Icon>
 </IconStyle>
 <LabelStyle>
 <scale>1.1</scale>
 </LabelStyle>
 </Style>
 <Styleid="default+icon=http://maps.google.com/mapfiles/kml/pal3/icon60.png">
 <IconStyle>
 <Icon>
 <href>http://maps.google.com/mapfiles/kml/pal3/icon60.png</href>
 </Icon>
 </IconStyle>
 </Style>
 <StyleMapid="default+nicon=http://maps.google.com/mapfiles/kml/pal3/
 icon60.png+hicon=http://maps.google.com/mapfiles/kml/pal3/icon52.png">
 <Pair>
 <key>normal</key>
 <styleUrl>#default+icon=http://maps.google.com/mapfiles/kml/pal3/
 icon60.png</styleUrl>
 </Pair>
 <Pair>
 <key>highlight</key>115


你可以用 Google  Earth 来创建自己的.kml 文件来模拟不同的位置。当你想要制
作一个相应用户不同位置的活动时,这个非常有用。创建.kml 文件如此简单使
得模拟 GPS 硬件非常的灵活。
什么是轨迹文件
什么是轨迹文件  第九章(2)  (2)  (2)  (2)
Android 提供的在 gps 文件夹里的文件是一个.nmea 文件(国家海事电子协会文
件)。一个.nmea 文件可以从任何通用的 GPS 产品中输出。这些文件是常用格式
并且可以包含多重坐标和海拔,来表现行程和轨迹。下面的部分讨论并且在
Windows 和 Linux 下各自打开这个文件。
在 Windows 中得到 nmea 文件
Android 提供的 nmea 文件展示了一个贯穿旧金山的短的线路。让我们看看 nmea
文件的内部。使用 adb 工具把文件从服务器中 pull 到你的桌面:
adb  pull<远程文件><本地文件>

<styleUrl>#default+icon=http://maps.google.com/mapfiles/kml/pal3/
 icon52.png</styleUrl>
 </Pair>
 </StyleMap>
 <Placemark>
 <name>Tampa,  FL</name>
 <open>1</open>
 <address>Tampa,  FL</address>
 <LookAt>
 <longitude>-82.451142</longitude>
 <latitude>27.98146</latitude>
 <altitude>0</altitude>
 <range>38427.828125</range>
 <tilt>0</tilt>
 <heading>0</heading>
 </LookAt>
 <styleUrl>#default+nicon=http://maps.google.com/mapfiles/kml/pal3/
 icon60.png+hicon=http://maps.google.com/mapfiles/kml/pal3/icon52.png</styleUrl>
 <Point>
 <coordinates>-82.451142,27.98146,0</coordinates>
 </Point>
 </Placemark>
 </Document>
 </kml>116


下面的插图描述使用 adb 工具 pull 命令来检索文件(略)。如果命令执行成功,
你应当看到一条消息指示文件下载的大小。导航到  C:\Android  文件夹,你可以
看到 adb  pull 工具放在这里。
现在 nmea 文件在桌面上,把它与 Notepad 关联。最后打开它来看看它的内容。
你会看到很多的坐标数据。
在 Linux 中得到 nmea 文件
如果你在使用 Linux 开发 Android,启动一个终端部分来进入 adb 服务器。让我
们来看看如何在 Linux 中检索并且编辑 nmea 文件。
注意(和插图有关,略)
第 一 步 是 打 开 一 个 新 的 终 端 部 分    (Applications   |  System   Tools   |
Terminal)。
下一步,使用 adb  pull 命令来 pull  nmea 文件到 Android 文件夹:
adb  pull  data/misc/location/gps/nmea  Android/
如果你读了关于 Windows 如何得到 nmea 文件的说明,你会发现语法上的不同。C:\
是没有必要的因为路径结构的不同。
从终端中执行了命令后,结果应当如下所示:
使用 Is 命令来在 Android 文件夹中列出文件。如果命令执行正确,nmea 文件应
当出现。我使用 Fedora  GUI 来导航并且使用系统的 Text  Editor 打开它。
提示
你也可以使用 vi 编辑器从命令行来打开,读取并且编辑 nmea 文件。
现在你已经查看了 nmea 文件并且知道模拟一个 GPS 设备的不同方式,你可以开
始来使用 Android 位置基础 API 来创建一个完整特性的活动了。
使用 Android 位置基础 API 读取 GPS
使用 Android         Android         Android         Android 位置基础 API  API  API  API 读取 GPS  GPS  GPS  GPS 第九章(3)  (3)  (3)  (3)
本章剩下的部分是致力于建造一个活动,AndroidLBS,它会从服务器中 nmea 文
件中识别用户的位置。本活动的第一个过程非常的简单。
你会创建一个简单的过程,该过程会得到用户当前的 GPS 位置。然后你可以在屏
幕上显示这个位置的坐标。在做这个的时候,你会了解到一个对 Android 位置基
础 API 比较到位的介绍和它的功用。
创建 AndroidLBS 活动
下面是创建这个简单活动的步骤:117
1.调整许可的权限
2.创建活动的布局
3.书写代码来允许活动。
4.运行活动。
调整许可的权限
使用 Android 位置基础 API 是调整认可的权限。使用 Android 位置基础本身不要
求任何特别的许可。但是在 GPS 使用 Android 位置基础来存取位置信息需要。
从 Eclipse 中有两种方式可以设置许可。第一个是通过 Android  Manifest 许可
向导,这个你在第七章用过。在 Eclipse 中,双击 AndroidManifest.xml 来打开
Android  Manifest  综 览 窗 口 。 点 击 许 可 链 接 并 使 用 第 七 章 的 方 法 增 加
ACCESS_GPS  和  ACCESS_LOCATION  使用许可。
第二种方法是,你可以手动编辑 AndroidManifest.xml 文件增加许可值到活动
中。你会需要下面的代码行到 AndroidManifest.xml 中:
这里的语句是用来在<uses-permission>标签中增加许可名称。
当你结束了增加许可,你的 AndroidManifest.xml 文件应当像下面的代码片段。
这样的代码应当非常的熟悉了。你在 Intent 过滤器内使用了一个活动和一对许
可。
创建你的布局

<uses-permission  android:name="android.permission.ACCESS_GPS">
 </uses-permission>
 <uses-permission  android:name="android.permission.ACCESS_LOCATION">
 </uses-permission>
 <?xml  version="1.0"  encoding="utf-8"?>
 <manifest  xmlns:android=http://schemas.android.com/apk/res/android
 package="android_programmers_guide.AndroidLBS">
 <application  android:icon="@drawable/icon">
 <activity  android:name=".AndroidLBS"
 android:label="@string/app_name">
 <intent-filter>
 <action  android:name="android.intent.action.MAIN"  />
 <category  android:name="android.intent.category.LAUNCHER"  />
 </intent-filter>
 </activity>
 </application>
 <uses-permission  android:name="android.permission.ACCESS_GPS">
 </uses-permission><uses-permission
 android:name="android.permission.ACCESS_LOCATION">
 </uses-permission></manifest>118


要开始创建布局,在 Eclipse 中打开 main.xml,你会一共增加一个按钮和 4 个
TextViews 到布局中。按钮可以从 GPS 呼叫信息并显示到 TextViews 中。
按照下面设置按钮,也就是在屏幕的顶部并使用“Where  am  I”作为显示文本。
下一步,设置 4 个 Texviews,你应当在布局中安排它们,2 个 TextViews 会显示
在另外 2 个 TextViews 之上。这样可以把其中的 2 个作为另外 2 个的标签来使用 。
要完成这个工作,你需要多两个 LinearLayouts.
请注意在 main.xml 文件中的所有元素是包含在一个 LinearLayout 标签中。这个
标签用某些规则来绑定内含的元素。对于 LinearLayouts,元素被以依次水平或
者垂直的方乡堆栈。
LinerLayout 的方向由 android:orientation 属性管理。假如属性没有被赋值,
初始的设定是水平方式。
请注意,有一些槽(或者架子)是垂直放置的。你可以在屏幕上的这些槽上放置
元素。总之,如果你要在立式 LinearLayout 布局的同一个架子上放置少量的条
目,你需要先在架子上放置一个水平的 LinearLayout。
现在你可以上下左右的码放这些元素了。这是个在本活动中需要利用的概念。因
此,在按钮的下面,增加一个水平的 LinearLayout 来放置 2 个 TextViews。

<Button
 android:id="@+id/gpsButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Where  Am  I"
 />
 <LinearLayout  xmlns:android=http://schemas.android.com/apk/res/android
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/latLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Latitude:  "
 />
 <TextView
 android:id="@+id/latText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 Figure  9-2  Vertical  LinearLayout  with  embedded  horizontal  LinearLayout
 Horizontal  LinearLayout
 Android  screen
 />119


这两个 TextViews 保留标签和你将要从 GPS 中采集的纬度数据。下一个,增加另
一个水平 LinearLayout 来保留剩下的 TextViews:
这个为特定的活动提供了比较好的布局。你完成的 main.xml 文件应当如下:

</LinearLayout>
 <LinearLayout  xmlns:android=http://schemas.android.com/apk/res/android
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/lngLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Longitude:  "
 />
 <TextView
 android:id="@+id/lngText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <?xml  version="1.0"  encoding="utf-8"?>
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <Button
 android:id="@+id/gpsButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Where  Am  I"
 />
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/latLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Latitude:  "120


书写代码来允许活动
书写代码来允许活动  第九章(4)  (4)  (4)  (4)
现在已经创建了布局,你可以开始写代码来允许活动了。你的按钮需要从 GPS
中来呼叫用户当前的位置。一旦你有了这些信息,你可以发送纬度和经度坐标到
TextViews 中了。
首先,你需要增加输入声明。你需要输入 4 个包装:
和一个 Android 位置基础 API:
下一步,为按钮创建代码。目标是从 GPS 中检索当前坐标信息。你已经在本书中
创建了一些按钮了,而且这个的格式没有不同。你需要设置按钮并且从 main.xml

/>
 <TextView
 android:id="@+id/latText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/lngLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Longitude:  "
 />
 <TextView
 android:id="@+id/lngText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 </LinearLayout>
 import  android.view.View;
 import  android.widget.TextView;
 import  android.content.Context;
 import  android.widget.Button;
 import  android.location.LocationManager;121


中装载布局。然后你可以设置 onClick 事件来呼叫一个函数,LoadCoords()。
创建活动的最后步骤是填充代码到 LoadCoords()函数中。创建 TextViews 需要
接受坐标数据:
注意
你没必要必须创建两个 TextViews 来作为标签,因为你不会发送任何东西到它
们。
现在,创建一个可以 pull 坐标数据的 LocationManager。这个示例的重要部分
是你必须要传递给 LocationManager 一个上下文;使用 LOCATION_SERVICE:
要从 myManager 中 pull 坐标,使用 getCurrentLocation()方法。这个方法需要
一个参数,一个提供者,它们会展示 API 将要从中 pull 的坐标。在这种情况下,
Android 提供了一个本章早些时候讨论过的包含 nmea 文件的模拟位置 GPS:
最后,拿去双击数据并且把它们传递到 TextViews:

final  Button  gpsButton  =  (Button)  findViewById(R.id.gpsButton);
 gpsButton.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 LoadCoords();
 }});
 TextView  latText  =  (TextView)  findViewById(R.id.latText);
 TextView  lngText  =  (TextView)  findViewById(R.id.lngText);
 LocationManager  myManager  =
 (LocationManager)getSystemService(Context.LOCATION_SERVICE);
 Double  latPoint  =  myManager.getCurrentLocation("gps").getLatitude();
 Double  lngPoint  =  myManager.getCurrentLocation("gps").getLongitude();
 latText.setText(latPoint.toString());
 lngText.setText(lngPoint.toString());
 Your  finished  code  should  look  like  this:
 package  android_programmers_guide.AndroidLBS;
 import  android.app.Activity;
 import  android.os.Bundle;
 import  android.location.LocationManager;
 import  android.view.View;
 import  android.widget.TextView;
 import  android.content.Context;
 import  android.widget.Button;
 public  class  AndroidLBS  extends  Activity  {
 /**  Called  when  the  activity  is  first  created.  */
 @Override
 public  void  onCreate(Bundle  icicle)  {122


运行活动
在 Android 模拟器中运行你的活动。该活动将会打开如下的屏幕(略)。点击
“Where  Am  I”按钮。你将会看到图片中显示的坐标。
传递坐标到 Google 地图
传递坐标到 Google       Google       Google       Google 地图  第九章(5)  (5)  (5)  (5)
在本节中,你将继续在前一节的基础上构造。对 AndroidLBS 活动的主要修改就
是传递坐标到 Google 地图中。你将使用 Google 地图来显示用户的当前位置。在
main.xml 文件中的唯一修改指出就是为 MpaView 增加一个布局。在目前版本的
Android  SDK 中,MapView 被建立为一个类 View。可能在将来的版本中 MapView
会相当于这个布局。
完成后的 main.xml 文件应当像这样:

super.onCreate(icicle);
 setContentView(R.layout.main);
 final  Button  gpsButton  =  (Button)  findViewById(R.id.gpsButton);
 gpsButton.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 LoadCoords();
 }});
 }
 public  void  LoadCoords(){
 TextView  latText  =  (TextView)  findViewById(R.id.latText);
 TextView  lngText  =  (TextView)  findViewById(R.id.lngText);
 LocationManager  myManager  =  (LocationManager)
 getSystemService(Context.LOCATION_SERVICE);
 Double  latPoint  =  myManager.getCurrentLocation("gps").getLatitude();
 Double  lngPoint  =  myManager.getCurrentLocation("gps").getLongitude();
 latText.setText(latPoint.toString());
 lngText.setText(lngPoint.toString());
 }
 }
 <view  class="com.google.android.maps.MapView"
 android:id="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
 <?xml  version="1.0"  encoding="utf-8"?>
 <LinearLayout  xmlns:android=http://schemas.android.com/apk/res/android
 android:orientation="vertical"
 android:layout_width="fill_parent"123
 android:layout_height="fill_parent"
 >
 <Button
 android:id="@+id/gpsButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Where  Am  I"
 />
 <LinearLayout  xmlns:android=http://schemas.android.com/apk/res/android
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/latLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Latitude:  "
 />
 <TextView
 android:id="@+id/latText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <LinearLayout  xmlns:android=http://schemas.android.com/apk/res/android
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/lngLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Longitude:  "
 />
 <TextView
 android:id="@+id/lngText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <view  class="com.google.android.maps.MapView"
 android:id="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>124


因为在这个活动中嵌入 MapView,你需要改变类的定义。现在,主要类扩展了活
动。但是要正确的使用 Google  MapView,你必须扩展 MapActivity。因此,你需
要输入 MapActivity 包装并且替换在头部的 Activity  包装。
输入下列包装:
Point 包装将被用于保留 point 的值,它就是展示地图坐标的,而 MapController
将你的 point 置于地图中央。这两个包装在使用 MapView 时非常的关键。
现在准备增加建立地图并传递坐标的代码。首先,设置一个一个 MapView,并且
从 main.xml 文件中把它赋值到布局:
下一步,设置一个 Point 并且把从 GPS 检索的数值赋值给 latPoint 和 IngPoint:
现在,可以创建 MapController 了,它将被用于移动 Google 地图来定位你定义
的 Point。从 MapView 使用 getController()方法在定制的地图中建立一个控制
器:
唯一剩下的工作就是使用控制器来移动地图到你的位置(要让地图更容易辨认,
把 zoom 设定为 9):
你刚才所写的所有代码就是从活动中利用 Google 地图。完整的类应当像这样:

</LinearLayout>
 import  com.google.android.maps.MapActivity;
 import  com.google.android.maps.MapView;
 import  com.google.android.maps.Point;
 import  com.google.android.maps.MapController
 MapView  myMap  =  (MapView)  findViewById(R.id.myMap);
 Point  myLocation  =  new  Point(latPoint.intValue(),lngPoint.intValue());
 MapController  myMapController  =  myMap.getController();
 myMapController.centerMapTo(myLocation,  false);
 myMapController.zoomTo(9);
 package  android_programmers_guide.AndroidLBS;
 import  android.os.Bundle;
 import  android.location.LocationManager;
 import  android.view.View;
 import  android.widget.TextView;
 import  android.content.Context;
 import  android.widget.Button;
 import  com.google.android.maps.MapActivity;
 import  com.google.android.maps.MapView;
 import  com.google.android.maps.Point;
 import  com.google.android.maps.MapController;125


在模拟器中运行活动。活动应当打开一个空白的地图。点击“Where  Am  I”按钮 ,
应当会看到地图聚焦并且放大到旧金山。看看下图就会知道地图会如何出现
(略)。
增加缩放控制
增加缩放控制  第九章(6)  (6)  (6)  (6)
本章的最后一个练习是再增加两个按钮到 AndroidLBS 活动中。这些按钮将控制
Google  MapView 放大和缩小 的方法 。让这个修改有一点不同的是我将为
main.xml 文件介绍一个布局的新类型:RelativeLayout。LinearLayouts 允许你
直接的一个接一个的放置 Views,RelativeLayouts 允许你在每一个 View 上放置 。
对于这个活动,将会放置两个按钮到 Google  Map 上,要实现这个效果,你可以

public  class  AndroidLBS  extends  MapActivity  {
 /**  Called  when  the  activity  is  first  created.  */
 @Override
 public  void  onCreate(Bundle  icicle)  {
 super.onCreate(icicle);
 setContentView(R.layout.main);
 final  Button  gpsButton  =  (Button)  findViewById(R.id.gpsButton);
 gpsButton.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 LoadProviders();
 }});
 }
 public  void  LoadProviders(){
 TextView  latText  =  (TextView)  findViewById(R.id.latText);
 TextView  lngText  =  (TextView)  findViewById(R.id.lngText);
 LocationManager  myManager  =  (LocationManager)
 getSystemService(Context.LOCATION_SERVICE);
 Double  latPoint  =
 myManager.getCurrentLocation("gps").getLatitude()*1E6;
 Double  lngPoint  =
 myManager.getCurrentLocation("gps").getLongitude()*1E6;
 latText.setText(latPoint.toString());
 lngText.setText(lngPoint.toString());
 MapView  myMap  =  (MapView)  findViewById(R.id.myMap);
 Point  myLocation  =  new  Point(latPoint.intValue(),lngPoint.intValue());
 MapController  myMapController  =  myMap.getController();
 myMapController.centerMapTo(myLocation,  false);
 myMapController.zoomTo(9);
 }
 }126


增加在地图上的按钮。
现在可以增加另外的两个按钮了。它们会出现在 MapView 的左上方和左下方。你
需要对 Button 布局做一个修改。按照默认的方式,RelativeLayout 增加 Button
来和锚视图顶部的边缘排列,本例中,就是这个 MapView。因此,在这个布局,
使用 android:layout_alignBottom 属性并赋值 MapView 的 id。这样就排列按钮
到地图的底部了。
提示
仔细看一下按钮的布局属性。我使用一个新的属性,style,来把这个按钮改小。
完整的 main.xml 应当看上去像这样:

<RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <view  class="com.google.android.maps.MapView"
 android:id="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
 </RelativeLayout>
 <Button  android:id="@+id/buttonZoomIn"
 style="?android:attr/buttonStyleSmall"
 android:text="+"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 <Button  android:id="@+id/buttonZoomOut"
 style="?android:attr/buttonStyleSmall"
 android:text="-"
 android:layout_alignBottom="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 <?xml  version="1.0"  encoding="utf-8"?>
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <Button
 android:id="@+id/gpsButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Where  Am  I"127
 />
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/latLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Latitude:  "
 />
 <TextView
 android:id="@+id/latText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/lngLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Longitude:  "
 />
 <TextView
 android:id="@+id/lngText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <view  class="com.google.android.maps.MapView"
 android:id="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
 <Button  android:id="@+id/buttonZoomIn"
 style="?android:attr/buttonStyleSmall"128


你会去适当的修改这个代码。除了为新的 views 增加代码之外,你需要移除现存
的一些代码。为了让活动更灵活,你需要移除 MapView 的示例和类主要部分的
MapController。这样将会允许你传递这些项目到其它所需的函数(比如将要创
建的放大和缩小特性)。
现在可以创建两个新按钮的代码了。和你之前创建的按钮一样,增加呼叫到下一
步将要构建的函数:
最后,创建控制放大缩小特性的函数。最大放大位置是 21 并且最小是 1.因 此 ,
在函数中,在调整前测试当前的位置。这样将确保不会出现任何的运行问题。

android:text="+"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 <Button  android:id="@+id/buttonZoomOut"
 style="?android:attr/buttonStyleSmall"
 android:text="-"
 android:layout_alignBottom="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 </RelativeLayout>
 </LinearLayout>
 final  MapView  myMap  =  (MapView)  findViewById(R.id.myMap);
 final  MapController  myMapController  =  myMap.getController();
 final  Button  zoomIn  =  (Button)  findViewById(R.id.buttonZoomIn);
 zoomIn.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ZoomIn(myMap,myMapController);
 }});
 final  Button  zoomOut  =  (Button)  findViewById(R.id.buttonZoomOut);
 zoomOut.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ZoomOut(myMap,myMapController);
 }});
 public  void  ZoomIn(MapView  mv,  MapController  mc){
 if(mv.getZoomLevel()!=21){
 mc.zoomTo(mv.getZoomLevel()+  1);
 }
 }
 public  void  ZoomOut(MapView  mv,  MapController  mc){
 if(mv.getZoomLevel()!=1){
 mc.zoomTo(mv.getZoomLevel()-  1);
 }129


注意你传递 MapView 和 MapController 到函数中,从那里,对缩放位置进行整数
处理非常简单。唯一的关键是这个函数是 MapController 本身移动 MapView 到希
望的缩放位置,而 MapView 本身保留缩放位置。
提示
想想这个关系和一个遥控器和电视机相类似。遥控器切换电视到第 5 频道,但是
频道本身是储存在电视上的。
你完成的 AndroidLBS.java 文件应当看上去像这样:

}
 package  android_programmers_guide.AndroidLBS;
 import  android.os.Bundle;
 import  android.location.LocationManager;
 import  android.view.View;
 import  android.widget.TextView;
 import  android.content.Context;
 import  android.widget.Button;
 import  com.google.android.maps.MapActivity;
 import  com.google.android.maps.MapView;
 import  com.google.android.maps.Point;
 import  com.google.android.maps.MapController;
 public  class  AndroidLBS  extends  MapActivity  {
 /**  Called  when  the  activity  is  first  created.  */
 @Override
 public  void  onCreate(Bundle  icicle)  {
 super.onCreate(icicle);
 setContentView(R.layout.main);
 final  MapView  myMap  =  (MapView)  findViewById(R.id.myMap);
 final  MapController  myMapController  =  myMap.getController();
 final  Button  zoomIn  =  (Button)  findViewById(R.id.buttonZoomIn);
 zoomIn.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ZoomIn(myMap,myMapController);
 }});
 final  Button  zoomOut  =  (Button)  findViewById(R.id.buttonZoomOut);
 zoomOut.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ZoomOut(myMap,myMapController);
 }});
 final  Button  gpsButton  =  (Button)  findViewById(R.id.gpsButton);
 gpsButton.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 LoadProviders(myMap,myMapController);130


在 Android 模拟器中运行这个活动。活动会打开一个新的 MapView,如图所示放
了按钮(略)。
试一下放大和缩小按钮。当你放大,你应当看到和下面类似的图形(略)。
试试这个:在 MapView 之间转换
试试这个:在 MapView         MapView         MapView         MapView 之间转换  第九章(7)  (7)  (7)  (7)
标 准 视 图 和 卫 星 视 图 再 编 辑 AndroidLBS 活 动 一 次 。 再 增 加 两 个 按 钮 到
RelativeLayout。这些按钮可以转换标准视图和卫星视图。下面是需要考虑的地
方:
●  使用排列布局属性使这转换按钮在 MapView 的另外一面。
●  研究 MapView 来找到转换的方式。
●  创建一个可以传递并转换 MapView 的函数。

}});
 }
 public  void  LoadProviders(MapView  mv,  MapController  mc){
 TextView  latText  =  (TextView)  findViewById(R.id.latText);
 TextView  lngText  =  (TextView)  findViewById(R.id.lngText);
 LocationManager  myManager  =  (LocationManager)
 getSystemService(Context.LOCATION_SERVICE);
 Double  latPoint  =  myManager.getCurrentLocation("gps").getLatitude()*1E6;
 Double  lngPoint  =
 myManager.getCurrentLocation("gps").getLongitude()*1E6;
 latText.setText(latPoint.toString());
 lngText.setText(lngPoint.toString());
 Point  myLocation  =  new  Point(latPoint.intValue(),lngPoint.intValue());
 mc.centerMapTo(myLocation,  false);
 mc.zoomTo(9);
 }
 public  void  ZoomIn(MapView  mv,  MapController  mc){
 if(mv.getZoomLevel()!=21){
 mc.zoomTo(mv.getZoomLevel()+  1);
 }
 }
 public  void  ZoomOut(MapView  mv,  MapController  mc){
 if(mv.getZoomLevel()!=1){
 mc.zoomTo(mv.getZoomLevel()-  1);
 }
 }
 }131


完成的 main.xml 和 AndroidLBS.java 文件应当如下:

main.xml
 <?xml  version="1.0"  encoding="utf-8"?>
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <Button
 android:id="@+id/gpsButton"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Where  Am  I"
 />
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/latLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Latitude:  "
 />
 <TextView
 android:id="@+id/latText"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >
 <TextView
 android:id="@+id/lngLabel"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Longitude:  "
 />
 <TextView
 android:id="@+id/lngText"
 android:layout_width="wrap_content"132
 AndroidLBS.java
 android:layout_height="wrap_content"
 />
 </LinearLayout>
 <RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <view  class="com.google.android.maps.MapView"
 android:id="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
 <Button  android:id="@+id/buttonZoomIn"
 style="?android:attr/buttonStyleSmall"
 android:text="+"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 <Button  android:id="@+id/buttonMapView"
 style="?android:attr/buttonStyleSmall"
 android:text="Map"
 android:layout_alignRight="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 <Button  android:id="@+id/buttonSatView"
 style="?android:attr/buttonStyleSmall"
 android:text="Sat"
 android:layout_alignRight="@+id/myMap"
 android:layout_alignBottom="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 <Button  android:id="@+id/buttonZoomOut"
 style="?android:attr/buttonStyleSmall"
 android:text="-"
 android:layout_alignBottom="@+id/myMap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"  />
 </RelativeLayout>
 </LinearLayout>
 package  android_programmers_guide.AndroidLBS;
 import  android.os.Bundle;
 import  android.location.LocationManager;
 import  android.view.View;133
 import  android.widget.TextView;
 import  android.content.Context;
 import  android.widget.Button;
 import  com.google.android.maps.MapActivity;
 import  com.google.android.maps.MapView;
 import  com.google.android.maps.Point;
 import  com.google.android.maps.MapController;
 public  class  AndroidLBS  extends  MapActivity  {
 /**  Called  when  the  activity  is  first  created.  */
 @Override
 public  void  onCreate(Bundle  icicle)  {
 super.onCreate(icicle);
 setContentView(R.layout.main);
 final  MapView  myMap  =  (MapView)  findViewById(R.id.myMap);
 final  MapController  myMapController  =  myMap.getController();
 final  Button  zoomIn  =  (Button)  findViewById(R.id.buttonZoomIn);
 zoomIn.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ZoomIn(myMap,myMapController);
 }});
 final  Button  zoomOut  =  (Button)  findViewById(R.id.buttonZoomOut);
 zoomOut.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ZoomOut(myMap,myMapController);
 }});
 final  Button  gpsButton  =  (Button)  findViewById(R.id.gpsButton);
 gpsButton.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 LoadProviders(myMap,myMapController);
 }});
 final  Button  viewMap  =  (Button)  findViewById(R.id.buttonMapView);
 viewMap.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ShowMap(myMap);
 }});
 final  Button  viewSat  =  (Button)  findViewById(R.id.buttonSatView);
 viewSat.setOnClickListener(new  Button.OnClickListener()  {
 public  void  onClick(View  v){
 ShowSat(myMap);
 }});
 }
 public  void  LoadProviders(MapView  mv,  MapController  mc){
 TextView  latText  =  (TextView)  findViewById(R.id.latText);
 TextView  lngText  =  (TextView)  findViewById(R.id.lngText);134


当你运行活动时,应当可以启动和关闭卫星视图,如下图(略)。
在下一章,你将进入更深层次的 Google  API。第十章将一步一步学习使用 Google
API 从 Android 手机发送信息到 GTalk。
问专家
Q:最终版本的 Android 还会继续使用.kml 或者.nmea 文件吗?
A:本书写的时候,最终的 Android 还没有发布,可以假定的是,是的,最后版本
的 Android 还会利用.kml 和/或者.nmea 文件。这将允许应用程序开发者在应用
程序内使用包括静态坐标文件。
Q:有没有可能来创建有标记的 Google  Map?

LocationManager  myManager  =  (LocationManager)
 getSystemService(Context.LOCATION_SERVICE);
 Double  latPoint  =
 myManager.getCurrentLocation("gps").getLatitude()*1E6;
 Double  lngPoint  =
 myManager.getCurrentLocation("gps").getLongitude()*1E6;
 latText.setText(latPoint.toString());
 lngText.setText(lngPoint.toString());
 Point  myLocation  =  new  Point(latPoint.intValue(),lngPoint.intValue());
 mc.centerMapTo(myLocation,  false);
 mc.zoomTo(9);
 }
 public  void  ZoomIn(MapView  mv,  MapController  mc){
 if(mv.getZoomLevel()!=21){
 mc.zoomTo(mv.getZoomLevel()+  1);
 }
 }
 public  void  ZoomOut(MapView  mv,  MapController  mc){
 if(mv.getZoomLevel()!=1){
 mc.zoomTo(mv.getZoomLevel()-  1);
 }
 }
 public  void  ShowMap(MapView  mv){
 if  (mv.isSatellite()){
 mv.toggleSatellite();
 }
 }
 public  void  ShowSat(MapView  mv){
 if  (!mv.isSatellite()){
 mv.toggleSatellite();
 }
 }


}A:是的,在第十一章,你将学习如何熟练操作 Google  地图  Overlays。这些视
图允许在 Google 地图的上面你绘制文本,标记和其它形状。