Android Google 地圖 API for Android


 

從健康類 app Runkeeper 到游戲 app 精靈寶可夢,位置服務對現代 app 來說越來越重要。

在本文中,我們將創建一個 app,名字就叫做 City Guide。這個 app 允許用戶搜索一個地點,使用 Google 地圖顯示這個地點的位置並監聽用戶的位置改變。

我們將學習如何使用 Google 地圖 API for Android,Google 的位置服務 API 和 Google 的 Places API for Android 完成如下工作:

  • 顯示用戶當前位置
  • 在地圖上顯示和自定義大頭釘
  • 查詢給定坐標的位置信息
  • 監聽位置變化
  • 搜索興趣點

開始

打開 Android Studio,在快速啟動菜單中選擇 Start a new Android Studio project

在創建新項目對話框,New Project 視圖,輸入 app 名稱 City Guide,選擇保存地址,點擊 Next。

在 Target Android Devices 窗口,勾選 Phone and Tablet 選框,選擇你想要 app 支持的 minimum SDK。從 Minimum SDK 的下拉框中選擇 API 14。然后點 Next。

在 Add an Activity to Mobile 窗口,選擇 Google Maps Activity 然后點 Next。

在 Customize the Activity 窗口,點擊 Finish,完成項目的創建。

Android Studio 將啟動 Gradle 並編譯項目。這會花幾分鍾。 
打開 MapsActivity.java。它應該是這個樣子:

package com.raywenderlich.cityguide;

import android.support.v4.app.FragmentActivity;
import android.os.Bundle;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

// 1
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

  private GoogleMap mMap;

  // 2
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
        .findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
  }

  // 3
  @Override
  public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;

    // Add a marker in Sydney and move the camera
    LatLng sydney = new LatLng(-34, 151);
    mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
    mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
  }
}
  1. MapsActivity 實現了 OnMapReadyCallback 接口並繼承了 FragmentActivity。
  2. 這個類覆蓋了 FragmentActivity 的 onCreate() 方法。
  3. 同時覆蓋了 OnMapReadyCallback 的 onMapReady() 方法。這個方法在地圖准備就緒時調用。在這個方法中,創建了一個 marker(大頭釘),坐標位於澳大利亞悉尼,然后將 marker 方到地圖上。

Android Studio 在 manifests/AndroidManifest.xml 中添加了如下代碼:

  1. 一個 ACCESS_FINE_LOCATION 權限聲明。要訪問用戶的精確位置,這必不可少。
  2. 添加了一個 com.google.android.geo.API_KEY 的 meta-data。這保存了 API key。

Android Studio 也在 build.gradle 中添加了一個 Google Play Service 的依賴。這個依賴將 Google 地圖和定位服務 API 暴露給 app 使用。

當編譯完成后,運行 app 你會看到:

你看到一個空白窗口,上面沒有地圖;你還沒有為 Google Map 創建 API key。我們將在下一節創建。

注意:如果你使用模擬器,模擬器所安裝的版本必須滿足 build.gradle 文件中 Google Play Service 所要求的版本。如果你看到提示需要升級模擬器的 Google Play Service 版本,你可以從 Android Studio SDK 管理器中下載最新的 Google APIs 並安裝到虛擬設備,或者降低 gradle 依賴中的版本。

使用 Google 地圖 APIs

要使用任何 Google 地圖 API,都需要創建一個 API key 並從開發者控制台中啟用所需的 API。如果你沒有 Google 賬號,現在就去創建它——免費的!

創建 API Key

打開 res/values/google_maps_api.xml,你會看到:

在下一頁,點 Create API key 按鈕。

然后,復制 API key created 對話框中的 API key,點擊 Close。

回到 google_maps_api.xml, 將 google_maps_key 替換成剛才拷貝的 API key。 
運行 app,你會看到地圖和地圖上的紅色大頭釘。

回到 developer console,打開 Google Places API for Android。我們會在后面用這個 API 查找 Place。

建立 Play Services 連接

在編寫 Java 代碼之前,我們需要配置一下 Android Studio 讓它自動為我們插入 import 語句,節省我們的工作量。 
依次打開 Android Studio > Preferences > Editor > General > Auto Import 菜單,選擇 Add unambiguous imports on the fly 和 Show import popup 選項,點擊 OK。

