http://www.linuxidc.com/Linux/2011-07/38867p2.htm
本篇文章主要講解Baidu Map API中MyLocationOverlay的使用。故名思義,MyLocation中文釋義為“我的位置”,而Overlay則是“圖層”或“覆蓋物”的意思,MyLocationOverlay的作用正是用於在地圖上標註自己所處的位置。它跟使用ItemizedOverlay非常相似,只不過MyLocationOverlay標記的只有一個點。
在地圖上標記使用者當前所處位置其實是一個GPS定位應用。首先通過GPS定位擷取到使用者當前所在位置的經緯度,再將該經緯度所代表的點在地圖上標出來。其實除了在地圖上標註自己所處的位置外,我們通常還有這樣的需求:“如果我的位置發生改變,要能夠即時在地圖上體現出來”。
下面我們就來一步步實現上面想要的功能,主要是通過MyLocationOverlay結合LocationListener來實現的。
1)建立布局檔案res/layout/main.xml
<?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.baidu.mapapi.MapView Android:id="@+id/map_View" Android:layout_width="fill_parent" Android:layout_height="fill_parent" Android:clickable="true" /> </LinearLayout>
2)建立Activity繼承com.baidu.mapapi.MapActivity
package com.liufeng.baidumap; import Android.location.Location; import Android.os.Bundle; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.GeoPoint; import com.baidu.mapapi.LocationListener; import com.baidu.mapapi.MKLocationManager; import com.baidu.mapapi.MapActivity; import com.baidu.mapapi.MapController; import com.baidu.mapapi.MapView; import com.baidu.mapapi.MyLocationOverlay; /** * 建立Activity(繼承com.baidu.mapapi.MapActivity) * * @author liufeng * @date 2011-05-02 */ public class MyLocationActivity extends MapActivity implements LocationListener { private BMapManager mapManager; private MKLocationManager mLocationManager = null; private MyLocationOverlay myLocationOverlay; private MapView mapView; private MapController mapController; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 初始化MapActivity mapManager = new BMapManager(getApplication()); // init方法的第一個參數需填入申請的API Key mapManager.init("285B415EBAB2A92293E85502150ADA7F03C777C4", null); super.initMapActivity(mapManager); mLocationManager = mapManager.getLocationManager(); // 註冊位置更新事件 mLocationManager.requestLocationUpdates(this); // 使用GPS定位 mLocationManager.enableProvider((int) MKLocationManager.MK_GPS_PROVIDER); mapView = (MapView) findViewById(R.id.map_View); // 設定地圖模式為交通地圖 mapView.setTraffic(true); // 設定啟用內建的縮放控制項 mapView.setBuiltInZoomControls(true); // 構造一個經緯度點 GeoPoint point = new GeoPoint((int) (26.597239 * 1E6), (int) (106.720397 * 1E6)); // 取得地圖控制器對象,用於控制MapView mapController = mapView.getController(); // 設定地圖的中心 mapController.setCenter(point); // 設定地圖預設的縮放層級 mapController.setZoom(7); // 添加定位元影像層 myLocationOverlay = new MyLocationOverlay(this, mapView); // 註冊GPS位置更新的事件,讓地圖能即時顯示當前位置 myLocationOverlay.enableMyLocation(); // 開啟磁場感應感應器 myLocationOverlay.enableCompass(); mapView.getOverlays().add(myLocationOverlay); } @Override protected boolean isRouteDisplayed() { return false; } @Override protected void onDestroy() { if (mapManager != null) { mapManager.destroy(); mapManager = null; } mLocationManager = null; super.onDestroy(); } @Override protected void onPause() { if (mapManager != null) { mapManager.stop(); } super.onPause(); } @Override protected void onResume() { if (mapManager != null) { mapManager.start(); } super.onResume(); } /** * 根據MyLocationOverlay配置的屬性確定是否在地圖上顯示當前位置 */ @Override protected boolean isLocationDisplayed() { return myLocationOverlay.isMyLocationEnabled(); } /** * 當位置發生變化時觸發此方法 * * @param location 當前位置 */ @Override public void onLocationChanged(Location location) { if (location != null) { // 將當前位置轉換成地理座標點 final GeoPoint pt = new GeoPoint((int) (location.getLatitude() * 1000000), (int) (location.getLongitude() * 1000000)); // 將當前位置設定為地圖的中心 mapController.setCenter(pt); } } }
簡單解釋:代碼中是通過MyLocationOverlay在地圖上標記當前所在位置的,通過實現監聽器介面com.baidu.mapapi.LocationListener並重寫它的onLocationChanged方法來監聽位置的變化。(注意LocationListener是baidu map api裡的,而不是Android內建的)
3)在AndroidManifest.xml中配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:Android="http://schemas.android.com/apk/res/android" package="com.liufeng.baidumap" Android:versionCode="1" Android:versionName="1.0"> <application Android:icon="@drawable/icon" android:label="@string/app_name"> <activity Android:name=".MyLocationActivity" 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> <uses-sdk Android:minSdkVersion="4" /> <!-- 訪問網路的許可權 --> <uses-permission Android:name="android.permission.INTERNET" /> <!-- 訪問精確位置的許可權 --> <uses-permission Android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 訪問網路狀態的許可權 --> <uses-permission Android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 訪問WIFI網路狀態的許可權 --> <uses-permission Android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 改變WIFI網路狀態的許可權 --> <uses-permission Android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 讀寫儲存卡的許可權 --> <uses-permission Android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 讀取電話狀態的許可權 --> <uses-permission Android:name="android.permission.READ_PHONE_STATE" /> </manifest>
到這裡,我們的代碼編寫工作就已經完成了。接下來,再模擬器裡運行一下,看看效果,是在模擬器裡啟動並執行:
怎麼回事?不是說能夠在地圖上標註出當前位置的,好像並沒有標註。沒錯,的確是沒有標註出當前位置。這是什麼原因呢?前面說了,我們要實現的是一個GPS結合Map的簡單定位應用。手機的GPS定位是依賴於手機裝置上的一塊GPS物理晶片來實現的,並不是手機上裝個GPS軟體就能實現定位的。問題就很明顯了,因為我們是在模擬器裡運行上面的程式,模擬器上可沒有GPS物理晶片,當然也就不能實現定位了。
好在DDMS為我們提供了類比GPS訊號的功能,它可以通過三種方式來類比:自訂經緯度、使用GPX檔案或KML檔案(這些檔案是GPS資訊記錄的檔案格式)。依次點擊Eclipse上的"Window"-"Show View"-"Other"-"Android"-"Emulator Control",就能夠看到如下所示的介面:
這個就是用來類比GPS訊號的。可以看到有三個標籤項:Manual、GPX和KML,分別對應於上面所說的“自訂經緯度”、“使用GPX檔案”和“使用KML檔案”三種方式來類比GPS訊號。這裡我們採用第一種方式,因為它最簡單,也最直觀,好理解。
我們輸入一個經緯度值(經度:106.720397,緯度:26.597239),然後點擊“Send”按鈕,模擬器將會收到該訊號,這意味模擬器裝置當前所處的位置正是我們所發送的這個經緯度所代表的位置,這樣就能在地圖上標記出當前所處位置了。真的是這樣嗎?那我們趕緊嘗試一下。輸入經緯度值的如下:
點擊發送按鈕後,再來看下模擬器上顯示的地圖是否發生變化:
可以看到,已經在地圖上標記出了“當前位置”,就是那個藍色的圓點。到這裡我們工作還沒有完,請繼續往下看。
最開始的需求裡有這樣一條:“如果我的位置發生改變,要能夠即時在地圖上體現出來”,我們的代碼裡是否已實現該功能呢,當然。那如何來類比示範該功能呢?其實也很簡單,我們再向模擬器發送另外一組不同的經緯度值,這就相當於裝置所處的位置發生了改變。比如我們再次發送的經緯度值為“經度:106.720397,緯度:24.597239”,再來看下運行結果的變化:
可以看到,當位置發生變動時,是能夠時時體現在地圖上的。到這裡我們的所有工作就算完成了。
備忘:建議如果有條件的朋友,盡量買部Android真機來做開發測試,因為模擬器並不能真實地類比手機上的所有功能。有很多時候很可能你的程式一點問題也沒有,但是由於模擬器不支援或者你不清楚如何通過模擬器來類比某種功能而導致你反覆的調試修改代碼,造成不必要的時間浪費。