高德地图官方有API,有文档,但是大家懂的,有些东西并没有说明。所以,我打算将自己实现对高德地图展示、定位以及导航的功能步骤做一个总结,分享给大家。本文作为总结高德地图使用,首先总结自己对于拾取地点这样的功能的实现。
第一步,准备工作:
首先,我们需要去高德地图获取key,然后下载sdk(包括jar包和so文件),本项目需要的sdk,如图:
当我们将key下载好以后,并且将jar包和so文件放好——建议直接将jar包放在libs文件夹,然后将so文件放在jinLibs文件夹下面,当然,我们也可以将so文件也放到libs,然后将Module的build.gradle文件修改如图:
接下来就是修改文件清单,这些我们可以直接将API的代码复制过来了:
//地图包、搜索包、定位包、导航包需要的基础权限 <!--允许程序打开网络套接字--> <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" /> <!--用于访问GPS定位--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!--这个权限用于允许程序在手机屏幕关闭后后台进程仍然运行--> <user-permission android:name="android.permission.WAKE_LOCK"/> <application android:icon="@drawable/icon" android:label="@string/app_name" > <meta-data android:name="com.amap.api.v2.apikey" android:value="e969e70f8daf003d370ab982e6ba656d"/> …… <service android:name="com.amap.api.location.APSService"></service> </application>
讯享网
准备工作的最后一步只需要执行sync project with gradle files就完成整个项目的准备工作了!
准备工作完成了,我们接下来就执行第二步,展示地图:
首先,我们需要布局中有一个地图的布局,代码如下:
讯享网<?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:orientation="vertical" 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"/> </LinearLayout>
接下来就是对于地图的调用,这个非常简单,API里面都是现成的,代码如下:
public class PiclocationActivity extends Activity {
private MapView mMapView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_piclocation); ActionBar actionBar = getActionBar(); if(actionBar!=null)actionBar.hide(); mMapView = (MapView) findViewById(R.id.map); 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); } }
于是地图就展示出来了。