打開 MapsActivity.java ,讓 MapsActivity 實現下列接口:

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, GoogleMap.OnMarkerClickListener, LocationListener

import LocationListener 一句產生了歧義,因此告訴 Android Studio 去 Google Mobile Services 進行導入:

import com.google.android.gms.location.LocationListener;

上述代碼解釋如下:

  • GoogleApiClient.ConnectionCallbacks 提供了一個回調,當客戶端和服務器成功建立連接時調用(onConnected()) 或者臨時性的斷開時調用 (onConnectionSuspended())。
  • GoogleApiClient.OnConnectionFailedListener 提供了一個回調方法 (onConnectionFailed()) ,當客戶端連接服務器失敗時調用。
  • GoogleMap.OnMarkerClickListener 定義了一個 onMarkerClick() 方法,當大頭釘被點擊時調用。
  • LocationListener 定義了 onLocationChanged() 方法,當用戶位置改變時調用。這個方法只有 LocationListener 注冊以后才會調用。

現在,實現上述接口定義的方法。要這樣做,可以按以下步驟:

  1. 把光標放在類聲明的任意地方,點擊類聲明上顯示的紅色燈泡。
  2. 選擇 Implement methods。

  3. 在 Select Methods to implement 對話框,點擊 OK。

這些方法的實現會添加到類中。

要連接 Google Play Services 庫中的 Google API,你需要先創建一個 GoogleApiClient。

在 MapsActivity.java 中添加一個字段:

private GoogleApiClient mGoogleApiClient;

在 onCreate() 中加入:

// 1
if (mGoogleApiClient == null) {
  mGoogleApiClient = new GoogleApiClient.Builder(this)
      .addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this)
      .addApi(LocationServices.API)
      .build();
}

添加兩個方法:

@Override
protected void onStart() {
  super.onStart();
  // 2
  mGoogleApiClient.connect();
}

@Override
protected void onStop() {
  super.onStop();
  // 3
  if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ) {
    mGoogleApiClient.disconnect();
  }
}

代碼說明:

  1. 如果 mGoogleApiClient 變量為空,進行初始化。
  2. 打開一個后台連接,連接到 Google Play 服務。
  3. 如果客服端不為空且狀態為已連接的話,關閉連接。

添加下列代碼到 onMapReady():

mMap.getUiSettings().setZoomControlsEnabled(true); 
mMap.setOnMarkerClickListener(this);

這里我們開啟了地圖的縮放控制並指定了 MapsActdivity 作為回調,這樣當用戶點擊大頭釘時能夠進行處理。

現在,點擊地圖上位於悉尼的大頭釘,你會看到顯示了標題文本:

輸入另外一個坐標,大頭釘會移到你指定的位置。

添加下列代碼將大頭釘設置到紐約,標題文本設置“My Favorite City”:

LatLng myPlace = new LatLng(40.73, -73.99);  // this is New York 
mMap.addMarker(new MarkerOptions().position(myPlace).title("My Favorite City"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(myPlace));

編譯運行。

注意,地圖自動將中心和大頭釘對齊,moveCamera() 的作用就在於次。但是,地圖的縮放比例不正確,因為它是縮得太小了。

將 moveCamera() 方法調用修改為:

mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myPlace, 12));

縮方比例 0 表示將地圖縮小為最小的世界地圖。大部分地圖都支持縮放比例到 20,更遠的地區僅僅支持到 13,將它設為二者之間的 12 比較合適,顯示較多的細節且不會太近。

運行 app 以查看效果。

用戶權限

我們的 app 需要使用 ACCESS_FINE_LOCATION 權限以獲得用戶定位信息;在 AndroidManifest.xml 中我們已經進行了聲明。

從 Android 6.0 開始,用戶權限與之前發生了一點點區別。你不會在安裝 app 時請求權限,而是在運行時,當權限真正需要用到時才請求。

權限分為兩種類別:普通權限和危險權限。對於危險權限需要在運行時向用戶請求授權。要求訪問用戶隱私的權限比如訪問用戶通訊錄、日歷、定位等就屬於危險權限。

