十、定位服务

在本章中,我们将学习如何使用谷歌地图片段向我们的应用添加地图视图。我们将在地图上添加标记,用于指出感兴趣的位置。

为了做到这一点,我们还将讨论如何在谷歌开发者控制台中创建一个项目,并设置我们的应用使用谷歌游戏服务软件开发工具包,这是在任何安卓应用中使用谷歌服务所必需的。

每个工作机会在解析中都有一个位置字段;基于此,我们将在地图上显示标记。

  • 配置项目
    • 获取谷歌地图应用编程接口密钥
    • 配置AndroidManifest.xml
  • 添加地图
    • 为可视寻呼机创建片段
    • 实现地图片段
  • 添加标记
    • 从分析中检索数据
    • 为每个位置显示一个标记
  • 添加标题

配置项目

为了让我们使用谷歌游戏服务应用编程接口,我们需要用谷歌游戏服务软件开发工具包来设置我们的项目。如果你还没有安装这个,去安卓软件开发工具包管理器,获取谷歌游戏服务软件开发工具包。

既然我们的应用使用了谷歌游戏服务,要测试该应用,您必须确保在以下任一平台上运行该应用:

  1. 安卓 2.3 或更高版本的安卓设备有谷歌 Play 商店(推荐)。
  2. An emulator that has Google Play Services set up. If you use Genymotion, Google Play services will not be installed by default:

    Configuring the project

我们需要让谷歌游戏服务应用接口对我们的应用可用。

打开应用的build.gradle文件,在依赖项下添加play-services库。添加build.gradle文件的行应该类似于这样:

compile 'com.google.android.gms:play-services:7.8.0'

确保您将此更改为最新版本的play-services,并在新版本发布时进行更新。

保存文件,点击将项目与渐变文件同步。

获取 API 密钥

为了使用谷歌地图应用编程接口,我们需要在谷歌开发者控制台注册我们的项目,并接收一个应用编程接口密钥,然后我们将把它添加到我们的应用中。

首先,我们需要获得我们独特应用的 SHA-1 指纹。我们可以从调试证书发布证书获得此信息。

  • 调试构建完成后,会自动创建调试 证书。此证书只能用于当前正在测试的应用。不要使用调试证书发布应用。
  • 发布版本 证书是在发布构建完成时制作的。也可以使用键工具程序创建证书。该证书必须在应用准备发布到 Play Store 时使用。

显示调试证书指纹

  • 找到名为debug.keystore的调试密钥库文件。该文件通常与安卓虚拟设备文件位于同一目录:
    • OS X 和 Linux : ~/.android/
    • Windows Vista 和 Windows 7 : C:\Users\your_user_name\.android\
  • 要显示 SHA-1 指纹,请打开终端或命令提示符窗口,并输入以下内容:
    • OS X 和 Linux :我们使用keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android命令。
    • Windows Vista 和 Windows 7 :我们使用keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android命令。

输入命令后,按回车键,会看到类似如下的输出:

Alias name: androiddebugkey
Creation date: Dec 16, 2014
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 32f30c87
Valid from: Tue Dec 16 11:35:40 CAT 2014 until: Thu Dec 08 11:35:40 CAT 2044
Certificate fingerprints:
         MD5:  7E:06:3D:45:D7:1D:48:FE:96:88:18:20:0F:09:B8:2A
         SHA1: BD:24:B2:7C:DA:67:E5:80:78:1D:75:8C:C6:66:B3:D0:63:3E:EE:84
         SHA256: E4:8C:BD:4A:24:CD:55:5C:0E:7A:1E:B7:FC:A3:9E:60:28:FB:F7:20:C6:C0:E9:1A:C8:13:29:A6:F2:10:42:DB
         Signature algorithm name: SHA256withRSA
         Version: 3

创建谷歌开发者控制台项目

https://console.developers.google.com/project创建一个账户,如果你还没有这样做的话。首先,用你想要的名字创建一个新项目。创建项目后,请执行以下步骤:

  1. In the left sidebar, click on APIs & auth and then select the APIs option:

    Creating a Google Developer Console project

  2. 选择谷歌地图安卓 API 并启用。

  3. 打开凭证,点击【新建键】
  4. 选择安卓键,输入您的 SHA-1 指纹,后跟您项目的包名,用分号隔开,如下所示:

    java BD:24:B2:7C:DA:67:E5:80:78:1D:75:8C:C6:66:B3:D0:63:3E:EE:84;com.packtpub.masteringandroidapp

  5. Once you complete this, you will be able to view the credentials as in the following screenshot:

    Creating a Google Developer Console project

配置 AndroidManifest.xml

现在我们已经有了安卓应用的 API 密钥,我们需要将其添加到我们的AndroidManifest.xml文件中。

打开您的AndroidManifest.xml文件,在<application>元素中添加以下代码作为子元素:

<meta-data
  android:name="com.google.android.geo.API_KEY"
  android:value="API_KEY"/>

value属性中的API_KEY替换为谷歌开发者控制台上给出的 API 键。

我们还需要给我们的AndroidManifest增加一些其他的设置。按如下方式设置谷歌游戏服务版本:

