九、出行路线规划
首先要搞清楚什么是路线规划,比如有两个地点,A和B。从A到B有多种方路线和交通工具可以选择,这就是路线规划。
那么平时常见的路线规划有哪些呢?步行、驾车、公交地铁等。这也是导航中使用最多的,那么下面先来说说这个步行出行路线规划,以下简称步行。
① 准备工作
这个路线规划我是打算单独放在一个Activity中,这样看起来会更加的清晰,因为我在MainActivity中已经写了很多的其他的功能的业务代码了,再加进去看起来好像就不是很容易去理解。也避免一个Activity打天下的尴尬局面。右键点击这个包名,然后New → Activity → Empty Activity
然后命名为RouteActivity,点击Finish完成Activity的创建。
创建好之后先做好准备工作,我先说说需要做好什么样的准备。一进入RouteActivity之后就要定位到当前所在地,这在前面我已经说过了,因此下面这一部分的代码我就不做讲解,如果你不理解是为什么,那么请从头看起。
首先修改activity_route.xml。
代码语言:txt复制<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RouteActivity">
<!--地图-->
<com.amap.api.maps.MapView
android:id="@ id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
然后修改一下RouteActivity的代码:
代码语言:txt复制package com.llw.mapdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.LocationSource;
import com.amap.api.maps.MapView;
import com.amap.api.maps.UiSettings;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.MyLocationStyle;
/**
* 路线规划
* @author llw
*/
public class RouteActivity extends AppCompatActivity implements
AMapLocationListener,LocationSource {
private static final String TAG = "RouteActivity";
//地图
private MapView mapView;
//地图控制器
private AMap aMap = null;
//声明AMapLocationClient类对象
public AMapLocationClient mLocationClient = null;
//声明AMapLocationClientOption对象
public AMapLocationClientOption mLocationOption = null;
//位置更改监听
private OnLocationChangedListener mListener;
//定义一个UiSettings对象
private UiSettings mUiSettings;
//定位样式
private MyLocationStyle myLocationStyle = new MyLocationStyle();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_route);
//初始化定位
initLocation();
//初始化地图
initMap(savedInstanceState);
//启动定位
mLocationClient.startLocation();
}
/**
* 初始化定位
*/
private void initLocation() {
//初始化定位
mLocationClient = new AMapLocationClient(getApplicationContext());
mLocationClient.setLocationListener(this);
mLocationOption = new AMapLocationClientOption();
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
mLocationOption.setOnceLocationLatest(true);
mLocationOption.setNeedAddress(true);
mLocationOption.setHttpTimeOut(20000);
mLocationOption.setLocationCacheEnable(false);
mLocationClient.setLocationOption(mLocationOption);
}
/**
* 初始化地图
*
* @param savedInstanceState
*/
private void initMap(Bundle savedInstanceState) {
mapView = findViewById(R.id.map_view);
mapView.onCreate(savedInstanceState);
//初始化地图控制器对象
aMap = mapView.getMap();
//设置最小缩放等级为16 ,缩放级别范围为[3, 20]
aMap.setMinZoomLevel(16);
//开启室内地图
aMap.showIndoorMap(true);
//实例化UiSettings类对象
mUiSettings = aMap.getUiSettings();
//隐藏缩放按钮 默认显示
mUiSettings.setZoomControlsEnabled(false);
//显示比例尺 默认不显示
mUiSettings.setScaleControlsEnabled(true);
// 自定义定位蓝点图标
myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.gps_point));
//设置定位蓝点的Style
aMap.setMyLocationStyle(myLocationStyle);
// 设置定位监听
aMap.setLocationSource(this);
// 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
aMap.setMyLocationEnabled(true);
}
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
if (aMapLocation != null) {
if (aMapLocation.getErrorCode() == 0) {
//地址
String address = aMapLocation.getAddress();
//获取纬度
double latitude = aMapLocation.getLatitude();
//获取经度
double longitude = aMapLocation.getLongitude();
Log.d(TAG, aMapLocation.getCity());
Log.d(TAG,address);
//停止定位后,本地定位服务并不会被销毁
mLocationClient.stopLocation();
//显示地图定位结果
if (mListener != null) {
// 显示系统图标
mListener.onLocationChanged(aMapLocation);
}
} else {
//定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
Log.e("AmapError", "location Error, ErrCode:"
aMapLocation.getErrorCode() ", errInfo:"
aMapLocation.getErrorInfo());
}
}
}
@Override
public void activate(OnLocationChangedListener onLocationChangedListener) {
mListener = onLocationChangedListener;
if (mLocationClient == null) {
mLocationClient.startLocation();//启动定位
}
}
@Override
public void deactivate() {
mListener = null;
if (mLocationClient != null) {
mLocationClient.stopLocation();
mLocationClient.onDestroy();
}
mLocationClient = null;
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
protected void onDestroy() {
super.onDestroy();
//销毁定位客户端,同时销毁本地定位服务。
if (mLocationClient != null) {
mLocationClient.onDestroy();
}
mapView.onDestroy();
}
}
然后我们需要从MainActivity中进入RouteActivity,因此修改一下activity_main.xml。
代码语言:txt复制 <!--浮动按钮 跳转路线Activity-->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@ id/fab_route"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@ id/fab_poi"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:clickable="true"
android:onClick="jumpRouteActivity"
app:fabSize="mini"
android:src="@drawable/icon_route"
app:backgroundTint="#FFF"
app:backgroundTintMode="screen"
app:hoveredFocusedTranslationZ="18dp"
app:pressedTranslationZ="18dp" />
你可能没有这个icon_route图标,建议去我的源码里面拿,当然我也可以贴出来。
这个浮动按钮会出现在获取poi浮动按钮的左边。
然后我们在MainActivity中增加jumpRouteActivity方法,如下所示:
代码语言:txt复制 /**
* 进入路线规划
* @param view
*/
public void jumpRouteActivity(View view) {
startActivity(new Intent(this,RouteActivity.class));
}
然后就会跳转到RouteActivity。运行效果如下:
那么现在准备工作就做完了。下面正式进入到路线规划的代码编写。
② 步行路线规划
路线规划首先需要两个点,起点和终点。起点其实已经有了,那就是我们当前所在地,至于终点可以由用户来控制,比如我在当前所在位置,然后点击了地图上某一个地方,把这个地方作为终点,这样一想也是可行的。那么按照这个思路来写一下代码。首先我先在RouteActivity创建两个对象。
代码语言:txt复制 //起点
private LatLonPoint mStartPoint;
//终点
private LatLonPoint mEndPoint;
这里面会用到一些帮助方法,因此可以放到一个类里面进行管理,作为一个工具类。而工具类应该放到一个工具包下管理,在com.llw.mapdemo下新建一个util包,然后在这个包下新增ChString类,这个类是高德示例Demo里面的,我就直接拿过来了,代码如下:
代码语言:txt复制package com.llw.mapdemo.util;
public class ChString {
public static final String Kilometer = "u516cu91cc";// "公里";
public static final String Meter = "u7c73";// "米";
public static final String ByFoot = "u6b65u884c";// "步行";
public static final String To = "u53bbu5f80";// "去往";
public static final String Station = "u8f66u7ad9";// "车站";
public static final String TargetPlace = "u76eeu7684u5730";// "目的地";
public static final String StartPlace = "u51fau53d1u5730";// "出发地";
public static final String About = "u5927u7ea6";// "大约";
public static final String Direction = "u65b9u5411";// "方向";
public static final String GetOn = "u4e0au8f66";// "上车";
public static final String GetOff = "u4e0bu8f66";// "下车";
public static final String Zhan = "u7ad9";// "站";
public static final String cross = "u4ea4u53c9u8defu53e3"; // 交叉路口
public static final String type = "u7c7bu522b"; // 类别
public static final String address = "u5730u5740"; // 地址
public static final String PrevStep = "u4e0au4e00u6b65";
public static final String NextStep = "u4e0bu4e00u6b65";
public static final String Gong = "u516cu4ea4";
public static final String ByBus = "u4e58u8f66";
public static final String Arrive = "u5230u8FBE";// 到达
}
再在这个包下新增一个MapUtil类,来源于高德示例Demo,我只拿了目前用得到的方法,代码如下:
代码语言:txt复制package com.llw.mapdemo.util;
import com.amap.api.maps.model.LatLng;
import com.amap.api.services.core.LatLonPoint;
import java.text.DecimalFormat;
/**
* 地图帮助类
* @author llw
*/
public class MapUtil {
/**
* 把LatLng对象转化为LatLonPoint对象
*/
public static LatLonPoint convertToLatLonPoint(LatLng latLng) {
return new LatLonPoint(latLng.latitude, latLng.longitude);
}
/**
* 把LatLonPoint对象转化为LatLon对象
*/
public static LatLng convertToLatLng(LatLonPoint latLonPoint) {
return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude());
}
public static String getFriendlyTime(int second) {
if (second > 3600) {
int hour = second / 3600;
int miniate = (second % 3600) / 60;
return hour "小时" miniate "分钟";
}
if (second >= 60) {
int miniate = second / 60;
return miniate "分钟";
}
return second "秒";
}
public static String getFriendlyLength(int lenMeter) {
if (lenMeter > 10000) // 10 km
{
int dis = lenMeter / 1000;
return dis ChString.Kilometer;
}
if (lenMeter > 1000) {
float dis = (float) lenMeter / 1000;
DecimalFormat fnum = new DecimalFormat("##0.0");
String dstr = fnum.format(dis);
return dstr ChString.Kilometer;
}
if (lenMeter > 100) {
int dis = lenMeter / 50 * 50;
return dis ChString.Meter;
}
int dis = lenMeter / 10 * 10;
if (dis == 0) {
dis = 10;
}
return dis ChString.Meter;
}
}
下面回到RouteActivity中,首先对起点进行赋值
代码语言:txt复制 //设置起点
mStartPoint = convertToLatLonPoint(new LatLng(latitude, longitude));
通过经纬度,构建LatLng对象,然后将LatLng转为LatLonPoint。最后赋值给起点mStartPoint。
那么下面就是终点了。刚才说到终点通过点击地图时产生,那么既然要点击地图,自然要使当前RouteActivity实现AMap.OnMapClickListener接口。然后在initMap中,进行监听。
代码语言:txt复制 //地图点击监听
aMap.setOnMapClickListener(this);
之后重写onMapClick方法,然后将获取到的LatLng对象转为LatLonPoint,最后赋值给终点mEndPoint。
代码语言:txt复制 /**
* 点击地图
*/
@Override
public void onMapClick(LatLng latLng) {
//终点
mEndPoint = convertToLatLonPoint(latLng);
}
这样我们就拿到了起点和终点,下面就要去搜索路线了。
搜索路线需要一个RouteSearch对象,在RouteActivity中创建。
代码语言:txt复制 //路线搜索对象
private RouteSearch routeSearch;
然后RouteActivity实现RouteSearch.OnRouteSearchListener接口,新增一个initRoute方法。在这个方法里面初始化,然后设置路线搜索监听,方法代码如下:
代码语言:txt复制 /**
* 初始化路线
*/
private void initRoute() {
routeSearch = new RouteSearch(this);
routeSearch.setRouteSearchListener(this);
}
记得在onCreate方法中调用。
之后需要重写四个方法,如下所示:
代码语言:txt复制 @Override
public void onBusRouteSearched(BusRouteResult busRouteResult, int code) {
}
@Override
public void onDriveRouteSearched(DriveRouteResult driveRouteResult, int code) {
}
/**
* 步行规划路径结果
*
* @param walkRouteResult 结果
* @param code 结果码
*/
@Override
public void onWalkRouteSearched(WalkRouteResult walkRouteResult, int code) {
}
@Override
public void onRideRouteSearched(RideRouteResult rideRouteResult, int code) {
}
其中onWalkRouteSearched方法我标注了是步行搜索返回的结果。那么现在先不管这个结果,因为我们需要先去发起路线搜索的请求之后,才会有结果,这里新增一个startRouteSearch方法,代码如下:
代码语言:txt复制 /**
* 开始路线搜索
*/
private void startRouteSearch() {
//在地图上添加起点Marker
aMap.addMarker(new MarkerOptions()
.position(convertToLatLng(mStartPoint))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.start)));
//在地图上添加终点Marker
aMap.addMarker(new MarkerOptions()
.position(convertToLatLng(mEndPoint))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.end)));
//搜索路线 构建路径的起终点
final RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(
mStartPoint, mEndPoint);
//构建步行路线搜索对象
RouteSearch.WalkRouteQuery query = new RouteSearch.WalkRouteQuery(fromAndTo, RouteSearch.WalkDefault);
// 异步路径规划步行模式查询
routeSearch.calculateWalkRouteAsyn(query);
}
前面两句代码就是给起点和终点各增加一个标注,这里有两个图标,你应该没有,我这里贴一下:
最好去我的源码里面去拿。然后代码继续往下看就是通过起点和终点构建路径的起终点对象fromAndTo,再通过这个对象去构建步行路线搜索对象,最后通过routeSearch对象发起搜索请求。到此为止请求就写完了。我们在onMapClick中去调用它。
下面就该去处理搜索路线的返回了。返回后最重要的是对这个路线进行绘制,从哪里到哪里,绘制在地图上,之前高德的SDK中这一部分是不开放的,不过在地图SDK V4.1.3版本开始,就已经是开源的了,只不过你要到高德示例Demo中去寻找,为了减少你的工作量,我已经提前找好了,并且只拿我需要的。下面在com.llw.mapdemo下新增一个overlay包,这个包下新增三个类。
AMapServicesUtil.java
代码语言:txt复制package com.llw.mapdemo.overlay;
/**
* 地图服务工具类
*/
import android.graphics.Bitmap;
import com.amap.api.maps.model.LatLng;
import com.amap.api.services.core.LatLonPoint;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
class AMapServicesUtil {
public static int BUFFER_SIZE = 2048;
public static byte[] inputStreamToByte(InputStream in) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while ((count = in.read(data, 0, BUFFER_SIZE)) != -1){
outStream.write(data, 0, count);
}
data = null;
return outStream.toByteArray();
}
public static LatLonPoint convertToLatLonPoint(LatLng latlon) {
return new LatLonPoint(latlon.latitude, latlon.longitude);
}
public static LatLng convertToLatLng(LatLonPoint latLonPoint) {
return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude());
}
public static ArrayList<LatLng> convertArrList(List<LatLonPoint> shapes) {
ArrayList<LatLng> lineShapes = new ArrayList<LatLng>();
for (LatLonPoint point : shapes) {
LatLng latLngTemp = AMapServicesUtil.convertToLatLng(point);
lineShapes.add(latLngTemp);
}
return lineShapes;
}
public static Bitmap zoomBitmap(Bitmap bitmap, float res) {
if (bitmap == null) {
return null;
}
int width, height;
width = (int) (bitmap.getWidth() * res);
height = (int) (bitmap.getHeight() * res);
Bitmap newbmp = Bitmap.createScaledBitmap(bitmap, width, height, true);
return newbmp;
}
}
RouteOverlay.java
代码语言:txt复制package com.llw.mapdemo.overlay;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.LatLngBounds;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.Polyline;
import com.amap.api.maps.model.PolylineOptions;
import com.llw.mapdemo.R;
/**
* 路线图层叠加
*/
public class RouteOverlay {
protected List<Marker> stationMarkers = new ArrayList<Marker>();
protected List<Polyline> allPolyLines = new ArrayList<Polyline>();
protected Marker startMarker;
protected Marker endMarker;
protected LatLng startPoint;
protected LatLng endPoint;
protected AMap mAMap;
private Context mContext;
private Bitmap startBit, endBit, busBit, walkBit, driveBit;
protected boolean nodeIconVisible = true;
public RouteOverlay(Context context) {
mContext = context;
}
/**
* 去掉BusRouteOverlay上所有的Marker。
* @since V2.1.0
*/
public void removeFromMap() {
if (startMarker != null) {
startMarker.remove();
}
if (endMarker != null) {
endMarker.remove();
}
for (Marker marker : stationMarkers) {
marker.remove();
}
for (Polyline line : allPolyLines) {
line.remove();
}
destroyBit();
}
private void destroyBit() {
if (startBit != null) {
startBit.recycle();
startBit = null;
}
if (endBit != null) {
endBit.recycle();
endBit = null;
}
if (busBit != null) {
busBit.recycle();
busBit = null;
}
if (walkBit != null) {
walkBit.recycle();
walkBit = null;
}
if (driveBit != null) {
driveBit.recycle();
driveBit = null;
}
}
/**
* 给起点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
* @return 更换的Marker图片。
* @since V2.1.0
*/
protected BitmapDescriptor getStartBitmapDescriptor() {
return BitmapDescriptorFactory.fromResource(R.drawable.amap_start);
}
/**
* 给终点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
* @return 更换的Marker图片。
* @since V2.1.0
*/
protected BitmapDescriptor getEndBitmapDescriptor() {
return BitmapDescriptorFactory.fromResource(R.drawable.amap_end);
}
/**
* 给公交Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
* @return 更换的Marker图片。
* @since V2.1.0
*/
protected BitmapDescriptor getBusBitmapDescriptor() {
return BitmapDescriptorFactory.fromResource(R.drawable.amap_bus);
}
/**
* 给步行Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。
* @return 更换的Marker图片。
* @since V2.1.0
*/
protected BitmapDescriptor getWalkBitmapDescriptor() {
return BitmapDescriptorFactory.fromResource(R.drawable.amap_man);
}
protected BitmapDescriptor getDriveBitmapDescriptor() {
return BitmapDescriptorFactory.fromResource(R.drawable.amap_car);
}
protected void addStartAndEndMarker() {
startMarker = mAMap.addMarker((new MarkerOptions())
.position(startPoint).icon(getStartBitmapDescriptor())
.title("u8D77u70B9"));
// startMarker.showInfoWindow();
endMarker = mAMap.addMarker((new MarkerOptions()).position(endPoint)
.icon(getEndBitmapDescriptor()).title("u7EC8u70B9"));
// mAMap.moveCamera(CameraUpdateFactory.newLatLngZoom(startPoint,
// getShowRouteZoom()));
}
/**
* 移动镜头到当前的视角。
* @since V2.1.0
*/
public void zoomToSpan() {
if (startPoint != null) {
if (mAMap == null) {
return;
}
try {
LatLngBounds bounds = getLatLngBounds();
mAMap.animateCamera(CameraUpdateFactory
.newLatLngBounds(bounds, 100));
} catch (Throwable e) {
e.printStackTrace();
}
}
}
protected LatLngBounds getLatLngBounds() {
LatLngBounds.Builder b = LatLngBounds.builder();
b.include(new LatLng(startPoint.latitude, startPoint.longitude));
b.include(new LatLng(endPoint.latitude, endPoint.longitude));
return b.build();
}
/**
* 路段节点图标控制显示接口。
* @param visible true为显示节点图标,false为不显示。
* @since V2.3.1
*/
public void setNodeIconVisibility(boolean visible) {
try {
nodeIconVisible = visible;
if (this.stationMarkers != null && this.stationMarkers.size() > 0) {
for (int i = 0; i < this.stationMarkers.size(); i ) {
this.stationMarkers.get(i).setVisible(visible);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
protected void addStationMarker(MarkerOptions options) {
if(options == null) {
return;
}
Marker marker = mAMap.addMarker(options);
if(marker != null) {
stationMarkers.add(marker);
}
}
protected void addPolyLine(PolylineOptions options) {
if(options == null) {
return;
}
Polyline polyline = mAMap.addPolyline(options);
if(polyline != null) {
allPolyLines.add(polyline);
}
}
protected float getRouteWidth() {
return 18f;
}
protected int getWalkColor() {
return Color.parseColor("#6db74d");
}
/**
* 自定义路线颜色。
* return 自定义路线颜色。
* @since V2.2.1
*/
protected int getBusColor() {
return Color.parseColor("#537edc");
}
protected int getDriveColor() {
return Color.parseColor("#537edc");
}
// protected int getShowRouteZoom() {
// return 15;
// }
}
WalkRouteOverlay.java
代码语言:txt复制package com.llw.mapdemo.overlay;
import java.util.List;
import com.amap.api.maps.AMap;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.PolylineOptions;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.route.WalkPath;
import com.amap.api.services.route.WalkStep;
import android.content.Context;
/**
* 步行路线图层类。在高德地图API里,如果要显示步行路线规划,可以用此类来创建步行路线图层。如不满足需求,也可以自己创建自定义的步行路线图层。
* @since V2.1.0
*/
public class WalkRouteOverlay extends RouteOverlay {
private PolylineOptions mPolylineOptions;
private BitmapDescriptor walkStationDescriptor= null;
private WalkPath walkPath;
/**
* 通过此构造函数创建步行路线图层。
* @param context 当前activity。
* @param amap 地图对象。
* @param path 步行路线规划的一个方案。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类 <strong><a href="../../../../../../Search/com/amap/api/services/route/WalkStep.html" title="com.amap.api.services.route中的类">WalkStep</a></strong>。
* @param start 起点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类<strong><a href="../../../../../../Search/com/amap/api/services/core/LatLonPoint.html" title="com.amap.api.services.core中的类">LatLonPoint</a></strong>。
* @param end 终点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类<strong><a href="../../../../../../Search/com/amap/api/services/core/LatLonPoint.html" title="com.amap.api.services.core中的类">LatLonPoint</a></strong>。
* @since V2.1.0
*/
public WalkRouteOverlay(Context context, AMap amap, WalkPath path,
LatLonPoint start, LatLonPoint end) {
super(context);
this.mAMap = amap;
this.walkPath = path;
startPoint = AMapServicesUtil.convertToLatLng(start);
endPoint = AMapServicesUtil.convertToLatLng(end);
}
/**
* 添加步行路线到地图中。
* @since V2.1.0
*/
public void addToMap() {
initPolylineOptions();
try {
List<WalkStep> walkPaths = walkPath.getSteps();
for (int i = 0; i < walkPaths.size(); i ) {
WalkStep walkStep = walkPaths.get(i);
LatLng latLng = AMapServicesUtil.convertToLatLng(walkStep
.getPolyline().get(0));
addWalkStationMarkers(walkStep, latLng);
addWalkPolyLines(walkStep);
}
addStartAndEndMarker();
showPolyline();
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* 检查这一步的最后一点和下一步的起始点之间是否存在空隙
*/
private void checkDistanceToNextStep(WalkStep walkStep,
WalkStep walkStep1) {
LatLonPoint lastPoint = getLastWalkPoint(walkStep);
LatLonPoint nextFirstPoint = getFirstWalkPoint(walkStep1);
if (!(lastPoint.equals(nextFirstPoint))) {
addWalkPolyLine(lastPoint, nextFirstPoint);
}
}
/**
* @param walkStep
* @return
*/
private LatLonPoint getLastWalkPoint(WalkStep walkStep) {
return walkStep.getPolyline().get(walkStep.getPolyline().size() - 1);
}
/**
* @param walkStep
* @return
*/
private LatLonPoint getFirstWalkPoint(WalkStep walkStep) {
return walkStep.getPolyline().get(0);
}
private void addWalkPolyLine(LatLonPoint pointFrom, LatLonPoint pointTo) {
addWalkPolyLine(AMapServicesUtil.convertToLatLng(pointFrom), AMapServicesUtil.convertToLatLng(pointTo));
}
private void addWalkPolyLine(LatLng latLngFrom, LatLng latLngTo) {
mPolylineOptions.add(latLngFrom, latLngTo);
}
/**
* @param walkStep
*/
private void addWalkPolyLines(WalkStep walkStep) {
mPolylineOptions.addAll(AMapServicesUtil.convertArrList(walkStep.getPolyline()));
}
/**
* @param walkStep
* @param position
*/
private void addWalkStationMarkers(WalkStep walkStep, LatLng position) {
addStationMarker(new MarkerOptions()
.position(position)
.title("u65B9u5411:" walkStep.getAction()
"nu9053u8DEF:" walkStep.getRoad())
.snippet(walkStep.getInstruction()).visible(nodeIconVisible)
.anchor(0.5f, 0.5f).icon(walkStationDescriptor));
}
/**
* 初始化线段属性
*/
private void initPolylineOptions() {
if(walkStationDescriptor == null) {
walkStationDescriptor = getWalkBitmapDescriptor();
}
mPolylineOptions = null;
mPolylineOptions = new PolylineOptions();
mPolylineOptions.color(getWalkColor()).width(getRouteWidth());
}
private void showPolyline() {
addPolyLine(mPolylineOptions);
}
}
这里面有一些图标你应该没有,我贴一下:
最好去我的源码里去拿,因为这样你就不用手动去命名了。
下面你的这三个类应该是不会报错了,OK,回到RouteActivity中,修改onWalkRouteSearched方法,代码如下:
代码语言:txt复制 /**
* 步行规划路径结果
*
* @param walkRouteResult 结果
* @param code 结果码
*/
@Override
public void onWalkRouteSearched(WalkRouteResult walkRouteResult, int code) {
aMap.clear();// 清理地图上的所有覆盖物
if (code == AMapException.CODE_AMAP_SUCCESS) {
if (walkRouteResult != null && walkRouteResult.getPaths() != null) {
if (walkRouteResult.getPaths().size() > 0) {
final WalkPath walkPath = walkRouteResult.getPaths().get(0);
if (walkPath == null) {
return;
}
//绘制路线
WalkRouteOverlay walkRouteOverlay = new WalkRouteOverlay(
this, aMap, walkPath,
walkRouteResult.getStartPos(),
walkRouteResult.getTargetPos());
walkRouteOverlay.removeFromMap();
walkRouteOverlay.addToMap();
walkRouteOverlay.zoomToSpan();
int dis = (int) walkPath.getDistance();
int dur = (int) walkPath.getDuration();
String des = MapUtil.getFriendlyTime(dur) "(" MapUtil.getFriendlyLength(dis) ")";
Log.d(TAG, des);
} else if (walkRouteResult.getPaths() == null) {
showMsg("对不起,没有搜索到相关数据!");
}
} else {
showMsg("对不起,没有搜索到相关数据!");
}
} else {
showMsg("错误码;" code);
}
}
返回结果时,先清空地图,然后判断是否搜索成功,否的话调用showMsg方法提示一下,方法代码如下:
代码语言:txt复制 private void showMsg(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
然后判断返回值是否为空,之后判断返回的路径是否大于0,大于的话则就可以开始绘制路线了,绘制完之后清空原来的,然后添加新的图层到地图上,然后进行缩放,之后就是一些其他信息的打印了。写了这么久代码了,也该运行一下了。
③ 骑行路线规划
骑行其实和步行差不多,只是路线限制时图层不同而已,其他的都类似,写起来也是比较简单的,不过我们的布局要做一下改变,假如我把骑行也就入到RouteActivity中,那么在一个地图上就有两种出行方式了,因此需要方便用户来切换不同的方式才行。因此我们打开activity_route.xml
,修改后的代码如下:
代码语言:txt复制<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".RouteActivity">
<!--设置出行方式-->
<LinearLayout
android:id="@ id/top_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="6dp"
android:paddingEnd="6dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="出行方式:"
android:textColor="#000"
android:textSize="16sp" />
<Spinner
android:id="@ id/spinner"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1" />
</LinearLayout>
<!--地图-->
<com.amap.api.maps.MapView
android:id="@ id/map_view"
android:layout_below="@ id/top_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
一目了然,很简单的布局代码,首先将外部布局修改为相对布局,然后添加一个线性布局,里面添加一个下来选择控件,用于切换不同的出行方式,然后修改一下地图控件,使它置于刚才所添加的顶部布局的下方。就是android:layout_below="@ id/top_layout"这行代码,不加的话,你的地图控件会覆盖上方的布局。
布局修改好了,进入到RouteActivity中,先创建三个成员变量,如下所示:
代码语言:txt复制 //出行方式数组
private static final String[] travelModeArray = {"步行出行", "骑行出行"};
//出行方式值
private static int TRAVEL_MODE = 0;
//数组适配器
private ArrayAdapter<String> arrayAdapter;
这里我们就指定了出现方式的种类,目前三种,后面些其他方式可以再加,然后就是方式值,当我们点击下拉框选择类型之后,通过位置赋值给这个TRAVEL_MODE 变量,然后我们在路线规划的方法中去根据这个值进行不同的路线搜索即可。最后一个是用来配置下拉框数据的。
下面新建一个initTravelMode方法用于初始化出行方式的数据,代码如下:
代码语言:txt复制 /**
* 初始化出行方式
*/
private void initTravelMode() {
Spinner spinner = findViewById(R.id.spinner);
//将可选内容与ArrayAdapter连接起来
arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, travelModeArray);
//设置下拉列表的风格
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//将adapter 添加到spinner中
spinner.setAdapter(arrayAdapter);
//添加事件Spinner事件监听
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
TRAVEL_MODE = position;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
然后别忘了在onCreater方法中调用。
然后在startRouteSearch方法中,进行出行方式的判断,代码如下:
代码语言:txt复制 //出行方式判断
switch (TRAVEL_MODE) {
case 0://步行
//构建步行路线搜索对象
RouteSearch.WalkRouteQuery query = new RouteSearch.WalkRouteQuery(fromAndTo, RouteSearch.WalkDefault);
// 异步路径规划步行模式查询
routeSearch.calculateWalkRouteAsyn(query);
break;
case 1://骑行
//构建骑行路线搜索对象
RouteSearch.RideRouteQuery rideQuery = new RouteSearch.RideRouteQuery(fromAndTo, RouteSearch.WalkDefault);
//骑行规划路径计算
routeSearch.calculateRideRouteAsyn(rideQuery);
break;
default:
break;
}
然后就是回调了,之前我们写了步行的回调,下面就是骑行的回调,因此有一些东西需要加进来才行,下面先做这一步操作,首先是修改原来的MapUtil工具类,在里面新增一个方法,代码如下:
代码语言:txt复制 /**
* 把集合体的LatLonPoint转化为集合体的LatLng
*/
public static ArrayList<LatLng> convertArrList(List<LatLonPoint> shapes) {
ArrayList<LatLng> lineShapes = new ArrayList<LatLng>();
for (LatLonPoint point : shapes) {
LatLng latLngTemp = convertToLatLng(point);
lineShapes.add(latLngTemp);
}
return lineShapes;
}
然后在overlay包下,新增一个RideRouteOverlay类,用于在地图上绘制骑行的图层,里面的代码如下:(这个代码是源码里面有的)
代码语言:txt复制package com.llw.mapdemo.overlay;
import android.content.Context;
import com.amap.api.maps.AMap;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.PolylineOptions;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.route.RidePath;
import com.amap.api.services.route.RideStep;
import com.llw.mapdemo.R;
import com.llw.mapdemo.util.MapUtil;
import java.util.List;
/**
* 骑行路线图层类。在高德地图API里,如果要显示步行路线规划,可以用此类来创建骑行路线图层。如不满足需求,也可以自己创建自定义的骑行路线图层。
* @since V3.5.0
*/
public class RideRouteOverlay extends RouteOverlay {
private PolylineOptions mPolylineOptions;
private BitmapDescriptor walkStationDescriptor= null;
private RidePath ridePath;
/**
* 通过此构造函数创建骑行路线图层。
* @param context 当前activity。
* @param amap 地图对象。
* @param path 骑行路线规划的一个方案。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类 <strong><a href="../../../../../../Search/com/amap/api/services/route/WalkStep.html" title="com.amap.api.services.route中的类">WalkStep</a></strong>。
* @param start 起点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类<strong><a href="../../../../../../Search/com/amap/api/services/core/LatLonPoint.html" title="com.amap.api.services.core中的类">LatLonPoint</a></strong>。
* @param end 终点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类<strong><a href="../../../../../../Search/com/amap/api/services/core/LatLonPoint.html" title="com.amap.api.services.core中的类">LatLonPoint</a></strong>。
* @since V3.5.0
*/
public RideRouteOverlay(Context context, AMap amap, RidePath path,
LatLonPoint start, LatLonPoint end) {
super(context);
this.mAMap = amap;
this.ridePath = path;
startPoint = MapUtil.convertToLatLng(start);
endPoint = MapUtil.convertToLatLng(end);
}
/**
* 添加骑行路线到地图中。
* @since V3.5.0
*/
public void addToMap() {
initPolylineOptions();
try {
List<RideStep> ridePaths = ridePath.getSteps();
for (int i = 0; i < ridePaths.size(); i ) {
RideStep rideStep = ridePaths.get(i);
LatLng latLng = MapUtil.convertToLatLng(rideStep
.getPolyline().get(0));
addRideStationMarkers(rideStep, latLng);
addRidePolyLines(rideStep);
}
addStartAndEndMarker();
showPolyline();
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* @param rideStep
*/
private void addRidePolyLines(RideStep rideStep) {
mPolylineOptions.addAll(MapUtil.convertArrList(rideStep.getPolyline()));
}
/**
* @param rideStep
* @param position
*/
private void addRideStationMarkers(RideStep rideStep, LatLng position) {
addStationMarker(new MarkerOptions()
.position(position)
.title("u65B9u5411:" rideStep.getAction()
"nu9053u8DEF:" rideStep.getRoad())
.snippet(rideStep.getInstruction()).visible(nodeIconVisible)
.anchor(0.5f, 0.5f).icon(walkStationDescriptor));
}
/**
* 初始化线段属性
*/
private void initPolylineOptions() {
if(walkStationDescriptor == null) {
walkStationDescriptor = BitmapDescriptorFactory.fromResource(R.drawable.amap_ride);
}
mPolylineOptions = null;
mPolylineOptions = new PolylineOptions();
mPolylineOptions.color(getDriveColor()).width(getRouteWidth());
}
private void showPolyline() {
addPolyLine(mPolylineOptions);
}
}
下面回到RouteActivity中,找到onRideRouteSearched方法,这是骑行的搜索路线回调方法,修改代码如下:
代码语言:txt复制 /**
* 骑行规划路径结果
*
* @param rideRouteResult 结果
* @param code 结果码
*/
@Override
public void onRideRouteSearched(final RideRouteResult rideRouteResult, int code) {
aMap.clear();// 清理地图上的所有覆盖物
if (code == AMapException.CODE_AMAP_SUCCESS) {
if (rideRouteResult != null && rideRouteResult.getPaths() != null) {
if (rideRouteResult.getPaths().size() > 0) {
final RidePath ridePath = rideRouteResult.getPaths()
.get(0);
if(ridePath == null) {
return;
}
RideRouteOverlay rideRouteOverlay = new RideRouteOverlay(
this, aMap, ridePath,
rideRouteResult.getStartPos(),
rideRouteResult.getTargetPos());
rideRouteOverlay.removeFromMap();
rideRouteOverlay.addToMap();
rideRouteOverlay.zoomToSpan();
int dis = (int) ridePath.getDistance();
int dur = (int) ridePath.getDuration();
String des = MapUtil.getFriendlyTime(dur) "(" MapUtil.getFriendlyLength(dis) ")";
Log.d(TAG, des);
} else if (rideRouteResult.getPaths() == null) {
showMsg("对不起,没有搜索到相关数据!");
}
} else {
showMsg("对不起,没有搜索到相关数据!");
}
} else {
showMsg("错误码;" code);
}
}
很熟悉的代码吧,和步行的基本没有什么两样,只是里面使用的值不同而已。那么到这里你的代码就写完了,我们来运行一下吧。
这样就完成了骑行的路线规划了。