Android 集成 高德地图API

1 参考文档

Github源码地址:https://github.com/lilongweidev/GaodeMapDemo
博主博客地址:
Android 高德地图API(详细步骤+源码)7
Android 高德地图API(详细步骤+源码)6
Android 高德地图API(详细步骤+源码)5
Android 高德地图API(详细步骤+源码)4
Android 高德地图API(详细步骤+源码)3
Android 高德地图API(详细步骤+源码)2
Android 高德地图API(详细步骤+源码)1

2 集成步骤

2.1 创建开发者账号配置应用

直接在 高德开放平台 注册一个账号,新建一个应用。

这里添加应用的key的时候,需要两个SHA1,怎么获取呢?

  • 获取调试版安全码SHA1
    AS的右侧边栏,点击Gradle → app → android → signingReport,最后双击signingReport。

  • 获取发布版安全帽SHA1
    这个需要根据发布签名来,比如我们拿到一个jks文件,通过以下命令来获取:

keytool -list -v -keystore 后面加个空格 再跟上你打正式包后的 jks 文件完整地址

2.2 本地集成方式

首先在这里下载sdk
Android地图SDK 一键下载
这个包括:开发包(2D地图包、3D地图包、搜索包)、示例代码、开发文档(2D地图、3D地图、搜索服务)等。

下载好后放在app模块的libs文件夹下,没有则新建。

然后打开你的app下的build.gradle文件,在android闭包下添加:

sourceSets {
    main{
        jniLibs.srcDirs = ['libs']
    }
}

这样程序就能访问到这个文件夹下的sdk了。

然后配置下AndroidManifest.xml文件:
打开AndroidManifest.xml,首先在application标签下添加定位服务:

<!--定位service-->
<service android:name="com.amap.api.location.APSService"/>

然后添加权限,有的则无需重复添加:

<!--用于访问网络,网络定位需要上网-->
<uses-permission android:name="android.permission.INTERNET" />
<!--用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--用于写入缓存数据到扩展存储卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--用于申请调用A-GPS模块-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<!--用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--用于获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

然后在application标签下添加高德的key:

 <meta-data
            android:name="com.amap.api.v2.apikey"
            android:value="d3347ee0f2928f9a0c199cae009ae7f7" />

2.3 远程集成方式

这里通过远程依赖直接下载依赖包,可以在app模块的dependencies下方填写需要的依赖:

SDK 引入代码
3D地图 ‘com.amap.api:3dmap:latest.integration’
2D地图 ‘com.amap.api:map2d:latest.integration’
导航 ‘com.amap.api:navi-3dmap:latest.integration‘
搜索 ‘com.amap.api:search:latest.integration’
定位 ‘com.amap.api:location:latest.integration’

然后最好设置下ndk:

ndk {
    //设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so)
    abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64"
}

一般情况,设置一个arm64-v8a就可以了,目前主流手机都支持。

注意:

1、3D地图 SDK 和导航 SDK,5.0.0 版本以后全面支持多平台 so 库(armeabi、armeabi-v7a、arm64-v8a、x86、x86_64),开发者可以根据需要选择。同时还需要注意的是:如果您涉及到新旧版本更替请移除旧版本的 so 库之后替换新版本 so 库到工程中。
2、navi导航SDK 5.0.0以后版本包含了3D地图SDK,所以请不要同时引入 map3d 和 navi SDK。
3、如果build失败提示com.amap.api:XXX:X.X.X 找不到,请确认拼写及版本号是否正确,如果访问不到maven仓库尝试一下配置下阿里云镜像。
4、依照上述方法引入 SDK 以后,不需要在libs文件夹下导入对应SDK的 so 和 jar 包,会有冲突。

3 获取当前定位信息

3.1 申请定位权限

首先可以考虑引入官方的
easypermissions 进行动态权限申请。
需要引入此依赖:

implementation 'pub.devrel:easypermissions:3.0.0'

申请权限:

/**
    * 动态请求权限
    */
@AfterPermissionGranted(REQUEST_PERMISSIONS)
private void requestPermission() {
    String[] permissions = {
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    if (EasyPermissions.hasPermissions(this, permissions)) {
        //true 有权限 开始定位
        //showMsg("已获得权限,可以定位啦!");
        Log.d(TAG, "已获得权限,可以定位啦!");
        //启动定位
        mLocationClient.startLocation();
    } else {
        //false 无权限
        EasyPermissions.requestPermissions(this, "需要权限", REQUEST_PERMISSIONS, permissions);
    }
}

然后需要在Activity的onRequestPermissionsResult获取请求权限回调:

/**
    * 请求权限结果
    * @param requestCode
    * @param permissions
    * @param grantResults
    */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    //设置权限请求结果
    EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}

3.2 初始化定位

变量声明:

//声明AMapLocationClient类对象
public AMapLocationClient mLocationClient = null;
//声明AMapLocationClientOption对象
public AMapLocationClientOption mLocationOption = null;

变量初始化

/**
    * 初始化定位
    */
private void initLocation() {
    //初始化定位
    mLocationClient = new AMapLocationClient(getApplicationContext());
    //设置定位回调监听
    mLocationClient.setLocationListener(this);
    //初始化AMapLocationClientOption对象
    mLocationOption = new AMapLocationClientOption();
    //设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。
    mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
    //获取最近3s内精度最高的一次定位结果:
    //设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。如果设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。
    mLocationOption.setOnceLocationLatest(true);
    //设置是否返回地址信息(默认返回地址信息)
    mLocationOption.setNeedAddress(true);
    //设置定位请求超时时间,单位是毫秒,默认30000毫秒,建议超时时间不要低于8000毫秒。
    mLocationOption.setHttpTimeOut(20000);
    //关闭缓存机制,高精度定位会产生缓存。
    mLocationOption.setLocationCacheEnable(false);
    //给定位客户端对象设置定位参数
    mLocationClient.setLocationOption(mLocationOption);
}

开始定位:
这里通过 mLocationClient.startLocation(); 去开始定位,结果会通过回调传给activity。

@Override
public void onLocationChanged(AMapLocation aMapLocation) {
    if (aMapLocation != null) {
        if (aMapLocation.getErrorCode() == 0) {
            //地址
            String address = aMapLocation.getAddress();
            //城市赋值
            city = aMapLocation.getCity();
            //获取纬度
            double latitude = aMapLocation.getLatitude();
            //获取经度
            double longitude = aMapLocation.getLongitude();

            Log.d("MainActivity", aMapLocation.getCity());
            showMsg(address);

            //停止定位后,本地定位服务并不会被销毁
            mLocationClient.stopLocation();

            //显示地图定位结果
            if (mListener != null) {
                // 显示系统图标
                mListener.onLocationChanged(aMapLocation);
            }

            //显示浮动按钮
            fabPOI.show();
            //赋值
            cityCode = aMapLocation.getCityCode();
        } else {
            //定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
            Log.e("AmapError", "location Error, ErrCode:"
                    + aMapLocation.getErrorCode() + ", errInfo:"
                    + aMapLocation.getErrorInfo());
        }
    }
}

获取到定位结果后,需要我们解析一下。
同时需要停止定位。

同时需要再onDestory里面进行销毁引用:

@Override
protected void onDestroy() {
    super.onDestroy();
    //销毁定位客户端,同时销毁本地定位服务。
    if (mLocationClient != null) {
        mLocationClient.onDestroy();
    }
    //在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
    mapView.onDestroy();
}

4 显示地图

4.1 添加MapView

<?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:gravity="center"
    tools:context=".MainActivity">

    <!--地图-->
    <com.amap.api.maps.MapView
        android:id="@+id/map_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

简单看下源码:

public class MapView extends FrameLayout implements BaseMapView {
    private IMapFragmentDelegate mapFragmentDelegate;
    private AMap aMap;
    private int visibility = 0;