打開 MapsActivity.java 添加下列變量:

private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;

新加一個方法 setUpMap() 。

private void setUpMap() {
  if (ActivityCompat.checkSelfPermission(this,
    android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]
      {android.Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);  
    return;
  }
}

上述代碼判斷 app 是否獲得了 ACCESS_FINE_LOCATION 權限。如果沒有,向用戶請求授權。

然后在 onConnectded() 方法中調用這個方法:

@Override
public void onConnected(@Nullable Bundle bundle) {
  setUpMap();
}
注意:關於用戶權限的完整介紹超出了本文的范疇,請參考運行時請求授權的文檔。

獲取當前坐標

定位服務的最常見任務是獲得用戶當前坐標。我們通過 Google Play 服務定位 API 請求用戶設備的最新坐標來實現這個目的。

在 MapsActivity.java, 添加變量:

private Location mLastLocation;

然后,在setUpMap() 最后一句添加代碼:

// 1
mMap.setMyLocationEnabled(true);

// 2
LocationAvailability locationAvailability =
    LocationServices.FusedLocationApi.getLocationAvailability(mGoogleApiClient);
if (null != locationAvailability && locationAvailability.isLocationAvailable()) {
  // 3
  mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
  // 4
  if (mLastLocation != null) { 
    LatLng currentLocation = new LatLng(mLastLocation.getLatitude(), mLastLocation
        .getLongitude());
    mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLocation, 12));
  }
}

代碼說明:

  1. setMyLocationEnabled 一句打開了 my-location 圖層,用於在用戶坐標出繪制一個淺藍色的圓點。同時加一個按鈕到地圖上,當你點擊它,地圖中心會移動到用戶的坐標。
  2. getLocationAvailability 一句判斷設備上的位置信息是否有效。
  3. getLastLocation 一句允許你獲得當前有效的最新坐標。
  4. 如果能夠獲得最新坐標,將鏡頭對准用戶當前坐標。

編譯運行,查看效果。你會看到在用戶當前坐標有一個淺藍色的圓點:

在模擬器上進行測試

要測試地圖類 app,最好用真正的 Android 設備。如果因為某種原因不得不在模擬器上測試,你可以用模擬器模擬出坐標數據。

要做到這個,一種辦法是使用模擬器的擴展控制(extended controls)。你需要這樣做:

  1. 打開模擬器。在右邊面板中,點擊 More 按鈕(…) 以訪問 extended controls。

  2. 在 Extended Controls 對話框的左邊,選擇 Location。
  3. 在下圖指定位置輸入經緯度,點擊 Send。

大頭釘

注意最后一次運行 app 時,用戶位置所在的藍點非常顯眼。Android 地圖 API 允許你使用大頭釘,這是一種圖標,用於放在地圖上層的指定位置。

在 MapsActivity.java 中添加代碼:

protected void placeMarkerOnMap(LatLng location) {
  // 1
  MarkerOptions markerOptions = new MarkerOptions().position(location);
  // 2
  mMap.addMarker(markerOptions);
}
  1. 創建了一個 MarkerOptions 對象並將大頭釘要放在的位置設置為用戶當前坐標。
  2. 將大頭釘添加到地圖。

將 setUpMap() 方法替換為:

private void setUpMap() {
  if (ActivityCompat.checkSelfPermission(this,
      android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]
        {android.Manifest.permission.ACCESS_FINE_LOCATION},LOCATION_PERMISSION_REQUEST_CODE);
    return;
  }

  mMap.setMyLocationEnabled(true);

  LocationAvailability locationAvailability =
      LocationServices.FusedLocationApi.getLocationAvailability(mGoogleApiClient);
  if (null != locationAvailability && locationAvailability.isLocationAvailable()) {
    mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
    if (mLastLocation != null) {
      LatLng currentLocation = new LatLng(mLastLocation.getLatitude(), mLastLocation
          .getLongitude());
      //add pin at user's location
      placeMarkerOnMap(currentLocation);
      mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLocation, 12));
    }
  }
}

