Android仿美团地址选择

最近做了这个功能,分享一下,用的是百度地图api,和美团外卖的地址选择界面差不多,也就是可以搜索或者滑动地图展示地址列表给用户选择,看下效果图先。

 

          分享图片

 

文章重点

1、展示地图并定位到“我”的位置
2、滑动地图获取周边poi(逆地理编码)
3、搜索框输入查询poi(POI检索)


 

前言

这里先提一下,我们要选择的地址信息其实是POI(Point of Interest),即“兴趣点”。在地理信息系统中,一个POI可以是一栋房子、一个景点、一个邮筒或者一个公交站等。
百度地图SDK提供三种类型的POI检索:城市内检索、周边检索和区域检索(即矩形区域检索)。这里我就不详细介绍了,具体请查看百度地图开发文档(http://lbsyun.baidu.com/index.php?title=androidsdk)。

 

需求分析

我们要实现的功能主要包括两个操作:滑动地图和搜索框搜索。

  • 滑动地图:滑动地图主要是获取滑动后地图中心点坐标,然后获取poi信息,但是这里不能用上面提到的三种POI检索方式,POI检索都需要传入关键字(不能为空),而我们仅仅只是滑动地图,所以需要用另外一种方式:逆地理编码检索。使用逆地理编码检索时,可以通过检索结果ReverseGeoCodeResult类的getPoiList()方法获取传入位置周围的POI信息。
  • 搜索框搜索:这里就可以使用百度地图SDK提供的三种POI检索方式来进行检索,同时为了方便查看,还可以计算出每个POI和用户之间的距离。

 

具体实现

一、展示地图并定位到“我”的位置

1.展示地图

展示地图非常简单,首先需要调用SDKInitializer.initialize()方法来进行初始化操作,它接收一个全局的Context参数,记得初始化操作一定要在setContentView()方法前调用(可以到application中进行初始化),然后调用findViewById()方法获取MapView实例,最后记得要对MapView进行资源释放。

2.移动到我的位置

 2.1 获取我的位置
 首先要确定自己的位置,代码如下所示:

public class MainActivity extends AppCompatActivity implements OnGetPoiSearchResultListener {
    private MyLocationListener myListener = new MyLocationListener();
    public LocationClient mLocationClient = null;
    private LocationClientOption option = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initLocation();
    }

    /**
     * 初始化定位相关
     */
    private void initLocation() {
        // 声明LocationClient类
        mLocationClient = new LocationClient(getApplicationContext());
        mLocationClient.setLocOption(option);
        // 注册监听函数
        mLocationClient.registerLocationListener(myListener);
        mLocationClient.start();
    }

    /**
     * 监听当前位置
     */
    public class MyLocationListener extends BDAbstractLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
            //mapView 销毁后不在处理新接收的位置
            if (location == null || mMapView == null) {
                return;
            }
            if (location.getLocType() == BDLocation.TypeGpsLocation
                    || location.getLocType() == BDLocation.TypeNetWorkLocation) {
                Log.e(TAG, "当前“我”的位置:" + location.getAddrStr());
                navigateTo(location);
            }
        }
    }
}

 

可以看到,我们首先创建LocationClient实例,然后调用LocationClient的registerLocationListener()方法来注册一个定位监听器,当获取到位置信息的时候,就会回调这个定位监听器。开启定位很简单,只需要调用一下LocationClient的start()方法就可以了。
定位的结果会回调到监听器中,也就是MyLocationListener,在onReceiveLocation()方法中即可通过BDLocation对象获取相关位置详细信息。

注:定位属于危险权限,所以要动态权限申请,记得不要忘记了。

 

