最近作了這個功能,分享一下,用的是百度地圖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
原創不易,轉載請註明出處!