setUpMap() 方法中的改變僅僅是調用了 placeMarkerOnMap() 以顯示大頭釘。 
編譯運行查看效果。你現在會在用戶位置看到一個大頭釘:

如果你不喜歡 Android 默認的大頭釘樣式,你也可以創建自己的圖片取代。回到 placeMarkerOnMap() 方法,在 MarkerOptions 初始化之后加入下句:

markerOptions.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource
    (getResources(), R.mipmap.ic_user_location)));

這里下載自定義大頭釘文件 ic_user_location,然后解壓縮。將所有文件拷貝到 mipmap 目錄:

編譯運行查看效果。在你當前位置的大頭釘現在使用了項目中的 ic_user_location 圖片:

如果僅僅是修改默認大頭釘的顏色呢?請自行進行嘗試,如果有難度請參考這個答案:

在 placeMarkerOnMap() 中使用這句:

```java

markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)); 
“` 
這會將默認大頭釘的紅色換成綠色。

![](https://koenig-media.raywenderlich.com/uploads/2016/10/Screen-Shot-2016-10-02-at-10.57.55-PM.png)

改變地圖類型

根據 app 要實現的功能,一般的地圖視圖可能對你就不夠用了。

Android 地圖 API 提供了幾種地圖類型:MAP_TYPE_NORMAL、MAP_TYPE_SATELLITE、 MAP_TYPE_TERRAIN、MAP_TYPE_HYBRID。 
在 setUpMain() 方法的 setMyLocationEnabled() 后面加入一句:

mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);

GoogleMap.MAP_TYPE_TERRAIN 顯示更詳細的地形,顯示地貌變化:

視圖 a more detailed view of the area, showing changes in elevation:

其它類型的效果:

GoogleMap.MAP_TYPE_SATELLITE 顯示衛星地圖,沒有文字標注。

GoogleMap.MAP_TYPE_HYBRID 顯示衛星地圖和普通視圖的結合。

GoogleMap.MAP_TYPE_NORMAL 顯示典型的街道地圖並標注標簽。這也是默認的類型。

實現地理編碼

現在你已經獲得了用戶的坐標,如果在用戶點擊大頭釘時顯示地理名稱就好了。Google 有一個 Geocoder 就是用來干這個的。它將經緯度坐標轉換為一個人類可讀的地址,或者與此相反。

打開 MapsActivity,添加方法:

private String getAddress( LatLng latLng ) {
  // 1
  Geocoder geocoder = new Geocoder( this );
  String addressText = "";
  List<Address> addresses = null;
  Address address = null;
  try {
    // 2
    addresses = geocoder.getFromLocation( latLng.latitude, latLng.longitude, 1 );
    // 3
    if (null != addresses && !addresses.isEmpty()) {
      address = addresses.get(0);
      for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
        addressText += (i == 0)?address.getAddressLine(i):("\n" + address.getAddressLine(i));
      }
    }
  } catch (IOException e ) {
  }
  return addressText;
}

關鍵在於 Address 類是有歧義的,要解決這個問題,需要將 import 語句指定為:

import android.location.Address;

代碼說明:

  1. 創建一個 Geocoder 對象,用於將一個經緯度坐標轉換成地址或進行相反的轉換。
  2. 使用 geocoder 將方法參數接收到的經緯度轉換成地址信息。
  3. 如果響應的 addresses 中包含有地址信息,將這些信息拼接為一個字符串返回。

將 placeMarkerOnMap() 方法修改為:

protected  void placeMarkerOnMap(LatLng location) {
  MarkerOptions markerOptions = new MarkerOptions().position(location);

  String titleStr = getAddress(location);  // add these two lines
  markerOptions.title(titleStr);

  mMap.addMarker(markerOptions);
}

在這個方法中添加了一句 getAddress() 調用,並將地址設置為大頭釘標題。

編譯運行以查看效果。點擊大頭釘,你會看到地址:

點擊地圖的其他地方,地址消失。 
注意,如果你走動位置,藍點會跟着你一起移動,但大頭釘不會。如果你在真機上測試,試着四處移動一下位置。如果在模擬器上測試,將你的坐標用 emulator control 修改到別的地方。

