1. 简介
高德地图 Android SDK 是一套地图开发调用接口,开发者可以轻松地在自己的Android应用中加入地图相关的功能,包括:地图显示(含室内、室外地图)、与地图交互、在地图上绘制、兴趣点搜索、地理编码、离线地图等功能。
2. 获取高德 Key
要使用高德地图首先要去高德开放平台注册成为开发者(http://lbs.amap.com/), 注册成为高德开发者需要分三步:
- 第一步,注册高德开发者;
- 第二步,去控制台创建应用;
- 第三步,获取Key
3. 准备
1. 下载并解压
高德地图SDK - 开发包定制下载
2. 向工程中添加地图开发包
2.1 添加 so 文件,在 src/main/ 目录下新建 jniLibs 目录,并将文件放入其中。
2.2 将 jar 包放入 libs 目录下。然后 右键-选择 Add As Library,导入到工程中。
4. 地图
1. 配置 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gyq.mapdemo">
<!--允许程序打开网络套接字-->
<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" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="com.amap.api.v2.apikey"
android:value="高德key">
</meta-data>
</application>
</manifest>
3. 布局文件
<?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=".MainActivity">
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
4. 只要在 onCreate() 中添加如下几行代码,高德地图就显示出来了
public class MainActivity extends AppCompatActivity {
private MapView mMapView;
private AMap mAMap;
private MyLocationStyle mLocationStyle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapView = findViewById(R.id.map);
mMapView.onCreate(savedInstanceState);
if (mAMap == null) {
mAMap = mMapView.getMap();
}
mAMap.getUiSettings().setZoomControlsEnabled(false);
mAMap.moveCamera(CameraUpdateFactory.zoomTo(15));
mAMap.setMapTextZIndex(2);
mLocationStyle = new MyLocationStyle();
mLocationStyle.interval(2000);
mLocationStyle.showMyLocation(true);
mAMap.setMyLocationStyle(mLocationStyle);
mAMap.setMyLocationEnabled(true);
mAMap.setOnMyLocationChangeListener(new AMap.OnMyLocationChangeListener() {
@Override
public void onMyLocationChange(Location location) {
double lat = location.getLatitude();
double lng = location.getLongitude();
Log.e("duo", "onMyLocationChange: lat=" + lat + "|lng=" + lng);
}
});
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
}
5. 定位
//初始化定位
mLocationClient = new AMapLocationClient(getApplicationContext());
//初始化AMapLocationClientOption对象
mLocationOption = new AMapLocationClientOption();
//设置定位模式为高精度模式。
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//设置定位回调监听
mLocationClient.setLocationListener(this);
//获取一次定位结果
mLocationOption.setOnceLocation(true);
//设置是否返回地址信息(默认返回地址信息)
mLocationOption.setNeedAddress(true);
//给定位客户端对象设置定位参数
mLocationClient.setLocationOption(mLocationOption);
//启动定位
mLocationClient.startLocation();
之后在监听器的回调方法内解析 AMapLocation
对象,里面包含地址,省市县街道,地区编码,城市编码等等,可以在此添加标记,定位当前位置,你问我如何把标记固定在屏幕中央?也在这里面:
//----------------------这是位置改变监听------------------------------------
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
if (aMapLocation != null) {
if (aMapLocation.getErrorCode() == 0) {
//可在其中解析amapLocation获取相应内容。
LatLng latLng = new LatLng(aMapLocation.getLatitude(),
aMapLocation.getLongitude());//取出经纬度
//添加Marker显示定位位置
if (locationMarker == null) {
//如果是空的添加一个新的,icon方法就是设置定位图标,可以自定义
locationMarker = aMap.addMarker(new MarkerOptions()
.position(latLng).snippet("最快1分钟到达").draggable(true).setFlat(true));
locationMarker.showInfoWindow();//主动显示indowindow
aMap.addText(new TextOptions().position(latLng).text(aMapLocation.getAddress()));
//固定标签在屏幕中央
locationMarker.setPositionByPixels(mMapView.getWidth() / 2,mMapView.getHeight() / 2);
} else {
//已经添加过了,修改位置即可
locationMarker.setPosition(latLng);
}
//然后可以移动到定位点,使用animateCamera就有动画效果
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));//参数提示:1.经纬度 2.缩放级别
}else {
//定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
Log.e("AmapError","location Error, ErrCode:" +aMapLocation.getErrorCode() + ", errInfo:"+ aMapLocation.getErrorInfo());
}
}
}
- 第二种方式
实现OnMapLoadedListener
,在回调方法中首先添加一个标记在地图中央,之后封装一个方法,开启单次定位即可:
aMap.setOnMapLoadedListener(this);
//----------------------OnMapLoaded 当地图加载完成时回调此方法------------------------------------------
@Override
public void onMapLoaded() {
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.setFlat(true);
markerOptions.anchor(0.5f, 0.5f);
markerOptions.position(new LatLng(0, 0));
markerOptions.snippet("最快1分钟到达").draggable(true).setFlat(true);
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_loaction_start)));
mPositionMark = aMap.addMarker(markerOptions);
mPositionMark.showInfoWindow();//主动显示indowindow
mPositionMark.setPositionByPixels(mMapView.getWidth() / 2,mMapView.getHeight() / 2);
mLocationTask.startSingleLocate(); }
封装的单次定位方法在此,很简单:
//开启单次定位
public void startSingleLocate() {
AMapLocationClientOption option=new AMapLocationClientOption();
option.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
option.setOnceLocation(true);
mLocationClient.setLocationOption(option);
mLocationClient.startLocation();
}
- 效果图
布局文件,让按钮悬浮在地图上
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.amap.api.maps.MapView>
<!--导航栏 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/menu"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:clickable="true"
android:src="@mipmap/hanbao"/>
<ImageView
android:visibility="gone"
android:id="@+id/btn_back"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:src="@drawable/btn_back"/>
<Button
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="打车/顺风车"/>
</RelativeLayout>
<ImageView
android:id="@+id/iv_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:layout_marginTop="400dp"
android:layout_above="@+id/fromto_container"
android:src="@mipmap/btn_location"/>
</RelativeLayout>
</FrameLayout>
6. Poi搜索
先上效果图:
- 实现
这边已经提供了两个封装好的类,一个是输入提示 一个是Poi搜索
public class InputTipTask implements InputtipsListener {
private static InputTipTask mInputTipTask;
private Inputtips mInputTips;
private RecomandAdapter mAdapter;
Context mContext;
public static InputTipTask getInstance(Context context, RecomandAdapter adapter){
if(mInputTipTask==null){
mInputTipTask=new InputTipTask(context);
} //单例情况,多次进入DestinationActivity传进来的RecomandAdapter对象会不是同一个
mInputTipTask.setRecommandAdapter(adapter);
return mInputTipTask;
}
public void setRecommandAdapter(RecomandAdapter adapter){
mAdapter=adapter;
}
private InputTipTask(Context context ){
mInputTips=new Inputtips(context, this);
}
public void searchTips(String keyWord, String city){
try {
mInputTips.requestInputtips(keyWord, city);
} catch (AMapException e) {
e.printStackTrace();
}
}
@Override
public void onGetInputtips(List<Tip> tips, int resultCode) {
//v3.2.1及以上版本SDK 返回码1000是正常 千万注意
if(resultCode==1000&&tips!=null){
ArrayList<PositionEntity> positions=new ArrayList<PositionEntity>();
for(Tip tip:tips){
//经纬度 address city(adcode)
positions.add(new PositionEntity(0, 0, tip.getName(),tip.getAdcode()));
}
mAdapter.setPositionEntities(positions);
mAdapter.notifyDataSetChanged();
PoiSearchTask poiSearchTask=new PoiSearchTask(mContext.getApplicationContext(), mAdapter);
for(int i = 0;i<positions.size();i++){
PositionEntity entity = (PositionEntity)
mAdapter.getItem(i);
poiSearchTask.search(entity.address,RouteTask.getInstance(mContext.getApplicationContext()).getStartPoint().city);
}
}else {
//可以根据app自身需求对查询错误情况进行相应的提示或者逻辑处理
}
}
}
public class PoiSearchTask implements OnPoiSearchListener {
private Context mContext;
private RecomandAdapter mRecommandAdapter;
public PoiSearchTask(Context context, RecomandAdapter recomandAdapter) {
mContext = context;
mRecommandAdapter = recomandAdapter;
}
public void search(String keyWord, String city) {
Query query = new PoiSearch.Query(keyWord, "", city);
query.setPageSize(10);
query.setPageNum(0);
PoiSearch poiSearch = new PoiSearch(mContext, query);
poiSearch.setOnPoiSearchListener(this);
poiSearch.searchPOIAsyn();
}
@Override
public void onPoiSearched(PoiResult poiResult, int resultCode) {
if (resultCode == 1000 && poiResult != null) {
ArrayList<PoiItem> pois=poiResult.getPois();
if(pois==null){
return;
}
List<PositionEntity> entities=new ArrayList<PositionEntity>();
for(PoiItem poiItem:pois){
PositionEntity entity=new PositionEntity(poiItem.getLatLonPoint().getLatitude(),
poiItem.getLatLonPoint().getLongitude(),
poiItem.getTitle(),poiItem.getCityName());
entities.add(entity);
}
mRecommandAdapter.setPositionEntities(entities);
mRecommandAdapter.notifyDataSetChanged();
}
//TODO 可以根据app自身需求对查询错误情况进行相应的提示或者逻辑处理
}
@Override
public void onPoiItemSearched(PoiItem poiItem, int i) {}}
你要做的 只是拷贝这连个类到工程中,然后在搜索的Activity中实现TextWatcher接口后,进行如下调用:
//在onTextChanged方法中调用InputTipTask 的getInstance方法
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (RouteTask.getInstance(getApplicationContext()).getStartPoint() == null) {
Toast.makeText(getApplicationContext(), "检查网络,Key等问题", Toast.LENGTH_SHORT).show();
return;
}
InputTipTask.getInstance(getApplicationContext(), mRecomandAdapter).searchTips(s.toString(),
RouteTask.getInstance(getApplicationContext()).getStartPoint().city);
}
输入提示完成.如果你希望点选后进行更详细的Poi搜索,也只需在相应位置调用:
//生成poiSearchTask对象
PoiSearchTask poiSearchTask=new PoiSearchTask(getApplicationContext(),mRecomandAdapter);
//开始进行POI搜索
poiSearchTask.search(mDestinaionText.getText().toString(),RouteTask.getInstance(getApplicationContext()).getStartPoint().city);
以上需要一个位置的实体Bean:
public class PositionEntity {
@Override
public String toString() {
return "PositionEntity{" +
"latitue=" + latitue +
", longitude=" + longitude +
", address='" + address + '\'' +
", city='" + city + '\'' +
'}';
}
public double latitue;
public double longitude;
public String address;
public String city;
public PositionEntity() {}
public PositionEntity(double latitude, double longtitude, String address, String city) {
this.latitue = latitude;
this.longitude = longtitude;
this.address = address;
this.city = city;
}
}
RouteTask 代码如下:
public class RouteTask implements OnRouteSearchListener {
private static RouteTask mRouteTask;
private RouteSearch mRouteSearch;
private PositionEntity mFromPoint;
private PositionEntity mToPoint;
private List<OnRouteCalculateListener> mListeners = new ArrayList<OnRouteCalculateListener>();
private DrivePath drivepath;
public interface OnRouteCalculateListener {
public void onRouteCalculate(float cost, float distance, int duration,DrivePath drivepath);
}
public static RouteTask getInstance(Context context) {
if (mRouteTask == null) {
mRouteTask = new RouteTask(context);
}
return mRouteTask;
}
public PositionEntity getStartPoint() {
return mFromPoint;
}
public void setStartPoint(PositionEntity fromPoint) {
mFromPoint = fromPoint;
}
public PositionEntity getEndPoint() {
return mToPoint;
}
public void setEndPoint(PositionEntity toPoint) {
mToPoint = toPoint;
}
private RouteTask(Context context) {
mRouteSearch = new RouteSearch(context);
mRouteSearch.setRouteSearchListener(this);
}
public void search() {
if (mFromPoint == null || mToPoint == null) {
return;
}
Log.e(":", "search: "+mFromPoint+","+mToPoint);
FromAndTo fromAndTo = new FromAndTo(new LatLonPoint(mFromPoint.latitue,
mFromPoint.longitude), new LatLonPoint(mToPoint.latitue,
mToPoint.longitude));
DriveRouteQuery driveRouteQuery = new DriveRouteQuery(fromAndTo,
RouteSearch.DrivingDefault, null, null, "");
mRouteSearch.calculateDriveRouteAsyn(driveRouteQuery);
}
public void search(PositionEntity fromPoint, PositionEntity toPoint) {
mFromPoint = fromPoint;
mToPoint = toPoint;
search();
}
public void addRouteCalculateListener(OnRouteCalculateListener listener) {
synchronized (this) {
if (mListeners.contains(listener))
return;
mListeners.add(listener);
}
}
public void removeRouteCalculateListener(OnRouteCalculateListener listener) {
synchronized (this) {
mListeners.add(listener);
}
}
//驾车路线规划回调
@Override
public void onDriveRouteSearched(DriveRouteResult driveRouteResult,
int resultCode) {
if (resultCode == 1000 && driveRouteResult != null) {
synchronized (this) {
for (OnRouteCalculateListener listener : mListeners) {
List<DrivePath> drivepaths = driveRouteResult.getPaths();
float distance = 0;
int duration = 0;
if (drivepaths.size() > 0) {
drivepath = drivepaths.get(0);
distance = drivepath.getDistance() / 1000;
duration = (int) (drivepath.getDuration() / 60);
}
float cost = driveRouteResult.getTaxiCost();
listener.onRouteCalculate(cost, distance, duration,drivepath);
}
List<DrivePath> paths = driveRouteResult.getPaths();
}
}
//这里可以根据需求对查询错误情况进行相应的提示或者逻辑处理
}
@Override
public void onWalkRouteSearched(WalkRouteResult arg0, int arg1) {}
@Override
public void onRideRouteSearched(RideRouteResult rideRouteResult, int i) {}
@Override
public void onBusRouteSearched(BusRouteResult arg0, int arg1) {}
}