网络知识 娱乐 Android 高德地图API(详细步骤+源码)

Android 高德地图API(详细步骤+源码)

高德地图API使用详解

  • 前言
  • 正文
    • 一、创建应用
      • ① 获取PackageName
      • ② 获取调试版安全码SHA1
      • ③ 获取发布版安全码SHA1
    • 二、配置Android Studio工程
      • ① 导入SDK
      • ② 配置AndroidManifest.xml
    • 三、获取当前定位信息
      • ① 版本判断
      • ② 动态权限请求
      • ③ 初始化定位
      • ④ 获取定位结果
    • 四、显示地图
    • 五、显示当前定位地图
    • 六、地图设置
      • ① 修改自定义定位图标
      • ② 设置缩放等级
      • ③ 开启室内地图
      • ④ 地图控件设置
    • 七、获取POI数据
    • 八、地图点击长按事件
      • ① 逆地理编码
      • ② 地理编码
      • ③ 添加标点Marker
      • ④ 删除标点Marker
      • ⑤ 绘制动画效果Marker
      • ⑥ Marker的点击和拖拽事件
      • ⑦ 绘制 InfoWindow
      • ⑧ InfoWindow的点击事件
      • ⑨ 改变地图中心点
    • 九、出行路线规划
      • ① 准备工作
      • ② 步行路线规划
      • ③ 骑行路线规划
      • ④ 驾车路线规划
      • ⑤ 公交路线规划
      • ⑥ 步行路线详情
      • ⑦ 骑行路线详情
      • ⑧ 驾车路线详情
      • ⑨ 公交路线详情
      • ⑩ 手动输入目的地和出发地
    • 十、源码
  • 尾声

前言

  为了丰富对地图、定位的了解,在写了百度、腾讯的地图定位之后,终于到了高德地图了。我个人平时日常使用最多的定位软件就是高德地图。所以这篇文章,就带你来初步了解使用。

正文

  在此之前呢,默认都没有账号,因此需要先注册一个账号账号才行,点击高德开放平台进入主页面。

在这里插入图片描述
点击右上角的注册按钮,然后选择注册开发者的类型,有个人开发者、企业开发者。
在这里插入图片描述
如果你已经工作了可以选择企业开发者,只不过需要填写的资料要多一些,这里我选择成为个人开发者,点击。
在这里插入图片描述
勾选上这个选项,然后点击确认注册开发者,进入下一步,填写个人资料。
在这里插入图片描述
填写个人资料,实名认证,请注意实名认证,一个身份只能认证一个开发者账号,后面个人主页和开发者简介属于选填的。
在这里插入图片描述
点击下一步就注册完成了。然后重新进入高德开放平台登录你刚才注册的账号,之后进入控制台选择应用管理,进入我的应用。

一、创建应用

在这里插入图片描述
然后点击创建新应用按钮
在这里插入图片描述
填写应用信息,然后点击新建。
在这里插入图片描述
建好应用之后需要添加key,点击添加。
在这里插入图片描述
添加key需要你的应用的一些信息,你可以和我的这个图一样。
在这里插入图片描述
从上面的图来看还需要三个值,发布版安全码SHA1、调试版安全码SHA1、PackageName。

① 发布版安全码SHA1就是你的应用发布正式版本时的安全码,常规是使用jks秘钥来生成release包。
② 调试版安全码SHA1就是你的应用通过usb直接运行在手机或者虚拟机时的安全码,同一个项目在不同的电脑上运行,这个安全码各不相同。
③ PackageName 就是你的应用包名。

① 获取PackageName

下面我将在一一获取这三个值,保留当前的网页,然后打开你的Android Studio,新建一个名为GaodeMapDemo的项目,如下图所示
在这里插入图片描述
注意这个包名,现在你就可以复制这个包名到刚才的网页中的输入框中,位置如下图所示,key的包名需要和你的应用包名保持一致。
在这里插入图片描述
然后回到AS,点击Finish完成你的项目的创建。

② 获取调试版安全码SHA1

下面来获取调试版安全码SHA1

