2.地圖位置
2.1 問題
需要在地圖上為使用者顯示一個或多個位置。此外,要在同一張地圖上顯示使用者自己的位置。
2.2 解決方案
(API Level 9)
向用戶顯示地圖的最簡單方式就是用位置資料建立一個Intent並把它傳遞給Android系統來啟動地圖應用程式。另外,Google+Play/">Google Play Services庫的Google Maps v2庫元件提供的Map View和MapActivity可以在應用程式中嵌入地圖。
要點:
Google Maps v2是作為Google Play Services庫的一部分進行分發的,它在任意平臺級別都不是原生SDK的一部分。然而,目標平臺為API Level 9或以後版本的應用程式以及Google Play體系內的裝置都可以使用此繪畫庫。
1. 獲取API金鑰
要開始使用Maps v2,需要建立一個API專案,在該專案內啟用Maps v2服務,生成API金鑰幷包括在應用程式程式碼中。如果沒有API金鑰,雖然也可以利用繪圖類,但不會嚮應用程式返回任何地圖圖塊(map tile)。請遵循如下步驟:
(1)進入 ofollow,noindex">https://code.google.com/apis/console/ ,使用你的Google賬戶登入以訪問Google API控制檯。
(2)選擇Create Project選項,為你的地圖建立新的專案。如果已有專案,則可以根據喜好向其中新增Maps v2服務和金鑰。在此例中,選擇要新增Maps v2的專案。
(3)在導航面板中,選擇Services,向下滾動到Google Maps Android API v2並啟用該服務。
(4)在導航面板中選擇API Access,並且選擇Create new Android Key選項。
(5)遵循螢幕上的說明,根據想要使用的應用程式向金鑰新增金鑰庫簽名/應用程式包對。在此例中,示例應用程式的包名是com.androidrecipes.mapper,而簽名來自開發機器上的除錯金鑰,通常位於<USERHOME>/.android/debug.keystore。
如果在模擬器上測試你的執行程式碼,模擬器必須使用目標平臺為Android 4.3或以後版本的SDK構建,這些版本包含Google API,從而繪圖操作可以正常執行。以前版本的SDK捆綁了Map v1 庫而不是Google Play Services,因此它們不能用於測試。
如果是通過命令列建立模擬器,目標的名稱就是“Google Inc:Google APIs:X”,其中的X指示API的版本。如果在IDE(例如Eclipse)中建立模擬器,目標的命名約定也是類似的:Google APIs(Google Inc.) -X,其中X指示API的版本。
2.滿足清單要求
獲得了有效的API金鑰之後,需要在我們的AndroidManifest.xml檔案包括該金鑰。下面的程式碼塊必須放在<application>元素內:
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR_KEY_HERE" />
此外,Maps v2有一項裝置要求,即至少要具備OpenGL ES2.0。我們可以將此要求作為裝置特性提出,方法是在<manifest>元素內新增如下程式碼塊,通常是放在<application>元素的上方:
<!-- Maps v2 requires OpenGL ES 2.0 --> <uses-feature android:glEsVersion="0x00020000" android:required="true" />
最後,Maps v2 需要一組許可權才能與Google Play Services通訊以及渲染地圖圖塊。因此,我們必須在<manifest>元素內再新增一個程式碼塊,通常是放在<application>元素的上方:
<!-- 顯示地圖所需要的許可權 --> <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="com.google.android.providers.gsf.permission.READ_GSERVICES" />
將上述內容整合在一起,清單檔案如下所示。
AndroidManifest.xml的部分程式碼
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androidrecipes.mapper" > <!-- 需要在地圖上顯示使用者的位置 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 顯示地圖所需的許可權 --> <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="com.google.android.providers.gsf.permission.READ_GSERVICES" /> <!-- Maps v2 requires OpenGL ES 2.0 --> <uses-feature android:glEsVersion="0x00020000" android:required="true" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <!--活動、服務、提供程式等--> <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR_KEY_HERE" /> </application> </manifest>
手邊有了API金鑰和合適的測試平臺之後,就可以開始正式工作了。
2.3 實現機制
要想顯示地圖,只需要建立一個MapView或MapFragment例項。API金鑰在應用程式中全域性可用,因此這些元素的任何例項都會使用該值。不需要像在Maps v1中一樣將金鑰新增到每個例項中。
注意:
除了上述許可權之後,還必須為此例新增android.permission.ACCESS_FINE_LOCATION許可權。需要此許可權的唯一原因是該例會連線到LoactionManager以獲得快取的位置值。
我們將建立一個簡單的應用程式,它會將使用者上次最新的已知位置顯示在Google地圖上。