当然,如果你是你现在看到的地图是没有中间的图标,位置也应该是北京为中心点的地图。而我们今天需要实现的是拾取坐标点,所以必须要显示当前定位的。而关于地图展示的相关方法,接下来还会用的到。所以,我们接下来就实现上面的效果吧!
第二步,开始定位:
其实,这个也代码量也非常简单,如下:
讯享网 private AMap amap; private OnLocationChangedListener mListener; private AMapLocationClient mlocationClient; private AMapLocationClientOption mLocationOption; @Override protected void onCreate(Bundle savedInstanceState) { ... initMap(); } / * 地图实例化 */ private void initMap() { if (amap == null) { amap = mMapView.getMap(); amap.setLocationSource(this);//设置了定位的监听,这里要实现LocationSource接口 amap.getUiSettings().setMyLocationButtonEnabled(true); // 是否显示定位按钮 amap.setMyLocationEnabled(true);//显示定位层并且可以触发定位,默认是flase } / * 激活定位 */ @Override public void activate(OnLocationChangedListener listener) { mListener = listener; if (mlocationClient == null) { //初始化定位 mlocationClient = new AMapLocationClient(this); //初始化定位参数 mLocationOption = new AMapLocationClientOption(); //设置定位回调监听 mlocationClient.setLocationListener(this); //设置为高精度定位模式 mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); //设置为定位一次 // mLocationOption.setOnceLocation(true); //设置定位参数 mlocationClient.setLocationOption(mLocationOption); // 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗, // 注意设置合适的定位时间的间隔(最小间隔支持为2000ms),并且在合适时间调用stopLocation()方法来取消定位请求 // 在定位结束后,在合适的生命周期调用onDestroy()方法 // 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除 mlocationClient.startLocation();//启动定位 } } / * 注销定位 */ @Override public void deactivate() { mListener = null; if (mlocationClient != null) { mlocationClient.stopLocation(); mlocationClient.onDestroy(); } mlocationClient = null; } / * 实现定位 * @param amapLocation */ @Override public void onLocationChanged(AMapLocation amapLocation) { if (mListener != null && amapLocation != null) { if (amapLocation != null &&amapLocation.getErrorCode() == 0) { String location = "当前地点:"+amapLocation.getProvider()+amapLocation.getCity()+amapLocation.getStreet()+amapLocation.getStreetNum(); Log.d(TAG, "onLocationChanged: "); } else { String errText = "定位失败," + amapLocation.getErrorCode()+ ": " + amapLocation.getErrorInfo(); Log.e("AmapErr",errText); } } }
接下来我们看看这个效果,首先看看日志:
04-29 23:53:04.267 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:06.268 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:08.270 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:10.277 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:12.327 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:14.281 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:16.282 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:18.288 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:20.297 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:22.291 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:24.640 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:26.295 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:28.301 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:30.298 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:32.303 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:34.304 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:36.352 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:38.339 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:40.340 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号 04-29 23:53:42.341 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
定位这么多次没有问题,但是地图并没有显示,why?

那我们现在将地图定位成功这个方法增加一句代码,实现地图显示当前位置:
讯享网mListener.onLocationChanged(amapLocation);// 显示系统小蓝点
其实上面还有一个问题,就是地图显示的缩放级别不一样,对吧?所以我们还需要在地图显示的时候预先设置缩放级别(程度).代码如下:
/ * 地图实例化 */ private void initMap() { if (amap == null) { ... amap.moveCamera(CameraUpdateFactory.zoomTo(15));//设置地图缩放级别 } }
接下来我们看看效果:

这个效果已经基本成型了,但是我们还需要修改一下地图中心点的图标,我们不要圆形及边框,接下来看看代码:
讯享网/ * 地图实例化 */ private void initMap() { if (amap == null) { ... MyLocationStyle myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类 myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。 myLocationStyle.strokeColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的边框颜色 myLocationStyle.radiusFillColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的填充颜色 amap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style } }
OK,搞定!接下来,就要开始放大招了!我们需要点击屏幕的时候,显示当前位置以及周边的位置,实现高德地图这样的效果:


需要实现上述效果,这里需要实现几点功能:
- 定位(已经实现)
- 周边搜索(下一步需要实现)
- 手指滑动地图停止的时候获取位置——逆地理编码(下下一步需要实现)
- 将结果以列表的形式展现出来
于是我们接着上面的内容,开始进行第三步,周边搜索。
第三步,周边搜索:
API里面介绍周边搜索叫做“获取POI数据”,并且有六种方式,如下:
- 根据关键字检索POI
- 检索周边POI
- 检索多边形内的POI
- 根据ID检索POI
- 根据输入自动提示
- 获取道路沿途的POI数据
因为我们需要的是周围的数据,所以这里我们选择第二种,这种情况比较合适。代码如下:
/ * 周边数据实体封装 */ private class ListViewHoldier{
String title; String address; LatLonPoint lp; } private List<ListViewHoldier> data = new ArrayList<>(); //第一个位置数据,设为成员变量是因为有多个地方需要使用 private ListViewHoldier lvHolder; / * 地图实例化 */ private void initMap() { ... lvHolder = new ListViewHoldier(); ... } / * 实现定位 * @param amapLocation */ @Override public void onLocationChanged(AMapLocation amapLocation) { if (mListener != null && amapLocation != null) { if (amapLocation != null &&amapLocation.getErrorCode() == 0) { mListener.onLocationChanged(amapLocation);// 显示系统小蓝点 if(lvHolder.title==null){
//只要第一次的数据,否则第一项的位置始终不会变。当然,也可以在这里关闭定位 // mlocationClient.stopLocation();//停止定位 lvHolder.title = "[位置]"; //关于定位数据,可以查看API http://lbs.amap.com/api/android-location-sdk/guide/android-location/getlocation lvHolder.address = amapLocation.getProvider()+amapLocation.getCity()+amapLocation.getStreet()+amapLocation.getStreetNum(); lvHolder.lp = new LatLonPoint(amapLocation.getLatitude(),amapLocation.getLongitude()); data.add(0,lvHolder); doSearchQuery(); } } else { String errText = "定位失败," + amapLocation.getErrorCode()+ ": " + amapLocation.getErrorInfo(); Log.e("AmapErr",errText); } } } / * 搜查周边数据 */ private void doSearchQuery() { //搜索类型 String type = "汽车服务|汽车销售|" + "汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|" + "住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|" + "金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施"; query = new PoiSearch.Query("", type, "");// 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国) query.setPageSize(20);// 设置每页最多返回多少条poiitem query.setPageNum(0);// 设置查第一页 poiSearch = new PoiSearch(this, query); //搜索回调 poiSearch.setOnPoiSearchListener(this); //搜索位置及范围 poiSearch.setBound(new PoiSearch.SearchBound(lvHolder.lp, 1000)); //同步搜索 // poiSearch.searchPOI();//不能在主线程实现耗时操作 //异步搜索 poiSearch.searchPOIAsyn(); } / * 返回POI搜索异步处理的结果。 * @param result * @param rcode */ @Override public void onPoiSearched(PoiResult result, int rcode) { if (rcode == AMapException.CODE_AMAP_SUCCESS) { if (result != null && result.getQuery() != null) {
// 搜索poi的结果 if (result.getQuery().equals(query)) {
// 是否是同一条 // 取得搜索到的poiitems有多少页 List<PoiItem> poiItems = result.getPois();// 取得第一页的poiitem数据,页数从数字0开始 if (poiItems != null && poiItems.size() > 0) { for (int i = 0;i<poiItems.size();i++){ PoiItem poiitem = poiItems.get(i); ListViewHoldier holder = new ListViewHoldier(); holder.address = poiitem.getSnippet(); holder.title = poiitem.getTitle(); holder.lp = poiitem.getLatLonPoint(); if(data.size()>i+1){ data.remove(i+1); } data.add(i+1,holder); Log.d(TAG, "onPoiSearched: \n"+holder.address+"\n"+holder.title+"\n"+holder.lp.toString()); } } else { } } } else { } } else { } } / * poi id搜索的结果回调 * @param poiItem * @param i */ @Override public void onPoiItemSearched(PoiItem poiItem, int i) { }
OK,代码比较长,我们接下来看看日志输出结果,检验一下我们的成果,日志如下:
讯享网04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段 小勇硅胶 30.,104.044311 04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段698号附11号 老草屋烧烤(华阳店) 30.,104.044336 04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 协和街道剑南大道南段712号 华宜·上锦 30.,104.044425 04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段 依爱思洗衣 30.,104.044673 04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 协和街道剑南大道南段698号附8号 子墨西点 30.52902,104.04488 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段708-710号附近 乐诚家乐(剑南大道南段) 30.,104.044926 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 协和街道剑南大道南段706号 大邑血旺家常菜 30.,104.045092 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段708-710号附近 欣家超市 30.,104.04507 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段708-710 美家装饰建材(华阳店) 30.,104.045118 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段706号附近 贝尔康药业(同福堂大药房) 30.,104.045141 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段美家建材旁 牛肉面&馄饨王&黄焖鸡米饭(剑南大道店) 30.,104.045165 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 双流区 元景路与剑南大道南段交叉口 30.,104.045866 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 515路;521路;814路 剑南大道元景路口站(公交站) 30.,104.045593 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 天府新区华府大道一段996号(金茂)光明城市5栋4单元 菜鸟驿站(光明城市优超市周6寄件免费) 30.5273,104.04558 04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 元景路与剑南大道南段交叉口东100米 停车场(元景路) 30.,104.046573 04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: G123路;T207路 元景路剑南大道口站(公交站) 30.,104.046562 04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 元景路金茂光明城市3号门12栋4单元2层 海鸣画室二号店 30.,104.046657 04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 剑南大道南段 顺发超市 30.,104.044417 04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 华阳镇剑南大道南段688号 骑龙小区 30.,104.043652 04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 华阳华府大道一段996号 11摄影工作室 30.,104.046519
OK,周边搜索已经搞定,接下来执行下一步。
第四步,随手指的滑动更新数据:
其实上面有一个bug,就是我们手指滑动是时候,然后定位成功就会将地图的中心点重新定位会我们当前的位置,如图:

原因是高德地图默认每秒更新一次当前位置,所以每秒地图就会回到中心点一次。解决方法很多,可以关闭定位,或者将定位成功后的显示系统小蓝点给关掉,代码如下:
mListener.onLocationChanged(amapLocation);// 显示系统小蓝点
现在我们的地图还只是可以随意滑动,但是我们需要在手指滑动结束的时候获取到经纬度,然后更新我们的周边数据。更新周边的数据已经封装好一个方法了,问题是在手指滑动的时候获取到经纬度。并且,在打开地图的时候中心点覆盖系统图标上面还有一个图标,并且该图标随手指移动,在手指结束滑动的时候还有一个动画。所以整理一下思路,接下来需要实现的有如下几点:
- 打开地图的时候显示自定义图标
- 图标随手指移动
- 手指移动结束的时候图标有一个动画
- 手指移动结束的时候获取到当前的经纬度并且更新数据
首先我们来实现对地图添加自定义图标,代码如下:
讯享网private Marker mEndMarker; / * 实现定位 * @param amapLocation */ @Override public void onLocationChanged(AMapLocation amapLocation) { ... // 初始化Marker添加到地图 mEndMarker = amap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.end)))); mEndMarker.setPosition(new LatLng(amapLocation.getLatitude(),amapLocation.getLongitude())); ... }
完成了第1步,接下来实现第2步,就是手指滑动的时候图标随手指滑动。这里我们需要实现一个地图状态发生变化的监听接口——AMap.OnCameraChangeListener,它有两个抽象方法,如下:
/ * 地图实例化 */ private void initMap() { if (amap == null) { ... amap.setOnCameraChangeListener(this); } } / * 在地图状态改变过程中回调此方法。 * @param cameraPosition */ @Override public void onCameraChange(CameraPosition cameraPosition) { mEndMarker.setPosition(cameraPosition.target); } / * 在地图状态改变完成时回调此方法。 * @param cameraPosition */ @Override public void onCameraChangeFinish(CameraPosition cameraPosition) { }
接下来实现第3步,在滑动结束时启动一个动画。所以我们直接在onCameraChangeFinish(CameraPosition cameraPosition)方法里面执行动画的方法即可,代码如下:
讯享网 / * 在地图状态改变完成时回调此方法。 * @param cameraPosition */ @Override public void onCameraChangeFinish(CameraPosition cameraPosition) { jumpPoint(mEndMarker); } / * marker点击时跳动一下 */ public void jumpPoint(final Marker marker) { final Handler handler = new Handler(); final long start = SystemClock.uptimeMillis(); //获取地图投影坐标转换器 Projection proj = amap.getProjection(); final LatLng markerLatlng = marker.getPosition(); Point markerPoint = proj.toScreenLocation(markerLatlng); markerPoint.offset(0, -50); final LatLng startLatLng = proj.fromScreenLocation(markerPoint); final long duration = 500; final Interpolator interpolator = new BounceInterpolator(); handler.post(new Runnable() { @Override public void run() { long elapsed = SystemClock.uptimeMillis() - start; float t = interpolator.getInterpolation((float) elapsed / duration); double lng = t * markerLatlng.longitude + (1 - t) * startLatLng.longitude; double lat = t * markerLatlng.latitude + (1 - t) * startLatLng.latitude; marker.setPosition(new LatLng(lat, lng)); if (t < 1.0) { handler.postDelayed(this, 16); } } }); }
上面这些比较花哨的动作完成以后,我们得干一点实事,就是获取数据,并且及时更新。首先,我们需要在手指滑动结束的时候获取到经纬度,然后通过逆地理编码方法拿到地址的详细数据,再接着通过经纬度查询周边数据。所以这1步其实有三个动作。我们就不一步步的列了,直接上代码,看注释:
/ * 逆地理编码业务类 */ private GeocodeSearch geocoderSearch; / * 地图实例化 */ private void initMap() { if (amap == null) { ... //初始化 geocoderSearch geocoderSearch = new GeocodeSearch(this); //注册 逆地理编码异步处理回调接口 geocoderSearch.setOnGeocodeSearchListener(this); } } / * 在地图状态改变完成时回调此方法。 * @param cameraPosition */ @Override public void onCameraChangeFinish(CameraPosition cameraPosition) { //当地图定位成功的时候该方法也会回调,为了避免不必要的搜索,因此此处增加一个判断 if(isFirstTime){ isFirstTime = false; return; } //marker 动画 jumpPoint(mEndMarker); lvHolder.lp = new LatLonPoint(cameraPosition.target.latitude,cameraPosition.target.longitude); RegeocodeQuery query = new RegeocodeQuery(lvHolder.lp, 200, GeocodeSearch.AMAP);// 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系 geocoderSearch.getFromLocationAsyn(query);// 设置异步逆地理编码请求 doSearchQuery(); } / * 逆地理编码回调方法 * 经纬度转位置 * @param result * @param rCode */ @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (rCode == AMapException.CODE_AMAP_SUCCESS) { if (result != null && result.getRegeocodeAddress() != null && result.getRegeocodeAddress().getFormatAddress() != null) { lvHolder.address = result.getRegeocodeAddress().getFormatAddress(); data.remove(0); data.add(0,lvHolder); } else { // ToastUtil.show(ReGeocoderActivity.this, R.string.no_result); } } else { // ToastUtil.showerror(this, rCode); } } / * 地理编码回调方法 * 位置转经纬度 * @param geocodeResult * @param i */ @Override public void onGeocodeSearched(GeocodeResult geocodeResult, int i) { }
第五步、将结果以列表的形式展现出来
讯享网<?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:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp" android:background="@color/gray"> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@mipmap/back" android:layout_alignParentLeft="true" android:layout_centerHorizontal="true"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:layout_centerInParent="true" android:text="地图选点" android:textSize="16sp"/> </RelativeLayout> <com.amap.api.maps.MapView android:id="@+id/map" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="5"/> <android.support.v7.widget.RecyclerView android:id="@+id/ll_rl_locations" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:visibility="gone"> </android.support.v7.widget.RecyclerView> <LinearLayout android:id="@+id/ll_ll_holderview" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:orientation="horizontal" android:gravity="center"> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="@string/loading"/> </LinearLayout> </LinearLayout>
接下来是所有代码,直接看注释吧:
package cn.com.vicent.mymap; import android.app.ActionBar; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Point; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.animation.BounceInterpolator; import android.view.animation.Interpolator; import android.widget.TextView; import com.amap.api.location.AMapLocation; import com.amap.api.location.AMapLocationClient; import com.amap.api.location.AMapLocationClientOption; import com.amap.api.location.AMapLocationListener; import com.amap.api.maps.AMap; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.LocationSource; import com.amap.api.maps.MapView; import com.amap.api.maps.Projection; import com.amap.api.maps.model.BitmapDescriptorFactory; import com.amap.api.maps.model.CameraPosition; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.Marker; import com.amap.api.maps.model.MarkerOptions; import com.amap.api.maps.model.MyLocationStyle; import com.amap.api.services.core.AMapException; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.geocoder.GeocodeResult; import com.amap.api.services.geocoder.GeocodeSearch; import com.amap.api.services.geocoder.RegeocodeQuery; import com.amap.api.services.geocoder.RegeocodeResult; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import com.zhy.adapter.recyclerview.CommonAdapter; import com.zhy.adapter.recyclerview.base.ViewHolder; import java.util.ArrayList; import java.util.List; public class PiclocationActivity extends Activity implements LocationSource, AMapLocationListener, PoiSearch.OnPoiSearchListener, AMap.OnCameraChangeListener, GeocodeSearch.OnGeocodeSearchListener {
private static final String TAG = "PiclocationActivity"; private MapView mMapView = null; private AMap amap; private Marker mEndMarker; private OnLocationChangedListener mListener; private AMapLocationClient mlocationClient; private AMapLocationClientOption mLocationOption; / * 周边搜索条件 */ private PoiSearch.Query query; / * 周边搜索的业务执行 */ private PoiSearch poiSearch; / * 逆地理编码业务类 */ private GeocodeSearch geocoderSearch; / * 第一次定位的标志位 */ private boolean isFirstTime = true; private Context mContext; private List<ListViewHoldier> data = new ArrayList<>(); //第一个位置数据,设为成员变量是因为有多个地方需要使用 private ListViewHoldier lvHolder; private RecyclerView mRecyclerView; private CommonAdapter mAdapter; private View progressDialogView; private TextView tvHint; private View progressbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_piclocation); ActionBar actionBar = getActionBar(); if(actionBar!=null)actionBar.hide(); initView(); mMapView.onCreate(savedInstanceState);// 此方法必须重写 initMap(); } private void initView() { mMapView = (MapView) findViewById(R.id.map); mRecyclerView = (RecyclerView) findViewById(R.id.ll_rl_locations); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mAdapter = getAdapter(); mRecyclerView.setAdapter(mAdapter); progressDialogView = findViewById(R.id.ll_ll_holderview); tvHint = (TextView) findViewById(R.id.ll_tv_hint); progressbar = findViewById(R.id.ll_progressbar); } / * 地图实例化 */ private void initMap() { if (amap == null) { amap = mMapView.getMap(); amap.setLocationSource(this);//设置了定位的监听,这里要实现LocationSource接口 amap.getUiSettings().setMyLocationButtonEnabled(true); // 是否显示定位按钮 amap.setMyLocationEnabled(true);//显示定位层并且可以触发定位,默认是flase amap.moveCamera(CameraUpdateFactory.zoomTo(15));//设置地图缩放级别 MyLocationStyle myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类 myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。 myLocationStyle.strokeColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的边框颜色 myLocationStyle.radiusFillColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的填充颜色 amap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style lvHolder = new ListViewHoldier(); //天添加屏幕移动的监听 amap.setOnCameraChangeListener(this); // 初始化Marker添加到地图 mEndMarker = amap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.end)))); //初始化 geocoderSearch geocoderSearch = new GeocodeSearch(this); //注册 逆地理编码异步处理回调接口 geocoderSearch.setOnGeocodeSearchListener(this); } } @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); } / * 激活定位 * @param listener */ @Override public void activate(OnLocationChangedListener listener) { mListener = listener; if (mlocationClient == null) { //初始化定位 mlocationClient = new AMapLocationClient(this); //初始化定位参数 mLocationOption = new AMapLocationClientOption(); //设置定位回调监听 mlocationClient.setLocationListener(this); //设置为高精度定位模式 mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); //设置为定位一次 // mLocationOption.setOnceLocation(true); //设置定位参数 mlocationClient.setLocationOption(mLocationOption); // 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗, // 注意设置合适的定位时间的间隔(最小间隔支持为2000ms),并且在合适时间调用stopLocation()方法来取消定位请求 // 在定位结束后,在合适的生命周期调用onDestroy()方法 // 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除 mlocationClient.startLocation();//启动定位 } } / * 注销定位 */ @Override public void deactivate() { mListener = null; if (mlocationClient != null) { mlocationClient.stopLocation(); mlocationClient.onDestroy(); } mlocationClient = null; } / * 实现定位 * @param amapLocation */ @Override public void onLocationChanged(AMapLocation amapLocation) { if (mListener != null && amapLocation != null) { if (amapLocation != null &&amapLocation.getErrorCode() == 0) { if(isFirstTime){
//只要第一次的数据,当然,也可以在这里关闭定位 // mlocationClient.stopLocation();//停止定位 mListener.onLocationChanged(amapLocation);// 显示系统小蓝点 lvHolder.title = "[位置]"; lvHolder.address = amapLocation.getProvider()+amapLocation.getCity()+amapLocation.getStreet()+amapLocation.getStreetNum(); lvHolder.lp = new LatLonPoint(amapLocation.getLatitude(),amapLocation.getLongitude()); mEndMarker.setPosition(new LatLng(amapLocation.getLatitude(),amapLocation.getLongitude())); data.add(0,lvHolder); doSearchQuery(); } } else { String errText = "定位失败," + amapLocation.getErrorCode()+ ": " + amapLocation.getErrorInfo(); Log.e("AmapErr",errText); } } } / * 搜查周边数据 */ private void doSearchQuery() { //搜索类型 String type = "汽车服务|汽车销售|" + "汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|" + "住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|" + "金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施"; query = new PoiSearch.Query("", type, "");// 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国) query.setPageSize(20);// 设置每页最多返回多少条poiitem query.setPageNum(0);// 设置查第一页 poiSearch = new PoiSearch(this, query); //搜索回调 poiSearch.setOnPoiSearchListener(this); //搜索位置及范围 poiSearch.setBound(new PoiSearch.SearchBound(lvHolder.lp, 1000)); //同步搜索 // poiSearch.searchPOI();//不能在主线程实现耗时操作 //异步搜索 poiSearch.searchPOIAsyn(); } / * 返回POI搜索异步处理的结果。 * @param result * @param rcode */ @Override public void onPoiSearched(PoiResult result, int rcode) { if (rcode == AMapException.CODE_AMAP_SUCCESS) { if (result != null && result.getQuery() != null) {
// 搜索poi的结果 if (result.getQuery().equals(query)) {
// 是否是同一条 // 取得搜索到的poiitems有多少页 List<PoiItem> poiItems = result.getPois();// 取得第一页的poiitem数据,页数从数字0开始 if (poiItems != null && poiItems.size() > 0) { for (int i = 0;i<poiItems.size();i++){ PoiItem poiitem = poiItems.get(i); ListViewHoldier holder = new ListViewHoldier(); holder.address = poiitem.getSnippet(); holder.title = poiitem.getTitle(); holder.lp = poiitem.getLatLonPoint(); if(data.size()>i+1){ data.remove(i+1); } data.add(i+1,holder); } mAdapter.notifyDataSetChanged(); mRecyclerView.setVisibility(View.VISIBLE); progressDialogView.setVisibility(View.GONE); } else { progressbar.setVisibility(View.GONE); tvHint.setText(R.string.no_location); } } } else { progressbar.setVisibility(View.GONE); tvHint.setText(R.string.no_location); } } else { progressbar.setVisibility(View.GONE); tvHint.setText(R.string.no_location); } } / * poi id搜索的结果回调 * @param poiItem * @param i */ @Override public void onPoiItemSearched(PoiItem poiItem, int i) { } / * 在地图状态改变过程中回调此方法。 * @param cameraPosition */ @Override public void onCameraChange(CameraPosition cameraPosition) { mEndMarker.setPosition(cameraPosition.target); } / * 在地图状态改变完成时回调此方法。 * @param cameraPosition */ @Override public void onCameraChangeFinish(CameraPosition cameraPosition) { //当地图定位成功的时候该方法也会回调,为了避免不必要的搜索,因此此处增加一个判断 if(isFirstTime){ isFirstTime = false; return; } //隐藏数据 mRecyclerView.setVisibility(View.GONE); //展示dialogView progressDialogView.setVisibility(View.VISIBLE); findViewById(R.id.ll_progressbar).setVisibility(View.VISIBLE); tvHint.setText(R.string.loading); //marker 动画 jumpPoint(mEndMarker); lvHolder.lp = new LatLonPoint(cameraPosition.target.latitude,cameraPosition.target.longitude); RegeocodeQuery query = new RegeocodeQuery(lvHolder.lp, 200, GeocodeSearch.AMAP);// 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系 geocoderSearch.getFromLocationAsyn(query);// 设置异步逆地理编码请求 doSearchQuery(); } / * marker点击时跳动一下 */ public void jumpPoint(final Marker marker) { final Handler handler = new Handler(); final long start = SystemClock.uptimeMillis(); //获取地图投影坐标转换器 Projection proj = amap.getProjection(); final LatLng markerLatlng = marker.getPosition(); Point markerPoint = proj.toScreenLocation(markerLatlng); markerPoint.offset(0, -50); final LatLng startLatLng = proj.fromScreenLocation(markerPoint); final long duration = 500; final Interpolator interpolator = new BounceInterpolator(); handler.post(new Runnable() { @Override public void run() { long elapsed = SystemClock.uptimeMillis() - start; float t = interpolator.getInterpolation((float) elapsed / duration); double lng = t * markerLatlng.longitude + (1 - t) * startLatLng.longitude; double lat = t * markerLatlng.latitude + (1 - t) * startLatLng.latitude; marker.setPosition(new LatLng(lat, lng)); if (t < 1.0) { handler.postDelayed(this, 16); } } }); } / * 逆地理编码回调方法 * 经纬度转位置 * @param result * @param rCode */ @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (rCode == AMapException.CODE_AMAP_SUCCESS) { if (result != null && result.getRegeocodeAddress() != null && result.getRegeocodeAddress().getFormatAddress() != null) { lvHolder.address = result.getRegeocodeAddress().getFormatAddress(); data.remove(0); data.add(0,lvHolder); } else { // ToastUtil.show(ReGeocoderActivity.this, R.string.no_result); } } else { // ToastUtil.showerror(this, rCode); } } / * 地理编码回调方法 * 位置转经纬度 * @param geocodeResult * @param i */ @Override public void onGeocodeSearched(GeocodeResult geocodeResult, int i) { } private CommonAdapter getAdapter() { return new CommonAdapter<ListViewHoldier>(this,R.layout.item_listview_location,data) { @Override protected void convert(ViewHolder holder, final ListViewHoldier listViewHoldier, int position) { holder.setText(R.id.rl_tv_name,listViewHoldier.title); holder.setText(R.id.rl_tv_location,listViewHoldier.address); holder.getView(R.id.rl_tv_subit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(mContext,NavigationActivity.class); intent.putExtra("value",listViewHoldier.lp); intent.putExtra("address",listViewHoldier.address); setResult(RESULT_OK,intent); finish(); } }); } }; } / * 周边数据实体封装 */ private class ListViewHoldier{
String title; String address; LatLonPoint lp; } }
最后我们来看看效果怎么样?

由于对地图相关API不熟悉,走了很多弯路。最后还有一个bug,就是加载地图的时候有时候不会显示到我们设置的缩放级别,还有就是有时候打开地图的时候,会看不见marker,一般都是时间或者网络的原因,大家可以测试一下。最后,如有错误,希望大家多多指正,一起进步,谢谢!
相关API:
http://lbs.amap.com/api/android-sdk/summary/
http://a.amap.com/lbs/static/unzip/Android_Map_Doc/index.html
http://lbs.amap.com/api/android-location-sdk/locationsummary/
http://amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/unzip/Android_Location_Doc/index.html
http://lbs.amap.com/api/android-navi-sdk/summary/
http://a.amap.com/lbs/static/unzip/Android_Navi_Doc/index.html
参考文章:
http://blog.csdn.net/u0/article/details/
源码
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/46513.html