创建好项目之后,注意你的AS的右侧边栏,点击Gradle → app → android → signingReport,最后双击signingReport。
在这里插入图片描述
当你的AS版本为最新版时,你会发现这里好像有点不一样了,找不到signingReport了,这是AS更新之后默认这个功能关掉了,需要去手动打开它。在设置里面
在这里插入图片描述
如下图这里默认勾选上了,现在把这根勾选上的去掉,
在这里插入图片描述
然后Sync,或者点击这个小图标。
在这里插入图片描述

然后你的项目的右侧边栏就会出现这个熟悉的task了。

然后你会看到Run下会出现如下图类似的信息。
在这里插入图片描述
debug就是调试版,那么它的SHA1就是调试版安全码SHA1,这正是我所需要的。下面将它复制到刚才的网页中,如下图所示,别放错位置了。
在这里插入图片描述
最后来获取发布版安全码SHA1。

③ 获取发布版安全码SHA1

这个稍微有一些麻烦,因此需要先创建一个jks文件才行。

点击Build 选择点击Generate Signed Bundle / APK…
在这里插入图片描述
选择APK,然后Next。
在这里插入图片描述
然后是配置,这里需要填写jks的路径,但是我没有这个jks,因此点击**Create new,**按钮去创建一个。
在这里插入图片描述
首先要指定这个jks的文件存放路径和文件名。
在这里插入图片描述
这里我存放在D盘下的APK文件夹中,然后设置jks的名字为GaodeMapDemo,然后点击OK。
在这里插入图片描述
输入jks的密码和key的密码,最后点击OK。
在这里插入图片描述
会弹出这样一个窗口,不用管它,点击OK。
在这里插入图片描述
勾选记住密码,然后点击Next。
在这里插入图片描述
选择release,然后两个都勾选上,最后点击Finish。
在这里插入图片描述
当你看到AS的右下角出现这一提示时,就说明你的release包已经生成好了。

在这里插入图片描述
在你的AS中查看这个apk,你可以复制它通过电脑QQ发给你的手机,然后在手机上直接打开安装。
在这里插入图片描述
然后你就能看到熟悉的Hello World!

接下来进入Android Studio的Terminal工具输入如下图所示的命令和jks存放路径。

//如果你前面的步骤和我一模一样的话,这里你就可以复制粘贴,不一样的话就修改jks的路径就可以了
keytool -list -v -keystore D:APKGaodeMapDemo.jks

在这里插入图片描述
输入密码,输入时不可见。输入完直接回车,然后你会看到如下图类似的信息。

在这里插入图片描述
复制到网页中,位置如下图所示,点击提交。
在这里插入图片描述
这个key就生成好了。
在这里插入图片描述

二、配置Android Studio工程

首先要下载SDK,点击SDK下载

在这里插入图片描述
如上图配置,然后点击下载,下载到本地后解压,解压之后如下所示:
在这里插入图片描述

① 导入SDK

复制这些文件到你的libs下。然后点击这个小象图标进行工程的资源配置同步。
在这里插入图片描述
最终如下图所示,你可以看到你的这个jar现在是可以打开的。
在这里插入图片描述
不过有一部分人这样做了之后,jar包没有发生任何变化,那么你可以采取另一种方式,鼠标右键点击这个jar包,然后选择Add as Library…,之后会出现一个弹窗,默认是添加到你当前的app模块,你点击OK即可。然后等待同步,你就会发现你的jar包展开了。

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

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

在这里插入图片描述
然后点击Sync进行再一次同步。下面进行AndroidManifest.xml的配置

② 配置AndroidManifest.xml

打开AndroidManifest.xml,首先在application标签下添加定位服务

	
	<service android:name="com.amap.api.location.APSService"/>

然后添加在manifest标签下添加如下权限。

	
    <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" />
    
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
    
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

最后在application标签下添加高德的访问key

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

这个值和你创建的key的值一致,如下图所示则是我的key。
在这里插入图片描述

三、获取当前定位信息

  下面设想一个情景,我想要一打开应用就获取到当前定位信息。

那么基于这个目的下面来写代码。刚才在AndroidManifest.xml配置了比较多的权限,而在实际的使用过程中,Android6.0之后有一些权限是需要用户动态申请的,比如定位、获取手机状态、文件读写之类的。

