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

GPS 位置绘制 Android 应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (50投票s)

2013年10月8日

CPOL

13分钟阅读

viewsIcon

392300

downloadIcon

47032

开发一个 Android 应用,持续将用户位置描绘在地图上。

Sample Image

引言

最近,我被要求编写一个应用程序,该应用程序严重依赖 GPS 和定制硬件设备的地图绘制,这些设备是用具有位置感知功能的组件构建的。尽管我使用 Eclipse 处理这些任务已经有一段时间了,但 Android Studio 的闪亮新功能和易用性引起了我的注意,让我尝试用它来完成这项任务。在与 Android Studio 和 Google Maps Android API V2 合作的几周里,我学到了一些值得分享的经验,这样其他人就不必重新发明轮子,并在他们的探索中避免已知的陷阱。

在本文中,我们将尝试专注于使用 Android Studio IDE 开发一个使用 Google Maps Android API V2 的 Android 应用程序。最近,Google 弃用了 Google Maps Android V1,并于 2013 年 3 月 18 日停止颁发 API 密钥,开始推荐 V2。网络上大量的文档、书籍和其他资源讨论的是围绕 V1 进行的,对于新手来说,从中筛选出有关使用 API V2 开发应用程序的有用信息是令人望而生畏的。更令人困惑的是,网上关于使用 Android Studio 进行此类开发的资源并不多。希望我们能在此填补这一空白。

背景

Google 在 2013 年 5 月 16 日的 Google I/O 大会上宣布,Android Studio 是他们选择的用于开发 Android 应用程序的集成开发环境(IDE)。Android Studio 本质上是 JetBrains 的 IntelliJ IDEA,但它严重侧重于 Android 应用程序的开发。Android Studio 使用 Gradle 进行依赖解析和 IDE 中的大部分项目管理任务。Android Studio 可从 此处 下载。Android Studio 具有自动软件更新程序,可自动检查新更新并将 IDE 更新到最新版本。截至本文撰写之时,最新版本是 0.2.11,构建号为 AI-132.855830。

在 Android Studio 之前,甚至之后,大多数开发人员都使用 Eclipse 作为 Android 应用程序开发的基础平台。Eclipse 是一个优秀的平台,为 Java、C++ 和各种其他语言的软件开发提供了出色的 IDE。

创建 Android 应用

我将假设读者熟悉 Android 应用程序的结构和构建应用程序所需的文件,以及 Activity、Intent、Activity 生命周期及其各种状态、资源文件和字符串等概念。网络上有许多关于 Android 应用程序开发概念的书籍和文本。本文我们将重点介绍一个使用特定工具的特定应用程序。我使用了一台运行 Android Studio 的 Linux 机器。本文中的信息可以有效地用于运行相同版本的 Android Studio 的 Windows 或 Mac 机器。让我们开始吧。

当您在启动时从初始屏幕选择“新建项目”时,Android Studio 会创建一个骨架应用程序。您会看到一个如下所示的对话框。在此示例中,将应用程序的名称填写为任何有意义的名称,例如 GPSPlotter。填写代码的包名称,例如在此示例中为 com.siriusmicrotech.gpsplotter。这将是稍后在文章中讨论的 API 密钥生成所使用的应用程序名称。请注意,最小 SDK 要求选择为 AP11,因为 Google Maps Android API V2 需要 SDK 大于 AP8 才能工作。然后单击“下一步”,接受后续屏幕上的所有默认设置,直到通过单击“完成”创建项目。

Sample Image - maximum width is 600 pixels

这是我们创建的骨架项目的屏幕截图。

Sample Image

尽管此过程创建了许多文件,但幸运的是,我们只需要关注以下五个文件

  • MainActivity.java
  • activity_main.xml
  • strings.xml
  • AndroidManifest.xml
  • build.gradle

位置提供程序