2.2 移动到我的位置
获取到定位后就需要将地图中心点移动到当前位置,代码如下:

    private boolean isFirstLocation = true;
    /**
     * 根据获取到的位置在地图上移动“我”的位置
     *
     * @param location
     */
    private void navigateTo(BDLocation location) {
        double longitude = location.getLongitude();
        double latitude = location.getLatitude();
        if (isFirstLocation) {
            currentLatLng = new LatLng(latitude, longitude);
            MapStatus.Builder builder = new MapStatus.Builder();
            MapStatus mapStatus = builder.target(currentLatLng).zoom(17.0f).build();
            mBaiduMap.animateMapStatus(MapStatusUpdateFactory
                    .newMapStatus(mapStatus));
            isFirstLocation = false;
        }
       //让“我”显示在地图上
        MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
        locationBuilder.latitude(location.getLatitude());
        locationBuilder.longitude(location.getLongitude());
        MyLocationData locationData = locationBuilder.build();
        mBaiduMap.setMyLocationData(locationData);
    }

这里首先将位置信息封装到LatLng对象中,然后调用MapStatusUpdateFactory
的newMapStatus()将LatLng对象传入,接着返回的MapStatusUpdate对象作为参数传入到BaiduMap的animateMapStatus()方法中。上述代码中还使用了一个变量来防止多次调用animateMapStatus()方法,因为移动地图只需要在程序第一次定位时调用一次。
同时为了显示一个当前设备的光标,可以利用MyLocationData.Builder类来实现,如代码所示,就可将“我”显示在地图上了。

二、滑动地图获取poi(逆地理编码)

1. 逆地理编码

前面已经提到了,我们这里滑动地图需要用到逆地理编码,也就是反向地理解析,逆地理编码就是将坐标转换为详细的地址信息,代码如下:

    //反向地理解析(含有poi列表)
    mGeoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(center));

    /**
     * 反向地理解析,结果中含有poi信息,用于刚进入地图和移动地图时使用
     */
    private void initGeoCoder() {
        mGeoCoder = GeoCoder.newInstance();
        mGeoCoder.setOnGetGeoCodeResultListener(new OnGetGeoCoderResultListener() {
            @Override
            public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {

            }

            @Override
            public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
                if (reverseGeoCodeResult.error.equals(SearchResult.ERRORNO.NO_ERROR)) {
                    //获取poi列表
                    if (reverseGeoCodeResult.getPoiList() != null) {
                        poiInfoListForGeoCoder = reverseGeoCodeResult.getPoiList();
                    }
                } else {
                    Toast.makeText(mContext, "该位置范围内无信息", Toast.LENGTH_SHORT);
                }
            }
        });
    }

这里我们首先获取一个GeoCoder实例,然后注册监听器,当有解析结果时便会回调到onGetReverseGeoCodeResult()方法中,而解析结果便有我们需要的poi列表。反向解析只需要调用GeoCoder的reverseGeoCode()方法并传入移动后地图的中心坐标点即可。

 

2. 监听地图滑动

百度地图提供了一个地图状态改变的监听器,当双击、滑动、缩放等操作时便进行回调,如下:

        mBaiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {

            /**
             * 手势操作地图,设置地图状态等操作导致地图状态开始改变。
             * @param mapStatus 地图状态改变开始时的地图状态
             */
            @Override
            public void onMapStatusChangeStart(MapStatus mapStatus) {
            }

            /** 因某种操作导致地图状态开始改变。
             * @param mapStatus 地图状态改变开始时的地图状态
             * @param i 取值有:
             * 1:用户手势触发导致的地图状态改变,比如双击、拖拽、滑动底图
             * 2:SDK导致的地图状态改变, 比如点击缩放控件、指南针图标
             * 3:开发者调用,导致的地图状态改变
             */
            @Override
            public void onMapStatusChangeStart(MapStatus mapStatus, int i) {
                Log.e(TAG, "地图状态改变开始时:" + i + "");
            }

            /**
             * 地图状态变化中
             * @param mapStatus 当前地图状态
             */
            @Override
            public void onMapStatusChange(MapStatus mapStatus) {
                LatLng latlng = mBaiduMap.getMapStatus().target;
                addMarker(latlng);
            }

            /**
             * 地图状态改变结束
             * @param mapStatus 地图状态改变结束后的地图状态
             */
            @Override
            public void onMapStatusChangeFinish(MapStatus mapStatus) {
                center = mBaiduMap.getMapStatus().target;
                //反向地理解析(含有poi列表)
                mGeoCoder.reverseGeoCode(new ReverseGeoCodeOption()
                        .location(center));
            }
        });