使用者位置的地圖
現在,讓我們檢視構造此檢視所需的佈局,參見以下程式碼清單。
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text="Map Of Your Location" /> <RadioGroup android:id="@+id/group_maptype" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <RadioButton android:id="@+id/type_normal" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Normal Map" /> <RadioButton android:id="@+id/type_satellite" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Satellite Map" /> </RadioGroup> <fragment class="com.google.android.gms.maps.SupportMapFragment" android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
注意:
在XML佈局中新增MapView或MapFragment時,必須指定包的完全限定名稱,這是由於該類並沒有包含在android.view或android.widget中。
在此建立了一個簡單佈局,其中包括一個選擇器,用於切換在MapFragment例項旁邊顯示的地圖型別。以下程式碼清單顯示了控制地圖的Activity程式碼。
顯示快取位置的Activity
public class BasicMapActivity extends FragmentActivity implements RadioGroup.OnCheckedChangeListener { private static final String TAG = "AndroidRecipes"; private SupportMapFragment mMapFragment; private GoogleMap mMap; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //檢查 play services 是否啟用且為最新版本 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); switch (resultCode) { case ConnectionResult.SUCCESS: Log.d(TAG, "Google Play Services is ready to go!"); break; default: showPlayServicesError(resultCode); return; } mMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mMap = mMapFragment.getMap(); //快速檢查使用者的最新已知位置是否有效, 並且圍繞該點將地圖居中 // 如果該位置無效,則使用預設位置 LocationManager manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); Location location = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER); LatLng mapCenter; if(location != null) { mapCenter = new LatLng(location.getLatitude(), location.getLongitude()); } else { //使用預設位置 mapCenter = new LatLng(37.4218,-122.0840); } //居中地圖並同時縮放 CameraUpdate newCamera = CameraUpdateFactory.newLatLngZoom(mapCenter, 13); mMap.moveCamera(newCamera); // 連線地圖型別選擇器 UI RadioGroup typeSelect = (RadioGroup) findViewById(R.id.group_maptype); typeSelect.setOnCheckedChangeListener(this); typeSelect.check(R.id.type_normal); } @Override public void onResume() { super.onResume(); if (mMap != null) { //啟用地圖上使用者位置顯示功能 mMap.setMyLocationEnabled(true); } } @Override public void onPause() { super.onResume(); if (mMap != null) { //在不可見時禁用使用者位置 mMap.setMyLocationEnabled(false); } } /* * 當 Play Services缺失或版本不對時, * 客戶端庫將以對話方塊的形式幫助使用者進行更新。 */ private void showPlayServicesError(int errorCode) { // 從 Google Play services 獲得錯誤對話方塊 Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog( errorCode, this, 1000 /* RequestCode */); // 如果 Google Play services 可以提供錯誤對話方塊 if (errorDialog != null) { // 為錯誤對話方塊建立新的 DialogFragment SupportErrorDialogFragment errorFragment = SupportErrorDialogFragment.newInstance(errorDialog); // 在DialogFragment中顯示錯誤對話方塊 errorFragment.show( getSupportFragmentManager(), "Google Maps"); } } /** OnCheckedChangeListener 方法 */ @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.type_satellite: //顯示衛星地圖 mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); break; case R.id.type_normal: default: //顯示普通地圖 mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); break; } } }
首先執行的操作是確認將正確版本的Google Play Services安裝到此裝置上。在裝置的使用者與Google應用程式(如Google Play)互動時,Google會自動管理Google Play Services庫。Google Play Services在後臺自動更新,因此我們需要使用來自GooglePlayServicesUtil的方法,在執行時確認使用者具有我們所需的版本。從isGooglePlayServicesAvaiable()獲得的結果將告訴我們服務是否為正確的版本還是需要更新,甚至是需要完全安裝。
這個Activity會獲得使用者最新的位置資訊並將該位置設為地圖的中心。關於地圖的所有控制操作都是通過GoogleMap例項來完成的,而GoogleMap例項則是通過呼叫MapFragment.getMap()得到的。在此例中,我們使用了地圖的moveCamera()方法,通過CameraUpdate物件對地圖的顯示做了調整。
CameraUpdate用於一次性對地圖顯示的一個或多個元件進行調整,例如修改縮放以及中心點。地圖的縮放級別是2.0和21.0之間的離散值,其中的最小值會使整個世界地圖近似為1024dp寬,而每增加一級縮放就會使顯示中的世界地圖的寬度翻倍。
當用戶選擇不同的單選按鈕時,地圖型別會在衛星檢視和傳統的地圖檢視之間切換,除了此例中使用的值之外,其他允許的地圖型別如下:
-
Map_TYPE_HYBRID:在衛星地圖上顯示地圖資料(例如,街道和感興趣的點)。
-Map_TYPE_TERRAIN :使用地形海拔輪廓線顯示地圖。
最後,為啟用使用者位置顯示和控制元件,我們只需要對地圖呼叫setMyLocationEnabled()。該方法將啟用位置跟蹤並可能開啟GPS等元素,因此也應該在不再需要時禁用(當檢視不可見時)。
為正確構建此應用程式,以下程式碼清單顯示了build.gradle檔案中所需的依賴關係。
build.gradle檔案的部分程式碼
apply plugin: 'com.android.application' android { compileSdkVersion 21 buildToolsVersion '26.0.2' defaultConfig { applicationId "com.androidrecipes.mapper" ... } ...... } dependencies { compile 'com.android.support:support-v4:21.0.+' compile 'com.google.android.gms:play-services:6.1.+' }
這是很好的起點,但或許有點令人厭煩。為引入一些更具互動性的內容,下一節將在地圖上建立標識和其他標記,並且會介紹如何定製這些標記。