在开始修改这些文件之前,让我们先讨论一些背景知识,解释一下我们为什么要在接下来的章节中这样做。Android 设备配备了 GPS 硬件。Android 提供了一个非常直接的 API 来访问来自 GPS 硬件以及 Wi-Fi 和蜂窝网络连接源的位置信息,这些源有助于提供位置信息。由于应用程序需要能够访问设备的当前位置信息,因此我们需要在安装期间获得用户允许应用程序访问此服务的权限。在调试期间,应用程序被授予访问这些服务的权限,而无需提示用户确认。以下部分为应用程序设置了各种权限,例如访问互联网(因为地图需要从互联网下载),访问存储(因为地图需要缓存),以及访问 GPS 等位置提供程序和其他形式的位置服务。Android Maps 还需要 OpenGL。以下是我们添加到 `AndroidManifest.xml` 文件中的行,就在 `` 标签上方:标签。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <!-- External storage for caching. -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- My Location -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- Maps API needs OpenGL ES 2.0. -->
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />
         

在我们的代码中使用 Google Maps Android API V2

由于 Google Maps Android API V2 不包含在 Android 开发工具包 (ADK) 中,而是包含在 Google Play Services API 中,因此我们需要单独安装它。启动 SDK 管理器,转到“Extras”部分,勾选“Google Play Services”和“Google Repository”,然后单击“Install Packages”。

Sample Image

安装 Google Play Services API 后,我们需要将其包含在我们的项目中。Google 开发者网站上非常好的教程 Google's android developer site 针对 Eclipse 平台编写,它建议通过将 Google Services Library 导入工作区来引用它。这在 Android Studio 中将不起作用,因为 Gradle 在这里用于依赖解析。因此,我们需要如下编辑 `build.gradle` 文件,在其中添加一个依赖项。请注意,截至本文撰写之时,UI 无法用于为此 Gradle 添加此依赖项。唯一的方法是手动编辑 `build.gradle` 文件。然而,人们必须认识到,这比开发者网站上讨论的解决方案要简单得多。

dependencies {
    // Google Play Services
    compile 'com.google.android.gms:play-services:3.2.65'
    // Support Library
    compile 'com.android.support:appcompat-v7:18.0.0'
}

现在,要在我们的应用程序中显示地图,我们需要在布局中放置一个 `Fragment`。这可以通过将 `activity_main.xml` 文件中的 `TextView` 部分替换为如下的 `Fragment` 部分来实现:

<fragment
        class="com.google.android.gms.maps.SupportMapFragment"
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
 

并修改 `MainActivity.java`,使 `MainActivity` 类从 `FragmentActivity` 而不是 `Activity` 派生,如下所示:

public class MainActivity extends FragmentActivity 

现在,从技术上讲,我们已经拥有了应用程序设备端的所有内容。然而,地图必须来自云端。要从云端访问地图,我们需要一个特殊的身份验证密钥,称为 API 密钥,由提供地图的 Google Cloud 服务授予我们的应用程序。尽管听起来非常复杂,但按照 此处 提供的说明获取此密钥非常简单。

请注意,出于我们的目的,我们可以使用调试证书来获取 API 密钥。仅当我们要发布应用程序时,才应使用发布证书来生成此密钥。获得此密钥后,将其复制到 `AndroidManifest.xml` 文件中,就在 `

` 标签上方。

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="Your API Key Here!!" />
  

请注意,将 Android 设备连接到 PC 并将其安装在其上进行测试,比使用模拟器要简单得多。但是,请记住在设备上启用 USB 调试。如果您的设备运行的是 Jelly Bean,默认情况下,设置菜单中的“开发者选项”部分是隐藏的,以保护用户免受此功能造成任何损害。转到“系统设置”菜单,一直向下滚动,看看是否看到“开发者选项”列表。如果未列出,要显示“开发者选项”,请选择“关于手机”菜单,一直向下滚动到看到“版本号”。连续快速点按“版本号”几次,直到看到“开发者选项”已启用。进入“开发者选项”菜单,通过单击旁边的复选框启用 USB 调试。