因此首先得先判断当前是否需要动态请求权限,所以要根据Android的版本来判断。

① 版本判断

于是就可以在MainActivity中写入这样一个checkingAndroidVersion()方法。

	/**
     * 检查Android版本
     */
    private void checkingAndroidVersion() {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            //Android6.0及以上先获取权限再定位
            
        }else {
            //Android6.0以下直接定位
            
        }
    }

用于检查Android版本。6.0以下则直接定位,6.0及以上则动态申请权限,然后在权限通过的返回中进行定位,思路很清晰了,下面你要在onCreate中调用这个 checkingAndroidVersion() 方法。
在这里插入图片描述
下面来写这个动态权限的请求,这个其实也没有必要用原生的权限请求,网络上有很多优秀的框架可以帮助你快速实现,比如这个EasyPermission,在之前我是没有讲述过这个权限请求框架的。下面来看看。

② 动态权限请求

打开app下的build.gradle,在dependencies闭包下添加如下依赖:

	//Google推荐的EasyPermission库
    implementation 'pub.devrel:easypermissions:3.0.0'

如下图所示
在这里插入图片描述
然后点击AS右上角的Sync进行同步,同步完成之后,你就可以使用了。回到MainActivity中,
先增加一个成员变量

	//请求权限码
    private static final int REQUEST_PERMISSIONS = 9527;

新增requestPermission()方法。

	/**
     * 动态请求权限
     */
    @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("已获得权限,可以定位啦!");
        } else {
            //false 无权限
            EasyPermissions.requestPermissions(this, "需要权限", REQUEST_PERMISSIONS, permissions);
        }
    }

这个方法中对权限进行了判断,有权限则可以定位,没有权限则请求权限

	/**
     * 请求权限结果
     * @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);
    }

通过重写onRequestPermissionsResult方法来获取权限授予的返回结果,然后再通过EasyPermissions.onRequestPermissionsResult设置,设置之后会重新调用requestPermission()方法,为什么呢?就因为这个注解**@AfterPermissionGranted(REQUEST_PERMISSIONS)**,然后又会判断是否有权限,有则进行之后的定位,下面可以来写定位的方法了。

简单写一个消息提示的方法,用于当前Activity的使用。

	/**
     * Toast提示
     * @param msg 提示内容
     */
    private void showMsg(String msg){
        Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
    }

下面进行定位的初始化

③ 初始化定位

首先在MainActivity中新增两个成员变量

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

然后新增一个initLocation()方法。

	/**
     * 初始化定位
     */
    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);
    }

同样也要在onCreate方法中进行调用。
在这里插入图片描述
  注意initLocation()的调用在checkingAndroidVersion()之前,你可能会问为什么,那是因为你想定位,那么首先要先配置一些参数,配置好之后才能启动定位,否则会报错,常见的就是空指针异常。而检查版本之后当有权限时,就直接定位了,而把这个方法放到checkingAndroidVersion()里面去并不是很明智,因为你可能要调用两次,这样明显浪费资源,因此这就是为什么它要在checkingAndroidVersion()方法之前调用的原因。你想不到有这么多门道吧。

至于initLocation()方法本身我也没有什么好解释的,因为注释已经写的非常明白了,下面还有比较的最后一步,那就是定位的发起和定位结果的获取了。

④ 获取定位结果

首先要发起定位才能获取定位结果。
那么可以这样写。首先要在Android6.0以下直接启动定位。这次定位配置已经完毕,所以你完全不用担心会报错。
在这里插入图片描述
其次在权限允许之后进行定位。
在这里插入图片描述
现在定位已经发起了,那么定位结果怎么获取呢?不知道你有没有注意到我在initLocation()方法中的这行代码。

mLocationClient.setLocationListener(this);

