GPSLocator - 使用GPS查找当前(最近)位置的应用






4.85/5 (43投票s)
显示Google Maps中GPS当前位置的Android应用程序。

目录
引言
手机提供的一些最吸引人的功能之一就是 GPS 功能(至少对我来说是这样)。直到我开始使用 GPS 功能,我才意识到与之交互是如此简单。本文旨在帮助人们实现同样的认识。本文的目标读者是想要实现 GPS 功能并同时使用 Google Maps API 的**初学者**。
在本文中,我们将创建一个非常简单的应用程序,名为 `GPSLocator`。`GPSLocator` 从 GPS 获取 `纬度` 和 `经度` 信息,并在 Google 地图中显示精确的(有时是最近的)位置。
先决条件
为了开发 `GPSLocator`,我们需要预先安装以下应用程序:
除了以上两点,我们还需要:
一步一步来
解释一个应用程序,特别是开发一个应用程序是一项复杂的任务。我们将尝试将其分解为多个步骤,使其易于理解。我们将尝试一次完成一个步骤,然后朝着我们的最终应用程序前进。那么,让我们开始吧。
Google Maps API 密钥和 KeyStore 文件
首要且最重要的任务是获取 Google Maps API 密钥。获取一个密钥相对容易,但它要求我们的应用程序使用证书进行签名,并向 Google 告知证书的 Hash(MD5)指纹。现在,这个过程可以进一步分为两个部分:
- 创建证书并获取其 MD5 指纹
我们可以使用 `keytool.exe` 创建一个新的证书,它位于 JDK 安装路径的 `bin` 目录中(例如 `C:\Program Files\Java\jdk1.6.0_21\bin`)。我们需要提供一些信息,它将生成一个密钥库文件(公钥存储证书文件)。一个示例输出是:
为了开发和测试应用程序,我们通常在调试模式下对应用程序进行签名。为此,我们需要注册 Google Maps API 的调试证书 MD5 哈希。用于此目的的证书是 `debug.keystore`。它通常位于:
%userprofile%/.android/
要查找证书的 MD5,我们运行命令:
keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android
命令的输出如下:
- 注册 Google Maps API
访问 Maps API 密钥注册页面,并提供 MD5 密钥(上面已高亮显示),然后我们将获得一个 Maps API 密钥。该页面还显示了如何在 `MapView` 中使用 API 密钥。
有关获取 Google Maps API 密钥的更多详细信息,请查看 获取 Maps API 密钥。
创建新的 Android 项目
创建一个新的 Android 项目,并提供以下详细信息。另外,请确保将 Build Target 选择为 Google APIs。
点击 Finish,项目就创建好了。为该项目创建一个运行配置,以启动一个目标为 Google APIs 的 AVD(请参阅先决条件部分)。另外,请确保 AVD 和 Build Target 中选择的 Google APIs 版本是相同的。以下是配置的一些截图:
尝试使用指定的配置运行项目,它应该会显示类似以下内容:
添加 Google 地图和权限
在我们可以开始使用 Google APIs 的 `MapView` 控件之前,我们需要将 Google Maps 外部库(`com.google.android.map`)添加到库中。要将库添加到项目中,请使用 `uses-library` 标签。此标签需要添加到 `AndroidManifest.xml` 中。除了库之外,我们还需要添加相关的权限。要添加权限,我们使用 `uses-permission` 标签。对于我们的应用程序,我们将添加以下权限:
- `android.permission.ACCESS_COARSE_LOCATION`:允许应用程序访问粗略位置(基站 ID、Wi-Fi 等)
- `android.permission.ACCESS_FINE_LOCATION`:允许应用程序访问 GPS 位置。
- `android.permission.INTERNET`:允许应用程序打开网络套接字。
有关权限的更多信息,请查看 权限。
最终的 `AndroidManifest.xml` 应如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.VertexVerveInc.GPSLocator"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="com.google.android.maps" />
<activity android:name=".GPSLocatorActivity"
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>
</manifest>
最后,在 `res/layout` 下的 `main.xml` 中添加 `MapView` 控件。生成的代码应如下所示:
<?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"
>
<com.google.android.maps.MapView
android:id="@+id/mapView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="089FcDoNfk946GFlnxtjAi4zAK5ib0d3ttLUZnv"
/>
</LinearLayout>
显示最基本的 Google 地图
为了显示 Google Map 视图,我们需要更新我们的 `Activity` 类(`GPSLocatorActivity`)。此类应扩展 `MapActivity` 类而不是 `Activity` 类。我们还需要导入 `com.google.android.maps.MapActivity` 包来支持 `MapActivity` 类。我们还需要覆盖 `MapActivity` 类的 `isRouteDisplayed` 方法。这很简单,只需从该方法返回 `false`。在进行所有这些修改后,`GPSLocatorActivity.java` 应如下所示:
package com.VertexVerveInc.GPSLocator;
import com.google.android.maps.MapActivity;
import android.os.Bundle;
public class GPSLocatorActivity extends MapActivity
{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
}
尝试在此阶段运行应用程序,它应该会在模拟器中显示 Google 地图。
添加缩放(放大/缩小)控件
`MapView` 类有一个内置方法 `setBuiltInZoomControls`。使用 `true`/`false` 调用此方法可以启用/禁用放大/缩小控件。要调用此方法,我们需要通过调用 `findViewById` 并使用 `MapView` 控件的 ID 来获取 `MapView` 类的实例。请记住 `main.xml` 中的 `id="@+id/mapView"`。需要注意的一个重要事项是,一旦我们触摸/单击地图视图,缩放控件就会启用。
更改地图视图和缩放级别
我们可以选择显示卫星视图、交通视图或街景视图。这可以通过调用 `setSatellite`、`setStreetView` 和 `setTraffic` 方法轻松实现。在继续前进之前,我们还需要做另一件事,那就是稍微放大地图。为什么?因为上面输出中显示的地图视图没有任何作用。要设置地图的缩放级别,我们需要 `MapController` 的实例,我们可以调用其 `setZoom` 方法。因此,让我们更新 `GPSLocatorActivity` 类的 `onCreate` 方法以包含所有这些更改。
import com.google.android.maps.MapView;
import com.google.android.maps.MapController;
private MapView mapView;
private MapController mapController;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView = (MapView) findViewById(R.id.mapView);
// enable Street view by default
mapView.setStreetView(true);
// enable to show Satellite view
// mapView.setSatellite(true);
// enable to show Traffic on map
// mapView.setTraffic(true);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
mapController.setZoom(16);
}
此时运行应用程序会产生以下输出:
添加 GPS 位置映射
Android 通过 `LocationManager`(`package android.Location`)类提供基于位置的服务。此类提供有关设备位置的周期性更新。要使用 `LocationManager`,我们需要通过调用 `getSystemService` 方法获取 `LocationManager` 类的引用。之后,我们需要通过调用 `requestLocationUpdates` 方法注册位置更新。
我们需要创建一个实现 `abstract LocationListener` 类的类。这个类将被注册到 Location Manager 以接收位置更新。我们需要覆盖此类的所有四个方法,即 `onLocationChanged`、`onProviderDisabled/Enabled` 和 `onStatusChanged`。由于我们只对获取位置更新感兴趣,我们将修改 `onLocationChanged` 的代码,使其导航到地图视图中收到的新位置。这是通过调用 `MapController` 的 `animateTo` 方法来实现的。
让我们将代码片段添加到我们的项目中。
import com.google.android.maps.GeoPoint;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
private LocationManager locationManager;
private LocationListener locationListener;
@Override
public void onCreate(Bundle savedInstanceState) {
...
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationListener = new GPSLocationListener();
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
0,
0,
locationListener);
...
}
private class GPSLocationListener implements LocationListener
{
@Override
public void onLocationChanged(Location location) {
if (location != null) {
GeoPoint point = new GeoPoint(
(int) (location.getLatitude() * 1E6),
(int) (location.getLongitude() * 1E6));
Toast.makeText(getBaseContext(),
"Latitude: " + location.getLatitude() +
" Longitude: " + location.getLongitude(),
Toast.LENGTH_SHORT).show();
mapController.animateTo(point);
mapController.setZoom(16);
mapView.invalidate();
}
}
...
}
运行应用程序后,我们会看到一条显示 GPS 位置信息的消息。我们可以跳到 测试 GPS 功能 部分来使其正常工作。有关处理位置的更多信息,请查看 获取用户位置。
查找 GPS 位置对应的地址
如果我们知道一个点的纬度和经度,我们可以找到关于该地址的信息。为此,我们使用 `Geocoder` 类,这个过程称为地理编码。我们将调用该类的 `getFromLocation` 方法,并将该点作为参数传递。此方法返回一个 `Address` 列表,其中包含指定位置地址的信息。我们可以将这些信息组合起来,找到有关该点的完整信息。为此,我们将向 `GPSLocationListener` 类添加一个名为 `ConvertPointToLocation` 的方法。`ConvertPointToLocation` 返回一个包含位置地址的 `string` 对象。
import android.location.Geocoder;
import android.location.Address;
private class GPSLocationListener implements LocationListener {
@Override
public void onLocationChanged(Location location) {
if (location != null) {
...
String address = ConvertPointToLocation(point);
Toast.makeText(getBaseContext(), address, Toast.LENGTH_SHORT).show();
...
}
}
public String ConvertPointToLocation(GeoPoint point) {
String address = "";
Geocoder geoCoder = new Geocoder(
getBaseContext(), Locale.getDefault());
try {
List<Address> addresses = geoCoder.getFromLocation(
point.getLatitudeE6() / 1E6,
point.getLongitudeE6() / 1E6, 1);
if (addresses.size() > 0) {
for (int index = 0;
index < addresses.get(0).getMaxAddressLineIndex(); index++)
address += addresses.get(0).getAddressLine(index) + " ";
}
}
catch (IOException e) {
e.printStackTrace();
}
return address;
}
...
运行应用程序会在地图上显示地址,而不是经纬度值。
添加位置标记
很多时候,我们想在位置上添加一个标记(图像),因为小圆圈(地图默认显示的)有时没有用。要添加标记,请向项目中添加一个可绘制资源。我们可以将任何(我们想使用的)图像导入到我们的项目中(简单的拖放也可以)。将图像添加到 res/drawable 文件夹。
要向地图添加位置标记,我们需要创建一个扩展 Overlay(`com.google.android.maps` 包)的类。我们需要覆盖该类的 `draw` 方法并进行一些自定义绘制。`MapOverlay` 类如下所示:
import com.google.android.maps.Overlay;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
private class GPSLocationListener implements LocationListener
{
@Override
public void onLocationChanged(Location location) {
...
mapController.animateTo(point);
mapController.setZoom(16);
// add marker
MapOverlay mapOverlay = new MapOverlay();
mapOverlay.setPointToDraw(point);
List<Overlay> listOfOverlays = mapView.getOverlays();
listOfOverlays.clear();
listOfOverlays.add(mapOverlay);
String address = ConvertPointToLocation(point);
Toast.makeText(getBaseContext(), address, Toast.LENGTH_SHORT).show();
...
}
class MapOverlay extends Overlay
{
private GeoPoint pointToDraw;
public void setPointToDraw(GeoPoint point) {
pointToDraw = point;
}
public GeoPoint getPointToDraw() {
return pointToDraw;
}
@Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
super.draw(canvas, mapView, shadow);
// convert point to pixels
Point screenPts = new Point();
mapView.getProjection().toPixels(pointToDraw, screenPts);
// add marker
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.red);
canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 24, null);
return true;
}
}
是的,让我们运行这个应用程序,正如预期的那样,它应该会在当前 GPS 位置显示一个标记。
测试 GPS 功能
当我们电脑上没有 GPS 时,如何测试 GPS 功能?嗯,这确实很棘手,但 Google 的开发人员已经考虑到了这个问题。我的意思是,是的,我们有一种方法可以在模拟器上与 GPS 进行交互。我们可以模拟模拟器上的 GPS 位置变化。我们可以通过控制台(telnet)来控制和查询模拟器的运行实例。在命令提示符下输入 " `telnet localhost <port>` " 命令连接到控制台。端口号通常显示在模拟器的标题栏中。例如,对我来说是:5554。
我们可以通过发送 `geo fix` 命令来更改 GPS 位置。我们需要 alongwith 传递经度和纬度值。例如,为了将我们当前的位置更改为西雅图(经度:-122.332071,纬度:47.60621),我们从控制台发送 ' `geo fix -122.332071 47.60621` '。有关与控制台通信的更多信息,请查看 Android 模拟器。
摘要
使用 GPS 和 Google Maps API 都既简单又有趣,而且富有创造性。`GPSLocator` 是我第一次尝试让我的开发者同行了解这一事实。请提供您的反馈和建议。如果喜欢我的文章,我是否需要提及您也可以评价我的文章。
历史
- 初稿:2010 年 9 月 22 日
- 更新的 Google APIs 链接:2010 年 9 月 27 日