转载请标明出处:http://blog.csdn.net/android_ls/article/details/8586229
实践需求:通过GPS或者3G/2G网络(基站)定位,并将定位的结果(我当前的位置)标注在百度地图上。
当用户点击地图上代表“我的当前位置”的点时,显示当前我所在位置的详细信息。
一、通过GPS或者3G/2G网络(基站)定位。
基于百度地图定位SDK(版本v3.1)(一) .和百度地图定位SDK(版本v3.1)(二) .这两篇的积淀,导入百度提供的so和jar文件及在AndroidManifest.xml文件添加相应的配置等等就不罗嗦了,不了解的请查看前面的。
注意:由于要使用百度的提供的定位服务,所以在AndroidManifest.xml文件中一定要记得添加Service。(若没添加,运行不报错,也没有任何效果。)
设置定位参数,注册定位结果接收器。代码如下:
mLocationClient = new LocationClient(this.getApplicationContext()); mLocationListener = new MyLocationListener(); mLocationClient.registerLocationListener(mLocationListener); LocationClientOption locationOption = new LocationClientOption(); locationOption.setOpenGps(true); locationOption.setCoorType("bd09ll"); locationOption.setPriority(LocationClientOption.GpsFirst); locationOption.setAddrType("all"); locationOption.setProdName("通过GPS定位"); mLocationClient.setLocOption(locationOption); Log.i(TAG, "BaiduMapMyLocationActivity 开启定位"); mLocationClient.start();
二、将定位的结果(我当前的位置)标注在百度地图上。
1、在前面为 mLocationClient对象注册了BDLocationListener,自定义类实现BDLocationListener接口,处理定位得到的结果,具体代码如下:
class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation(BDLocation location) { if (location == null) { return; } Log.i(TAG, "BaiduMapMyLocationActivity onReceiveLocation()"); // TODO 为方便测试查看 testLog(location); // 在地图上标注定位得到我当前的位置 markLocation(location); mBDLocation = location; } @Override public void onReceivePoi(BDLocation arg0) { } }
2、构建一个负责在地图上显示用户当前位置的Overlay对象及为该Overlay对象设置数据。
mLocationOverlay = new LocationOverlay(mMapView); // 百度官方API文档解释:打开指南针,但是我试验觉得默认指南针就是打开的 // mLocationOverlay.enableCompass(); locData = new LocationData(); mLocationOverlay.setData(locData);
3、将负责在地图上显示用户当前位置的Overlay对象添加到覆盖物列表中,并刷新地图视图view。
mMapView.getOverlays().add(mLocationOverlay); mMapView.refresh();
4、将我的当前位置移动到地图的中心点
mMapController.animateTo(new GeoPoint( (int) (locData.latitude * 1e6), (int) (locData.longitude * 1e6)));
三、当用户点击地图上代表“我的当前位置”的点时,显示当前我所在位置的详细信息。自定义类继承MyLocationOverlay,重写 protected boolean dispatchTap() {}方法,在方法体中添加相应的处理即可。
class LocationOverlay extends MyLocationOverlay { public LocationOverlay(MapView mapView) { super(mapView); } // 处理在“我的位置”坐标上的点击事件。 @Override protected boolean dispatchTap() { Log.i(TAG, "BaiduMapMyLocationActivity 处理在“我的位置”坐标上的点击事件 dispatchTap()"); if (mBDLocation != null) { showLocation(mBDLocation); } return super.dispatchTap(); } }
四、运行效果图
点击地图上的蓝色指南针图标,效果图如下:
点击上图黑色区域,该区域消失;点击“重新定位”按钮,重新获取我当前的位置。
注:我在房子采用的是Wifi网络定位,所以地址信息不为null。
五、我发现存在的问题:
1、百度最新API与API文档中描述个别不符
a. 这行mLocationOverlay.enableCompass();代码,百度官方API文档解释:打开指南针,但是我经过测试,觉得默认指南针就是打开的。
b. 地图引擎管理类BMapManager,API文档中描述:boolean start() // 开启百度地图API;boolean stop() // 终止百度地图API,调用此函数后,不会再发生回调。实际上使用最新的SDK中的BMapManager类创建的对象已经没有什么start、stop方法。
2、当调用 mLocationClient.start(); (LocationClient类实例化后,调用它的start方法。)后,会重新执行一次BaiduMapApplication(# class BaiduMapApplication extends Application{})的onCreate()回调方法。目前道没发现影响使用,但是我个人觉得:若之前已执行过,也就是说初始化过地图引擎类,就不应该再次初始化。不知道这个算是个问题不?官方给的demo里也存在这个问题,怎么解决?(资源浪费使用)
六、完整代码:
AndroidManifest.xml文件配置:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.easi.baidu.map" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:name=".BaiduMapApplication" > <activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:name=".BaiduMapMyLocationActivity" android:screenOrientation="sensor" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:enabled="true" android:name="com.baidu.location.f" android:process=":remote" /> </application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" > </uses-permission> <uses-permission android:name="android.permission.INTERNET" > </uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" > </uses-permission> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" > </uses-permission> <uses-permission android:name="android.permission.READ_PHONE_STATE" > </uses-permission> <uses-permission android:name="android.permission.CALL_PHONE" > </uses-permission> <permission android:name="android.permission.BAIDU_LOCATION_SERVICE" > </permission> <uses-permission android:name="android.permission.BAIDU_LOCATION_SERVICE" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" > </uses-permission> <uses-permission android:name="android.permission.ACCES_MOCK_LOCATION" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_GPS" /> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="false" android:resizeable="true" android:smallScreens="true" /> </manifest>
初始化地图引擎对象,存放共享资源,继承自Application。
package com.easi.baidu.map; import android.app.Application; import android.content.Context; import android.util.Log; import android.widget.Toast; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.MKGeneralListener; import com.baidu.mapapi.map.MKEvent; /** * 初始化地图引擎对象,存放共享资源。 * 需要在AndroidMinifest.xml文件中的application标签添加name属性。 * {@link #<application android:name=".BaiduMapApplication" >} * @author android_ls */ public class BaiduMapApplication extends Application { /** * 申请的百度地图API Key密钥 */ private static final String BAIDU_MAP_KEY = "8BB7F0E5C9C77BD6B9B655DB928B74B6E2D838FD"; /** * Log打印标签 */ private static final String TAG = "BaiduMapApplication"; /* package */BMapManager mMapManager; /* package */Context mContext; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "BaiduMapApplication onCreate()"); mContext = getApplicationContext(); mMapManager = new BMapManager(getApplicationContext()); initMapManager(); } /** * 初始化BMapManager对象 * * @return boolean */ public void initMapManager() { boolean result = mMapManager.init(BAIDU_MAP_KEY, new MKGeneralListener() { @Override public void onGetNetworkState(int error) { if (error == MKEvent.ERROR_NETWORK_CONNECT) { Toast.makeText(mContext, "您的网络出错啦!", Toast.LENGTH_LONG).show(); } else if (error == MKEvent.ERROR_NETWORK_DATA) { Toast.makeText(mContext, "输入正确的检索条件!", Toast.LENGTH_LONG).show(); } } @Override public void onGetPermissionState(int error) { if (error == MKEvent.ERROR_PERMISSION_DENIED) { // 授权Key错误: Toast.makeText(mContext, "您的授权Key出错!", Toast.LENGTH_LONG).show(); } } }); if (!result) { Toast.makeText(mContext, "初始化地图引擎失败!", Toast.LENGTH_LONG).show(); } else { Log.i(TAG, "地图引擎初始化成功!"); } } @Override public void onTerminate() { Log.i(TAG, "BaiduMapApplication onTerminate()"); if (mMapManager != null) { mMapManager.destroy(); mMapManager = null; } super.onTerminate(); } }
需要用到百度地图的Activity基类,继承自Activity
package com.easi.baidu.map; import android.app.Activity; import android.content.res.Configuration; import android.os.Bundle; import android.util.Log; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.map.MapController; import com.baidu.mapapi.map.MapView; /** * 需要用到百度地图的Activity基类 * #public class BaiduMapMyLocationActivity extends BaiduMapBaseActivity{} * @author android_ls */ public abstract class BaiduMapBaseActivity extends Activity { /** * Application对象的引用 */ protected BaiduMapApplication mApplication; /** * 显示地图的View组件 */ protected MapView mMapView; /** * MapView控制器对象的引用 */ protected MapController mMapController; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mApplication = (BaiduMapApplication) this.getApplication(); if (mApplication.mMapManager == null) { Log.e("BaiduMapApplication", "BaiduMapBaseActivity mApplication.mMapManager is NULL!"); mApplication.mMapManager = new BMapManager(mApplication.mContext); mApplication.initMapManager(); } setContentView(getLayoutId()); mMapView = (MapView) this.findViewById(getMapViewId()); mMapController = mMapView.getController(); } /** * 获取代表布局文件的ID * #setContentView(getLayoutId()); * @return int */ public abstract int getLayoutId(); /** * 获取代表MapView对象的组件ID * #(MapView) this.findViewById(getMapViewId()); * @return int */ public abstract int getMapViewId(); @Override protected void onPause() { mMapView.onPause(); super.onPause(); } @Override protected void onResume() { mMapView.onResume(); super.onResume(); } @Override protected void onDestroy() { mMapView.destroy(); super.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mMapView.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mMapView.onRestoreInstanceState(savedInstanceState); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } }
定位功能具体实现类,继承自BaiduMapBaseActivity
package com.easi.baidu.map; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.TextView; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.baidu.mapapi.map.LocationData; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.MyLocationOverlay; import com.baidu.platform.comapi.basestruct.GeoPoint; /** * 提供通过GPS或者3G/2G网络(基站)定位,并将定位的结果(我当前的位置)标注在百度地图上。 * 当用户点击地图上代表“我的当前位置”的点时,显示当前我所在位置的详细信息。 * * 注:当gps可用,而且获取了定位结果时,不再发起网络请求,直接返回给用户坐标。 * 如果gps不可用,再发起网络请求,进行定位。 * @author android_ls * */ public class BaiduMapMyLocationActivity extends BaiduMapBaseActivity { /** * Log打印标签 */ private static final String TAG = "BaiduMapMyLocationActivity"; /** * 定位SDK的核心类 */ private LocationClient mLocationClient; /** * 显示定位结果详细信息的View */ private TextView tvLocationResult; /** * 标注我的位置的覆盖物 * # class LocationOverlay extends MyLocationOverlay{} */ private MyLocationOverlay mLocationOverlay; /** * 我的位置信息数据 */ private LocationData locData; /** * 定位结果处理器 * # class MyLocationListener implements BDLocationListener{} */ private MyLocationListener mLocationListener; /** * 暂时缓存我的当前位置数据 */ private BDLocation mBDLocation; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, "BaiduMapMyLocationActivity onCreate()"); mMapController.setZoom(15); mMapController.enableClick(true); tvLocationResult = (TextView) this.findViewById(R.id.tv_location_result); mLocationClient = new LocationClient(this.getApplicationContext()); mLocationListener = new MyLocationListener(); mLocationClient.registerLocationListener(mLocationListener); LocationClientOption locationOption = new LocationClientOption(); locationOption.setOpenGps(true); locationOption.setCoorType("bd09ll"); locationOption.setPriority(LocationClientOption.GpsFirst); locationOption.setAddrType("all"); locationOption.setProdName("通过GPS定位"); mLocationClient.setLocOption(locationOption); Log.i(TAG, "BaiduMapMyLocationActivity 开启定位"); mLocationClient.start(); // 重新定位 findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mLocationClient != null && mLocationClient.isStarted()) mLocationClient.requestLocation(); } }); mLocationOverlay = new LocationOverlay(mMapView); // 百度官方API文档解释:打开指南针,但是我试验觉得默认指南针就是打开的 // mLocationOverlay.enableCompass(); locData = new LocationData(); tvLocationResult.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tvLocationResult.setVisibility(View.GONE); } }); } class LocationOverlay extends MyLocationOverlay { public LocationOverlay(MapView mapView) { super(mapView); } // 处理在“我的位置”坐标上的点击事件。 @Override protected boolean dispatchTap() { Log.i(TAG, "BaiduMapMyLocationActivity 处理在“我的位置”坐标上的点击事件 dispatchTap()"); if (mBDLocation != null) { showLocation(mBDLocation); } return super.dispatchTap(); } } /** * 显示我当前位置的详细信息 * @param location */ private void showLocation(BDLocation location) { StringBuffer sb = new StringBuffer(256); sb.append(" time : "); sb.append(location.getTime()); sb.append("\n error code : "); sb.append(location.getLocType()); sb.append("\n latitude : "); sb.append(location.getLatitude()); sb.append("\n lontitude : "); sb.append(location.getLongitude()); // 判断是否有定位精度半径 if (location.hasRadius()) { // 获取定位精度半径,单位是米 float accuracy = location.getRadius(); Log.i(TAG, "accuracy = " + accuracy); sb.append("\n radius : "); sb.append(location.getRadius()); } if (location.getLocType() == BDLocation.TypeGpsLocation) { if (location.hasSpeed()) { sb.append("\n speed : "); sb.append(location.getSpeed()); } if (location.hasSateNumber()) { sb.append("\n satellite : "); sb.append(location.getSatelliteNumber()); } } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) { // 获取反地理编码。 只有使用网络定位的情况下,才能获取当前位置的反地理编码描述。 if (location.hasAddr()) { String address = location.getAddrStr(); Log.i(TAG, "address = " + address); sb.append("\n addr : "); sb.append(address); } } tvLocationResult.setVisibility(View.VISIBLE); tvLocationResult.setText(sb); } /** * 在地图上标注定位得到我当前的位置 * @param location */ private void markLocation(BDLocation location) { locData.latitude = location.getLatitude(); locData.longitude = location.getLongitude(); locData.direction = location.getDerect(); // 判断是否有定位精度半径 if (location.hasRadius()) { // 获取定位精度半径,单位是米 locData.accuracy = location.getRadius(); } mLocationOverlay.setData(locData); mMapView.getOverlays().add(mLocationOverlay); mMapView.refresh(); // 将我的当前位置移动到地图的中心点 mMapController.animateTo(new GeoPoint( (int) (locData.latitude * 1e6), (int) (locData.longitude * 1e6))); } class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation(BDLocation location) { if (location == null) { return; } Log.i(TAG, "BaiduMapMyLocationActivity onReceiveLocation()"); // TODO 为方便测试查看 testLog(location); // 在地图上标注定位得到我当前的位置 markLocation(location); mBDLocation = location; } @Override public void onReceivePoi(BDLocation arg0) { } } /** * 打印测试LOG信息 * @param location */ private void testLog(BDLocation location) { String province = location.getProvince(); // 获取省份信息 String city = location.getCity(); // 获取城市信息 String district = location.getDistrict(); // 获取区县信息 Log.i(TAG, "province = " + province); Log.i(TAG, "city = " + city); Log.i(TAG, "district = " + district); int type = location.getLocType(); Log.i(TAG, "当前定位采用的类型是:type = " + type); String coorType = location.getCoorType(); Log.i(TAG, "坐标系类型:coorType = " + coorType); float derect = location.getDerect(); Log.i(TAG, "derect = " + derect); } @Override public int getLayoutId() { return R.layout.main; } @Override public int getMapViewId() { return R.id.bmapView; } @Override protected void onDestroy() { Log.i(TAG, "BaiduMapMyLocationActivity onDestroy() start"); mLocationClient.stop(); mLocationClient.unRegisterLocationListener(mLocationListener); this.mApplication.onTerminate(); super.onDestroy(); Log.i(TAG, "BaiduMapMyLocationActivity onDestroy() end"); } }