用 USB 数据线将设备连接到 PC,然后在 Android Studio 菜单中点击“运行”。如果一切顺利,您将看到一个 Google 地图显示在非洲大陆附近!工作量不大,不是吗。

在地图上显示并跟踪我们的位置

仅仅显示世界地图并不令人兴奋,除非我们能以某种方式在上面追踪我们的位置。现在,让我们开始使用我们的应用程序已经在 `AndroidManifest.xml` 文件中申请了权限的位置服务。`GoogleMap` 类提供了许多功能的便捷集成,包括位置跟踪。因此,这里的任务顺序是:

  • 获取我们刚才显示的地图的对象引用
  • 将一个位置侦听器绑定到此地图,该侦听器响应位置服务的位置更改事件
  • 将我们的位置聚焦在地图上

我们可以通过获取我们在布局文件中插入的 Fragment 的引用来获得地图对象引用,如下所示:

myMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                    .getMap(); 

现在,为了在地图上用蓝色箭头图标标记我们的位置,我们通过如下方式在我们的地图对象中启用该功能。请注意,我们检查是否为 null,因为地图在完全渲染之前可能不可用。-

if(myMap != null)
{
    myMap.setMyLocationEnabled(true);
} 

既然我们现在有了地图的对象引用,我们就可以开始监听位置服务的位置更新了。推荐的方法是在我们的 Main Activity 中使用以下代码设置一个用于位置服务的 `LocationClient`:

myLocationClient = new LocationClient(getApplicationContext(), this, this);
// once we have the reference to the client, connect it
if(myLocationClient != null)
  myLocationClient.connect(); 

`LocationClient` 对象构造函数有三个参数,第一个是应用程序上下文,第二个是实现连接回调的对象(例如连接建立和断开),第三个是实现连接失败侦听器的对象。由于我们将 `MainActivity` 对象的引用作为两个回调输入传递,因此我们必须在 `MainActivity` 类中实现这些方法。一旦我们修改我们的 `MainActivity` 类定义行以实现这两个接口和 `LocationListener` 接口,Android Studio 将提示我们实现所需的方法,并将自动用这些方法填充我们的类。

public class ShowMeOnMap extends FragmentActivity
        implements GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener,
        com.google.android.gms.location.LocationListener{ 

以下是 Android Studio 填充以实现这三个接口的方法:

@Override
    public void onConnected(Bundle bundle) {
}

@Override
public void onDisconnected() {

}

@Override
public void onLocationChanged(Location location) {

}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {

} 

顾名思义,当服务连接到我们的 `LocationClient` 对象时,会触发 `OnConnected`。这是我们必须注册我们的 `LocationListener` 来监听位置更新的地方。因此,我们编辑此方法正文如下:

@Override
public void onConnected(Bundle bundle) {
   myLocationClient.requestLocationUpdates( REQUEST, this); 
} 

正如您已经知道的,`requestLocationUpdates` 调用中的 `this` 是我们 `MainActivity` 的引用,因为我们在这里实现了 `LocationListener` 接口。第一个参数是 `LocationRequest` 对象,它设置位置更新的选项,例如更新频率、精度等。定义如下:

private static final LocationRequest REQUEST = LocationRequest.create()
            .setInterval(5000)         // 5 seconds
            .setFastestInterval(16)    // 16ms = 60fps
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); 

在此应用程序中,我们不对 `onDisconnected()` 和 `onConnectionFailed()` 方法执行任何操作,并将其保留原样。

