Android仿美團地址選擇

最近做了這個功能,分享一下,用的是百度地圖api,和美團外賣的地址選擇界面差不多,也就是可以搜索或者滑動地圖展示地址列表給用戶選擇,看下效果圖先。


addressSelect.gif

文章重點

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地址:https://download.csdn.net/download/xch_yang/85594611

如果覺得有幫助,幫忙點個贊吧!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容