高德地图之拾取地点

高德地图官方有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成都市剑南大道南段71204-29 23:53:06.268 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:08.270 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:10.277 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:12.327 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:14.281 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:16.282 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:18.288 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:20.297 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:22.291 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:24.640 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:26.295 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:28.301 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:30.298 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:32.303 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:34.304 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:36.352 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:38.339 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-29 23:53:40.340 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段71204-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,搞定!接下来,就要开始放大招了!我们需要点击屏幕的时候,显示当前位置以及周边的位置,实现高德地图这样的效果:

这里写图片描述

需要实现上述效果,这里需要实现几点功能:

  1. 定位(已经实现)
  2. 周边搜索(下一步需要实现)
  3. 手指滑动地图停止的时候获取位置——逆地理编码(下下一步需要实现)
  4. 将结果以列表的形式展现出来

于是我们接着上面的内容,开始进行第三步,周边搜索。

第三步,周边搜索:

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.528865,104.044311
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段698号附11号
                                                                          老草屋烧烤(华阳店)
                                                                          30.528869,104.044336
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          协和街道剑南大道南段712号
                                                                          华宜·上锦
                                                                          30.528781,104.044425
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段
                                                                          依爱思洗衣
                                                                          30.528873,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.528738,104.044926
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          协和街道剑南大道南段706号
                                                                          大邑血旺家常菜
                                                                          30.528702,104.045092
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段708-710号附近
                                                                          欣家超市
                                                                          30.528637,104.04507
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段708-710
                                                                          美家装饰建材(华阳店)
                                                                          30.528752,104.045118
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段706号附近
                                                                          贝尔康药业(同福堂大药房)
                                                                          30.528835,104.045141
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段美家建材旁
                                                                          牛肉面&馄饨王&黄焖鸡米饭(剑南大道店)
                                                                          30.528964,104.045165
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          双流区
                                                                          元景路与剑南大道南段交叉口
                                                                          30.529386,104.045866
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          515;521路;814路
                                                                          剑南大道元景路口站(公交站)
                                                                          30.529922,104.045593
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          天府新区华府大道一段996号(金茂)光明城市54单元
                                                                          菜鸟驿站(光明城市优超市周6寄件免费)
                                                                          30.5273,104.04558
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          元景路与剑南大道南段交叉口东100米
                                                                          停车场(元景路)
                                                                          30.529275,104.046573
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          G123路;T207路
                                                                          元景路剑南大道口站(公交站)
                                                                          30.529392,104.046562
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          元景路金茂光明城市3号门124单元2层
                                                                          海鸣画室二号店
                                                                          30.529139,104.046657
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          剑南大道南段
                                                                          顺发超市
                                                                          30.531082,104.044417
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          华阳镇剑南大道南段688号
                                                                          骑龙小区
                                                                          30.526689,104.043652
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched: 
                                                                          华阳华府大道一段99611摄影工作室
                                                                          30.527685,104.046519

OK,周边搜索已经搞定,接下来执行下一步。

第四步,随手指的滑动更新数据:

其实上面有一个bug,就是我们手指滑动是时候,然后定位成功就会将地图的中心点重新定位会我们当前的位置,如图:

这里写图片描述

原因是高德地图默认每秒更新一次当前位置,所以每秒地图就会回到中心点一次。解决方法很多,可以关闭定位,或者将定位成功后的显示系统小蓝点给关掉,代码如下:

mListener.onLocationChanged(amapLocation);// 显示系统小蓝点

现在我们的地图还只是可以随意滑动,但是我们需要在手指滑动结束的时候获取到经纬度,然后更新我们的周边数据。更新周边的数据已经封装好一个方法了,问题是在手指滑动的时候获取到经纬度。并且,在打开地图的时候中心点覆盖系统图标上面还有一个图标,并且该图标随手指移动,在手指结束滑动的时候还有一个动画。所以整理一下思路,接下来需要实现的有如下几点:

  1. 打开地图的时候显示自定义图标
  2. 图标随手指移动
  3. 手指移动结束的时候图标有一个动画
  4. 手指移动结束的时候获取到当前的经纬度并且更新数据

首先我们来实现对地图添加自定义图标,代码如下:

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) {

    }

第五步、将结果以列表的形式展现出来

上面把主要内容都已经实现了,最后就是将数据封装好,放入ListView里面。因为后文还将用到RecycleView,所以这里就直接使用RecycleView,以便使用统一的Adapter。
修改代码,首先是布局,如下:

<?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/u010378579/article/details/53907749

源码

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页