65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (43投票s)

2010年9月23日

CPOL

8分钟阅读

viewsIcon

1117139

downloadIcon

24080

显示Google Maps中GPS当前位置的Android应用程序。

GPSLocator - Showing GPS Locations on Google Maps

目录

引言

手机提供的一些最吸引人的功能之一就是 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`)。我们需要提供一些信息,它将生成一个密钥库文件(公钥存储证书文件)。一个示例输出是:

    Create new keystore file

    为了开发和测试应用程序,我们通常在调试模式下对应用程序进行签名。为此,我们需要注册 Google Maps API 的调试证书 MD5 哈希。用于此目的的证书是 `debug.keystore`。它通常位于:

    %userprofile%/.android/

    要查找证书的 MD5,我们运行命令:

    keytool -list -alias androiddebugkey -keystore debug.keystore 
    -storepass android -keypass android

    命令的输出如下:

    Get MD5 fingerprint of certificate

  • 注册 Google Maps API

    访问 Maps API 密钥注册页面,并提供 MD5 密钥(上面已高亮显示),然后我们将获得一个 Maps API 密钥。该页面还显示了如何在 `MapView` 中使用 API 密钥。

    Get Maps API Key

    有关获取 Google Maps API 密钥的更多详细信息,请查看 获取 Maps API 密钥

创建新的 Android 项目

创建一个新的 Android 项目,并提供以下详细信息。另外,请确保将 Build Target 选择为 Google APIs。

Create new Android Project

点击 Finish,项目就创建好了。为该项目创建一个运行配置,以启动一个目标为 Google APIs 的 AVD(请参阅先决条件部分)。另外,请确保 AVD 和 Build Target 中选择的 Google APIs 版本是相同的。以下是配置的一些截图:

Run Configuration Android tab

Run Configuration Target tab

尝试使用指定的配置运行项目,它应该会显示类似以下内容:

Basic output of GPSLocator

添加 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 地图。

Basic Google Map view

添加缩放(放大/缩小)控件

`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); 
}

此时运行应用程序会产生以下输出:

Google Map in Street View

Zoom in/out controls

添加 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 功能 部分来使其正常工作。有关处理位置的更多信息,请查看 获取用户位置

Showing GPS Location information

查找 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;
  } 
  ...

运行应用程序会在地图上显示地址,而不是经纬度值。

Showing GPS Location information

添加位置标记

很多时候,我们想在位置上添加一个标记(图像),因为小圆圈(地图默认显示的)有时没有用。要添加标记,请向项目中添加一个可绘制资源。我们可以将任何(我们想使用的)图像导入到我们的项目中(简单的拖放也可以)。将图像添加到 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 位置显示一个标记。

Showing Location Marker

测试 GPS 功能

当我们电脑上没有 GPS 时,如何测试 GPS 功能?嗯,这确实很棘手,但 Google 的开发人员已经考虑到了这个问题。我的意思是,是的,我们有一种方法可以在模拟器上与 GPS 进行交互。我们可以模拟模拟器上的 GPS 位置变化。我们可以通过控制台(telnet)来控制和查询模拟器的运行实例。在命令提示符下输入 " `telnet localhost <port>` " 命令连接到控制台。端口号通常显示在模拟器的标题栏中。例如,对我来说是:5554。

Showing Location Marker

我们可以通过发送 `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 日
© . All rights reserved.