如上,当地图从滑动到结束会回调4个方法,我们需要用到的是:地图状态变化中和地图状态改变结束,也就是对应地图滑动中和滑动结束时。
滑动结束:当滑动结束时便调用反向地理解析出结果,这个上面已经说了。
滑动中:我们会发现当我们滑动地图时,地图上会有一个图标始终处于地图中心,这里就是利用地图状态变化中这个回调来添加一个marker,也就是在地图上添加一个图标,不过这个方法一次滑动可能会回调很多次,但是如果只在滑动结束后添加,用户体验不好,所以如果实在要考虑性能的话可以换个思路,将图标固定在屏幕上大致地图的中心,这样滑动地图看起来也一样的。
添加marker的方法就不详解了,源码里有,一看就懂了。

三、搜索框输入查询poi(POI检索)

搜索框搜索也就是使用关键字检索POI信息,这里不要和Sug检索弄混了,Sug(Suggestion POI search)检索是根据部分关键字检索出可能的完整关键字名称,即关键字匹配。而POI检索是根据关键字检索符合的POI具体信息。
上面说过POI检索有三种方式,这里结合我们的需求来说,使用城市内检索更加合适,也就是传入城市和关键字进行查询,当然你也可以使用另外两种检索方式,步骤如下:

1. 创建POI检索实例

mPoiSearch = PoiSearch.newInstance();

 

2. 创建POI检索监听器

OnGetPoiSearchResultListener listener = new OnGetPoiSearchResultListener() {
    /**
     * 获取POI搜索结果
     * @param poiResult Poi检索结果,包括城市检索,周边检索,区域检索
     */
    @Override
    public void onGetPoiResult(PoiResult poiResult) {
        if (poiResult.error == SearchResult.ERRORNO.NO_ERROR) {
            poiInfoListForSearch = poiResult.getAllPoi();//POI集合
        }

        if (poiResult.error == SearchResult.ERRORNO.AMBIGUOUS_KEYWORD) {
            // 当输入关键字在本市没有找到,但在其他城市找到时,返回包含该关键字信息的城市列表
            String strInfo = "在";
            for (CityInfo cityInfo : poiResult.getSuggestCityList()) {
                strInfo += cityInfo.city;
                strInfo += ",";
            }
            strInfo += "找到结果";
            Toast.makeText(mContext, strInfo, Toast.LENGTH_LONG).show();
        }
    }
    @Override
    public void onGetPoiDetailResult(PoiDetailSearchResult poiDetailSearchResult) {

    }
    @Override
    public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {

    }
    //废弃
    @Override
    public void onGetPoiDetailResult(PoiDetailResult poiDetailResult) {

    }
};

 

3. 设置检索监听器

mPoiSearch.setOnGetPoiSearchResultListener(listener);

 

4. 发起检索请求

mPoiSearch.searchInCity((new PoiCitySearchOption())
        .city(cityName)//城市名称
        .keyword(keyword)//必填
        .pageCapacity(pageSize)//每页条数
        .pageNum(loadIndex));//分页页码

 

5. 释放检索实例

mPoiSearch.destroy();


为了方便用户查看,我们可以在列表中展示每一个poi和用户之间的距离,利用DistanceUtil类的getDistance()方法传入两个点坐标的LatLng对象即可计算,如下:

double distance=DistanceUtil.getDistance(currentLatLng, latLng);

 

最后利用EditText的addTextChangedListener监听器监听输入框,如果值改变就进行检索。


 

至此,整个功能也就做完了,demo里没有做列表分页和动态权限申请,这个常用的你们就自个加咯,最后放下demo地址:
GitHub:https://github.com/yangxch/BaiDuMapSelectDemo

 

原创不易,转载请注明出处!

相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
公众号推荐
   一个历史类的公众号,欢迎关注
一两拨千金
2019香港 六 合 彩资料大全 启东市| 合作市| 马龙县| 张掖市| 棋牌| 澳门| 曲靖市| 岢岚县| 安泽县| 尉犁县| 舞钢市| 北海市| 遂川县| 射洪县| 卓资县| 得荣县| 雅安市| 邻水| 礼泉县| 天门市| 莱西市| 四平市| 高清| 务川| 布尔津县| 温宿县| 全椒县| 六枝特区| 上高县| 松溪县| 九龙县| 大荔县| 巴中市| 兴化市| 黄大仙区| 铁岭县| 梁河县| 淄博市| 广元市| 进贤县| 古蔺县| 大港区| 合山市| 景泰县| 平乡县| 涿州市| 交城县| 前郭尔| 巫山县| 若尔盖县| 敦煌市| 攀枝花市| 兴海县| 永兴县| 稻城县| 行唐县| 商城县| 璧山县| 阳泉市| 汉阴县| 蓬溪县| 红安县| 神农架林区| 黔东| 孝义市| 临洮县| 内黄县| 图片| 拉孜县| 乌审旗| 临沭县| 锦屏县| 西盟| 长沙县| 广元市| 宜阳县| 枞阳县| 乐东| 七台河市| 祁阳县| 睢宁县| 雅安市| 张家口市| 济南市| 庆城县| 微博| 肥城市| 邵东县| 云南省| 贵定县| 海口市| 永安市| 讷河市| 四平市| 府谷县| 台山市| 临城县| 林甸县| 高州市| 祥云县| 辽中县| 滁州市| 张家界市| 郯城县| 牡丹江市| 阜康市| 玉林市| 沂南县| 三江| 汾西县| 湖州市| 河北区| 靖宇县| 五华县| 南投市| 全椒县| 旌德县| 南澳县| 郓城县| 新干县| 天水市| 长寿区| 沁水县| 海丰县| 吉木萨尔县| 正安县| 长白| 临沧市| 崇州市| 尖扎县| 措勤县| 崇州市| 双牌县| 津市市| 罗江县| 密山市| 七台河市| 阿坝县| 武义县| 鲁甸县| 克山县| 武平县| 郓城县| 柯坪县| 海林市| 泰来县| 青冈县| 嘉鱼县| 靖安县| 南和县| 获嘉县| 肇东市| 榆林市| 西乌珠穆沁旗| 河源市| 乌什县| 淳安县| 许昌县| 台北县| 宽城| 资源县| 沅陵县| 太和县| 姜堰市| 乐亭县| 乳源| 五常市| 宜宾县| 广水市| 柞水县| 广西| 贡嘎县| 新和县| 沁源县| 左云县| 土默特右旗| 奉新县| 远安县| 济南市| 娱乐| 噶尔县| 西华县| 互助| 吉木萨尔县| 个旧市| 徐州市| 灌阳县| 同心县| 定襄县| 林口县| 平谷区| 金阳县| 沅陵县| 上蔡县| 定兴县| 黄大仙区| 威海市| 陆良县| 桦甸市| 从江县| 内丘县| 通城县| 静宁县| 呼伦贝尔市| 万盛区| 明光市| 淮北市| 晋城| 广元市| 绥中县| 岳池县| 孙吴县| 安龙县| 许昌市| 安西县| 南安市| 大足县| 阿克苏市| 喀什市| 东辽县| 建德市| 屏东市| 贡嘎县| 醴陵市| 巴南区| 乌拉特后旗| 岳池县| 承德县| 和政县| 卢湾区| 桑植县| 紫阳县| 西盟| 静宁县| 泾阳县| 乌苏市| 息烽县| 揭东县| 潮州市| 栾川县| 巴彦淖尔市| 渑池县| 介休市| 隆子县| 宁夏| 锦屏县| 柳河县| 汝阳县| 三亚市| 依兰县| 定陶县| 金溪县| 大田县| 黄浦区| 乌鲁木齐市| 银川市| 鲁山县| 北辰区| 淳化县| 多伦县| 广安市| 温泉县| 鄂托克旗| 陆丰市| 宾川县| 泸水县| 琼海市| 巨野县| 泰兴市| 呼伦贝尔市| 舒兰市| 贵州省| 新巴尔虎左旗| 嘉荫县| 吴堡县| 晋城| 龙口市| 姚安县| 聂荣县| 怀远县| 兰溪市| 龙山县| 永城市| 丽水市| 富阳市| 吉木乃县| 神农架林区| 邛崃市| 满城县| 中山市| 南涧| 太原市| 瓦房店市| 临朐县| 革吉县| 彭州市| 晋中市| 吉木萨尔县| 深州市| 白玉县| 昂仁县| 明水县| 富平县| 鹤峰县| 嘉定区| 凯里市| 房产| 佳木斯市| 洪洞县| 黑河市| 来安县| 华容县| 大英县| 怀化市| 美姑县| 确山县| 昌乐县| 集贤县| 抚松县| 贞丰县| 锡林浩特市| 天祝| 墨竹工卡县| 桓台县| 三穗县| 峨眉山市| 太保市| 呼伦贝尔市| 古蔺县| 丰都县| 红原县| 包头市| 叶城县| 京山县| 桃园市| 济阳县| 灵武市| 泊头市| 株洲市| 古浪县| 繁昌县| 丽水市| 四川省| 蒙山县| 崇州市| 诸暨市| 江西省| 城步| 福鼎市| 怀安县| 兴山县| 高碑店市| 曲阜市| 龙胜| 富锦市| 株洲县| 兴宁市| 马尔康县| 贵州省| 江安县| 荣昌县| 利辛县| 枣强县| 博白县| 濉溪县| 崇义县| 神木县| 汾西县| 南宁市| 城市| 奉化市| 谢通门县| 三台县| 古丈县| 沙洋县| 邵阳市| 灌阳县| 南城县| 连平县| 安泽县| 山丹县| 建始县| 仙居县| 陆丰市| 玉环县| 遵义市| 南木林县| 安图县| 克什克腾旗| 荥阳市| 公安县| 大英县| 蓝山县| 柘城县| 明水县| 固始县| 公主岭市| 七台河市| 安顺市| 板桥市| 南安市| 贵阳市| 藁城市| 徐州市| 辛集市| 通州市| 神农架林区| 博湖县| 察雅县| 辽中县| 阿克陶县| 华坪县| 邻水| 巴中市| 东宁县| 富蕴县| 涡阳县| 凤凰县| 雷波县| 札达县| 汉源县| 肇庆市| 大埔县| 武宣县| 宜昌市| 民县| 托里县| 襄城县| 南涧| 方山县| 中方县| 乐亭县| 禹城市| 礼泉县| 和政县| 石河子市| 收藏| 吴桥县| 务川| 新和县| 塘沽区| 阳曲县| 汉川市| 和林格尔县| 抚松县| 廉江市| 游戏| 怀仁县| 南丰县| 郁南县| 黄骅市| 北宁市| 石狮市| 南汇区| 罗田县| 赣榆县| 四子王旗| 周宁县| 泊头市| 扶绥县| 莱州市| 宜兰市| 开江县| 七台河市| 滕州市| 乡城县| 西畴县| 武义县| 昆山市| 朝阳县| 曲麻莱县| 湾仔区| 吉隆县| 无为县| 中西区| 惠州市| 北安市| 凤冈县| 柏乡县| 南康市| 洛浦县| 东乡| 静海县| 敦化市| 平南县| 安新县| 朔州市| 奈曼旗| 和田县| 西吉县| 越西县| 东台市| 旬邑县| 兴国县| 江津市| 徐水县| 句容市| 宾川县| 景宁| 泊头市| 安徽省| 登封市| 嘉祥县| 辽阳县| 阿克苏市| 和政县| 汝城县| 赤壁市| 杭锦后旗| 五寨县| http://m.bo2020dies.fun http://www.gz1980suckc.fun http://wap.bo2020calendars.fun http://wap.yqo9j5rl8v.fun http://wap.gz1980coursec.fun http://m.bo2020shifts.fun http://www.yqo7j9rl9v.fun http://www.jvz0j1r6o.fun http://m.gz1980steelc.fun http://www.bo2020pushs.fun http://wap.bo2020sazples.fun http://m.yqo9j5rl6v.fun http://m.bo2020filters.fun http://wap.yqo3j5rl2v.fun http://m.gz1980copyc.fun