    public MapView(Context var1) {
        super(var1);
        this.getMapFragmentDelegate().setContext(var1);
    }

    public MapView(Context var1, AttributeSet var2) {

没想到地图是用FrameLayout,我以为是用GLSurfaceView绘制的呢。
主要逻辑它通过这个代理来完成的。

4.2 同步生命周期

然后需要在activity的生命周期里面同步下mapView的生命周期。

  @Override
    protected void onDestroy() {
        super.onDestroy();
        //销毁定位客户端,同时销毁本地定位服务。
        if (mLocationClient != null) {
            mLocationClient.onDestroy();
        }
        //在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
        mapView.onDestroy();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
        mapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
        mapView.onPause();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
        mapView.onSaveInstanceState(outState);
    }

此时可以显示北京地图了。

5 显示当前定位地图

5.1 相关成员变量

//地图控制器
private AMap aMap = null;
//位置更改监听
private OnLocationChangedListener mListener;

5.2 初始化地图控制器

在onCreate中执行如下代码:

private void initMap(Bundle savedInstanceState) {
    mapView = findViewById(R.id.map_view);
    //在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
    mapView.onCreate(savedInstanceState);
    //初始化地图控制器对象
    aMap = mapView.getMap();

    // 设置定位监听
    aMap.setLocationSource(this);
    // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
    aMap.setMyLocationEnabled(true);
}

5.3 给地图注入监听

注意到上面的 aMap = aMap.setLocationSource(this);
这个this说明这个Activity要实现回调方法:

public interface LocationSource {
    void activate(LocationSource.OnLocationChangedListener var1);

    void deactivate();

    public interface OnLocationChangedListener {
        void onLocationChanged(Location var1);
    }
}

这个是高德提供的定位接口。

我们需要在activity里面去实现这个接口:

/**
 * 激活定位
 */
@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;
}

这里主要是在activate里面接收回调,拿到地址后,通过这个回调给map。这样map就指向定位地址了。

6 地图设置

6.1 修改自定义定位图标

//定位样式
private MyLocationStyle myLocationStyle = new MyLocationStyle();

初始化地图:

// 自定义定位蓝点图标
myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.gps_point));
// 自定义精度范围的圆形边框颜色  都为0则透明
myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));
// 自定义精度范围的圆形边框宽度  0 无宽度
myLocationStyle.strokeWidth(0);
// 设置圆形的填充颜色  都为0则透明
myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));

//设置定位蓝点的Style
aMap.setMyLocationStyle(myLocationStyle);

6.2 设置缩放等级

//设置最小缩放等级为16 ,缩放级别范围为[3, 20]
aMap.setMinZoomLevel(12);

6.3 开启室内地图

//开启室内地图
aMap.showIndoorMap(true);

6.4 地图控件设置

//定义一个UiSettings对象
private UiSettings mUiSettings;

然后可以隐藏缩放按钮:

//实例化UiSettings类对象
mUiSettings = aMap.getUiSettings();
//隐藏缩放按钮
mUiSettings.setZoomControlsEnabled(false);

可以设置比例尺控件:

//显示比例尺 默认不显示
mUiSettings.setScaleControlsEnabled(true);

7 获取POI数据

POI是什么呢?

POI (Point of Interest,兴趣点)。在地图表达中,一个 POI 可代表一栋大厦、一家商铺、一处景点等等。通过POI搜索,完成找餐馆、找景点、找厕所等等的功能。

回到首页,声明几个POI相关变量:

//POI查询对象
private PoiSearch.Query query;
//POI搜索对象
private PoiSearch poiSearch;
//城市码
private String cityCode = null;
//浮动按钮
private FloatingActionButton fabPOI;

在定位回调中,成功后,可显示浮动按钮。

我们设定,点击浮动按钮,去查询附近POI数据,
这里点击事件走一下逻辑:

public void queryPOI(View view) {
    //构造query对象
    query = new PoiSearch.Query("购物", "", cityCode);
    // 设置每页最多返回多少条poiitem
    query.setPageSize(10);
    //设置查询页码
    query.setPageNum(1);
    //构造 PoiSearch 对象
    try {
        poiSearch = new PoiSearch(this, query);
    } catch (AMapException e) {
        e.printStackTrace();
    }
    //设置搜索回调监听
    poiSearch.setOnPoiSearchListener(this);
    //发起搜索附近POI异步请求
    poiSearch.searchPOIAsyn();
}

这里设置了回调,所以在activity里面,应该要实现搜索回调接口。

该接口为:

public interface OnPoiSearchListener {
    void onPoiSearched(PoiResult var1, int var2);

    void onPoiItemSearched(PoiItem var1, int var2);
}

所以我们在首页里面去实现:

/**
    * POI搜索返回
    *
    * @param poiResult POI所有数据
    * @param i
    */
@Override
public void onPoiSearched(PoiResult poiResult, int i) {
    //解析result获取POI信息

    //获取POI组数列表
    ArrayList<PoiItem> poiItems = poiResult.getPois();
    for (PoiItem poiItem : poiItems) {
        Log.d("MainActivity", " Title:" + poiItem.getTitle() + " Snippet:" + poiItem.getSnippet());
    }
}

/**
    * POI中的项目搜索返回
    *
    * @param poiItem 获取POI item
    * @param i
    */
@Override
public void onPoiItemSearched(PoiItem poiItem, int i) {

}

这里我们只是打印了附近POI数据。

8 地图点击事件

8.1 点击事件

//设置地图点击事件
aMap.setOnMapClickListener(this);

这里需要Activity实现一下接口 AMap.OnMapClickListener

/**
    * 地图单击事件
    *
    * @param latLng
    */
@Override
public void onMapClick(LatLng latLng) {
    //通过经纬度获取地址
    //latlonToAddress(latLng);
    //添加标点
    addMarker(latLng);
    //改变地图中心点
    updateMapCenter(latLng);
}
/**
    * 添加地图标点
    *
    * @param latLng
    */
private void addMarker(LatLng latLng) {
    //显示浮动按钮
    fabClearMarker.show();
    //添加标点
    Marker marker = aMap.addMarker(new MarkerOptions()
            .draggable(true)//可拖动
            .position(latLng)
            .title("标题")
            .snippet("详细信息"));

    //绘制Marker时显示InfoWindow
    //marker.showInfoWindow();

    //设置标点的绘制动画效果
    Animation animation = new RotateAnimation(marker.getRotateAngle(), marker.getRotateAngle() + 180, 0, 0, 0);
    long duration = 1000L;
    animation.setDuration(duration);
    animation.setInterpolator(new LinearInterpolator());

    marker.setAnimation(animation);
    marker.startAnimation();

    markerList.add(marker);
}

上面添加了一个Marker标点,然后设置了动画旋转效果。

然后地图好像以marker居中了:

/**
    * 改变地图中心位置
    * @param latLng 位置
    */
private void updateMapCenter(LatLng latLng) {
    // CameraPosition 第一个参数: 目标位置的屏幕中心点经纬度坐标。
    // CameraPosition 第二个参数: 目标可视区域的缩放级别
    // CameraPosition 第三个参数: 目标可视区域的倾斜度,以角度为单位。
    // CameraPosition 第四个参数: 可视区域指向的方向,以角度为单位,从正北向顺时针方向计算,从0度到360度
    CameraPosition cameraPosition = new CameraPosition(latLng, 16, 30, 0);
    //位置变更
    CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition);
    //改变位置
    //aMap.moveCamera(cameraUpdate);
    //带动画的移动
    aMap.animateCamera(cameraUpdate);

}

8.2 设置地图长按事件

//设置地图长按事件
aMap.setOnMapLongClickListener(this);

所以需要Activity实现接口:

public interface OnMapLongClickListener {
    void onMapLongClick(LatLng var1);
}

具体实现如下:

/**
    * 地图长按事件
    *
    * @param latLng
    */