<meta-data
  android:name="com.google.android.gms.version"
  android:value="@integer/google_play_services_version" />

按如下方式设置所需权限:

  • INTERNET:此权限用于从谷歌地图服务器下载地图数据。
  • ACCESS_NETWORK_STATE:这将允许 API 检查连接的状态,以确定它是否能够下载数据。
  • WRITE_EXTERNAL_STORAGE:这将允许 API 缓存地图数据。
  • ACCESS_COARSE_LOCATION:这让 API 可以使用 Wi-Fi 或移动数据检索设备的位置。
  • ACCESS_FINE_LOCATION:这将给出比ACCESS_COARSE_LOCATION更精确的位置,它还将使用 GPS 以及 Wi-Fi 或移动数据。看看下面的代码:

    java <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

你还需要设置你的 OpenGL ES。地图应用编程接口使用 OpenGL ES 渲染地图,因此需要安装它才能显示地图。要将要求通知其他服务,并防止不支持 OpenGL 的设备在谷歌 Play 商店显示您的应用,请在您的AndroidManifest.xml文件中添加以下内容作为<manifest>的子级:

<uses-feature
  android:glEsVersion="0x00020000"
  android:required="true"/>

您当前的AndroidManifest.xml文件应该类似于以下代码:

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.packtpub.masteringandroidapp">
  <uses-feature android:glEsVersion="0x00020000" android:required="true" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <application android:name=".MAApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme">
    <activity android:name=".SplashActivity" android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name=".MainActivity" android:label="@string/title_activity_main" />
    <activity android:name=".OfferDetailActivity" android:label="@string/title_activity_offer_detail" />
    <provider android:name=".MAAProvider" android:authorities="com.packtpub.masteringandroidapp.MAAProvider" />
    <meta-data android:name="com.google.android.geo.API_KEY" android:value="AIzaSyC9o7cLdk_MIX_aQhaOLvoqYywK61bN0PQ" />
    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
  </application>
</manifest>

添加地图

现在我们的应用已经配置为使用地图服务,我们可以开始讨论如何向我们的应用添加可视化地图。对于地图,我们将创建另一个片段,它将被加载到ViewPager的第二页。

显示谷歌地图有两种方法;无论是MapFragment还是MapView对象都可以表示它。

添加片段

在我们的fragments目录中创建新的 Java 类,名称为MyMapFragment。班级应该扩展Fragment类型。然后,覆盖OnCreateView方法,让它返回fragment_my_map的膨胀视图:

package com.packtpub.masteringandroidapp.fragments;

import 

/**
* Created by Unathi on 7/29/2015.
*/
public class MyMapFragment extends Fragment {

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_my_map, container, false);

    return view;
  }
}

接下来,为片段创建布局文件,并将其命名为fragment_my_map。将布局的根元素设置为FrameLayout。我们将暂时把TextView添加到我们的布局中,只是为了验证它是否有效。fragment_my_map.xml文件的代码应该类似如下:

<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
  <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is a TextView" android:layout_gravity="center" android:textSize="25dp" />
</FrameLayout>

将我们的片段添加到应用的最后一步是编辑MyPagerAdapter.java文件,将其显示为第二页。为此,请更改getItem方法中的第二种情况以返回MyMapFragment的实例,并更改getPageTitle方法中的第二种情况下的页面标题以返回MAP:

@Override
public Fragment getItem(int i) {
  switch (i) {
    case 0 :
    return new ListFragment();
    case 1 :
    return new MyMapFragment();
    case 2 :
    return new SettingsFragment();
    default:
    return null;
  }
}

@Override
public CharSequence getPageTitle(int position) {
  switch (position) {
    case 0 :
    return "LIST";
    case 1 :
    return "MAP";
    case 2 :
    return "SETTINGS";
    default:
    return null;
  }
}

现在,当你运行该应用时,ViewPager的第二页应该被我们的新片段替换。

Adding the fragment

实现映射片段

我们现在将使用在我们的应用上显示地图。我们可以通过添加带有com.google.android.gms.maps.MapFragmentandroid:name<fragment>布局来做到这一点。这样做会自动将MapFragment添加到activity:

以下是fragment_my_map.xml的代码:

<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
  <fragment android:name="com.google.android.gms.maps.MapFragment" android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

接下来,为了能够处理我们添加到布局中的MapFragment,我们需要使用从getChildFragmentManagerfindFragmentByIdFragmentManager。这将在OnCreateView方法中完成:

FragmentManager fm = getChildFragmentManager();
mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map);
if (mapFragment == null) {
  mapFragment = SupportMapFragment.newInstance();
  fm.beginTransaction().add(R.id.map, mapFragment).commit();
}

我们将把我们的片段分配给SupportMapFragment而不仅仅是MapFragment,这样应用就可以支持低于 12 的安卓应用编程接口级别。使用以下代码:

以下是MyMapFragment.java的代码:

public class MyMapFragment extends Fragment{