设置好所有这些之后,我们现在就可以在收到来自位置服务通过我们与 `LocationClient` 注册的 `onLocationChanged(Location location)` 方法的新位置更新时,在地图上更新我们的位置了。`OnLocationChanged` 会以 `Location` 参数进行回调,该参数提供了我们的当前位置。要将地图移动到显示当前位置(或任何其他位置)的视图。地图视图被建模为向下观察平面的相机。相机的位置(因此地图的渲染)由要显示的位置的纬度、经度、缩放、倾斜和方位角指定。因此,要在地图上显示给定位置,我们需要将相机移动到该位置。将相机移动到我们位置的最安全方法是使用 `GoogleMap` 类中的 `moveCamera()` 方法,并附带回调,如下所示。回调可以防止出现地图未完全渲染而我们试图更新相机的情况。

myMap.moveCamera(CameraUpdateFactory.newCameraPosition(
           new CameraPosition.Builder().target(new LatLng(lat, lng))
        .zoom(15.5f)
        .bearing(0)
        .tilt(25)
        .build()
), new GoogleMap.CancelableCallback() {
    @Override
    public void onFinish() {
        // Your code here to do something after the Map is rendered
    }

    @Override
    public void onCancel() {
        // Your code here to do something after the Map rendering is cancelled
    }
});

本质上,到目前为止我们所做的一切应该会产生一个加载地图并在上面显示我们位置(带蓝色箭头)的应用程序。这个图标会随着我们的移动在地图上跟踪我们的位置。但是,为了简洁起见,我们忽略了一些细节。例如,我们忽略了 `MainActivity` 的生命周期事件,比如它可以被暂停和恢复等。在另一个 Activity 显示在我们的 `MainActivity` 之上,阻止其显示的事件中,我们的 `MainActivity` 将被暂停并发送到后台,并生成 `onPause()` 事件。当用户返回到我们的 Activity 时,它将被恢复并生成 `onResume()` 事件。位置更新会消耗大量电池寿命。因此,作为 Android 应用程序生态系统的负责任公民,我们的应用程序在发送到后台且未显示时应禁用位置更新,并在恢复时重新启用。这可以通过覆盖 `MainActivity` 的 `onPause()` 和 `onResume()` 方法来完成。请参考源代码包中的完整应用程序。 

要使用源代码并为自己创建应用程序,请遵循以下步骤: 

  1. 将源代码解压缩到一个文件夹中 
  2. 由于创建 API 密钥需要应用程序名称,请遵循本文中的说明,从在 Android Studio 中创建新项目开始,使用您首选的应用程序名称。
  3. 然后,将解压缩的源代码中的 `MainActivity.java` 内容复制并粘贴到您的新 `MainActivity.java` 文件中,完全替换其内容。然后转到文件中的第一行,将行“package com.siriusmicrotech.gpsplotter”更改为您在创建新项目时使用的包名称。
  4. 将解压缩的源代码中的 `activity_main.xml` 文件内容复制并粘贴到您的新 `activity_main.xml` 文件中,完全替换其内容。
  5. 将解压缩的源代码中的 `build.gradle` 文件内容复制并粘贴到您的新 `build.gradle` 文件中,完全替换其内容。
  6. 对 `AndroidManifest.xml` 文件执行相同的操作。但是,您需要将文件顶部的第三行 `com.siriusmicrotech.gpsplotter` 替换为您的包名称,并在 `` 标签下,将 `android:name="com.siriusmicrotech.gpsplotter.MainActivity"` 替换为您的 Activity 名称。
  7. 从 Google Cloud Services 获取您的 API 密钥
  8. 将您的 API 密钥复制并粘贴到 `AndroidManifest.xml` 文件中,以替换字符串“Your API Key Here!”
  9. 就是这样。

关注点

需要注意的事项 - 获取最新版本的 Android Studio 构建。如果您决定使用 Android Studio,请不要在网上搜索解决方案,因为几乎所有的文本都是为 Eclipse 编写的。Google Maps Android API V2 的实现方式与 V1 有显著差异。互联网其他地方的大部分文本都是参考 V1 编写的,这会严重阻碍您的进度。解决方案是转到 Google Developer 网站获取 API V2,您会做得更好。  

© . All rights reserved.