阐述一个概念,地图覆盖物:所有叠加或覆盖到地图的内容,我们统称为地图覆盖物。如标注、矢量图形元素(包括:折线和多边形和圆)、定位图标等。覆盖物拥有自己的地理坐标,当您拖动或缩放地图时,它们会相应的移动。
要实现的需求:假如我们知道北京天安门(建筑物)的GPS纬度经度值:39.915,116.404,想要把它在百度地图上标注出来。
实现上述需求的步骤:
一、准备工作:
1、创建android工程,并导入百度地图需要用到的jar包和so文件。
2、在AndroidManifest文件中添加使用百度地图SDK所需的权限及屏幕配置。
3、在布局文件layout中添加显示百度地图的MapView。
4、在继承了Activity类的子类中:
a. 创建并初始化地图引擎管理对象;
b. 通过组件ID获取代表地图显示View的MapView对象,并设置相应属性。
(比如:启用内置的缩放控件、设置允许的地图缩放级别等)
c. 重写Activity的生命周期回调方法onResume()、onPause()和onDestroy(),管理地图引擎管理类对象和显示对象生命周期。
5、详细的请阅读上一篇:Android 百度地图SDK (v2.0.0)初探
二、在地图上标注出北京天安门:
1、想要在地图上标注一个建筑物,总得有一个标识吧?
获取在地图上标识建筑物的图标对象:
// 获取用于在地图上标注一个地理坐标点的图标 Drawable drawable = this.getResources().getDrawable(R.drawable.icon_marka);
2、在基础图上添加覆盖物(添加图层)
a. 编写覆盖物类,自己定义一个类,继承自ItemizedOverlay<OverlayItem>类,需要重写父类的构造函数、createItem(int index)和size()方法。
注:从2.0.0开始,SDK不支持直接继承Overlay , 用户可通过继承ItemizedOverlay来添加覆盖物。
b. 在自定义的覆盖物类(继承自ItemizedOverlay<OverlayItem>)中, 声明一个用于存放覆盖物的集合:
/**覆盖物列表集合*/ private ArrayList<OverlayItem> mOverlayList = new ArrayList<OverlayItem>();
声明double类型的变量存储北京天安门的纬度、经度值:
// 声明double类型的变量存储北京天安门的纬度、经度值 private double mLat1 = 39.915; // point1纬度 private double mLon1 = 116.404; // point1经度
c. 在构造函数中,将GPS纬度经度值转换成以微度的整数形式存储的地理坐标点
/* 注:GeoPoint对象构造方法的参数列表:第一个是参数表示纬度,第二个是经度 (我们平时都是经纬度这么叫的,想着应该是经度在前的,呵呵。) 在网上查了下,GPS的值官方给的就是纬度经度,也就是说纬度是在前的,以前一直没太注意。*/ GeoPoint geoPoint1 = new GeoPoint((int) (mLat1 * 1E6), (int) (mLon1 * 1E6));
构造OverlayItem对象并添加到mOverlayList集合里
mOverlayList.add(new OverlayItem(geoPoint1, "point1", "point1"));
必须调用的方法:
/* * 官方的解释:在一个新ItemizedOverlay上执行所有操作的工具方法。 * 没搞明白啥意思,但是必须的调用这个方法,否则程序运行报错*/ populate();
d. 返回的是从指定List集合中,取出的一个OverlayItem对象。
/* * 返回的是从指定List集合中,取出的一个OverlayItem对象。 * mOverlayList集合里一旦有了数据,在调用其之前, * 一定的在MyOverlayItem的构造函数里调用这个方法populate(); */ @Override protected OverlayItem createItem(int index) { return mOverlayList.get(index); }
e. 获取当前覆盖物列表的大小
@Override public int size() { return mOverlayList.size(); }
自定义的覆盖物类的完整代码:
/** * 包含了一个覆盖物列表的覆盖物类 * @author android_ls */ final class MyOverlayItem extends ItemizedOverlay<OverlayItem> { /**覆盖物列表集合*/ private ArrayList<OverlayItem> mOverlayList = new ArrayList<OverlayItem>(); // 声明double类型的变量存储北京天安门的纬度、经度值 private double mLat1 = 39.915; // point1纬度 private double mLon1 = 116.404; // point1经度 // 传进来的Drawable对象用于在地图上标注一个地理坐标点 public MyOverlayItem(Drawable drawable) { super(drawable); // 将GPS纬度经度值转换成以微度的整数形式存储的地理坐标点 /* 注:GeoPoint对象构造方法的参数列表:第一个是参数表示纬度,第二个是经度 (我们平时都是经纬度这么叫的,想着应该是经度在前的,呵呵。) 在网上查了下,GPS的值官方给的就是纬度经度,也就是说纬度是在前的,以前一直没太注意。*/ GeoPoint geoPoint1 = new GeoPoint((int) (mLat1 * 1E6), (int) (mLon1 * 1E6)); // 构造OverlayItem对象并添加到mOverlayList集合里 mOverlayList.add(new OverlayItem(geoPoint1, "point1", "point1")); /* * 官方的解释:在一个新ItemizedOverlay上执行所有操作的工具方法。 * 没搞明白啥意思,但是必须的调用这个方法,否则程序运行报错*/ populate(); } /* * 返回的是从指定List集合中,取出的一个OverlayItem对象。 * mOverlayList集合里一旦有了数据,在调用其之前, * 一定的在MyOverlayItem的构造函数里调用这个方法populate(); */ @Override protected OverlayItem createItem(int index) { return mOverlayList.get(index); } @Override public int size() { return mOverlayList.size(); } }
创建覆盖物(MyOverlayItem)对象并添加到覆盖物列表中:
mMapView.getOverlays().add(new MyOverlayItem(drawable));
3、刷新地图
mMapView.refresh();
运行效果图如下:
package com.android.baidu.map; import java.util.ArrayList; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.widget.Toast; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.MKGeneralListener; import com.baidu.mapapi.map.ItemizedOverlay; import com.baidu.mapapi.map.MKEvent; import com.baidu.mapapi.map.MapController; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.OverlayItem; import com.baidu.platform.comapi.basestruct.GeoPoint; /** * 在地图上标注已知GPS纬度经度值的建筑物 * 场景:假如我们知道北京天安门(建筑物)的GPS纬度经度值:39.915,116.404,想要把它在地图上标注出来。 * @author android_ls * */ public class BaiduMapOverlayActivity extends Activity { /**地图引擎管理类*/ private BMapManager mBMapManager = null; /**显示地图的View*/ private MapView mMapView = null; /** * 经研究发现在申请KEY时:应用名称一定要写成my_app_应用名(也就是说"my_app_"是必须要有的)。 * 百度地图SDK提供的服务是免费的,接口无使用次数限制。您需先申请密钥(key),才可使用该套SDK。 * */ public static final String BAIDU_MAP_KEY = "07418AEC69BAAB7104C6230A6120C580DFFAEEBB"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 注意:请在调用setContentView前初始化BMapManager对象,否则会报错 mBMapManager = new BMapManager(this.getApplicationContext()); mBMapManager.init(BAIDU_MAP_KEY, new MKGeneralListener() { @Override public void onGetNetworkState(int iError) { if (iError == MKEvent.ERROR_NETWORK_CONNECT) { Toast.makeText(BaiduMapOverlayActivity.this.getApplicationContext(), "您的网络出错啦!", Toast.LENGTH_LONG).show(); } } @Override public void onGetPermissionState(int iError) { if (iError == MKEvent.ERROR_PERMISSION_DENIED) { // 授权Key错误: Toast.makeText(BaiduMapOverlayActivity.this.getApplicationContext(), "请在 DemoApplication.java文件输入正确的授权Key!", Toast.LENGTH_LONG).show(); } } }); setContentView(R.layout.main); mMapView = (MapView) this.findViewById(R.id.bmapsView); // 设置启用内置的缩放控件 mMapView.setBuiltInZoomControls(true); // 获取地图控制器,可以用它控制平移和缩放 MapController mMapController = mMapView.getController(); // 设置地图的缩放级别。 这个值的取值范围是[3,18]。 mMapController.setZoom(13); // 获取用于在地图上标注一个地理坐标点的图标 Drawable drawable = this.getResources().getDrawable(R.drawable.icon_marka); // 创建覆盖物(MyOverlayItem)对象并添加到覆盖物列表中 mMapView.getOverlays().add(new MyOverlayItem(drawable)); // 刷新地图 mMapView.refresh(); } /** * 包含了一个覆盖物列表的覆盖物类 * @author android_ls */ final class MyOverlayItem extends ItemizedOverlay<OverlayItem> { /**覆盖物列表集合*/ private ArrayList<OverlayItem> mOverlayList = new ArrayList<OverlayItem>(); // 声明double类型的变量存储北京天安门的纬度、经度值 private double mLat1 = 39.915; // point1纬度 private double mLon1 = 116.404; // point1经度 // 传进来的Drawable对象用于在地图上标注一个地理坐标点 public MyOverlayItem(Drawable drawable) { super(drawable); // 将GPS纬度经度值转换成以微度的整数形式存储的地理坐标点 /* 注:GeoPoint对象构造方法的参数列表:第一个是参数表示纬度, * 第二个是经度(我们平时都是经纬度这么叫的,想着应该是经度在前的,呵呵。) * 在网上查了下,GPS的值官方给的就是纬度经度,也就是说纬度是在前的,以前一直没太注意。*/ GeoPoint geoPoint1 = new GeoPoint( (int) (mLat1 * 1E6), (int) (mLon1 * 1E6)); // 构造OverlayItem对象并添加到mOverlayList集合里 mOverlayList.add(new OverlayItem(geoPoint1, "point1", "point1")); /* * 官方的解释:在一个新ItemizedOverlay上执行所有操作的工具方法。 * 没搞明白啥意思,但是必须的调用这个方法,否则程序运行报错*/ populate(); } /* * 返回的是从指定List集合中,取出的一个OverlayItem对象。 * mOverlayList集合里一旦有了数据,在调用其之前, * 一定的在MyOverlayItem的构造函数里调用这个方法populate(); */ @Override protected OverlayItem createItem(int index) { return mOverlayList.get(index); } @Override public int size() { return mOverlayList.size(); } } // 重写以下方法,管理API @Override protected void onResume() { mMapView.onResume(); if (mBMapManager != null) { mBMapManager.start(); } super.onResume(); } @Override protected void onPause() { mMapView.onPause(); if (mBMapManager != null) { mBMapManager.stop(); } super.onPause(); } @Override protected void onDestroy() { mMapView.destroy(); if (mBMapManager != null) { mBMapManager.destroy(); mBMapManager = null; } super.onDestroy(); } }
三、在地图上标注出北京天安门附近的几个点:
从2.0.0开始,SDK不支持直接继承Overlay 。 在地图上显示一个或一组覆盖物,都可以通过继承ItemizedOverlay来添加覆盖物。
在上面讲解的基础上,修改覆盖物类的部分代码就可以了。直接上代码:
class MyOverlayItem extends ItemizedOverlay<OverlayItem> { /**覆盖物列表集合*/ private ArrayList<OverlayItem> mOverlayList = new ArrayList<OverlayItem>(); // 场景:假如我们有一组建筑物的GPS经纬度值,想要把这些建筑物在地图上标注出来。 private double mLat1 = 39.90923; // point1纬度 private double mLon1 = 116.397428; // point1经度 private double mLat2 = 39.9022;// point2纬度 private double mLon2 = 116.3922; // point2经度 private double mLat3 = 39.917723; // point3纬度 private double mLon3 = 116.3722; // point3纬度 private double mLat4 = 39.915; // point4纬度 private double mLon4 = 116.404; // point4经度 // 传进来的Drawable对象用于在地图上标注一个地理坐标点 public MyOverlayItem(Drawable drawable) { super(drawable); // 将GPS纬度经度值转换成以微度的整数形式存储的地理坐标点 /* 注:GeoPoint对象构造方法的参数列表:第一个是参数表示纬度,第二个是经度 (我们平时都是经纬度这么叫的,想着应该是经度在前的,呵呵。) 在网上查了下,GPS的值官方给的就是纬度经度,也就是说纬度是在前的,以前一直没太注意。*/ GeoPoint geoPoint1 = new GeoPoint((int) (mLat1 * 1E6), (int) (mLon1 * 1E6)); GeoPoint geoPoint2 = new GeoPoint((int) (mLat2 * 1E6), (int) (mLon2 * 1E6)); GeoPoint geoPoint3 = new GeoPoint((int) (mLat3 * 1E6), (int) (mLon3 * 1E6)); GeoPoint geoPoint4 = new GeoPoint((int) (mLat4 * 1E6), (int) (mLon4 * 1E6)); // 构造OverlayItem对象并添加到mOverlayList集合里 mOverlayList.add(new OverlayItem(geoPoint1, "point1", "point1")); mOverlayList.add(new OverlayItem(geoPoint2, "point2", "point2")); mOverlayList.add(new OverlayItem(geoPoint3, "point3", "point3")); mOverlayList.add(new OverlayItem(geoPoint4, "point4", "point4")); // 必须的调用这个方法,否则程序运行报错 populate(); } /* * 返回的是从指定List集合中,取出的一个OverlayItem对象。 * mOverlayList集合里一旦有了数据,在调用其之前, * 一定的在MyOverlayItem的构造函数里调用这个方法populate(); */ @Override protected OverlayItem createItem(int index) { return mOverlayList.get(index); } @Override public int size() { return mOverlayList.size(); } }
对代码进行优化:
GPSPonit 实体类:
package com.android.baidu.map.entity; /** * 类名: Ponit.java * 功能描述:存放GPS纬度、经度值 * @author android_ls * 创建日期: 2013-2-10 下午07:43:47 * @version V1.0 */ public class GPSPonit { private double mLat; // 纬度 private double mLon; // 经度 public double getmLat() { return mLat; } public void setmLat(double mLat) { this.mLat = mLat; } public double getmLon() { return mLon; } public void setmLon(double mLon) { this.mLon = mLon; } public GPSPonit(double mLat, double mLon) { this.mLat = mLat; this.mLon = mLon; } public GPSPonit() { } @Override public String toString() { return "Ponit [mLat=" + mLat + ", mLon=" + mLon + "]"; } }
优化后的Activity类:
package com.android.baidu.map; import java.util.ArrayList; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.widget.Toast; import com.android.baidu.map.entity.GPSPonit; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.MKGeneralListener; import com.baidu.mapapi.map.ItemizedOverlay; import com.baidu.mapapi.map.MKEvent; import com.baidu.mapapi.map.MapController; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.OverlayItem; import com.baidu.platform.comapi.basestruct.GeoPoint; /** * 在地图上标注已知GPS纬度经度值的一组建筑物 * @author android_ls * */ public class BaiduMapOverlayItemsActivity extends Activity { /**地图引擎管理类*/ private BMapManager mBMapManager = null; /**显示地图的View*/ private MapView mMapView = null; /** * 经研究发现在申请KEY时:应用名称一定要写成my_app_应用名(也就是说"my_app_"是必须要有的)。 * 百度地图SDK提供的服务是免费的,接口无使用次数限制。您需先申请密钥(key),才可使用该套SDK。 * */ public static final String BAIDU_MAP_KEY = "07418AEC69BAAB7104C6230A6120C580DFFAEEBB"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 注意:请在调用setContentView前初始化BMapManager对象,否则会报错 mBMapManager = new BMapManager(this.getApplicationContext()); mBMapManager.init(BAIDU_MAP_KEY, new MKGeneralListener() { @Override public void onGetNetworkState(int iError) { if (iError == MKEvent.ERROR_NETWORK_CONNECT) { Toast.makeText(BaiduMapOverlayItemsActivity.this.getApplicationContext(), "您的网络出错啦!", Toast.LENGTH_LONG).show(); } } @Override public void onGetPermissionState(int iError) { if (iError == MKEvent.ERROR_PERMISSION_DENIED) { // 授权Key错误: Toast.makeText(BaiduMapOverlayItemsActivity.this.getApplicationContext(), "请在 DemoApplication.java文件输入正确的授权Key!", Toast.LENGTH_LONG).show(); } } }); setContentView(R.layout.main); mMapView = (MapView) this.findViewById(R.id.bmapsView); // 设置启用内置的缩放控件 mMapView.setBuiltInZoomControls(true); // 获取地图控制器,可以用它控制平移和缩放 MapController mMapController = mMapView.getController(); // 设置地图的缩放级别。 这个值的取值范围是[3,18]。 mMapController.setZoom(13); //TODO 构建一组数据 GPSPonit gp1 = new GPSPonit(39.90923, 116.397428); GPSPonit gp2 = new GPSPonit(39.9022, 116.3922); GPSPonit gp3 = new GPSPonit(39.917723, 116.3722); GPSPonit gp4 = new GPSPonit(39.915, 116.404); /**存放GPS纬度、经度值的数组*/ GPSPonit[] mGPSPonit = new GPSPonit[4]; mGPSPonit[0] = gp1; mGPSPonit[1] = gp2; mGPSPonit[2] = gp3; mGPSPonit[3] = gp4; Drawable drawable = this.getResources().getDrawable(R.drawable.icon_marka); // 创建覆盖物(MyOverlayItem)对象并添加到覆盖物列表中 mMapView.getOverlays().add(new MyOverlayItem(drawable, mGPSPonit)); // 刷新地图 mMapView.refresh(); } final class MyOverlayItem extends ItemizedOverlay<OverlayItem> { /**覆盖物列表集合*/ private ArrayList<OverlayItem> mOverlayList = new ArrayList<OverlayItem>(); // 场景:假如我们有一组建筑物的GPS经纬度值,想要把这些建筑物在地图上标注出来。 // 传进来的Drawable对象用于在地图上标注一个地理坐标点 public MyOverlayItem(Drawable drawable, GPSPonit[] gPSPonit) { super(drawable); for(int i = 0; i < gPSPonit.length; i++){ GPSPonit gpp = gPSPonit[i]; GeoPoint geoPoint = new GeoPoint( (int) (gpp.getmLat() * 1E6), (int) (gpp.getmLon() * 1E6)); mOverlayList.add(new OverlayItem(geoPoint, "point"+i, "point1"+i)); } // 必须的调用这个方法,否则程序运行报错 populate(); } /* * 返回的是从指定List集合中,取出的一个OverlayItem对象。 * mOverlayList集合里一旦有了数据,在调用其之前, * 一定的在MyOverlayItem的构造函数里调用这个方法populate(); */ @Override protected OverlayItem createItem(int index) { return mOverlayList.get(index); } @Override public int size() { return mOverlayList.size(); } } // 重写以下方法,管理API @Override protected void onResume() { mMapView.onResume(); if (mBMapManager != null) { mBMapManager.start(); } super.onResume(); } @Override protected void onPause() { mMapView.onPause(); if (mBMapManager != null) { mBMapManager.stop(); } super.onPause(); } @Override protected void onDestroy() { mMapView.destroy(); if (mBMapManager != null) { mBMapManager.destroy(); mBMapManager = null; } super.onDestroy(); } }
运行效果图如下: