上篇讲到百度地图基础地图的使用,这篇主要实现路线规划和自定义图层展示的功能,同时包括地图视野缩放旋转视野跟随等功能
效果图:
如何进行路径规划和展示路线:
首先路径规划需要2个坐标分别为起点和终点,包含经度和纬度,百度提供了一个类来封装经纬度
com.baidu.mapapi.model.LatLng.LatLng,其实就是一个包含2个浮点数的实体类。
// 天安门坐标
double mLat1 = 39.915291;
double mLon1 = 116.403857;
// 百度大厦坐标
double mLat2 = 40.056858;
double mLon2 = 116.308194;
LatLng loc_start = new LatLng(mLat1,mLon1);
LatLng loc_end = new LatLng(mLat2, mLon2);
接下来初始化搜索类RoutePlanSearch,并注册搜索监听:
mSearch = RoutePlanSearch.newInstance();
mSearch.setOnGetRoutePlanResultListener(this);
搜索监听器需要实现下面几个方法,分别返回的是驾车路线、公交车行车线、步行路线三种方案:
@Override
public void onGetDrivingRouteResult(DrivingRouteResult result) {
//TODO: 在地图上绘制驾车路线
}
@Override
public void onGetTransitRouteResult(TransitRouteResult result) {
}
@Override
public void onGetWalkingRouteResult(WalkingRouteResult result) {
}
我只需要驾车路线,所以只重新onGetDrivingRouteResult接口就可以了:
@Override
public void onGetDrivingRouteResult(DrivingRouteResult result) {
//TODO: 在地图上绘制驾车路线
if (result.error == SearchResult.ERRORNO.AMBIGUOUS_ROURE_ADDR) {
//起终点或途经点地址有岐义,通过以下接口获取建议查询信息
//result.getSuggestAddrInfo()
Log.d("baiduMap", "起终点或途经点地址有岐义");
return;
}
if (result.error == SearchResult.ERRORNO.PERMISSION_UNFINISHED) {
//权限鉴定未完成则再次尝试
Log.d("baiduMap", "权限鉴定未完成,再次尝试");
startSearch(loc_start,loc_end);
return;
}
if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) {
Toast.makeText(this, "抱歉,未找到结果", Toast.LENGTH_SHORT).show();
return;
}
if (result.error == SearchResult.ERRORNO.NO_ERROR) {
route = result.getRouteLines().get(0);
WalkingRouteOverlay overlay = new MyWalkingRouteOverlay(mBaidumap);
mBaidumap.setOnMarkerClickListener(overlay);
overlay.setData(result.getRouteLines().get(0));
overlay.addToMap();
overlay.zoomToSpan();
}
}
在上面的代码中对返回的结果进行了多次验证以保证容错性,只有在返回NO_ERROR时才绘制路线,注意PERMISSION_UNFINISHED这个错误,这个错误是在我们调用了搜索方法之后,但是sdk还没有获取到当前客户端是否有使用搜索功能的权限则会出现这个错误,就是说sdk还在获取权限资格的时候我们提前调用了搜索方法,通常情况下在我们开始搜索的时候已经获取到权限了,但是如果你的应用在地图页面启动的时候就需要开始搜索路线就有可能造成这个错误,但是我们又有这个需求,那么怎么办呢。我们只需要检查PERMISSION_UNFINISHED这个异常,如果返回的是这个异常的话,那么再重新请求搜索就可以了。如上的startSearch(loc_start,loc_end)方法,就是我自定义的一个方法,用于开始搜索。
我把这个方法贴上:
private void startSearch(Location start,Location end) {
PlanNode stNode = PlanNode.withLocation(new LatLng(start.getLat(), start.getLng()));
PlanNode enNode = PlanNode.withLocation(new LatLng(end.getLat(), end.getLng()));
mSearch.drivingSearch((new DrivingRoutePlanOption()).from(stNode).to(enNode));
}
最近查看官方的更新日志,在3.6.0的更新日志中给出了优化了这个错误并另一种解决方法,下面贴上日志原文:
【 修 复 】
优化
1、 鉴权结果广播策略调整,当失败、成功或鉴权状态变化时均广播通知(SDKInitializer新增key验证成功广播常量SDK_BROADTCAST_ACTION_STRING_PERMISSION_CHECK_OK)
鉴权未完成时,发起检索失败,请监听广播,当SDK_BROADTCAST_ACTION_STRING_PERMISSION_CHECK_OK时再次发起检索
新版的SDK在遇到权限未鉴定完成的时候会发送SDK_BROADTCAST_ACTION_STRING_PERMISSION_CHECK_OK广播,可以通过监听这个广播来处理这个异常。
在获取路径的回调中调用overlay.addToMap()
方法来绘制路径时,最好加入try catch来捕获异常,因为,百度规定搜索出的路线节点超过1000个时,这个方法会抛出异常。但在实际使用中我发现小于1000个也偶尔会抛出异常,比如搜索成都到拉萨的行车路线,3.5.0的官方demo也发生异常崩溃了。但是升级到3.6.0之后似乎修复了这个bug,不再崩溃也能正常显示,但是更新日志中却没有写到,所以我任然建议这里手动捕获处理一下:
//路线查询成功
try {
overlay.setData(result.getRouteLines().get(0));
overlay.addToMap();
overlay.zoomToSpan();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "路径规划异常", Toast.LENGTH_SHORT).show();
}
onGetDrivingRouteResult()方法中返回NO_ERROR时,我们把路径绘制到地图上,其中的MyWalkingRouteOverlay是自定义的路线绘制的类,继承自DrivingRouteOverlay,该类提供了几个方法用于绘制起点、终点图标和路径颜色等方法,我们可以重写它来,达到自定义绘制的效果:
private class MyDrivingRouteOverlay extends DrivingRouteOverlay {
public MyDrivingRouteOverlay(BaiduMap baiduMap) {
super(baiduMap);
}
@Override
public int getLineColor() {
//红色的路径
return Color.RED;
}
@Override
public BitmapDescriptor getStartMarker() {
//自定义的起点图标
return BitmapDescriptorFactory.fromResource(R.drawable.myicon_start);
}
@Override
public BitmapDescriptor getTerminalMarker() {
//自定义的终点图标
return BitmapDescriptorFactory.fromResource(R.drawable.myicon_end);
}
}
这样就可以在界面上绘制出自定义的路线了,自百度更新3.6.0之后更是开源了路径绘制部分的源代码,现在下载最新的可以修改源码绘制出带动画的路径。具体就查看官方文档,目前我还没有看过源码,以后再补上。
绘制了路径,通常我们会在起点和终点做2个弹出窗,像下面这样的:
百度提供了在地图上绘制弹出窗的方法,同时也可以设置它的动画和点击事件具有交互性,但是SDK强制规定了这样的具有交互性的弹出窗在地图中只能有一个,这也就导致我之前遇到的问题,只出现了一个窗口。
对于这个问题,没有什么解决方案,必将是SDK的强制规定。通过查阅资料,了解到有一个替代方案,就是不使用弹出窗,而使用图标,百度地图上图标可以存在多个,这样我们就需要把我们的弹出窗做出图像格式的图标来进行显示,百度提供了这样一种图像格式BitmapDescriptor ,同时提供了把View转换成BitmapDescriptor 的静态方法:
BitmapDescriptorFactory.fromView(View view);
//根据一个 View 创建 Bitmap 描述信息, 当 view 为 null 时返回 null
使用这个方法把我们需要展示的View转换为BitmapDescriptor在用来构造一个覆盖物OverlayOptions:
View popstart = LayoutInflater.from(this).inflate(R.layout.layout_mappop, null, false);
BitmapDescriptor startbitsp = BitmapDescriptorFactory.fromView(popstart);
OverlayOptions startpop = new MarkerOptions().position(new LatLng(纬度,经度)).icon(startbitsp);
然后在需要显示覆盖物的时候,调用如下方法:
mBaiduMap.addOverlay(startpop);
这样就完成了自定义效果的展示,使用下面的方法可以调整覆盖物的横纵偏移量:
MarkerOptions.anchor(0f, 0f)
两个参数否是0-1的浮点数,分别表示横坐标和纵坐标相对应当前位置的偏移比例。
比如起点设置的是0f,1f;终点设置的是0f,0f