  private SupportMapFragment mapFragment;

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_my_map, container, false);

    FragmentManager fm = getChildFragmentManager();
    mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map);
    if (mapFragment == null) {
      mapFragment = SupportMapFragment.newInstance();
      fm.beginTransaction().add(R.id.map, mapFragment).commit();
    }

    return view;
  }

}

现在,当我们运行该应用时,地图将显示在屏幕上。

Implementing MapFragment

添加标记

谷歌地图现在是可见的,但它还没有为用户显示任何有用的数据。为此,我们将添加地图标记来指示用户的兴趣点。这些将是不同工作机会的位置,我们将从我们的 Parse 数据库下载。

我们还将学习如何将用于在地图上标记点的图标更改为自定义图像,以及在标记上设置标题。这将使我们的应用看起来更加有趣和信息丰富。

从解析中检索数据

在我们可以显示我们所有的标记之前,我们需要从 Parse 下载所有必要的数据。

MyMapFragment.java中,我们将使用ParseQuery检索位置列表,并在显示之前使用该列表获取每个工作机会的相关信息。请执行以下步骤:

  • 创建名为GoogleMap类型的googleMap的私有成员变量,并覆盖onResume()方法。
  • onResume()中,检查googleMap是否为空;如果是,这意味着我们还没有向地图的当前实例添加标记。如果googleMap是空的,从MapFragment分配地图,我们已经创建了。这是使用getMap() :

    ```java if (googleMap == null) {

    googleMap = MapFragment.getMap();

    } ```

    完成的 * 创建一个 ParseQuery,它将检索我们的解析数据库的JobOffer表的所有数据。将findInBackground()功能与FindCallback配合使用,这样我们可以只在数据全部下载后才开始处理数据。使用以下代码:

    ```java ParseQuery query = ParseQuery.getQuery("JobOffer"); query.findInBackground(new FindCallback() { @Override public void done(List list, ParseException e) {

    } }); ```

显示每个位置的标记

现在,我们将遍历从 Parse 收到的工作邀请列表,并使用addMarker()googleMap添加一个标记。请执行以下步骤:

  1. findInBackground完成后,创建一个ParseGeoPoint变量和一个循环,循环将迭代列表中的每个项目。我们将使用ParseGeoPoint变量来存储解析数据库中的坐标:

    ```java ParseGeoPoint geoPoint = new ParseGeoPoint();

    for(int i =0;i<list.size();i++){

    } ```

  2. 在循环中,从列表中获取GeoPoint数据,并将其保存到我们的ParseGeoPoint变量:

    java geoPoint = list.get(i).getParseGeoPoint("coordinates");

  3. 最后,在每次迭代中用addMarker() :

    java googleMap.addMarker(new MarkerOptions() .position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude())));

    googleMap添加一个标记

您的 MyMapFragment.java文件应该类似于以下内容:

public class MyMapFragment extends Fragment{

  private SupportMapFragment mapFragment;
  private GoogleMap googleMap;

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_my_map, container, false);

    FragmentManager fm = getChildFragmentManager();
    mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map);
    if (mapFragment == null) {
      mapFragment = SupportMapFragment.newInstance();
      fm.beginTransaction().add(R.id.map, mapFragment).commit();
    }

    return view;
  }

  @Override
  public void onResume() {
    super.onResume();

    if (googleMap == null) {
      googleMap = mapFragment.getMap();

      ParseQuery<JobOffer> query = ParseQuery.getQuery("JobOffer");
      query.findInBackground(new FindCallback<JobOffer>() {
        @Override
        public void done(List<JobOffer> list, ParseException e) {

          ParseGeoPoint geoPoint;

          for(int i =0;i<list.size();i++){
            geoPoint = list.get(i).getParseGeoPoint("coordinates");

            googleMap.addMarker(new MarkerOptions()
            .position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude())));
          }

        }
      });

    }
  }
}

这些标记现在应该在应用上可见:

Displaying a marker for each location

给标记添加标题

在地图上有一个标记的用处不仅仅是显示一个点,也是给用户一个简单易懂的方法来获取这个位置的信息。我们将通过在点击标记时在标记上显示标题来做到这一点。

这可以通过简单地将.title(string)添加到我们的addMarker()方法中来实现:

googleMap.addMarker(new MarkerOptions()
.position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude()))
.title(list.get(i).getTitle()));

我们现在有了一个功能齐全的地图显示,当用户点击标记时,它会在标记上方显示一个标题,如下图所示:

Adding a title to a marker

总结

在本章中,您学习了如何向我们的应用添加地图。这需要我们在谷歌开发者控制台上创建一个项目,并配置我们的应用来访问必要的应用编程接口。一旦我们的应用完全配置完毕,我们就开始将地图添加到我们选择的视图中。我们讨论了用我们的MyMapFragment处理片段中的片段(MapFragment)。虽然单个的MapFragment可以通过代码单独添加,但是将它放在另一个带有布局的片段中,如果需要的话,我们可以将其他 UI 小部件添加到页面中,比如FloatingActionButton。最后,我们通过显示标记和位置信息使地图变得有用,这些信息是从 Parse 下载的。

在下一章中,您将学习如何调试和测试我们的应用。