饿了么地址定位android_android 仿饿了么选择地址 - CSDN
  • http://www.cnblogs.com/RGogoing/p/5711843.html

    原文地址:http://www.cnblogs.com/RGogoing/p/5711843.html


    1.使用LocationClient核心类实现定位

    2.使用GeoCoder实现地理编码和反地理编码

    3.使用PoiSearch实现相关的Poi搜索

    4.使用SuggestionSearch实现在线建议查询

    5.ele定位效果的实现

     

      百度地图定位的相关流程我就不进行介绍了.以前也写过流程,至于如何创建应用,如何申请什么的,我就不进行介绍了,官方上有关于如何创建应用申请AK的流程,还是非常的详细的.还是说一下如何实现ele的定位效果吧,可能最后的效果稍微有些偏差,不过大体还是差不多的,主要还是提供一个思路.

    1.LocationClient定位核心类

      LocationClient是实现地图定位的核心类,LBS基站定位就是通过使用LocationClient来实现的,具体的使用方式如下: 

    复制代码
     /**
       * 设置定位的option
       * */
    mLocClient = new LocationClient(this);  //实例化LocationClient
    mLocClient.registerLocationListener(new MyLocationListener()); //注册定位监听
    LocationClientOption option = new LocationClientOption();
    option.setOpenGps(true);            // 打开gps
    option.setCoorType("bd09ll");       // 设置坐标类型
    option.setScanSpan(1000);           // 设置查询范围,默认500
    mLocClient.setLocOption(option);    // 设置option
    mLocClient.start();
    复制代码

      LocationClient实例化之后,需要设置相应的LocationOption,也就是定位的一些选项,通过指定的设置,决定以怎样的形式实现定位,比如说定位的时候需要打开gps,设置坐标的类型,以及搜索的范围等等.同时需要设置相关的监听.

    复制代码
    /**
         * LBS定位监听
         * */
        public class MyLocationListener implements BDLocationListener {
    
            @Override
            public void onReceiveLocation(BDLocation location) {
                // map view 销毁后不在处理新接收的位置
    
                if (IsFirstLoc) {
                    IsFirstLoc = false;
                    point = new LatLng(location.getLatitude(), location.getLongitude());
                    geoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(point));
    
                    /**
                     * 设置当前位置为定位到的地理位置
                     * */
                    MapStatus.Builder builder = new MapStatus.Builder();
                    builder.target(point).zoom(20.0f);
                    baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
                }
            }
        }
    复制代码

      设置完相关的监听之后就可以在LocationClient完成定位后去做一些其他的事情,比如说在地图上显示定位到的当前位置,获取到定位的坐标,坐标的形式是以经度和纬度的组合形式,获取到定位到的坐标后,我们就可以根据坐标实现地理编码和反地理编码.

    2.使用GeoCoder实现地理编码与反编码

     GeoCoder的使用方式还是非常简单的,只需要实例化对象,然后设置监听回调就可以了..

     地理编码:将当前的位置信息转化成坐标的形式(经度+纬度)

    复制代码
    geoCoder = GeoCoder.newInstance(); //GeoCoder对象的实例化
    geoCoder.setOnGetGeoCodeResultListener(this); //设置监听
    //需要实现的方法:
    @Override
    public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
        //这里一般不做其他处理
    }
    复制代码

      反地理编码:将我们当前的坐标信息转化成物理位置.需要额外注意:反地理编码需要在网络状态连接良好的情况下才能够实现

    复制代码
    geoCoder = GeoCoder.newInstance(); //GeoCoder对象的实例化
    geoCoder.setOnGetGeoCodeResultListener(this); //设置监听
    //需要实现的方法:
    @Override
    public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
        /**
          * 获取反地理编码后的城市信息,街道信息,Poi信息等
          * */
        currentCity = reverseGeoCodeResult.getAddress();
    }
    复制代码
    ReverseGeoCodeResult类的具体形式
    复制代码
    package com.baidu.mapapi.search.geocode;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.os.Parcelable.Creator;
    import com.baidu.mapapi.model.LatLng;
    import com.baidu.mapapi.search.core.PoiInfo;
    import com.baidu.mapapi.search.core.SearchResult;
    import com.baidu.mapapi.search.core.SearchResult.ERRORNO;
    import com.baidu.mapapi.search.geocode.c;
    import com.baidu.mapapi.search.geocode.d;
    import java.util.List;
    
    public class ReverseGeoCodeResult extends SearchResult {
        private String a;
        private String b;
        private ReverseGeoCodeResult.AddressComponent c;
        private LatLng d;
        private List<PoiInfo> e;
        public static final Creator<ReverseGeoCodeResult> CREATOR = new c();
    
        ReverseGeoCodeResult() {
        }
    
        ReverseGeoCodeResult(ERRORNO var1) {
            super(var1);
        }
    
        protected ReverseGeoCodeResult(Parcel var1) {
            super(var1);
            this.a = var1.readString();
            this.b = var1.readString();
            this.c = (ReverseGeoCodeResult.AddressComponent)var1.readParcelable(ReverseGeoCodeResult.AddressComponent.class.getClassLoader());
            this.d = (LatLng)var1.readValue(LatLng.class.getClassLoader());
            this.e = var1.createTypedArrayList(PoiInfo.CREATOR);
        }
    
        public String getBusinessCircle() {
            return this.a;
        }
    
        void a(String var1) {
            this.a = var1;
        }
    
        public String getAddress() {
            return this.b;
        }
    
        void b(String var1) {
            this.b = var1;
        }
    
        public ReverseGeoCodeResult.AddressComponent getAddressDetail() {
            return this.c;
        }
    
        void a(ReverseGeoCodeResult.AddressComponent var1) {
            this.c = var1;
        }
    
        public LatLng getLocation() {
            return this.d;
        }
    
        void a(LatLng var1) {
            this.d = var1;
        }
    
        public List<PoiInfo> getPoiList() {
            return this.e;
        }
    
        void a(List<PoiInfo> var1) {
            this.e = var1;
        }
    
        public int describeContents() {
            return 0;
        }
    
        public void writeToParcel(Parcel var1, int var2) {
            super.writeToParcel(var1, var2);
            var1.writeString(this.a);
            var1.writeString(this.b);
            var1.writeParcelable(this.c, 0);
            var1.writeValue(this.d);
            var1.writeTypedList(this.e);
        }
    
        public static class AddressComponent implements Parcelable {
            public String streetNumber;
            public String street;
            public String district;
            public String city;
            public String province;
            public static final Creator<ReverseGeoCodeResult.AddressComponent> CREATOR = new d();
    
            public int describeContents() {
                return 0;
            }
    
            public void writeToParcel(Parcel var1, int var2) {
                var1.writeString(this.streetNumber);
                var1.writeString(this.street);
                var1.writeString(this.district);
                var1.writeString(this.city);
                var1.writeString(this.province);
            }
    
            public AddressComponent() {
            }
    
            protected AddressComponent(Parcel var1) {
                this.streetNumber = var1.readString();
                this.street = var1.readString();
                this.district = var1.readString();
                this.city = var1.readString();
                this.province = var1.readString();
            }
        }
    }
    复制代码

      反地理编码结束之后,我们就可以拿到一些相关信息,比如说我们当前位置所在的省市,城市,区域,街道,以及街道号,以及附近的一些Poi等等.这些都可以通过反地理编码的结束后的回调拿到相关的信息.这里我们的反地理编码只是获取了相关的城市信息.为了后续的在线建议查询做准备.

    3.使用PoiSearch实现相关的Poi搜索

      Poi:poi中文翻译为兴趣点.其实就是周边的一些ktv,酒店,餐馆,理发店等等都是一个poi.在实现了基础定位的前提后,去搜索附近的poi.这样就可以完成一些其他事情.比如说订一份外卖,预定一个房间等等.这些都是基于poi搜索才能够实现的.

      Poi搜索有三种不同的方式,周边搜索,区域搜索,城市内搜索.这里我只说周边搜索,因为ele应该也是使用的周边搜索.

      三种搜索方式代码上其实大体相同,只是搜索的方式不大一样而已.大体分为三个步骤,首先是对象的实例化,然后设置PoiNearBySearchOption,最后设置监听回调即可.

    复制代码
    /**
         * 房子Poi数据搜索
         * @注意: 所有的Poi搜索都是异步完成的
         * */
        private void nearByAllPoiSearch() {
            allpoiSearch = PoiSearch.newInstance();
            allPoiData.clear();
            allpoiSearch.setOnGetPoiSearchResultListener(new OnGetPoiSearchResultListener() {
                @Override
                public void onGetPoiResult(PoiResult poiResult) {
                    if (poiResult.getAllPoi() == null) {
                        Toast.makeText(getApplicationContext(),"定位失败,暂无数据信息",Toast.LENGTH_LONG).show();
                    } else {
                        allPoiData.addAll(poiResult.getAllPoi());
                    }
                }
    
                @Override
                public void onGetPoiDetailResult(PoiDetailResult poiDetailResult) {
                    //Poi详情数据
                }
    
                @Override
                public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {
                    //室内Poi数据
                }
            });
    
            /**
             * 设置Poi Option
             * 当前搜索为附近搜索:以圆形的状态进行搜索
             * 还有两种其他的搜素方式:范围搜素和城市内搜索
             * */
            PoiNearbySearchOption nearbySearchOption = new PoiNearbySearchOption();
            nearbySearchOption.location(new LatLng(point.latitude, point.longitude)); //设置坐标
            nearbySearchOption.keyword("房子");                                       //设置关键字
            nearbySearchOption.radius(2000);                                          //搜索范围的半径
            nearbySearchOption.pageCapacity(15);                                      //设置最多允许加载的poi数量,默认10
            allpoiSearch.searchNearby(nearbySearchOption);
        }
    复制代码

       这里我设置了Poi允许加载的数量,默认是10条数据,也就是说,如果我们不设置分页,百度只会给我们返回10条Poi数据信息,这是默认的情况,这里我修改了允许加载的Poi数量,也就是15个Poi数据信息,如果大家不想写分页,那么可以设置这个属性,按照自己的方式去指定加载多少条数据.

    4.使用SuggestionSearch实现在线建议查询

      在线建议查询:根据城市和关键字搜索出相应的位置信息(模糊查询)使用起来还是非常的简单的.只需要实例化对象,设置结果回调就可以根据我们输入的关键字搜索相关的地理位置信息.

    复制代码
    /**
                 * 在线建议查询对象实例化+设置监听
                 * @在线建议查询: 根据城市和关键字搜索出相应的位置信息(模糊查询)
                 * */
                keyWordsPoiSearch = SuggestionSearch.newInstance();
                keyWordsPoiSearch.setOnGetSuggestionResultListener(new OnGetSuggestionResultListener() {
                    @Override
                    public void onGetSuggestionResult(SuggestionResult suggestionResult) {
                        keyWordPoiData.clear();
                        if (suggestionResult.getAllSuggestions() == null) {
                            Toast.makeText(getApplicationContext(),"暂无数据信息",Toast.LENGTH_LONG).show();
                        } else {
                            keyWordPoiData = suggestionResult.getAllSuggestions();
                            //设置Adapter结束
                            suggestAdapter = new SuggestAddressAdapter(getApplicationContext(), keyWordPoiData);
                            inputPoiListView.setAdapter(suggestAdapter);
                        }
                    }
                });
                keyWordsPoiSearch.requestSuggestion((new SuggestionSearchOption()).keyword(location_name.getText().toString()).city(currentCity));
    复制代码

      大体的东西基本就介绍完了.还是来分析一下ele是如何实现的定位效果吧.我是按照我的思路去实现的,可能会有一些与其并不是特别的一样.总之还是提供一个大体的思路才是关键.我们先看一下效果图

      我们看着这个效果图来说,上层是一个搜索框和一个MapView.并且中间位置有一个ImageView.下层是一个ViewIndicator,我这个ViewIndicator并没有自定义View,只是一个布局,因此实现起来可能没有那么的优雅,大家可以选择去优化这里,然后下面是一个ViewPager来实现4个Fragment的切换.

      需要说明的就是中间这个ImageView,我在这里标识了它并不是地图上的Marker.而是直接定义了一个FrameLayout,让这个ImageView粘在了这个中间位置,我们在移动地图的时候,我们看着好像是这个ImageView也在移动,其实只是这个MapView在移动而已,这个ImageView实际是一直保持不动的.那么移动的时候我们明显看到数据发生了变化,这里只是每次在移动的时候都获取MapView的中心点坐标,因为ImageView是始终在中心显示的,因此每次取得就是中心坐标,然后再进行Poi搜素就可以了.这样就可以发现Fragment里的数据会发生明显的变化.

      这里我第一次定位和Poi搜索都是在MainActivity里面完成的,然后将数据通过setArguments()传递过去.Fragment在首次加载的时候只需要getArguments()来获取相应的数据,然后在自己的ListView当中设置adapter就可以第一次直接显示数据了,同时Fragment在onAttach到Activity的时候需要注册一个广播,这个广播的用来接收,当地图状态发生改变的时候,也就是我们平移了地图,MainActivity需要告知Fragment地图状态已经发生了变化,需要更新Poi数据了.那么Fragment在接收到这条广播的时候,就知道地图状态已经改变,需要根据当前的中心点坐标搜索出现在的Poi数据,然后通过adapter.notifyDataSetChanged来更新ListView里面的数据即可.

    复制代码
    /**
       * 第一次加载的时候由Activity搜索,将数据传递给Fragment,后续由Fragment来完成搜索功能
       * */
    
    Bundle allPoiBundle = new Bundle();
    allPoiBundle.putParcelableArrayList("allPoiData", (ArrayList<? extends Parcelable>) allPoiData);
    allPoiFragment.setArguments(allPoiBundle);
    
    //获取数据,只需要在OnCreateView获取
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_all, null);
        allPoiSearch = PoiSearch.newInstance();
        allData.clear();
        allData = getArguments().getParcelableArrayList("allPoiData");
        initView(view);
        return view;
    }
    复制代码

      这里我们可以看到我只在MainActivity搜索了一次Poi,如果我们每次都在MainActivity中搜索完Poi然后再传递Fragment的话,这样其实会耗费大量的时间,数据更新的速度也比较的慢,大家可以去试试每次在MainActivity中定位完数据之后再发送个Fragment.看看这样实现是否优雅.其实我们也可以完全不在MainActivity中搜索Poi,完全交给Fragment其实也是可以的.

      这里还需要说一下Fragment的一个数据预加载问题,我们都知道Fragment是有预加载机制的,默认情况下Fragment只会预加载一页数据,因此这里我改变了它的预加载数量.

     /**
       * 这里修改了Fragment的预加载数量,一次加载三页数据,默认是一页
       * */
    viewPager.setOffscreenPageLimit(3);

       改变这个也是有原因的,因为我们需要在OnAttach时为Fragment注册一个广播,监听地图状态是否发生了变化,如果使用默认的加载机制,比如说我们现在就在全部这个Fragment页面,那么只有全部和写字楼注册了这个广播,其他两个页面还没有OnAttach到Activity上,这时我们改变地图状态,前面这两页数据会发生明显变化,而后面的两页是根本不知道数据已经变化了.同理一样.如果我们在最后一页,前两页已经被销毁,已经onDetach()Activity了,那么这两页也是拿不到数据的.因此我这里改变了它的预加载数量.无论如何滑动,都能够接收到数据.使用默认的预加载机制,出现问题的主要原因其实和Fragment的生命周期有紧密关联的.有空我会去写一篇关于Fragment的生命周期的博客.现在我们只需要知道就可以了.

      最后就是这个蛋疼的搜索框,浪费了我相当长的时间.按照ele的效果来看,当点击搜索框的时候,需要弹出一个页面覆盖掉这个页面,然后根据关键字搜索出数据,以列表项的形式展现出来,其实这里就使用到了在下建议查询,根据我们输入的关键字搜素出相应的数据信息.但是这里蛋疼的问题在于当我们点击返回的键的时候不是结束这个Activity,而是返回到地图这个页面.因此这里我这里只能重写onKeyDown事件,然后拦截Back事件.如果搜索框中的EditText在获取焦点状态的情况下,点击返回键的话,那么返回地图页面,一直不结束这个Activity,但是蛋疼的事就来了,当返回到这个MapView页面的时候,搜索框中的EditText仍然优先获取到了焦点,无论我们怎么点击返回键,这个Activity都不会被销毁.这样就有很大的问题.最后我找到了一种方法,只能让整个搜索框这个布局去抢占EditText的焦点.那么在返回地图的时候,EditText就永远不会优先获取到焦点事件了.而且还能够正常销毁Activity.

    复制代码
     /**
         * 监听onKeyDown事件
         * 目的是判断当前页面是地图显示页面还是在线建议查询页面
         * */
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                if (isFocus) {
                    inputPoiSearchLayout.setVisibility(View.GONE);
                    location_name.setText("");
                    location_name.clearFocus();
                    keyWordPoiData.clear();
                    layout.setFocusable(true);
                    layout.setFocusableInTouchMode(true);
                    layout.requestFocus();
                    isFocus = false;
                    return true;
                }
            }
            return super.onKeyDown(keyCode, event);
        }
    复制代码

      该说的也就这么多了,用上的知识点,以及如何具体实现.最后放上一个源代码.还有一些需要注意的就是如果使用Android Studio开发.并且jar包都保存在libs文件夹中的话,在build.gradle中别忘了配置.

    复制代码
    sourceSets {
            main {
                jniLibs.srcDir 'libs'  //这里必须要有,否则会报.so文件的异常
            }
            instrumentTest.setRoot('tests')
            debug.setRoot('build-types/debug')
            release.setRoot('build-types/release')
        }
    展开全文
  • 因为最近项目里边需要用到定位收货地址,所以就像弄一个饿了么或者美团那样的能滑动地图,而指针不动的效果,找了小半天终于找到了。 废话不多说了,直接上 1.使用LocationClient核心类实现定位 2.使用...

    转载自http://www.cnblogs.com/RGogoing/p/5711843.html

    因为最近项目里边需要用到定位收货地址,所以就像弄一个饿了么或者美团那样的能滑动地图,而指针不动的效果,找了小半天终于找到了。

    废话不多说了,直接上

    1.使用LocationClient核心类实现定位

    2.使用GeoCoder实现地理编码和反地理编码

    3.使用PoiSearch实现相关的Poi搜索

    4.使用SuggestionSearch实现在线建议查询

    5.ele定位效果的实现

     

      百度地图定位的相关流程我就不进行介绍了.以前也写过流程,至于如何创建应用,如何申请什么的,我就不进行介绍了,官方上有关于如何创建应用申请AK的流程,还是非常的详细的.还是说一下如何实现ele的定位效果吧,可能最后的效果稍微有些偏差,不过大体还是差不多的,主要还是提供一个思路.

    1.LocationClient定位核心类

      LocationClient是实现地图定位的核心类,LBS基站定位就是通过使用LocationClient来实现的,具体的使用方式如下: 

    复制代码
     /**
       * 设置定位的option
       * */
    mLocClient = new LocationClient(this);  //实例化LocationClient
    mLocClient.registerLocationListener(new MyLocationListener()); //注册定位监听
    LocationClientOption option = new LocationClientOption();
    option.setOpenGps(true);            // 打开gps
    option.setCoorType("bd09ll");       // 设置坐标类型
    option.setScanSpan(1000);           // 设置查询范围,默认500
    mLocClient.setLocOption(option);    // 设置option
    mLocClient.start();
    复制代码

      LocationClient实例化之后,需要设置相应的LocationOption,也就是定位的一些选项,通过指定的设置,决定以怎样的形式实现定位,比如说定位的时候需要打开gps,设置坐标的类型,以及搜索的范围等等.同时需要设置相关的监听.

    复制代码
    /**
         * LBS定位监听
         * */
        public class MyLocationListener implements BDLocationListener {
    
            @Override
            public void onReceiveLocation(BDLocation location) {
                // map view 销毁后不在处理新接收的位置
    
                if (IsFirstLoc) {
                    IsFirstLoc = false;
                    point = new LatLng(location.getLatitude(), location.getLongitude());
                    geoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(point));
    
                    /**
                     * 设置当前位置为定位到的地理位置
                     * */
                    MapStatus.Builder builder = new MapStatus.Builder();
                    builder.target(point).zoom(20.0f);
                    baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
                }
            }
        }
    复制代码

      设置完相关的监听之后就可以在LocationClient完成定位后去做一些其他的事情,比如说在地图上显示定位到的当前位置,获取到定位的坐标,坐标的形式是以经度和纬度的组合形式,获取到定位到的坐标后,我们就可以根据坐标实现地理编码和反地理编码.

    2.使用GeoCoder实现地理编码与反编码

     GeoCoder的使用方式还是非常简单的,只需要实例化对象,然后设置监听回调就可以了..

     地理编码:将当前的位置信息转化成坐标的形式(经度+纬度)

    复制代码
    geoCoder = GeoCoder.newInstance(); //GeoCoder对象的实例化
    geoCoder.setOnGetGeoCodeResultListener(this); //设置监听
    //需要实现的方法:
    @Override
    public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
        //这里一般不做其他处理
    }
    复制代码

      反地理编码:将我们当前的坐标信息转化成物理位置.需要额外注意:反地理编码需要在网络状态连接良好的情况下才能够实现

    复制代码
    geoCoder = GeoCoder.newInstance(); //GeoCoder对象的实例化
    geoCoder.setOnGetGeoCodeResultListener(this); //设置监听
    //需要实现的方法:
    @Override
    public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
        /**
          * 获取反地理编码后的城市信息,街道信息,Poi信息等
          * */
        currentCity = reverseGeoCodeResult.getAddress();
    }
    复制代码
    ReverseGeoCodeResult类的具体形式
    复制代码
    package com.baidu.mapapi.search.geocode;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.os.Parcelable.Creator;
    import com.baidu.mapapi.model.LatLng;
    import com.baidu.mapapi.search.core.PoiInfo;
    import com.baidu.mapapi.search.core.SearchResult;
    import com.baidu.mapapi.search.core.SearchResult.ERRORNO;
    import com.baidu.mapapi.search.geocode.c;
    import com.baidu.mapapi.search.geocode.d;
    import java.util.List;
    
    public class ReverseGeoCodeResult extends SearchResult {
        private String a;
        private String b;
        private ReverseGeoCodeResult.AddressComponent c;
        private LatLng d;
        private List<PoiInfo> e;
        public static final Creator<ReverseGeoCodeResult> CREATOR = new c();
    
        ReverseGeoCodeResult() {
        }
    
        ReverseGeoCodeResult(ERRORNO var1) {
            super(var1);
        }
    
        protected ReverseGeoCodeResult(Parcel var1) {
            super(var1);
            this.a = var1.readString();
            this.b = var1.readString();
            this.c = (ReverseGeoCodeResult.AddressComponent)var1.readParcelable(ReverseGeoCodeResult.AddressComponent.class.getClassLoader());
            this.d = (LatLng)var1.readValue(LatLng.class.getClassLoader());
            this.e = var1.createTypedArrayList(PoiInfo.CREATOR);
        }
    
        public String getBusinessCircle() {
            return this.a;
        }
    
        void a(String var1) {
            this.a = var1;
        }
    
        public String getAddress() {
            return this.b;
        }
    
        void b(String var1) {
            this.b = var1;
        }
    
        public ReverseGeoCodeResult.AddressComponent getAddressDetail() {
            return this.c;
        }
    
        void a(ReverseGeoCodeResult.AddressComponent var1) {
            this.c = var1;
        }
    
        public LatLng getLocation() {
            return this.d;
        }
    
        void a(LatLng var1) {
            this.d = var1;
        }
    
        public List<PoiInfo> getPoiList() {
            return this.e;
        }
    
        void a(List<PoiInfo> var1) {
            this.e = var1;
        }
    
        public int describeContents() {
            return 0;
        }
    
        public void writeToParcel(Parcel var1, int var2) {
            super.writeToParcel(var1, var2);
            var1.writeString(this.a);
            var1.writeString(this.b);
            var1.writeParcelable(this.c, 0);
            var1.writeValue(this.d);
            var1.writeTypedList(this.e);
        }
    
        public static class AddressComponent implements Parcelable {
            public String streetNumber;
            public String street;
            public String district;
            public String city;
            public String province;
            public static final Creator<ReverseGeoCodeResult.AddressComponent> CREATOR = new d();
    
            public int describeContents() {
                return 0;
            }
    
            public void writeToParcel(Parcel var1, int var2) {
                var1.writeString(this.streetNumber);
                var1.writeString(this.street);
                var1.writeString(this.district);
                var1.writeString(this.city);
                var1.writeString(this.province);
            }
    
            public AddressComponent() {
            }
    
            protected AddressComponent(Parcel var1) {
                this.streetNumber = var1.readString();
                this.street = var1.readString();
                this.district = var1.readString();
                this.city = var1.readString();
                this.province = var1.readString();
            }
        }
    }
    复制代码

      反地理编码结束之后,我们就可以拿到一些相关信息,比如说我们当前位置所在的省市,城市,区域,街道,以及街道号,以及附近的一些Poi等等.这些都可以通过反地理编码的结束后的回调拿到相关的信息.这里我们的反地理编码只是获取了相关的城市信息.为了后续的在线建议查询做准备.

    3.使用PoiSearch实现相关的Poi搜索

      Poi:poi中文翻译为兴趣点.其实就是周边的一些ktv,酒店,餐馆,理发店等等都是一个poi.在实现了基础定位的前提后,去搜索附近的poi.这样就可以完成一些其他事情.比如说订一份外卖,预定一个房间等等.这些都是基于poi搜索才能够实现的.

      Poi搜索有三种不同的方式,周边搜索,区域搜索,城市内搜索.这里我只说周边搜索,因为ele应该也是使用的周边搜索.

      三种搜索方式代码上其实大体相同,只是搜索的方式不大一样而已.大体分为三个步骤,首先是对象的实例化,然后设置PoiNearBySearchOption,最后设置监听回调即可.

    复制代码
    /**
         * 房子Poi数据搜索
         * @注意: 所有的Poi搜索都是异步完成的
         * */
        private void nearByAllPoiSearch() {
            allpoiSearch = PoiSearch.newInstance();
            allPoiData.clear();
            allpoiSearch.setOnGetPoiSearchResultListener(new OnGetPoiSearchResultListener() {
                @Override
                public void onGetPoiResult(PoiResult poiResult) {
                    if (poiResult.getAllPoi() == null) {
                        Toast.makeText(getApplicationContext(),"定位失败,暂无数据信息",Toast.LENGTH_LONG).show();
                    } else {
                        allPoiData.addAll(poiResult.getAllPoi());
                    }
                }
    
                @Override
                public void onGetPoiDetailResult(PoiDetailResult poiDetailResult) {
                    //Poi详情数据
                }
    
                @Override
                public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {
                    //室内Poi数据
                }
            });
    
            /**
             * 设置Poi Option
             * 当前搜索为附近搜索:以圆形的状态进行搜索
             * 还有两种其他的搜素方式:范围搜素和城市内搜索
             * */
            PoiNearbySearchOption nearbySearchOption = new PoiNearbySearchOption();
            nearbySearchOption.location(new LatLng(point.latitude, point.longitude)); //设置坐标
            nearbySearchOption.keyword("房子");                                       //设置关键字
            nearbySearchOption.radius(2000);                                          //搜索范围的半径
            nearbySearchOption.pageCapacity(15);                                      //设置最多允许加载的poi数量,默认10
            allpoiSearch.searchNearby(nearbySearchOption);
        }
    复制代码

       这里我设置了Poi允许加载的数量,默认是10条数据,也就是说,如果我们不设置分页,百度只会给我们返回10条Poi数据信息,这是默认的情况,这里我修改了允许加载的Poi数量,也就是15个Poi数据信息,如果大家不想写分页,那么可以设置这个属性,按照自己的方式去指定加载多少条数据.

    4.使用SuggestionSearch实现在线建议查询

      在线建议查询:根据城市和关键字搜索出相应的位置信息(模糊查询)使用起来还是非常的简单的.只需要实例化对象,设置结果回调就可以根据我们输入的关键字搜索相关的地理位置信息.

    复制代码
    /**
                 * 在线建议查询对象实例化+设置监听
                 * @在线建议查询: 根据城市和关键字搜索出相应的位置信息(模糊查询)
                 * */
                keyWordsPoiSearch = SuggestionSearch.newInstance();
                keyWordsPoiSearch.setOnGetSuggestionResultListener(new OnGetSuggestionResultListener() {
                    @Override
                    public void onGetSuggestionResult(SuggestionResult suggestionResult) {
                        keyWordPoiData.clear();
                        if (suggestionResult.getAllSuggestions() == null) {
                            Toast.makeText(getApplicationContext(),"暂无数据信息",Toast.LENGTH_LONG).show();
                        } else {
                            keyWordPoiData = suggestionResult.getAllSuggestions();
                            //设置Adapter结束
                            suggestAdapter = new SuggestAddressAdapter(getApplicationContext(), keyWordPoiData);
                            inputPoiListView.setAdapter(suggestAdapter);
                        }
                    }
                });
                keyWordsPoiSearch.requestSuggestion((new SuggestionSearchOption()).keyword(location_name.getText().toString()).city(currentCity));
    复制代码

      大体的东西基本就介绍完了.还是来分析一下ele是如何实现的定位效果吧.我是按照我的思路去实现的,可能会有一些与其并不是特别的一样.总之还是提供一个大体的思路才是关键.我们先看一下效果图

      我们看着这个效果图来说,上层是一个搜索框和一个MapView.并且中间位置有一个ImageView.下层是一个ViewIndicator,我这个ViewIndicator并没有自定义View,只是一个布局,因此实现起来可能没有那么的优雅,大家可以选择去优化这里,然后下面是一个ViewPager来实现4个Fragment的切换.

      需要说明的就是中间这个ImageView,我在这里标识了它并不是地图上的Marker.而是直接定义了一个FrameLayout,让这个ImageView粘在了这个中间位置,我们在移动地图的时候,我们看着好像是这个ImageView也在移动,其实只是这个MapView在移动而已,这个ImageView实际是一直保持不动的.那么移动的时候我们明显看到数据发生了变化,这里只是每次在移动的时候都获取MapView的中心点坐标,因为ImageView是始终在中心显示的,因此每次取得就是中心坐标,然后再进行Poi搜素就可以了.这样就可以发现Fragment里的数据会发生明显的变化.

      这里我第一次定位和Poi搜索都是在MainActivity里面完成的,然后将数据通过setArguments()传递过去.Fragment在首次加载的时候只需要getArguments()来获取相应的数据,然后在自己的ListView当中设置adapter就可以第一次直接显示数据了,同时Fragment在onAttach到Activity的时候需要注册一个广播,这个广播的用来接收,当地图状态发生改变的时候,也就是我们平移了地图,MainActivity需要告知Fragment地图状态已经发生了变化,需要更新Poi数据了.那么Fragment在接收到这条广播的时候,就知道地图状态已经改变,需要根据当前的中心点坐标搜索出现在的Poi数据,然后通过adapter.notifyDataSetChanged来更新ListView里面的数据即可.

    复制代码
    /**
       * 第一次加载的时候由Activity搜索,将数据传递给Fragment,后续由Fragment来完成搜索功能
       * */
    
    Bundle allPoiBundle = new Bundle();
    allPoiBundle.putParcelableArrayList("allPoiData", (ArrayList<? extends Parcelable>) allPoiData);
    allPoiFragment.setArguments(allPoiBundle);
    
    //获取数据,只需要在OnCreateView获取
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_all, null);
        allPoiSearch = PoiSearch.newInstance();
        allData.clear();
        allData = getArguments().getParcelableArrayList("allPoiData");
        initView(view);
        return view;
    }
    复制代码

      这里我们可以看到我只在MainActivity搜索了一次Poi,如果我们每次都在MainActivity中搜索完Poi然后再传递Fragment的话,这样其实会耗费大量的时间,数据更新的速度也比较的慢,大家可以去试试每次在MainActivity中定位完数据之后再发送个Fragment.看看这样实现是否优雅.其实我们也可以完全不在MainActivity中搜索Poi,完全交给Fragment其实也是可以的.

      这里还需要说一下Fragment的一个数据预加载问题,我们都知道Fragment是有预加载机制的,默认情况下Fragment只会预加载一页数据,因此这里我改变了它的预加载数量.

     /**
       * 这里修改了Fragment的预加载数量,一次加载三页数据,默认是一页
       * */
    viewPager.setOffscreenPageLimit(3);

       改变这个也是有原因的,因为我们需要在OnAttach时为Fragment注册一个广播,监听地图状态是否发生了变化,如果使用默认的加载机制,比如说我们现在就在全部这个Fragment页面,那么只有全部和写字楼注册了这个广播,其他两个页面还没有OnAttach到Activity上,这时我们改变地图状态,前面这两页数据会发生明显变化,而后面的两页是根本不知道数据已经变化了.同理一样.如果我们在最后一页,前两页已经被销毁,已经onDetach()Activity了,那么这两页也是拿不到数据的.因此我这里改变了它的预加载数量.无论如何滑动,都能够接收到数据.使用默认的预加载机制,出现问题的主要原因其实和Fragment的生命周期有紧密关联的.有空我会去写一篇关于Fragment的生命周期的博客.现在我们只需要知道就可以了.

      最后就是这个蛋疼的搜索框,浪费了我相当长的时间.按照ele的效果来看,当点击搜索框的时候,需要弹出一个页面覆盖掉这个页面,然后根据关键字搜索出数据,以列表项的形式展现出来,其实这里就使用到了在下建议查询,根据我们输入的关键字搜素出相应的数据信息.但是这里蛋疼的问题在于当我们点击返回的键的时候不是结束这个Activity,而是返回到地图这个页面.因此这里我这里只能重写onKeyDown事件,然后拦截Back事件.如果搜索框中的EditText在获取焦点状态的情况下,点击返回键的话,那么返回地图页面,一直不结束这个Activity,但是蛋疼的事就来了,当返回到这个MapView页面的时候,搜索框中的EditText仍然优先获取到了焦点,无论我们怎么点击返回键,这个Activity都不会被销毁.这样就有很大的问题.最后我找到了一种方法,只能让整个搜索框这个布局去抢占EditText的焦点.那么在返回地图的时候,EditText就永远不会优先获取到焦点事件了.而且还能够正常销毁Activity.

    复制代码
     /**
         * 监听onKeyDown事件
         * 目的是判断当前页面是地图显示页面还是在线建议查询页面
         * */
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                if (isFocus) {
                    inputPoiSearchLayout.setVisibility(View.GONE);
                    location_name.setText("");
                    location_name.clearFocus();
                    keyWordPoiData.clear();
                    layout.setFocusable(true);
                    layout.setFocusableInTouchMode(true);
                    layout.requestFocus();
                    isFocus = false;
                    return true;
                }
            }
            return super.onKeyDown(keyCode, event);
        }
    复制代码

      该说的也就这么多了,用上的知识点,以及如何具体实现.最后放上一个源代码.还有一些需要注意的就是如果使用Android Studio开发.并且jar包都保存在libs文件夹中的话,在build.gradle中别忘了配置.

    复制代码
    sourceSets {
            main {
                jniLibs.srcDir 'libs'  //这里必须要有,否则会报.so文件的异常
            }
            instrumentTest.setRoot('tests')
            debug.setRoot('build-types/debug')
            release.setRoot('build-types/release')
        }
    复制代码

     源代码(链接) http://pan.baidu.com/s/1nuKcdM5


    展开全文
  • Android 仿美团选择城市、微信通讯录、饿了么点餐列表的导航悬停分组索引列表

    SuspensionIndexBar

    简介:快速实现分组悬停、右侧索引导航联动 列表。
    如 美团选择城市界面,微信通讯录界面、饿了么点餐界面等。

    SuspensionIndexBar

    相关博文:

    从0实现: http://blog.csdn.net/zxt0601/article/details/52355199

    http://blog.csdn.net/zxt0601/article/details/52420706

    使用说明 and 封装: http://gold.xitu.io/post/583c133eac502e006c23cc81

    喜欢随手点个star 多谢

    在哪里找到我:

    我的github:

    https://github.com/mcxtzhang

    我的CSDN博客:

    http://blog.csdn.net/zxt0601

    我的稀土掘金:

    http://gold.xitu.io/user/56de210b816dfa0052e66495

    我的简书:

    http://www.jianshu.com/users/8e91ff99b072/timeline

    效果一览:

    配合我另一个库组装的效果(SuspensionIndexBar + SwipeMenuLayout):

    (SwipeDelMenuLayout : https://github.com/mcxtzhang/SwipeDelMenuLayout)

    SwipeDelMenuActivity: image

    高仿美团选择城市界面(MeituanSelectCityActivity):

    image

    高仿微信通讯录界面(WeChatActivity):

    image

    普通城市列表界面(MainActivity):

    image

    引入

    Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end of repositories:

    	allprojects {
    		repositories {
    			...
    			maven { url 'https://jitpack.io' }
    		}
    	}
    

    Step 2. Add the dependency

    	dependencies {
    	        compile 'com.github.mcxtzhang:SuspensionIndexBar:V1.0.0'
    	}
    

    使用说明:

    http://gold.xitu.io/post/583c133eac502e006c23cc81

    更新记录:

    2016 11 29 :

    • 重构悬停分组,将TitleItemDecoration更名为SuspensionDecoration,数据源依赖ISuspensionInterface接口。
    • 重构索引导航,将IndexBar对数据源的操作,如排序,转拼音等分离出去,以接口IIndexBarDataHelper通信。
    • 有N多兄弟给我留言、加QQ问的:如何实现美团选择城市列表页面,
    • 添加一个不带悬停分组的HeaderView(微信通讯录界面)

    2016 11 10 : 1 IndexBar也考虑了HeaderView不需要索引的情况:

            mIndexBar.setmPressedShowTextView(mTvSideBarHint)//设置HintTextView
                    .setNeedRealIndex(true)//设置需要真实的索引
                    .setmLayoutManager(mManager)//设置RecyclerView的LayoutManager
                    .setmSourceDatas(mDatas)//设置数据
                    .setHeaderViewCount(mWrapperAdapter.getHeaderViewCount());//设置HeaderView数量
    

    2016 11 07 : 1 考虑了HeaderView不需要索引的情况,TitleItemDecoration增加了一个headerView的字段和方法:

    new TitleItemDecoration(this, mDatas).setHeaderViewCount(mWrapperAdapter.getHeaderViewCount())
    

    2 暂时抽离TitleItemDecoration至lib包,并且以 接口形式接收数据 。

    to do list

    1 扩展 SuspensionDecoration 支持传入layout or View。

    闲言碎语

    网上关于实现带悬停分组头部的列表的方法有很多,像我看过有主席的自定义ExpandListView实现的,也看过有人用一个额外的父布局里面套 RecyclerView/ListView+一个头部View(位置固定在父布局上方)实现的。 对于以上解决方案,有以下几点个人觉得不好的地方:

    1. 现在RecyclerView是主流

    2. 在RecyclerView外套一个父布局总归是增加布局层级,容易overdraw,显得不够优雅。

    3. item布局实现带这种分类头部的方法有两种,一种是把分类头部当做一种itemViewtype(麻烦),另一种是每个Item布局都包含了分类头部的布局,代码里根据postion等信息动态Visible,Gone头部(布局冗余,item效率降低)。

    况且Google为我们提供了ItemDecoration,它本身就是用来修饰RecyclerView里的Item的,它的getItemOffsets() onDraw()方法用于为Item分类头部留出空间和绘制(解决缺点3),它的onDrawOver()方法用于绘制悬停的头部View(解决缺点2)。

    而且更重要的是,ItemDecoration出来这么久了,你还不用它

    本文就利用ItemDecoration 打造 分组列表,并配有悬停头部功能。

    亮点预览:添加多个ItemDecoration、它们的执行顺序、ItemDecoration方法执行顺序、ItemDecoration和RecyclerView的绘制顺序

    http://blog.csdn.net/zxt0601/article/details/52355199)

    我们用ItemDecoration为RecyclerView打造了带悬停头部的分组列表。其实Android版微信的通讯录界面,它的分组title也不是悬停的,我们已经领先了微信一小步(认真脸)~ 再看看市面上常见的分组列表(例如饿了么点餐商品列表),不仅有悬停头部,悬停头部在切换时,还会伴有切换动画。 关于ItemDecoration还有一个问题,简单布局还好,我们可以draw出来,如果是复杂的头部呢?能否写个xml,inflate进来,这样使用起来才简单,即另一种简单使用onDraw和onDrawOver的姿势。 so,本文开头我们就先用两节完善一下我们的ItemDecoration。然后进入正题:自定义View实现右侧索引导航栏IndexBar,对数据源的排序字段按照拼音排序,最后将RecyclerView和IndexBar联动起来,触摸IndexBar上相应字母,RecyclerView滚动到相应位置。(在屏幕中间显示的其实就是一个TextView,我们set个体IndexBar即可) 由于大部分使用右侧索引导航栏的场景,都需要这几个固定步骤,对数据源排序,set给IndexBar,和RecyclerView联动等,所以最后再将其封装一把,成一个高度封装,因此扩展性不太高的控件,更方便使用,如果需要扩展的话,反正看完本文再其基础上修改应该很简单~。

    本文摘要:

    1. 用ItemDecoration实现悬停头部切换动画
    2. 另一种**简单使用onDraw()和onDrawOver()**的姿势
    3. 自定义View实现右侧索引导航栏IndexBar
    4. 使用TinyPinyin对数据源排序
    5. 联动IndexBar和RecyclerView。
    6. 封装重复步骤,方便二次使用,并可定制导航数据源

    http://blog.csdn.net/zxt0601/article/details/52420706)

    本文是这个系列的第三篇,不出意外也是终结篇。因为使用经过重构后的控件已经可以快速实现市面上带 索引导航、悬停分组的列表界面了。 在前两篇里,我们从0开始,一步一步实现了仿微信通讯录、饿了么选餐界面。 (第一篇戳我 第二篇戳我) 这篇文章作为终结篇,和前文相比,主要涉及以下内容:

    • 重构悬停分组,将TitleItemDecoration更名为SuspensionDecoration,数据源依赖ISuspensionInterface接口。
    • 重构索引导航,将IndexBar对数据源的操作,如排序,转拼音等分离出去,以接口IIndexBarDataHelper通信。
    • 有N多兄弟给我留言、加QQ问的:如何实现美团选择城市列表页面,
    • 添加一个不带悬停分组的HeaderView(微信通讯录界面)

    http://gold.xitu.io/post/583c133eac502e006c23cc81

    展开全文
  • 高仿饿了么新增位置

    2018-05-29 18:05:16
    最近由于公司的需求需要实现饿了么新增地址的效果,于是总结一下实现的方式以及在实现的过程中需要注意的一些地方。首先我们来看一下饿了么新增地址的效果。总结一下需要实现的效果有哪些:定位滑动地图时能够及时的...

    最近由于公司的需求需要实现饿了么新增地址的效果,于是总结一下实现的方式以及在实现的过程中需要注意的一些地方。

    首先我们来看一下饿了么新增地址的效果。


    总结一下需要实现的效果有哪些:

    1. 定位

    2. 滑动地图时能够及时的获取到地图中心点的位置信息

    3. 滑动时以及停止滑动的时的动画效果

    4. 点击位置信息后获取附近位置列表

    首先我们来分析一下这几个问题:定位的话可以使用高德地图,当然也可以使用百度地图,最终我们采用的是高德地图;滑动时要获取中心点的位置信息这个功能需要调用高德地图的相关API来实现,具体是什么后面讲;第三个动画效果,我们需要分析一下有哪些状态以及触发的条件是什么;最后一个和第二个一样,也是调用相关的API就可以了。下面我们来一个一个分析。

    前提

    需要说明一下:关于地图的集成以及一些相关的配置信息我就不展开了,详细的配置教程可以去官网查看。

    高德地图入门指南

    定位

    首先需要在布局文件中添加<MapView>

    <com.amap.api.maps.MapView
    
        android:id="@+id/map"
    
        android:layout_width="match_parent"
    
        android:layout_height="match_parent"/>

    然后需要在Activity中添加对地图的生命周期管理

    
    public class MainActivity extends Activity {
      MapView mMapView = null;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main);
        //获取地图控件引用
        mMapView = (MapView) findViewById(R.id.map);
        //在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
        mMapView.onCreate(savedInstanceState);
      }
      @Override
      protected void onDestroy() {
        super.onDestroy();
        //在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
        mMapView.onDestroy();
      }
     @Override
     protected void onResume() {
        super.onResume();
        //在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
        mMapView.onResume();
        }
     @Override
     protected void onPause() {
        super.onPause();
        //在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
        mMapView.onPause();
        }
     @Override
     protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
        mMapView.onSaveInstanceState(outState);
      } 
    }

    最后需要获取到AMap,AMap是地图的控制器。我们后面的很多操作都需要用到它。

    
    //定义了一个地图view
    mapView = (MapView) findViewById(R.id.map);
    mapView.onCreate(savedInstanceState);// 此方法须覆写,虚拟机需要在很多情况下保存地图绘制的当前状态。
    //初始化地图控制器对象
    AMap aMap;
    if (aMap == null) {
        aMap = mapView.getMap();        
    }

    最后我们的地图就可以显示了。

    不出意味的话应该是这种效果:

    (图片来自高德地图文档)

    很显然这不符合我们的UI要求,所以我们还需要做一些定制化。在MapView中对UI处理的类是UiSettings。

    
    if (mAMap == null) {
                mAMap = mMapView.getMap();
                //获取UiSettings实例
                UiSettings uiSettings = mAMap.getUiSettings();
                //设置缩放控件
                uiSettings.setZoomControlsEnabled(false);
                //设置定位按钮
                uiSettings.setMyLocationButtonEnabled(false);
            }

    注释已经写好了,所以我就不多重复了。

    地图是显示了但并没有定位到我们当前的位置,所以下面还需要实现定位的功能。

    高德地图中管理定位的类是AMapLocationClient ,AMapLocationClientOption 是用于配置定位。OnLocationChangedListener是定位监听器。

    
    //初始化定位
            mLocationClient = new AMapLocationClient(getApplicationContext());
            //设置定位回调监听
            mLocationClient.setLocationListener(this);
            //初始化定位参数
            mLocationOption = new AMapLocationClientOption();
            //设置定位模式为高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式
            mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
            //设置是否返回地址信息(默认返回地址信息)
            mLocationOption.setNeedAddress(true);
            //设置是否只定位一次,默认为false
            mLocationOption.setOnceLocation(true);
            //设置是否强制刷新WIFI,默认为强制刷新
            mLocationOption.setWifiActiveScan(true);
            //设置是否允许模拟位置,默认为false,不允许模拟位置
            mLocationOption.setMockEnable(false);
            //设置定位间隔,单位毫秒,默认为2000ms
            mLocationOption.setInterval(2000);
            //给定位客户端对象设置定位参数
            mLocationClient.setLocationOption(mLocationOption);
            //启动定位
            mLocationClient.startLocation();

    然后我们需要在onLocationChanged()中监听位置信息。所有的定位信息都在AMapLocation中。

    
    //定位成功回调信息,设置相关消息
                    amapLocation.getLocationType();//获取当前定位结果来源,如网络定位结果,详见官方定位类型表
                    amapLocation.getLatitude();//获取纬度
                    amapLocation.getLongitude();//获取经度
                    amapLocation.getAccuracy();//获取精度信息
                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date date = new Date(amapLocation.getTime());
                    df.format(date);//定位时间
                    amapLocation.getAddress();//地址,如果option中设置isNeedAddress为false,则没有此结果,网络定位结果中会有地址信息,GPS定位不返回地址信息。
                    amapLocation.getCountry();//国家信息
                    amapLocation.getProvince();//省信息
                    amapLocation.getCity();//城市信息
                    amapLocation.getDistrict();//城区信息
                    amapLocation.getStreet();//街道信息
                    amapLocation.getStreetNum();//街道门牌号信息
                    amapLocation.getCityCode();//城市编码
                    amapLocation.getAdCode();//地区编码

    定位是实现了,但还需要我们和MapView进行关联。这时我们需要调用setLocationSource()方法,该方法接收LocationSource,所以我们需要实现这个接口,这个接口需要重写两个方法:activate() , deactivate()方法。在activate()中我是做定位相关的初始化和配置操作的;在deactivate()中做释放资源。

    在获取到定位的经纬度后我们需要把当前的地图移到定位点,这个操作在高德地图中被称为camera,我的理解是视角的意思,也就是说需要把MapView当前的视角移到定位点。

    
    //设置缩放级别
                        mAMap.moveCamera(CameraUpdateFactory.zoomTo(17));
      //将地图移动到定位点
     mAMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(amapLocation.getLatitude(), amapLocation.getLongitude())));
    //点击定位按钮 能够将地图的中心移动到定位点
    mListener.onLocationChanged(amapLocation);

    mListener是activate()方法中的参数。

    这样配置以后定位的效果基本上已经实现了。但还是有几个小问题:

    1. 定位效果

    2. 定位蓝点

    对于第一个问题,由于我们在上面设置了uiSettings.setMyLocationButtonEnabled(false)所以需要我们自己实现定位的效果,如果你设置成true(默认是true)就不需要自己实现了,具体的还是要根据你的业务需求。由于项目的需求我在代码中设置了只定位一次(后面可能会改,但目前测试来讲并没有遇到问题),所以我们把定位的数据保存了起来,在点击定位按钮后我们调用了mAMap.animateCamera(CameraUpdateFactory.changeLatLng(new LatLng(mLocatingModel.getLatitude(), mLocatingModel.getLongitude())));这个方法的作用是改变MapView的视角,也就是把当前地图显示的位置切换到需要的位置。

    关于定位蓝点的问题,官网提供了自定义的方法。主要是使用MyLocationStyle这个类,做好配置以后,调用mAMap.setMyLocationStyle(myLocationStyle)来实现效果。

    最终我们的效果是这样的:



    滑动获取中心点位置信息

    刚开始的时候我觉得没法实现,原因是高德地图的文档中并没有提供相关的接口,但是我搜了一下发现是有的(高德地图的文档写的是真差,很多解释和没解释一样,全靠自己写demo来一个一个了解)。

    实现的核心方法是setOnCameraChangeListener()方法,我们需要监听AMap.OnCameraChangeListener这个接口。这个接口需要重写两个方法:onCameraChange() , onCameraChangeFinish();第一个视角改变的回调,第二个是视角改变完成以后的回调。

    为了显示中间位置,需要绘制一个图标。这个图标的位置是屏幕的中心点(可能有偏差,如果与实际不符,需要把中间点的坐标设置成MapView的中间位置)

    设置好以后我们就可以在每次滑动以后在onCameraChangeFinish()中的参数CameraPosition获取到地图中心点的位置信息。但是返回的只有经纬度,可视区域指向的方向等信息,并没有一些位置的文字信息,所以我们还需要调用高德地图的搜索功能。

    高德地图的搜索功能是调用GeocodeSearch的getFromLocationAsyn()方法实现的,需要传入RegeocodeQuery实例,最后我们还要监听GeocodeSearch.OnGeocodeSearchListener接口,由于我们是通过经纬度来查询位置信息(逆地理编码),所以我们需要在onRegeocodeSearched()中获取位置信息。

    
    /**
         * 逆地理编码返回接口
         * @param regeocodeResult
         * @param i
         */
        @Override
        public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
            RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress();
            String formatAddress = regeocodeAddress.getFormatAddress();
    
     mAddressInfoTv.setText(formatAddress);
    
        }

    这样我们的第二个问题也已经解决了。

    动画效果

    通过观察饿了么的效果图我们可以发现:但地图在移动的过程中,中间点是需要上移的,同时下方的阴影变大,当松手以后返回原来的位置以及效果。刚开始的时候我是想通过高德的API来实现,我最终找到的接口是:setAMapGestureListener(),这个方法是监听地图手势识别,需要传入AMapGestureListener实例。


    刚开始我是在onFling() , onScroll() , onMapStable()中来控制动画的,当最终实现的效果总是有点瑕疵,于是我就放弃监听这个回调了。

    最终我是监听AMap.OnMapTouchListener来实现的。

    
    mAMap.setOnMapTouchListener(new AMap.OnMapTouchListener() {
                @Override
                public void onTouch(MotionEvent motionEvent) {
                    int action = motionEvent.getAction();
                    if(action == MotionEvent.ACTION_DOWN){
                        isUp = false;
                    }else if(action == MotionEvent.ACTION_MOVE){
                        isUp = false;
                        Log.i(TAG, "onTouch: is Move");
                    }else if(action == MotionEvent.ACTION_UP){
                        isUp = true;
                    }else if(action == MotionEvent.ACTION_CANCEL){
                        isUp = false;
                    }
                }
            });

    在这个里面我设置了一个标志位isUp来判断手指有没有抬起。然后在onCameraChange() , onCameraChangeFinish()方法中来控制动画。可能有的人会疑惑为什么要在这两个方法中呢?其实之前我已经讲过了,当地图的视角改变了以后是会回调这两个方法的。

    
    @Override
        public void onCameraChange(CameraPosition cameraPosition) {
            if (!isFirstMoveCamera) {
                startLocatingAnimator();
            }
        }

    在方法中我们添加了isFirstMoveCamera这个标志位,这个标志位的作用是判断是不是第一次视角改变(定位时视角会改变,也会回调这个方法,根据需求是不需要动画效果的)。

    
    @Override
        public void onCameraChangeFinish(CameraPosition cameraPosition) {
            if (isUp && !isFirstMoveCamera) {
                stopLocatingAnimator();
            }
            isFirstMoveCamera = false;
            LogUtil.i(TAG,"Latitude: " + cameraPosition.target.latitude + ", Longitude: " + cameraPosition.target.longitude);
        }

    之所以在onCameraChangeFinish()中停止动画,原因是就算手指已经抬起,当可能由于惯性的原因此时地图还在改变而这个时候动画是不能停止的.动画停止的条件应该是手指抬起并且地图停止了,所以我们在onCameraChangeFinish()方法中停止动画。

    具体的动画效果我就不细说了,但有一点需要提一下:为了避免动画的重复执行,我们需要在添加一个动画是否开始的标志位isAnimatorStarted来控制一下,否则的话当你移动地图后手指按住地图的时候,动画会反复的执行。所以你可以在onAnimationStart()中把isAnimatorStarted设置成true,在执行动画的时候做个判断就可以了,当然你可以有其他的方式来做处理,这只是我的实现方式而已。

    获取附近位置列表

    获取附近位置列表是使用PoiSearch来实现的。

    
    //构造PoiSearch.Query实例,第一个参数是keyword,第二个是Poi搜索类型,第三个是poi搜索区域
    PoiSearch.Query query = new PoiSearch.Query("","","");
    //设置每页最多返回多少条position
    query.setPageSize(PER_PAGE_SIZE);
    //设置查询页码
    query.setPageNum(currentPage);
    //生成poiSearch实例
    mPoiSearch = new PoiSearch(mContext,query);
    //周边检索POI
    mPoiSearch.setBound(new PoiSearch.SearchBound(new LatLonPoint(mPlaceModel.getLatitude(),mPlaceModel.getLongitude()),1000));
    //设置搜索监听器
    mPoiSearch.setOnPoiSearchListener(this);
    //发送请求
    mPoiSearch.searchPOIAsyn();

    最终我们在onPoiSearched()方法中获取信息

    
    public void onPoiSearched(PoiResult result, int rCode) {
         //解析result获取POI信息
    }

    总结

    最终我们实现的效果图是怎样的呢?


    展开全文
  • 自己动手做了个类似饿了么的外卖APP,包括用户登录注册,商品,商品详情,购物车,高德地图定位,周边美食搜索,以及设置 清除缓存等功能。
  • 本项目是一个类似饿了么的外卖APP demo,做的比较渣,只有UI没啥实际功能,包括用户登录注册,商品,商品详情,购物车,高德地图定位,周边美食搜索,以及设置 清除缓存等功能。例子大全里也有几个类似的项目,感兴趣...
  • 仿饿了么地图

    2017-06-05 15:39:44
    package cn.locationbox.jtan.mvp.map...import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.support.
  • 学习学习
  • 本项目是一个类似饿了么的外卖APP demo,做的比较渣,只有UI没啥实际功能,包括用户登录注册,商品,商品详情,购物车,高德地图定位,周边美食搜索,以及设置 清除缓存等功能。例子大全里也有几个类似的项目,感兴趣...
  • 现在的饿了么或美团的点餐界面类似这样的; 下面来看代码咯,注释还是比较详细的; public class MainActivity extends AppCompatActivity { private LinkBean linkBean; private LAdapter lAdapte...
  • 简介:城市选择、定位、搜索及右侧字母导航,类似美团 百度糯米 饿了么等 APP 选择城市功能 更多:作者 提 Bug  标签:   现在使用较多的类似美团、外卖等 APP 的城市选择界面,一行代码搞定,就是这么...
  • 正好周末外卖点得多,就仿一仿“饿了么”好了。先上图吧,这样的订单页面是不是很眼熟:右边的listview分好组以后,在左边的Tab页建立索引。可以直接导航,是不是很方便。关键在于右边滑动,左边也会跟着滑;而点击...
  • 仿饿了么购物车下单效果前一段由于新项目需要,开发一个类似饿了么购物车下单效果,电商类、外卖类、点餐类项目都可以用的上,废话不多说请看效果。效果图如下:主要的功能:就是左侧展示分类,右侧展示分类下商品的,...
  • PS:最近项目要求,希望在选择地址的时候能够仿ele来实现定位效果.因此就去做了一下.不过ele使用高德地图实现的,我是用百度地图实现的.没办法,公司说用百度那就用百度的吧.个人觉得高德应该更加的精准.但也无所谓反正...
  • Hermes是一套新颖巧妙易用的Android进程间通信IPC框架。这个框架使得你不用了解IPC机制就可以进行进程间通信,像调用本地函数一样调用其他进程的函数。用户可以轻而易举在本地进程创建其他进程类的对象,轻而易举在...
  • 仿饿了吗点餐,仿美团点餐,listView二级联动
  • Android Studio开发(五)使用百度地图API实现实时定位Android Studio开发(五)使用百度地图API实现实时定位一、任务需求二、安卓基于位置的服务(Location Based Service, LBS)1. LBS定义2. 三种常见的定位模式...
  • 仿百度外卖地图拖动定位 [注:本内容来自网络,在此分享仅为帮助有需要的网友,如果侵犯了您的权利,麻烦联系我,我会第一时间删除,谢谢您。]
  • 1、需求分析在饿了么首页中我们能看到这样的布局(如下图)。红框内是一个可以左右滑动的页面,每一个页面类似于九宫格,都有可供点击图标。对于这样的布局,我在网上找了很久都没有找到相关的名称,所以我这里暂时...
  • 设计跟我说:需要定位图标随着人走动或者旋转 而指向也旋转。。。。这。。。刚开始根本没想到。。。 于是乎,就去翻高德提供的api文档。。。。坑啊。。真坑啊。。。 api文档是这样的: 哇塞。。看到这个方法。...
1 2 3 4 5 ... 20
收藏数 753
精华内容 301
关键字:

饿了么地址定位android