今天和大家分享一下如果在Android开发中引用高德地图的SDK以及实现定位,搜索,导航的功能
=-=
第一步:引入高德地图的SDK。
首先,进入高德地图开发平台,鼠标移至头像处,点击我的应用,然后新建一个应用
点击添加,添加key
key名称自己命名
PackageName填写的是你项目的包名,在工程的bulid.gradle下的defaultConfig
defaultConfig {
applicationId "com.cdw.study8"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
我的包名是com.cdw.study8
然后获取发布版的安全码
cmd打开终端,输入where keytool,获取keytool的路径
将ketool的路径配置到环境变量path中
终端继续
keytool -v -list -keystore 名证书地址 debug.keystore
签名证书一般在c盘的用户目录的.android包下,如果特殊情况找不到,那找sdk安装目录的.android包下
我的是 keytool -v -list -keystore D:\sdk\.android\debug.keystore
密钥库命令是小写的android,然后SHA1中的内容复制下来,粘贴到应用中发布版安全码中去就可以了
创建完成了
然后鼠标滑到开发支持,点击Android地图SDK,下拉找到相关下载,下载aar包
下载完成后解压,将解压后的包拷贝至项目的libc目录下
在bulid.gradle,加入
repositories {
flatDir {
dirs'libs'
}
加入依赖,注意,name后面是你的aar的包名,为防止有误差,请不要复制我的包名,复制你自己的
implementation(name:'AMap3DMap_7.9.1_AMapNavi_7.9.1_AMapSearch_7.9.0_AMapLocation_5.3.1_20210414',ext:'aar')
第二步:在项目当中显示地图
配置AndroidManifest.xml
android 6.0以后需要申请动态权限,这里我就不写了,在设置中给予权限。
//地图SDK(包含其搜索功能)需要的基础权限
<!--允许程序打开网络套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序设置内置sd卡的写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允许程序访问WiFi网络信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允许程序读写手机状态和身份-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允许程序访问CellID或WiFi热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
设置高德Key
在application标签中加入如下内容:
<meta-data android:name="com.amap.api.v2.apikey" android:value="key">
//开发者申请的key
</meta-data>
value输入的是高德应用中的key,例如我的是b94d25......
初始化地图容器
MapView 是 AndroidView 类的一个子类, 用于在 Android View 中放置地图。 MapView 是地图容器。用 MapView 加载地图的方法与 Android 提供的其他 View 一样,具体的使用步骤如下:首先在布局xml文件中添加地图控件:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
在项目中使用地图的时候需要注意,需要合理的管理地图生命周期,这非常的重要。
以下示例简述地图生命周期的管理
public class MainActivity extends Activity {
MapView mMapView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取地图控件引用
mMapView = (MapView) findViewById(R.id.map);
//在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
mMapView.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
mMapView.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
mMapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
mMapView.onSaveInstanceState(outState);
}
}
显示地图
AMap 类是地图的控制器类,用来操作地图。它所承载的工作包括:地图图层切换(如卫星图、黑夜地图)、改变地图状态(地图旋转角度、俯仰角、中心点坐标和缩放级别)、添加点标记(Marker)、绘制几何图形(Polyline、Polygon、Circle)、各类事件监听(点击、手势等)等,AMap 是地图 SDK 最重要的核心类,诸多操作都依赖它完成。
在 MapView 对象初始化完毕之后,构造 AMap 对象。示例代码如下:
//定义了一个地图view
mapView = (MapView) findViewById(R.id.map);
mapView.onCreate(savedInstanceState);// 此方法须覆写,虚拟机需要在很多情况下保存地图绘制的当前状态。
//初始化地图控制器对象
AMap aMap;
if (aMap == null) {
aMap = mapView.getMap();
}
这时,地图已经可以在项目显示了,也算是初步完成
显示蓝点功能!
定位蓝点指的是进入地图后显示当前位置点的功能
实现定位蓝点:
MyLocationStyle myLocationStyle;
myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。
myLocationStyle.interval(2000); //设置连续定位模式下的定位间隔,只在连续定位模式下生效,单次定位模式下不会生效。单位为毫秒。
aMap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
//aMap.getUiSettings().setMyLocationButtonEnabled(true);设置默认定位按钮是否显示,非必需设置。
aMap.setMyLocationEnabled(true);// 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false
定位蓝点提供8种模式:
可根据需求更换
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_SHOW);//只定位一次。
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE) ;//定位一次,且将视角移动到地图中心点。
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW) ;//连续定位、且将视角移动到地图中心点,定位蓝点跟随设备移动。(1秒1次定位)
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_MAP_ROTATE);//连续定位、且将视角移动到地图中心点,地图依照设备方向旋转,定位点会跟随设备移动。(1秒1次定位)
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)默认执行此种模式。
//以下三种模式从5.1.0版本开始提供
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER);//连续定位、蓝点不会移动到地图中心点,定位点依照设备方向旋转,并且蓝点会跟随设备移动。
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER);//连续定位、蓝点不会移动到地图中心点,并且蓝点会跟随设备移动。
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_MAP_ROTATE_NO_CENTER);//连续定位、蓝点不会移动到地图中心点,地图依照设备方向旋转,并且蓝点会跟随设备移动。
完整的Mainactivity.java
package com.cdw.study8;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdate;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.MapView;
import com.amap.api.maps.model.MyLocationStyle;
import java.util.AbstractMap;
public class MainActivity extends AppCompatActivity {
private MapView mapView;
private AMap aMap=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.map);
mapView.onCreate(savedInstanceState);// 此方法须覆写,虚拟机需要在很多情况下保存地图绘制的当前状态。
//初始化地图控制器对象
if (aMap == null) {
aMap = mapView.getMap();
}
MyLocationStyle myLocationStyle;
myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)默认执行此种模式。
myLocationStyle.interval(2000); //设置连续定位模式下的定位间隔,只在连续定位模式下生效,单次定位模式下不会生效。单位为毫秒。
aMap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
aMap.getUiSettings().setMyLocationButtonEnabled(true);//设置默认定位按钮是否显示,非必需设置。
aMap.setMyLocationEnabled(true);// 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false。
aMap.moveCamera(CameraUpdateFactory.zoomTo(15));//地图显示级别
}
@Override
protected void onDestroy() {
super.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);
}
public void gotoNavi(View view) {
startActivity(new Intent(this,SearchActivity.class));
}
}
目前效果应是:
第三步:地图的搜索导航功能、
新建一个activity,命名,SearchActivity,用于搜索
activity_search.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:orientation="vertical"
tools:context=".SearchActivity">
<EditText
android:id="@+id/edit_quer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</EditText>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit_query"
app:layout_constraintVertical_bias="1.0"
tools:layout_editor_absoluteX="-51dp" />
</LinearLayout>
新建serch_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="18dp">
</TextView>
创建一个适配器 RvAdapter.java,用于给RecyclerView提供数据
RecyclerView.java
package com.cdw.study8;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.amap.api.services.help.Tip;
import java.util.List;
import java.util.Map;
public class RvAdapter extends RecyclerView.Adapter<RvAdapter.MyViewHolder> implements View.OnClickListener {
private final List<Tip> list;
private final Context context;
private final RecyclerView rv;
private OnItemClickListener mOnItemClickListener;
@Override
public void onClick(View v) {
int position = rv.getChildAdapterPosition(v);
//程序执行到此,会去执行具体实现的onItemClick()方法
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(rv, v, position, list.get(position));
}
}
public interface OnItemClickListener {
void onItemClick(RecyclerView parent, View view, int position, Tip data);
}
public RvAdapter(Context context, RecyclerView rv, List<Tip> list) {
this.context = context;
this.rv = rv;
this.list = list;
}
public void setOnItemClickListener(OnItemClickListener clickListener) {
this.mOnItemClickListener = clickListener;
}
public void setData(List<Tip> list) {
if (list == null) return;
this.list.clear();
this.list.addAll(list);
notifyDataSetChanged();
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.serach_item, parent, false);
view.setOnClickListener(this);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Tip tip = list.get(position);
((TextView) holder.itemView).setText(tip.getName());
}
@Override
public int getItemCount() {
return list.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
SearchActivity.Java
注释很详细,不多说了
package com.cdw.study8;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Poi;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AmapNaviPage;
import com.amap.api.navi.AmapNaviParams;
import com.amap.api.navi.AmapNaviType;
import com.amap.api.navi.AmapPageType;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.help.Inputtips;
import com.amap.api.services.help.InputtipsQuery;
import com.amap.api.services.help.Tip;
import java.util.ArrayList;
import java.util.List;
public class SearchActivity extends AppCompatActivity implements Inputtips.InputtipsListener, TextWatcher, RvAdapter.OnItemClickListener{
private RvAdapter rvAdapter;
private Inputtips inputTips;
private AMapNavi mAMapNavi;
private EditText editText;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
editText=findViewById(R.id.edit_quer);
editText.addTextChangedListener(this);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
rvAdapter = new RvAdapter(this, recyclerView, new ArrayList<>());
rvAdapter.setOnItemClickListener(this);
recyclerView.setAdapter(rvAdapter);
//构造 Inputtips 对象,并设置监听
inputTips = new Inputtips(this, (InputtipsQuery) null);
//指定setInputtipsListener得到onGetInputtips结果的回调
inputTips.setInputtipsListener(this);
}
//通过适配器把数据展示到recyclerView中去
@Override
public void onGetInputtips(List<Tip> list, int i) {
rvAdapter.setData(list);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 构造InputtipsQuery 对象,通过 InputtipsQuery(第一个参数为输入的数据,第二个参数城市) 设置搜索条件,null为全国范围。
InputtipsQuery inputquery = new InputtipsQuery(String.valueOf(s), null);
inputquery.setCityLimit(true);//限制在当前城市
inputTips.setQuery(inputquery);
//异步提示请求
inputTips.requestInputtipsAsyn();
}
@Override
public void afterTextChanged(Editable s) {
}
}
在activity_main.xml新增一个button
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="前往导航"
android:onClick="gotoNavi"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints,OnClick" />
在MainActivity.java实现跳转事件
public void gotoNavi(View view) {
startActivity(new Intent(this,SearchActivity.class));
}
此时,已经完成了关键字的搜索功能:
继续完成导航功能!
通过AmapNaviPage来启动导航组件,它是一个单例:
//构建导航组件配置类,没有传入起点,所以起点默认为 “我的位置”
AmapNaviParams params = new AmapNaviParams(null, null, null, AmapNaviType.DRIVER, AmapPageType.ROUTE);
//启动导航组件
AmapNaviPage.getInstance().showRouteActivity(getApplicationContext(), params, null);
完善后的SearchActivity.java
package com.cdw.study8;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Poi;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AmapNaviPage;
import com.amap.api.navi.AmapNaviParams;
import com.amap.api.navi.AmapNaviType;
import com.amap.api.navi.AmapPageType;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.help.Inputtips;
import com.amap.api.services.help.InputtipsQuery;
import com.amap.api.services.help.Tip;
import java.util.ArrayList;
import java.util.List;
public class SearchActivity extends AppCompatActivity implements Inputtips.InputtipsListener, TextWatcher, RvAdapter.OnItemClickListener{
private RvAdapter rvAdapter;
private Inputtips inputTips;
private AMapNavi mAMapNavi;
private EditText editText;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
editText=findViewById(R.id.edit_quer);
editText.addTextChangedListener(this);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
rvAdapter = new RvAdapter(this, recyclerView, new ArrayList<>());
rvAdapter.setOnItemClickListener(this);
recyclerView.setAdapter(rvAdapter);
//构造 Inputtips 对象,并设置监听
inputTips = new Inputtips(this, (InputtipsQuery) null);
//指定setInputtipsListener得到onGetInputtips结果的回调
inputTips.setInputtipsListener(this);
mAMapNavi = AMapNavi.getInstance(this);
//设置内置语音播报
mAMapNavi.setUseInnerVoice(true, false);
}
//通过适配器把数据展示到recyclerView中去
@Override
public void onGetInputtips(List<Tip> list, int i) {
rvAdapter.setData(list);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 构造InputtipsQuery 对象,通过 InputtipsQuery(第一个参数为输入的数据,第二个参数城市) 设置搜索条件,null为全国范围。
InputtipsQuery inputquery = new InputtipsQuery(String.valueOf(s), null);
inputquery.setCityLimit(true);//限制在当前城市
inputTips.setQuery(inputquery);
//异步提示请求
inputTips.requestInputtipsAsyn();
}
@Override
public void afterTextChanged(Editable s) {
}
//当点击关键字
@Override
public void onItemClick(RecyclerView parent, View view, int position, Tip data) {
//得到点击的坐标
LatLonPoint point = data.getPoint();
//得到经纬度
Poi poi = new Poi(data.getName(), new LatLng(point.getLatitude(), point.getLongitude()), data.getPoiID());
//导航参数对象(起点,途径,终点,导航方式)DRIVER是导航方式(驾驶,步行...当前为驾驶)ROUTE会计算路程选择
AmapNaviParams params = new AmapNaviParams(null, null, poi, AmapNaviType.DRIVER, AmapPageType.ROUTE);
//传递上下文和导航参数
AmapNaviPage.getInstance().showRouteActivity(getApplicationContext(), params, null);
}
}
注意:导航功能需要更多的权限
声明定位 service 组件
application标签中声明service 组件
<service android:name="com.amap.api.location.APSService"/>
在AndroidManifest.xml中配置更多权限
<!--用于访问网络,网络定位需要上网-->
<uses-permission android:name="android.permission.INTERNET" />
<!--写入扩展存储,向扩展卡写入数据,用于写入缓存定位数据-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--这个权限用于允许程序在手机屏幕关闭后后台进程仍然运行-->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!--用户组件导航页蓝牙设备连接提醒-->
<uses-permission android:name="android.permission.BLUETOOTH" />
最后别忘了在 AndroidManifest.xml 中配置声明导航组件 Activity!
<activity
android:name="com.amap.api.navi.AmapRouteActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation" />
完成后的效果
结束,感谢观看!