大頭釘不會移動是因為我們的代碼還不知道什么時候位置發生了變化。小藍點位置由 Google API 自己管理,而不是我們的代碼做的。如果想讓 marker 跟隨小藍點移動,需要在代碼中接收位置變化通知。

接收位置變化

隨時知道用戶的位置有助於提供一種良好體驗。本節將介紹如何實時接收用戶位置的變化。 
為了做到這一點,你需要創建一個 location request。 
打開 MapsActivity,增加變量:

// 1
private LocationRequest mLocationRequest;
private boolean mLocationUpdateState;
// 2
private static final int REQUEST_CHECK_SETTINGS = 2;

聲明一個 LocationRequest 變量以及一個保存位置更新狀態的變量。
REQUEST_CHECK_SETTINGS 是用於傳遞給 onActivityResult 方法的 request code。
然后添加方法:

protected void startLocationUpdates() {
  //1
  if (ActivityCompat.checkSelfPermission(this,
      android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
    ActivityCompat.requestPermissions(this,
               new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
               LOCATION_PERMISSION_REQUEST_CODE);  
    return;
  }
  //2
  LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest,
  this);
}
  1. startLocationUpdates() 中,如果 ACCESS_FINE_LOCATION 權限未獲取,則請求授權並返回。
  2. 如果已經獲得授權,請求位置變化信息。

然后添加方法:

// 1
protected void createLocationRequest() {
  mLocationRequest = new LocationRequest();
  // 2
  mLocationRequest.setInterval(10000); 
  // 3
  mLocationRequest.setFastestInterval(5000); 
  mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

  LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
      .addLocationRequest(mLocationRequest);

  PendingResult<LocationSettingsResult> result =
      LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient,
          builder.build());

  result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
    @Override
    public void onResult(@NonNull LocationSettingsResult result) {
      final Status status = result.getStatus();
      switch (status.getStatusCode()) {
        // 4
        case LocationSettingsStatusCodes.SUCCESS: 
          mLocationUpdateState = true;
          startLocationUpdates();
          break;
        // 5
        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: 
          try {
            status.startResolutionForResult(MapsActivity.this, REQUEST_CHECK_SETTINGS);
          } catch (IntentSender.SendIntentException e) {
          }
          break;
        // 6
        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: 
          break;
      }
    }
  });
}

ResultCallback 類的 import 語句有歧義,因此添加下列 import 語句:

import com.google.android.gms.common.api.ResultCallback;

createLocationRequest() 方法代碼說明如下:

  1. 創建一個 LocationRequest 對象,將它添加到一個 LocationSettingsRequest.Builder 對象,並基於用戶位置設置的當前狀態查詢位置變化信息並處理。
  2. setInterval() 指定了 app 多長時間接受一次變化通知。
  3. setFastestInterval() 指定 app 能夠處理的變化通知的最快速度。設置fastestInterval 能夠限制位置變化通知發送給你的 app 的頻率。在開始請求位置變化通知之前,需要檢查用戶位置設置的狀態。
  4. SUCCESS 狀態說明一切正常,你可以初始化一個 location request。
  5. RESOLUTION_REQUIRED 狀態表明位置設置有一個問題有待修復。有可能是因為用戶的位置設置被關閉了。你可以向用戶顯示一個對話框:

  6. SETTINGS_CHANGE_UNAVAILABLE 狀態表明位置設置有一些無法修復的問題。有可能是用戶在上面的對話框里選擇了 NEVER。

現在添加下列方法:

// 1
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == REQUEST_CHECK_SETTINGS) {
    if (resultCode == RESULT_OK) {
      mLocationUpdateState = true;
      startLocationUpdates();
    }
  }
}

// 2
@Override
protected void onPause() {
  super.onPause();
  LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}

// 3
@Override
public void onResume() {
  super.onResume();
  if (mGoogleApiClient.isConnected() && !mLocationUpdateState) {
    startLocationUpdates();
  }
}

代碼說明:

  1. 覆蓋 FragmentActivity 的 onActivityResult() 方法,如果REQUEST_CHECK_SETTINGS 請求返回的是一個 RESULT_OK,則發起位置更新請求。
  2. 覆蓋 onPause() 方法,停止位置變化請求。
  3. 覆蓋 onResume() 方法,重新開始位置更新請求。