注意到这里传入的是this,则表明通过当前Activity来实现定位结果的监听,那么可以通过实现AMapLocationListener,重写里面的onLocationChanged方法来获取定位结果。
在这里插入图片描述
onLocationChanged方法如下:

	/**
     * 接收异步返回的定位结果
     *
     * @param aMapLocation
     */
    @Override
    public void onLocationChanged(AMapLocation aMapLocation) {
        if (aMapLocation != null) {
            if (aMapLocation.getErrorCode() == 0) {
                //地址
                String address = aMapLocation.getAddress();
            } else {
                //定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
                Log.e("AmapError", "location Error, ErrCode:"
                        + aMapLocation.getErrorCode() + ", errInfo:"
                        + aMapLocation.getErrorInfo());
            }
        }
    }

现在已经拿到了定位的结果了,为了更直观,所以需要显示在当前的Activity中,那么下面修改布局activity_main.xml。

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

    <TextView
        android:id="@+id/tv_content"
        android:padding="20dp"
        android:textSize="18sp"
        android:textColor="#000"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />


然后回到MainActivity,新增加一个成员变量。

	//内容
    private TextView tvContent;

然后在onCreate中,绑定控件id。它在initLocation()方法之前

	tvContent = findViewById(R.id.tv_content);

然后在onLocationChanged中显示
在这里插入图片描述
这个我解释一下这个用法

address == null ? "无地址" : address

这个是三目运算,address == null 表示这个条件是否成立,如果成立则返回 “无地址”,不成立则返回 address 。

它的意思和下面这个if else一样。

	if(address == null){
         tvContent.setText("无地址");
    }else {
         tvContent.setText(address);
    }

熟练了你就知道怎么使用了。它会比你使用if else 来更简洁,不是吗?

OK,写了这么多代码也该要运行一下了吧。
在这里插入图片描述
可以看到第一次打开时提醒你需要权限,然后进行授权,授权后获得定位显示定位数据,之前我关闭当前应用,再一次进入,此时已经有了权限,则直接进行定位,然后显示定位数据。如果要显示经纬度的话还可以再修改一下

				StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append("纬度:" + latitude + "n");
                stringBuffer.append("经度:" + longitude + "n");
                stringBuffer.append("地址:" + address + "n");

                tvContent.setText(stringBuffer.toString());

再运行一下:
在这里插入图片描述
当然,获取定位数据的信息还不止这些,还有如下这些,感兴趣可以自行去尝试。

aMapLocation.getLocationType();//获取当前定位结果来源,如网络定位结果,详见定位类型表
aMapLocation.getAccuracy();//获取精度信息
aMapLocation.getCountry();//国家信息
aMapLocation.getProvince();//省信息
aMapLocation.getCity();//城市信息
aMapLocation.getDistrict();//城区信息
aMapLocation.getStreet();//街道信息
aMapLocation.getStreetNum();//街道门牌号信息
aMapLocation.getCityCode();//城市编码
aMapLocation.getAdCode();//地区编码
aMapLocation.getAoiName();//获取当前定位点的AOI信息
aMapLocation.getBuildingId();//获取当前室内定位的建筑物Id
aMapLocation.getFloor();//获取当前室内定位的楼层
aMapLocation.getGpsAccuracyStatus();//获取GPS的当前状态

还可以在获取信息之后,将定位给停止

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

在页面销毁时同时销毁本地定位服务。

	@Override
    protected void onDestroy() {
        super.onDestroy();
        //销毁定位客户端,同时销毁本地定位服务。
        mLocationClient.onDestroy();
    }

四、显示地图

下面改变一下activity_main.xml

<?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"/>


我删除了TextView,改变了外部的父布局,进入到MainActivity中。按照下图进行改变,你可以将无用的代码删除掉。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后增加地图生命周期的管理方法。

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

下面重新运行一下,不出意外你就能看到地图了。
在这里插入图片描述
不出意外,你的页面会和我这个一毛一样,默认就是定位在北京。百度和腾讯也都是如此。

五、显示当前定位地图

很显然,默认的地图不能满足我们的要求,那么就需要开发者自行去设置了,比如我现在在深圳福田区,那么我就要定位到当前所在地这里才行,来看看要怎么做吧。

现在MainActivity中新增两个成员变量

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

然后新增一个initMap()方法,用于初始化地图

	/**
     * 初始化地图
     * @param savedInstanceState
     */
    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);
    }

同样要在onCreate中调用
在这里插入图片描述
同时,MainActivity要实现LocationSource