@Override
public void onMapLongClick(LatLng latLng) {
    //通过经纬度获取地址
    latlonToAddress(latLng);
}

这里通过点击的经纬度获取地址:

/**
    * 通过经纬度获取地址
    *
    * @param latLng
    */
private void latlonToAddress(LatLng latLng) {
    //位置点  通过经纬度进行构建
    LatLonPoint latLonPoint = new LatLonPoint(latLng.latitude, latLng.longitude);
    //逆编码查询  第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系
    RegeocodeQuery query = new RegeocodeQuery(latLonPoint, 20, GeocodeSearch.AMAP);
    //异步获取地址信息
    geocodeSearch.getFromLocationAsyn(query);
}

因为前面初始化的时候给gecodeSearch设置了监听

geocodeSearch.setOnGeocodeSearchListener(this);

所以当我们走异步获取地址信息的时候,结果会回调到这个接口上:

public interface OnGeocodeSearchListener {
    void onRegeocodeSearched(RegeocodeResult var1, int var2);

    void onGeocodeSearched(GeocodeResult var1, int var2);
}

所以我们Activity也需要实现这两个方法:

/**
    * 坐标转地址
    *
    * @param regeocodeResult
    * @param rCode
    */
@Override
public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int rCode) {
    //解析result获取地址描述信息
    if (rCode == PARSE_SUCCESS_CODE) {
        RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress();
        //显示解析后的地址
        Log.d("MainActivity", regeocodeAddress.getFormatAddress());
        //showMsg("地址:" + regeocodeAddress.getFormatAddress());

        LatLonPoint latLonPoint = regeocodeResult.getRegeocodeQuery().getPoint();
        LatLng latLng = new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude());
        addMarker(latLng);
    } else {
        showMsg("获取地址失败");
    }

}

/**
    * 地址转坐标
    *
    * @param geocodeResult
    * @param rCode
    */
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) {
    if (rCode == PARSE_SUCCESS_CODE) {
        List<GeocodeAddress> geocodeAddressList = geocodeResult.getGeocodeAddressList();
        if (geocodeAddressList != null && geocodeAddressList.size() > 0) {
            LatLonPoint latLonPoint = geocodeAddressList.get(0).getLatLonPoint();
            //显示解析后的坐标
            showMsg("坐标:" + latLonPoint.getLongitude() + "," + latLonPoint.getLatitude());
        }

    } else {
        showMsg("获取坐标失败");
    }
}

这里获取到地址后,打印下来。

这个GeocodeSearch主要是用来逆地址编码的,因为搜索结果只返回经纬度,我们需要通过经纬度获取具体地址名称,这时候就需要使用这个GeocodeSearch类来帮忙处理了。

8.3 地址编码

地址编码,就是地址转坐标。

比如说你到一个景点去游玩,不知道路线只知道景点名,那么这个时候通常你会在导航软件中输入这个景点名,然后搜索出前往的路线及搭乘的交通工具。此时,导航软件会将你输入的地址转成经纬度坐标,然后通过你当前的所在地坐标计算距离,获取两点之间的交通情况,然后规划路线。

所以要搞一个搜索框,这里直接搞一个EditText进去。

设置点击事件,以及键盘监听 EditText.OnKeyListener。

/**
    * 键盘点击
    *
    * @param v
    * @param keyCode
    * @param event
    * @return
    */
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
        //获取输入框的值
        String address = etAddress.getText().toString().trim();
        if (address == null || address.isEmpty()) {
            showMsg("请输入地址");
        } else {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            //隐藏软键盘
            imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);

            // name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode
            GeocodeQuery query = new GeocodeQuery(address, city);
            geocodeSearch.getFromLocationNameAsyn(query);
        }
        return true;
    }
    return false;
}

这里点击搜索时,判空,然后通过 GeocodeSearch实例的getFromLocationNameAsyn来通过名称搜索地址。

8.4 清空marker

/**
    * 清空地图Marker
    *
    * @param view
    */
public void clearAllMarker(View view) {
    if (markerList != null && markerList.size()>0){
        for (Marker markerItem : markerList) {
            markerItem.remove();
        }
    }
    fabClearMarker.hide();
}

这里是存储了Marker到一个集合,清除的时候,调用marker的remove方法即可。

8.5 Marker的点击和拖拽

需要实现下 Amap.OnMarkerClickListener 接口。

然后需要配置下map的marker点击事件:

//设置地图Marker点击事件
aMap.setOnMarkerClickListener(this);

具体实现如下:

/**
    * Marker点击事件
    *
    * @param marker
    * @return
    */
@Override
public boolean onMarkerClick(Marker marker) {
    //showMsg("点击了标点");
    //显示InfoWindow
    if (!marker.isInfoWindowShown()) {
        //显示
        marker.showInfoWindow();
    } else {
        //隐藏
        marker.hideInfoWindow();
    }
    return true;
}

拖拽比较类似,
先配置map的markerDragListener事件:

//设置地图Marker拖拽事件
aMap.setOnMarkerDragListener(this);

然后实现这个接口:

/**
    * 开始拖动
    *
    * @param marker
    */
@Override
public void onMarkerDragStart(Marker marker) {
    Log.d(TAG, "开始拖动");
}

/**
    * 拖动中
    *
    * @param marker
    */
@Override
public void onMarkerDrag(Marker marker) {
    Log.d(TAG, "拖动中");
}

/**
    * 拖动完成
    *
    * @param marker
    */
@Override
public void onMarkerDragEnd(Marker marker) {
    Log.d(TAG, "拖动完成");
}

但是实现需要配置marker可点拖动状态:

Marker marker = aMap.addMarker(new MarkerOptions()
            .draggable(true)//可拖动
            .position(latLng)
            .title("标题")
            .snippet("详细信息"));

8.6 绘制InfoWindow

Marker marker = aMap.addMarker(new MarkerOptions()
            .draggable(true)//可拖动
            .position(latLng)
            .title("标题")
            .snippet("详细信息"));

这里配置title和snippet,其实就是一个窗体。

然后通过marker.showInfoWindow或者hideInfoWindow方法实现显示或隐藏。

如何自定义InfoWindow呢?
首先需要在Activity中实现 AMAP.InfoWindowAdapter接口。

/**
    * 修改背景
    *
    * @param marker
    */
@Override
public View getInfoWindow(Marker marker) {
    View infoWindow = getLayoutInflater().inflate(
            R.layout.custom_info_window, null);

    render(marker, infoWindow);
    return infoWindow;
}

/**
    * 修改内容
    *
    * @param marker
    * @return
    */
@Override
public View getInfoContents(Marker marker) {
    View infoContent = getLayoutInflater().inflate(
            R.layout.custom_info_contents, null);
    render(marker, infoContent);
    return infoContent;
}

通过这两个方法可以自定义infoWindow。

其次infoWindow也是可以点击的,
我们需要再给Activity实现一个接口,AMap.OnInfoWindowClickListener,

//设置InfoWindow点击事件
aMap.setOnInfoWindowClickListener(this);

然后继续实现接口:

@Override
public void onInfoWindowClick(Marker marker) {
    showMsg("弹窗内容:标题:" + marker.getTitle() + "\n片段:" + marker.getSnippet());
}

8.7 如何改变地图中心点

希望点击一下就可以移动到所在地,这其实是比较容易做到的,回顾我们现在是一进入地图就会定位到当前所在地,而当我点击地图上其他位置时,会增加一个标点,而我们要做的就是把这个标点作为地图中心,然后移动地图位置即可。

/**
    * 改变地图中心位置
    * @param latLng 位置
    */
private void updateMapCenter(LatLng latLng) {
    // CameraPosition 第一个参数: 目标位置的屏幕中心点经纬度坐标。
    // CameraPosition 第二个参数: 目标可视区域的缩放级别
    // CameraPosition 第三个参数: 目标可视区域的倾斜度,以角度为单位。
    // CameraPosition 第四个参数: 可视区域指向的方向,以角度为单位,从正北向顺时针方向计算,从0度到360度
    CameraPosition cameraPosition = new CameraPosition(latLng, 16, 30, 0);
    //位置变更
    CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition);
    //改变位置
    aMap.moveCamera(cameraUpdate);
}

用这个方法即可。

9 出行路线规划

9.1 步行路线规划

//起点
private LatLonPoint mStartPoint;
//终点
private LatLonPoint mEndPoint;

帮助类:

package com.llw.mapdemo.util;

public class ChString {
    public static final String Kilometer = "\u516c\u91cc";// "公里";
    public static final String Meter = "\u7c73";// "米";
    public static final String ByFoot = "\u6b65\u884c";// "步行";
    public static final String To = "\u53bb\u5f80";// "去往";
    public static final String Station = "\u8f66\u7ad9";// "车站";
    public static final String TargetPlace = "\u76ee\u7684\u5730";// "目的地";
    public static final String StartPlace = "\u51fa\u53d1\u5730";// "出发地";
    public static final String About = "\u5927\u7ea6";// "大约";
    public static final String Direction = "\u65b9\u5411";// "方向";

    public static final String GetOn = "\u4e0a\u8f66";// "上车";
    public static final String GetOff = "\u4e0b\u8f66";// "下车";
    public static final String Zhan = "\u7ad9";// "站";

    public static final String cross = "\u4ea4\u53c9\u8def\u53e3"; // 交叉路口
    public static final String type = "\u7c7b\u522b"; // 类别
    public static final String address = "\u5730\u5740"; // 地址
    public static final String PrevStep = "\u4e0a\u4e00\u6b65";
    public static final String NextStep = "\u4e0b\u4e00\u6b65";
    public static final String Gong = "\u516c\u4ea4";
    public static final String ByBus = "\u4e58\u8f66";
    public static final String Arrive = "\u5230\u8FBE";// 到达
}

然后下面是地图帮助类:

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;
    }

}

这里定位之后,设置一个起点。

//设置起点
mStartPoint = convertToLatLonPoint(new LatLng(latitude, longitude));

通过经纬度,构建LatLng对象,然后将LatLng转为LatLonPoint。最后赋值给起点mStartPoint。

点击地图,设置终点:

/**
    * 点击地图
    */
@Override
public void onMapClick(LatLng latLng) {
    //终点
    mEndPoint = convertToLatLonPoint(latLng);
}

搜索路线需要建立一个RouteSearch对象:

//路线搜索对象
private RouteSearch routeSearch;

然后初始化:

/**
    * 初始化路线
    */
private void initRoute() {
    try {
        routeSearch = new RouteSearch(this);
    } catch (AMapException e) {
        e.printStackTrace();
    }
    routeSearch.setRouteSearchListener(this);
}

这里设置了监听,所以需要重写几个路线规划的方法:

步行规划路径结果:

/**
    * 步行规划路径结果
    *
    * @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) + ")";
                //显示步行花费时间
                tvTime.setText(des);
                bottomLayout.setVisibility(View.VISIBLE);
                //跳转到路线详情页面
                bottomLayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(RouteActivity.this,
                                RouteDetailActivity.class);
                        intent.putExtra("type", 0);
                        intent.putExtra("path", walkPath);
                        startActivity(intent);
                    }
                });
            } else if (walkRouteResult.getPaths() == null) {
                showMsg("对不起,没有搜索到相关数据!");
            }
        } else {
            showMsg("对不起,没有搜索到相关数据!");
        }
    } else {
        showMsg("错误码;" + code);
    }
}

怎么触发路线搜索:
当起点和终点通过地址转坐标方式,拿到地址后,就可以开始路线搜索了

if (mStartPoint != null && mEndPoint != null) {
    //开始路线搜索
    startRouteSearch();
}

然后开始路线搜索:

/**
    * 开始路线搜索
    */
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);

    //出行方式判断
    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;
        case 2://驾车
            //构建驾车路线搜索对象  剩余三个参数分别是:途经点、避让区域、避让道路
            RouteSearch.DriveRouteQuery driveQuery = new RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.WalkDefault, null, null, "");
            //驾车规划路径计算
            routeSearch.calculateDriveRouteAsyn(driveQuery);
            break;
        case 3://公交
            //构建驾车路线搜索对象 第三个参数表示公交查询城市区号,第四个参数表示是否计算夜班车,0表示不计算,1表示计算
            RouteSearch.BusRouteQuery busQuery = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BusLeaseWalk, city, 0);
            //公交规划路径计算
            routeSearch.calculateBusRouteAsyn(busQuery);
            break;
        default:
            break;
    }
}

好的,现在通过接口,拿到路线返回的结果了。现在需要对路线进行绘制。

需要先安排一个工具类:


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;
    }

}

图层叠加工具类:

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("\u8D77\u70B9"));
        // startMarker.showInfoWindow();

        endMarker = mAMap.addMarker((new MarkerOptions()).position(endPoint)
                .icon(getEndBitmapDescriptor()).title("\u7EC8\u70B9"));
        // 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;
    // }
}

步行路线图层类:

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("\u65B9\u5411:" + walkStep.getAction()
                        + "\n\u9053\u8DEF:" + 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);
    }
}

通过这三个类,完成步行路线绘制。
主要是地图的View,通过参数,传递给WalkRouteOverLay中,实现路线绘制。

9.2 骑行路线规划

骑行其实和步行差不多,只是路线限制时图层不同而已,其他的都类似,写起来也是比较简单的。

在startRouteSearch方法中:

 //出行方式判断
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;

这里通过routeSearch来计算骑行路线。

然后再MapUtil工具中,新增一个方法:

/**
    * 把集合体的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;
}

然后新增一个RideRouteOverlay,用于在地图上绘制骑行的图层:

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("\u65B9\u5411:" + rideStep.getAction()
                        + "\n\u9053\u8DEF:" + 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);
        }
}

然后在骑行路径规划的接口回调中使用这个图层绘制:

/**
    * 骑行规划路径结果
    *
    * @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);
    }
}

9.3 驾车路线规划

先增加计算驾车接口调用:

    case 2://驾车
        //构建驾车路线搜索对象  剩余三个参数分别是:途经点、避让区域、避让道路
        RouteSearch.DriveRouteQuery driveQuery = new RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.WalkDefault, null, null, "");
        //驾车规划路径计算
        routeSearch.calculateDriveRouteAsyn(driveQuery);
        break;

然后增加图层工具:

import android.content.Context;
import android.graphics.Color;

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.LatLngBounds;
import com.amap.api.maps.model.Marker;
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.DrivePath;
import com.amap.api.services.route.DriveStep;
import com.amap.api.services.route.TMC;
import com.llw.mapdemo.R;
import com.llw.mapdemo.util.MapUtil;

import java.util.ArrayList;
import java.util.List;


/**
 * 驾车路线图层类
 */
public class DrivingRouteOverlay extends RouteOverlay{

    private DrivePath drivePath;
    private List<LatLonPoint> throughPointList;
    private List<Marker> throughPointMarkerList = new ArrayList<Marker>();
    private boolean throughPointMarkerVisible = true;
    private List<TMC> tmcs;
    private PolylineOptions mPolylineOptions;
    private PolylineOptions mPolylineOptionscolor;
    private Context mContext;
    private boolean isColorfulline = true;
    private float mWidth = 25;
    private List<LatLng> mLatLngsOfPath;

    public void setIsColorfulline(boolean iscolorfulline) {
        this.isColorfulline = iscolorfulline;
    }

    /**
     * 根据给定的参数,构造一个导航路线图层类对象。
     *
     * @param amap      地图对象。
     * @param path 导航路线规划方案。
     * @param context   当前的activity对象。
     */
    public DrivingRouteOverlay(Context context, AMap amap, DrivePath path,
                               LatLonPoint start, LatLonPoint end, List<LatLonPoint> throughPointList) {
        super(context);
        mContext = context; 
        mAMap = amap; 
        this.drivePath = path;
        startPoint = MapUtil.convertToLatLng(start);
        endPoint = MapUtil.convertToLatLng(end);
        this.throughPointList = throughPointList;
    }