然后,在 onCreate() 方法的最后調用 createLocationRequest() 方法。

createLocationRequest();

然后,在 onConnected() 方法中添加如下語句:

if (mLocationUpdateState) {
  startLocationUpdates();
}

如果用戶的位置設置是打開狀態的話,啟動位置更新。

在 onLocationChanged() 方法中加入:

mLastLocation = location;
if (null != mLastLocation) {
  placeMarkerOnMap(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()));
}

這里,我們修改 mLastLocation 為最新的位置並用新位置坐標刷新地圖顯示。

你的 app 現在已經可以接受位置變化通知了。當你改變位置,地圖上的大頭釘會隨位置的改變而變。注意,點擊大頭釘仍然能夠看到地址信息。 
編譯運行,四處走動查看變化:

查詢興趣點

因為 app 是用於扮演一個向導的角色,用戶應該能夠找到他們感興趣的地方吧?

這就是 Google Places API 出場的時候了。它讓你的 app 能夠搜索數百萬計的興趣點和大型機構。Android 庫有許多非常酷的功能,其中之一就是 Place Picker,這是一個 UI widget,允許你用寥寥數行代碼就實現一個搜索 PIO(興趣點)的功能。太好了,這是真的嗎?你可以試一試。

打開MapsActivity,添加變量:

private static final int PLACE_PICKER_REQUEST = 3;

然后添加下列方法:

private void loadPlacePicker() {
  PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();

  try {
    startActivityForResult(builder.build(MapsActivity.this), PLACE_PICKER_REQUEST);
  } catch(GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) {
      e.printStackTrace();
  }
}

這個方法創建了新的 builder 用於創建 intent,這個 Intent 用於打開一個 Place Picker UI,然后打開這個 PlacePicker Intent。

將下列語句添加到 onActivityResult():

if (requestCode == PLACE_PICKER_REQUEST) {
  if (resultCode == RESULT_OK) {
    Place place = PlacePicker.getPlace(this, data);
    String addressText = place.getName().toString();
    addressText += "\n" + place.getAddress().toString();

    placeMarkerOnMap(place.getLatLng());
  }
}

在這里,如果請求代碼是 PLACE_PICKER_REQUEST 且返回碼是 RESULT_OK,則讀取所選地點的信息。然后放一個大頭釘在該位置。

搜索 PIO 基本搞定——剩下的就是調用 loadPlacePicker() 方法。

我們需要創建一個浮動的 Action 按鈕(FAB)在地圖右下角並用於調用這個方法。FAB 需要使用 CoordinatorLayout,這是 design 支持庫中的內容。

首先,打開 build.gradle 添加依賴 Android support design library:

dependencies {
  ...
  compile 'com.android.support:design:24.1.1'
}
注意:通常,如果你用的 Android SDK 版本比較新,你可能需要同時升級這個依賴的的版本,以便二者匹配。

然后修改 res > layout > activity_maps.xml 為:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

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

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:src="@android:drawable/ic_menu_search"/>

</android.support.design.widget.CoordinatorLayout>

我們在原先的地圖上已經有一個用於顯示地圖的 fragment;現在所做的就是添加一個 FAB。

在 MapsActivity 的 onCreate() 方法,添加如下代碼:

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    loadPlacePicker();
  }
});

編譯運行,點擊地圖下方的 search 按鈕,會彈出 place picker:

https://koenig-media.raywenderlich.com/uploads/2016/09/placepickerdemo4.gif” width= “320”/>

結束

這里下載最終完成的項目。 
關於 Google 地圖 APIs,本文只介紹了很少一部分。在 Google 官方文檔中,有更多關於 web service 和這個 Android API 的內容。

你還可以查看開發者頁面中其它定制大頭釘的方法。本文中的運行時用戶權限檢查需要改進,這里也有很好的東西可以參考:關於更高級的權限授權

更多閱讀,請參考開發者頁面:Google Places API for Android接受位置變化通知 和模擬位置數據模擬器的 extendet controls

有問題和建議,請在下面留言。

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM