最近作了这个功能,分享一下,用的是百度地图api,和美团外卖的地址选择界面差很少,也就是能够搜索或者滑动地图展现地址列表给用户选择,看下效果图先。php
一、展现地图并定位到“我”的位置
二、滑动地图获取周边poi(逆地理编码)
三、搜索框输入查询poi(POI检索)android
前言git
这里先提一下,咱们要选择的地址信息实际上是POI(Point of Interest),即“兴趣点”。在地理信息系统中,一个POI能够是一栋房子、一个景点、一个邮筒或者一个公交站等。
百度地图SDK提供三种类型的POI检索:城市内检索、周边检索和区域检索(即矩形区域检索)。这里我就不详细介绍了,具体请查看百度地图开发文档(http://lbsyun.baidu.com/index.php?title=androidsdk)。github
咱们要实现的功能主要包括两个操做:滑动地图和搜索框搜索。api
1.展现地图app
展现地图很是简单,首先须要调用SDKInitializer.initialize()方法来进行初始化操做,它接收一个全局的Context参数,记得初始化操做必定要在setContentView()方法前调用(能够到application中进行初始化),而后调用findViewById()方法获取MapView实例,最后记得要对MapView进行资源释放。ide
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对象获取相关位置详细信息。ui
注:定位属于危险权限,因此要动态权限申请,记得不要忘记了。
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类来实现,如代码所示,就可将“我”显示在地图上了。
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信息,这里不要和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
原创不易,转载请注明出处!