    @Override
    public float getRouteWidth() {
        return mWidth;
    }

    /**
     * 设置路线宽度
     *
     * @param mWidth 路线宽度,取值范围:大于0
     */
    public void setRouteWidth(float mWidth) {
        this.mWidth = mWidth;
    }

    /**
     * 添加驾车路线添加到地图上显示。
     */
    public void addToMap() {
        initPolylineOptions();
        try {
            if (mAMap == null) {
                return;
            }

            if (mWidth == 0 || drivePath == null) {
                return;
            }
            mLatLngsOfPath = new ArrayList<LatLng>();
            tmcs = new ArrayList<TMC>();
            List<DriveStep> drivePaths = drivePath.getSteps();
            for (DriveStep step : drivePaths) {
                List<LatLonPoint> latlonPoints = step.getPolyline();
                List<TMC> tmclist = step.getTMCs();
                tmcs.addAll(tmclist);
                addDrivingStationMarkers(step, convertToLatLng(latlonPoints.get(0)));
                for (LatLonPoint latlonpoint : latlonPoints) {
                    mPolylineOptions.add(convertToLatLng(latlonpoint));
                    mLatLngsOfPath.add(convertToLatLng(latlonpoint));
                }
            }
            if (startMarker != null) {
                startMarker.remove();
                startMarker = null;
            }
            if (endMarker != null) {
                endMarker.remove();
                endMarker = null;
            }
            addStartAndEndMarker();
            addThroughPointMarker();
            if (isColorfulline && tmcs.size()>0 ) {
                colorWayUpdate(tmcs);
                showcolorPolyline();
            }else {
                showPolyline();
            }            
            
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化线段属性
     */
    private void initPolylineOptions() {

        mPolylineOptions = null;

        mPolylineOptions = new PolylineOptions();
        mPolylineOptions.color(getDriveColor()).width(getRouteWidth());
    }

    private void showPolyline() {
        addPolyLine(mPolylineOptions);
    }
    
    private void showcolorPolyline() {
        addPolyLine(mPolylineOptionscolor);
        
    }

    /**
     * 根据不同的路段拥堵情况展示不同的颜色
     *
     * @param tmcSection
     */
    private void colorWayUpdate(List<TMC> tmcSection) {
        if (mAMap == null) {
            return;
        }
        if (tmcSection == null || tmcSection.size() <= 0) {
            return;
        }
        TMC segmentTrafficStatus;
        mPolylineOptionscolor = null;
        mPolylineOptionscolor = new PolylineOptions();
        mPolylineOptionscolor.width(getRouteWidth());
        List<Integer> colorList = new ArrayList<Integer>();
        mPolylineOptionscolor.add(MapUtil.convertToLatLng(tmcSection.get(0).getPolyline().get(0)));
        colorList.add(getDriveColor());
        for (int i = 0; i < tmcSection.size(); i++) {
            segmentTrafficStatus = tmcSection.get(i);
            int color = getcolor(segmentTrafficStatus.getStatus());
            List<LatLonPoint> mployline = segmentTrafficStatus.getPolyline();
            for (int j = 1; j < mployline.size(); j++) {
                mPolylineOptionscolor.add(MapUtil.convertToLatLng(mployline.get(j)));
                colorList.add(color);
            }
        }
        colorList.add(getDriveColor());
        mPolylineOptionscolor.colorValues(colorList);
    }
    
    private int getcolor(String status) {

        if (status.equals("畅通")) {
            return Color.GREEN;
        } else if (status.equals("缓行")) {
             return Color.YELLOW;
        } else if (status.equals("拥堵")) {
            return Color.RED;
        } else if (status.equals("严重拥堵")) {
            return Color.parseColor("#990033");
        } else {
            return Color.parseColor("#537edc");
        }	
    }

    public LatLng convertToLatLng(LatLonPoint point) {
        return new LatLng(point.getLatitude(),point.getLongitude());
  }
    
    /**
     * @param driveStep
     * @param latLng
     */
    private void addDrivingStationMarkers(DriveStep driveStep, LatLng latLng) {
        addStationMarker(new MarkerOptions()
                .position(latLng)
                .title("\u65B9\u5411:" + driveStep.getAction()
                        + "\n\u9053\u8DEF:" + driveStep.getRoad())
                .snippet(driveStep.getInstruction()).visible(nodeIconVisible)
                .anchor(0.5f, 0.5f).icon(getDriveBitmapDescriptor()));
    }

    @Override
    protected LatLngBounds getLatLngBounds() {
        LatLngBounds.Builder b = LatLngBounds.builder();
        b.include(new LatLng(startPoint.latitude, startPoint.longitude));
        b.include(new LatLng(endPoint.latitude, endPoint.longitude));
        if (this.throughPointList != null && this.throughPointList.size() > 0) {
            for (int i = 0; i < this.throughPointList.size(); i++) {
                b.include(new LatLng(
                        this.throughPointList.get(i).getLatitude(),
                        this.throughPointList.get(i).getLongitude()));
            }
        }
        return b.build();
    }

    public void setThroughPointIconVisibility(boolean visible) {
        try {
            throughPointMarkerVisible = visible;
            if (this.throughPointMarkerList != null
                    && this.throughPointMarkerList.size() > 0) {
                for (int i = 0; i < this.throughPointMarkerList.size(); i++) {
                    this.throughPointMarkerList.get(i).setVisible(visible);
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    
    private void addThroughPointMarker() {
        if (this.throughPointList != null && this.throughPointList.size() > 0) {
            LatLonPoint latLonPoint = null;
            for (int i = 0; i < this.throughPointList.size(); i++) {
                latLonPoint = this.throughPointList.get(i);
                if (latLonPoint != null) {
                    throughPointMarkerList.add(mAMap
                            .addMarker((new MarkerOptions())
                                    .position(
                                            new LatLng(latLonPoint
                                                    .getLatitude(), latLonPoint
                                                    .getLongitude()))
                                    .visible(throughPointMarkerVisible)
                                    .icon(getThroughPointBitDes())
                                    .title("\u9014\u7ECF\u70B9")));
                }
            }
        }
    }
    
    private BitmapDescriptor getThroughPointBitDes() {
        return BitmapDescriptorFactory.fromResource(R.drawable.amap_through);
       
    }

    /**
     * 获取两点间距离
     *
     * @param start
     * @param end
     * @return
     */
    public static int calculateDistance(LatLng start, LatLng end) {
        double x1 = start.longitude;
        double y1 = start.latitude;
        double x2 = end.longitude;
        double y2 = end.latitude;
        return calculateDistance(x1, y1, x2, y2);
    }

    public static int calculateDistance(double x1, double y1, double x2, double y2) {
        final double NF_pi = 0.01745329251994329; // 弧度 PI/180
        x1 *= NF_pi;
        y1 *= NF_pi;
        x2 *= NF_pi;
        y2 *= NF_pi;
        double sinx1 = Math.sin(x1);
        double siny1 = Math.sin(y1);
        double cosx1 = Math.cos(x1);
        double cosy1 = Math.cos(y1);
        double sinx2 = Math.sin(x2);
        double siny2 = Math.sin(y2);
        double cosx2 = Math.cos(x2);
        double cosy2 = Math.cos(y2);
        double[] v1 = new double[3];
        v1[0] = cosy1 * cosx1 - cosy2 * cosx2;
        v1[1] = cosy1 * sinx1 - cosy2 * sinx2;
        v1[2] = siny1 - siny2;
        double dist = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);

        return (int) (Math.asin(dist / 2) * 12742001.5798544);
    }


    //获取指定两点之间固定距离点
    public static LatLng getPointForDis(LatLng sPt, LatLng ePt, double dis) {
        double lSegLength = calculateDistance(sPt, ePt);
        double preResult = dis / lSegLength;
        return new LatLng((ePt.latitude - sPt.latitude) * preResult + sPt.latitude, (ePt.longitude - sPt.longitude) * preResult + sPt.longitude);
    }
    /**
     * 去掉DriveLineOverlay上的线段和标记。
     */
    @Override
    public void removeFromMap() {
        try {
            super.removeFromMap();
            if (this.throughPointMarkerList != null
                    && this.throughPointMarkerList.size() > 0) {
                for (int i = 0; i < this.throughPointMarkerList.size(); i++) {
                    this.throughPointMarkerList.get(i).remove();
                }
                this.throughPointMarkerList.clear();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

搜索结果中进行图层绘制:

/**
    * 驾车规划路径结果
    *
    * @param driveRouteResult 结果
    * @param code            结果码
    */
@Override
public void onDriveRouteSearched(DriveRouteResult driveRouteResult, int code) {
    aMap.clear();// 清理地图上的所有覆盖物
    if (code == AMapException.CODE_AMAP_SUCCESS) {
        if (driveRouteResult != null && driveRouteResult.getPaths() != null) {
            if (driveRouteResult.getPaths().size() > 0) {
                final DrivePath drivePath = driveRouteResult.getPaths()
                        .get(0);
                if(drivePath == null) {
                    return;
                }
                DrivingRouteOverlay drivingRouteOverlay = new DrivingRouteOverlay(
                        this, aMap, drivePath,
                        driveRouteResult.getStartPos(),
                        driveRouteResult.getTargetPos(), null);
                drivingRouteOverlay.removeFromMap();
                drivingRouteOverlay.addToMap();
                drivingRouteOverlay.zoomToSpan();

                int dis = (int) drivePath.getDistance();
                int dur = (int) drivePath.getDuration();
                String des = MapUtil.getFriendlyTime(dur)+"("+MapUtil.getFriendlyLength(dis)+")";
                Log.d(TAG, des);
            } else if (driveRouteResult.getPaths() == null) {
                showMsg("对不起,没有搜索到相关数据!");
            }
        } else {
            showMsg("对不起,没有搜索到相关数据!");
        }
    } else {
        showMsg("错误码;" + code);
    }
}

9.4 公交路线规划

接口请求:

    case 3://公交
        //构建驾车路线搜索对象 第三个参数表示公交查询城市区号,第四个参数表示是否计算夜班车,0表示不计算,1表示计算
        RouteSearch.BusRouteQuery busQuery = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BusLeaseWalk, "0755",0);
        //公交规划路径计算
        routeSearch.calculateBusRouteAsyn(busQuery);
        break;

图层工具添加:

import android.content.Context;

import com.amap.api.maps.AMap;
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.busline.BusStationItem;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.route.BusPath;
import com.amap.api.services.route.BusStep;
import com.amap.api.services.route.Doorway;
import com.amap.api.services.route.RailwayStationItem;
import com.amap.api.services.route.RouteBusLineItem;
import com.amap.api.services.route.RouteBusWalkItem;
import com.amap.api.services.route.RouteRailwayItem;
import com.amap.api.services.route.TaxiItem;
import com.amap.api.services.route.WalkStep;
import com.llw.mapdemo.util.MapUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * 公交路线图层类。在高德地图API里,如果需要显示公交路线,可以用此类来创建公交路线图层。如不满足需求,也可以自己创建自定义的公交路线图层。
 * @since V2.1.0
 */
public class BusRouteOverlay extends RouteOverlay {

    private BusPath busPath;
    private LatLng latLng;

    /**
     * 通过此构造函数创建公交路线图层。
     * @param context 当前activity。
     * @param amap 地图对象。
     * @param path 公交路径规划的一个路段。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类<strong> <a href="../../../../../../Search/com/amap/api/services/route/BusPath.html" title="com.amap.api.services.route中的类">BusPath</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 BusRouteOverlay(Context context, AMap amap, BusPath path,
                           LatLonPoint start, LatLonPoint end) {
        super(context);
        this.busPath = path;
        startPoint = MapUtil.convertToLatLng(start);
        endPoint = MapUtil.convertToLatLng(end);
        mAMap = amap;
    }

    /**
     * 添加公交路线到地图上。
     * @since V2.1.0
     */

    public void addToMap() {
        /**
         * 绘制节点和线<br>
         * 细节情况较多<br>
         * 两个step之间,用step和step1区分<br>
         * 1.一个step内可能有步行和公交,然后有可能他们之间连接有断开<br>
         * 2.step的公交和step1的步行,有可能连接有断开<br>
         * 3.step和step1之间是公交换乘,且没有步行,需要把step的终点和step1的起点连起来<br>
         * 4.公交最后一站和终点间有步行,加入步行线路,还会有一些步行marker<br>
         * 5.公交最后一站和终点间无步行,之间连起来<br>
         */
        try {
            List<BusStep> busSteps = busPath.getSteps();
            for (int i = 0; i < busSteps.size(); i++) {
                BusStep busStep = busSteps.get(i);
                if (i < busSteps.size() - 1) {
                    BusStep busStep1 = busSteps.get(i + 1);// 取得当前下一个BusStep对象
                    // 假如步行和公交之间连接有断开,就把步行最后一个经纬度点和公交第一个经纬度点连接起来,避免断线问题
                    if (busStep.getWalk() != null
                            && busStep.getBusLine() != null) {
                        checkWalkToBusline(busStep);
                    }

                    // 假如公交和步行之间连接有断开,就把上一公交经纬度点和下一步行第一个经纬度点连接起来,避免断线问题
                    if (busStep.getBusLine() != null
                            && busStep1.getWalk() != null 
                            && busStep1.getWalk().getSteps().size() > 0) {
                        checkBusLineToNextWalk(busStep, busStep1);
                    }
                    // 假如两个公交换乘中间没有步行,就把上一公交经纬度点和下一步公交第一个经纬度点连接起来,避免断线问题
                    if (busStep.getBusLine() != null
                            && busStep1.getWalk() == null
                            && busStep1.getBusLine() != null) {
                        checkBusEndToNextBusStart(busStep, busStep1);
                    }
                    // 和上面的很类似
                    if (busStep.getBusLine() != null
                            && busStep1.getWalk() == null
                            && busStep1.getBusLine() != null) {
                        checkBusToNextBusNoWalk(busStep, busStep1);
                    }
                    if (busStep.getBusLine() != null
                            && busStep1.getRailway() != null ) {
                        checkBusLineToNextRailway(busStep, busStep1);
                    }
                    if (busStep1.getWalk() != null &&
                            busStep1.getWalk().getSteps().size() > 0 &&
                            busStep.getRailway() != null) {
                        checkRailwayToNextWalk(busStep, busStep1);
                    }
                    
                    if ( busStep1.getRailway() != null &&
                            busStep.getRailway() != null) {
                        checkRailwayToNextRailway(busStep, busStep1);
                    }
                    
                    if (busStep.getRailway() != null && 
                        busStep1.getTaxi() != null ){
                        checkRailwayToNextTaxi(busStep, busStep1);
                    }
                    

                }

                if (busStep.getWalk() != null
                        && busStep.getWalk().getSteps().size() > 0) {
                    addWalkSteps(busStep);
                } else {
                    if (busStep.getBusLine() == null && busStep.getRailway() == null && busStep.getTaxi() == null) {
                        addWalkPolyline(latLng, endPoint);
                    }
                }
                if (busStep.getBusLine() != null) {
                    RouteBusLineItem routeBusLineItem = busStep.getBusLine();
                    addBusLineSteps(routeBusLineItem);
                    addBusStationMarkers(routeBusLineItem);
                    if (i == busSteps.size() - 1) {
                        addWalkPolyline(MapUtil.convertToLatLng(getLastBuslinePoint(busStep)), endPoint);
                    }
                }
                if (busStep.getRailway() != null) {
                    addRailwayStep(busStep.getRailway());
                    addRailwayMarkers(busStep.getRailway());
                    if (i == busSteps.size() - 1) {
                        addWalkPolyline(MapUtil.convertToLatLng(busStep.getRailway().getArrivalstop().getLocation()), endPoint);
                    }
                }
                if (busStep.getTaxi() != null) {
                    addTaxiStep(busStep.getTaxi());
                    addTaxiMarkers(busStep.getTaxi());
                }
            }
            addStartAndEndMarker();

        } catch (Throwable e) {
            e.printStackTrace();
        }
    }



    private void checkRailwayToNextTaxi(BusStep busStep, BusStep busStep1) {
        LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
        LatLonPoint taxiFirstPoint = busStep1.getTaxi().getOrigin();
        if (!railwayLastPoint.equals(taxiFirstPoint)) {
            addWalkPolyLineByLatLonPoints(railwayLastPoint, taxiFirstPoint);
        }
    }

    private void checkRailwayToNextRailway(BusStep busStep, BusStep busStep1) {
        LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
        LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation();
        if (!railwayLastPoint.equals(railwayFirstPoint)) {
            addWalkPolyLineByLatLonPoints(railwayLastPoint, railwayFirstPoint);
        }
        
    }

    private void checkBusLineToNextRailway(BusStep busStep, BusStep busStep1) {
        LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
        LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation();
        if (!busLastPoint.equals(railwayFirstPoint)) {
            addWalkPolyLineByLatLonPoints(busLastPoint, railwayFirstPoint);
        }
        
    }

    private void checkRailwayToNextWalk(BusStep busStep, BusStep busStep1) {
        LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
        LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1);
        if (!railwayLastPoint.equals(walkFirstPoint)) {
            addWalkPolyLineByLatLonPoints(railwayLastPoint, walkFirstPoint);
        }
        
    }

    private void addRailwayStep(RouteRailwayItem railway) {
        List<LatLng> railwaylistpoint = new ArrayList<LatLng>();
        List<RailwayStationItem> railwayStationItems = new ArrayList<RailwayStationItem>();
        railwayStationItems.add(railway.getDeparturestop());
        railwayStationItems.addAll(railway.getViastops());
        railwayStationItems.add(railway.getArrivalstop());
        for (int i = 0; i < railwayStationItems.size(); i++) {
            railwaylistpoint.add(MapUtil.convertToLatLng(railwayStationItems.get(i).getLocation()));
        }
        addRailwayPolyline(railwaylistpoint);
    }
    
    private void addTaxiStep(TaxiItem taxi){
        addPolyLine(new PolylineOptions().width(getRouteWidth())
                .color(getBusColor())
                .add(MapUtil.convertToLatLng(taxi.getOrigin()))
                .add(MapUtil.convertToLatLng(taxi.getDestination())));
    }

    /**
     * @param busStep
     */
    private void addWalkSteps(BusStep busStep) {
        RouteBusWalkItem routeBusWalkItem = busStep.getWalk();
        List<WalkStep> walkSteps = routeBusWalkItem.getSteps();
        for (int j = 0; j < walkSteps.size(); j++) {
            WalkStep walkStep = walkSteps.get(j);
            if (j == 0) {
                LatLng latLng = MapUtil.convertToLatLng(walkStep
                        .getPolyline().get(0));
                String road = walkStep.getRoad();// 道路名字
                String instruction = getWalkSnippet(walkSteps);// 步行导航信息
                addWalkStationMarkers(latLng, road, instruction);
            }

            List<LatLng> listWalkPolyline = MapUtil
                    .convertArrList(walkStep.getPolyline());
            this.latLng = listWalkPolyline.get(listWalkPolyline.size() - 1);

            addWalkPolyline(listWalkPolyline);

            // 假如步行前一段的终点和下的起点有断开,断画直线连接起来,避免断线问题
            if (j < walkSteps.size() - 1) {
                LatLng lastLatLng = listWalkPolyline.get(listWalkPolyline
                        .size() - 1);
                LatLng firstlatLatLng = MapUtil
                        .convertToLatLng(walkSteps.get(j + 1).getPolyline()
                                .get(0));
                if (!(lastLatLng.equals(firstlatLatLng))) {
                    addWalkPolyline(lastLatLng, firstlatLatLng);
                }
            }

        }
    }

    /**
     * 添加一系列的bus PolyLine
     *
     * @param routeBusLineItem
     */
    private void addBusLineSteps(RouteBusLineItem routeBusLineItem) {
        addBusLineSteps(routeBusLineItem.getPolyline());
    }

    private void addBusLineSteps(List<LatLonPoint> listPoints) {
        if (listPoints.size() < 1) {
            return;
        }
        addPolyLine(new PolylineOptions().width(getRouteWidth())
                .color(getBusColor())
                .addAll(MapUtil.convertArrList(listPoints)));
    }

    /**
     * @param latLng
     *            marker
     * @param title
     * @param snippet
     */
    private void addWalkStationMarkers(LatLng latLng, String title,
                                       String snippet) {
        addStationMarker(new MarkerOptions().position(latLng).title(title)
                .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
                .icon(getWalkBitmapDescriptor()));
    }

    /**
     * @param routeBusLineItem
     */
    private void addBusStationMarkers(RouteBusLineItem routeBusLineItem) {
        BusStationItem startBusStation = routeBusLineItem
                .getDepartureBusStation();
        LatLng position = MapUtil.convertToLatLng(startBusStation
                .getLatLonPoint());
        String title = routeBusLineItem.getBusLineName();
        String snippet = getBusSnippet(routeBusLineItem);

        addStationMarker(new MarkerOptions().position(position).title(title)
                .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
                .icon(getBusBitmapDescriptor()));
    }
    
    private void addTaxiMarkers(TaxiItem taxiItem) {
        
        LatLng position = MapUtil.convertToLatLng(taxiItem
                .getOrigin());
        String title = taxiItem.getmSname()+"打车";
        String snippet = "到终点";

        addStationMarker(new MarkerOptions().position(position).title(title)
                .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
                .icon(getDriveBitmapDescriptor()));
    }

    private void addRailwayMarkers(RouteRailwayItem railway) {
        LatLng Departureposition = MapUtil.convertToLatLng(railway
                .getDeparturestop().getLocation());
        String Departuretitle = railway.getDeparturestop().getName()+"上车";
        String Departuresnippet = railway.getName();

        addStationMarker(new MarkerOptions().position(Departureposition).title(Departuretitle)
                .snippet(Departuresnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
                .icon(getBusBitmapDescriptor()));
        
        
        LatLng Arrivalposition = MapUtil.convertToLatLng(railway
                .getArrivalstop().getLocation());
        String Arrivaltitle = railway.getArrivalstop().getName()+"下车";
        String Arrivalsnippet = railway.getName();

        addStationMarker(new MarkerOptions().position(Arrivalposition).title(Arrivaltitle)
                .snippet(Arrivalsnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
                .icon(getBusBitmapDescriptor()));
    }
    /**
     * 如果换乘没有步行 检查bus最后一点和下一个step的bus起点是否一致
     *
     * @param busStep
     * @param busStep1
     */
    private void checkBusToNextBusNoWalk(BusStep busStep, BusStep busStep1) {
        LatLng endbusLatLng = MapUtil
                .convertToLatLng(getLastBuslinePoint(busStep));
        LatLng startbusLatLng = MapUtil
                .convertToLatLng(getFirstBuslinePoint(busStep1));
        if (startbusLatLng.latitude - endbusLatLng.latitude > 0.0001
                || startbusLatLng.longitude - endbusLatLng.longitude > 0.0001) {
            drawLineArrow(endbusLatLng, startbusLatLng);// 断线用带箭头的直线连?
        }
    }

    /**
     *
     * checkBusToNextBusNoWalk 和这个类似
     *
     * @param busStep
     * @param busStep1
     */
    private void checkBusEndToNextBusStart(BusStep busStep, BusStep busStep1) {
        LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
        LatLng endbusLatLng = MapUtil.convertToLatLng(busLastPoint);
        LatLonPoint busFirstPoint = getFirstBuslinePoint(busStep1);
        LatLng startbusLatLng = MapUtil.convertToLatLng(busFirstPoint);
        if (!endbusLatLng.equals(startbusLatLng)) {
            drawLineArrow(endbusLatLng, startbusLatLng);//
        }
    }

    /**
     * 检查bus最后一步和下一各step的步行起点是否一致
     *
     * @param busStep
     * @param busStep1
     */
    private void checkBusLineToNextWalk(BusStep busStep, BusStep busStep1) {
        LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
        LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1);
        if (!busLastPoint.equals(walkFirstPoint)) {
            addWalkPolyLineByLatLonPoints(busLastPoint, walkFirstPoint);
        }
    }

    /**
     * 检查 步行最后一点 和 bus的起点 是否一致
     *
     * @param busStep
     */
    private void checkWalkToBusline(BusStep busStep) {
        LatLonPoint walkLastPoint = getLastWalkPoint(busStep);
        LatLonPoint buslineFirstPoint = getFirstBuslinePoint(busStep);

        if (!walkLastPoint.equals(buslineFirstPoint)) {
            addWalkPolyLineByLatLonPoints(walkLastPoint, buslineFirstPoint);
        }
    }

    /**
     * @param busStep1
     * @return
     */
    private LatLonPoint getFirstWalkPoint(BusStep busStep1) {
        return busStep1.getWalk().getSteps().get(0).getPolyline().get(0);
    }

    /**
     *
     */
    private void addWalkPolyLineByLatLonPoints(LatLonPoint pointFrom,
                                               LatLonPoint pointTo) {
        LatLng latLngFrom = MapUtil.convertToLatLng(pointFrom);
        LatLng latLngTo = MapUtil.convertToLatLng(pointTo);

        addWalkPolyline(latLngFrom, latLngTo);
    }

    /**
     * @param latLngFrom
     * @param latLngTo
     * @return
     */
    private void addWalkPolyline(LatLng latLngFrom, LatLng latLngTo) {
        addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo)
                .width(getRouteWidth()).color(getWalkColor()).setDottedLine(true));
    }

    /**
     * @param listWalkPolyline
     */
    private void addWalkPolyline(List<LatLng> listWalkPolyline) {

        addPolyLine(new PolylineOptions().addAll(listWalkPolyline)
                .color(getWalkColor()).width(getRouteWidth()).setDottedLine(true));
    }

    private void addRailwayPolyline(List<LatLng> listPolyline) {

        addPolyLine(new PolylineOptions().addAll(listPolyline)
                .color(getDriveColor()).width(getRouteWidth()));
    }
    
    
    private String getWalkSnippet(List<WalkStep> walkSteps) {
        float disNum = 0;
        for (WalkStep step : walkSteps) {
            disNum += step.getDistance();
        }
        return "\u6B65\u884C" + disNum + "\u7C73";
    }

    public void drawLineArrow(LatLng latLngFrom, LatLng latLngTo) {

        addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo).width(3)
                .color(getBusColor()).width(getRouteWidth()));// 绘制直线
    }

    private String getBusSnippet(RouteBusLineItem routeBusLineItem) {
        return "("
                + routeBusLineItem.getDepartureBusStation().getBusStationName()
                + "-->"
                + routeBusLineItem.getArrivalBusStation().getBusStationName()
                + ") \u7ECF\u8FC7" + (routeBusLineItem.getPassStationNum() + 1)
                + "\u7AD9";
    }

    /**
     * @param busStep
     * @return
     */
    private LatLonPoint getLastWalkPoint(BusStep busStep) {

        List<WalkStep> walkSteps = busStep.getWalk().getSteps();
        WalkStep walkStep = walkSteps.get(walkSteps.size() - 1);
        List<LatLonPoint> lonPoints = walkStep.getPolyline();
        return lonPoints.get(lonPoints.size() - 1);
    }

    private LatLonPoint getExitPoint(BusStep busStep) {
        Doorway doorway = busStep.getExit();
        if (doorway == null) {
            return null;
        }
        return doorway.getLatLonPoint();
    }

    private LatLonPoint getLastBuslinePoint(BusStep busStep) {
        List<LatLonPoint> lonPoints = busStep.getBusLine().getPolyline();

        return lonPoints.get(lonPoints.size() - 1);
    }

    private LatLonPoint getEntrancePoint(BusStep busStep) {
        Doorway doorway = busStep.getEntrance();
        if (doorway == null) {
            return null;
        }
        return doorway.getLatLonPoint();
    }

    private LatLonPoint getFirstBuslinePoint(BusStep busStep) {
        return busStep.getBusLine().getPolyline().get(0);
    }
}

然后是结果使用图层绘制:

    /**
    * 公交规划路径结果
    *
    * @param busRouteResult 结果
    * @param code           结果码
    */
@Override
public void onBusRouteSearched(BusRouteResult busRouteResult, int code) {
    aMap.clear();// 清理地图上的所有覆盖物
    if (code == AMapException.CODE_AMAP_SUCCESS) {
        if (busRouteResult != null && busRouteResult.getPaths() != null) {
            if (busRouteResult.getPaths().size() > 0) {
                final BusPath busPath = busRouteResult.getPaths().get(0);
                if (busPath == null) {
                    return;
                }
                BusRouteOverlay busRouteOverlay = new BusRouteOverlay(
                        this, aMap, busPath,
                        busRouteResult.getStartPos(),
                        busRouteResult.getTargetPos());
                busRouteOverlay.removeFromMap();
                busRouteOverlay.addToMap();
                busRouteOverlay.zoomToSpan();

                int dis = (int) busPath.getDistance();
                int dur = (int) busPath.getDuration();
                String des = MapUtil.getFriendlyTime(dur) + "(" + MapUtil.getFriendlyLength(dis) + ")";
                Log.d(TAG, des);
            } else if (busRouteResult.getPaths() == null) {
                showMsg("对不起,没有搜索到相关数据!");
            }
        } else {
            showMsg("对不起,没有搜索到相关数据!");
        }
    } else {
        showMsg("错误码;" + code);
    }
}

10 总结

  • 首先是配置依赖,可以远程或者本地引用,前提是配置key,注意配置SHA1安全码。
  • 然后是地图使用,主要是在布局中声明Map,这个Map本质上是一个ViewGroup。
  • 然后是设置地图的一些配置,比如marker,点击事件,拖拽事件等。
  • 然后是定位的使用,逆地址编码和地址编码,注意动态申请权限。
  • 然后是搜索结果的使用,这个调接口,配合地址编码,也是很容易。
  • 然后是路径规划了,这个主要是调接口, RouteSearch很关键,然后是各种图层绘制,总体上还是比较简单的。

   转载规则


《Android 集成 高德地图API》 Jason 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Android wear os 使用入门 Android wear os 使用入门
1 Wear OS 开发原则1.1 针对关键任务进行设计重点关注目标用户的一项或两项需求,而不是完整应用体验。不要迁移整个移动代码库,也不要将 Wear OS 界面放在顶层。 相反,您应寻找适合腕部佩戴的关键任务,并简化 Wear OS 的
2023-02-11
下一篇 
Android 集成 ChatGPT Android 集成 ChatGPT
1 效果 2 技术栈Timber: 日志打印工具(Github地址)Room:Jetpack包含的数据库(最全面的Room数据库指南)Splash Screens: 启动屏幕Android12支持(Android 12 SplashScr
2023-02-10
  目录