百度地圖點聚合仿鏈家定位點多級聚合,且滑動、刷新加載定位點

四個月前公司新項目,要求首頁仿鏈家實現地圖定位點多級聚合,效果圖如下:

好吧,我們沒效果圖,暫時拿鏈家的效果圖代替一下:

效果大概是這個樣子,而我們要求比鏈家更深一級,當時看到這需求和效果,第一想法就是下載百度官方 demo,下載運行後發現很不適用,然後嘗試着修改官方源碼,結果從入門改到放棄......

然後,網上各種搜索,找了一天發現幾乎沒有這方面寫的比較詳細、實用的文章......

只能自己開闢思路寫了,後來終於發現把問題想的太複雜了,被自己帶到溝裏去了,看似點聚合,實則是多點位標記。功能比較全,代碼比較多,耐心看下去或許會有收穫。不廢話了,說一下解決方案:

一、先獲取定位點,爲了方便使用可以採用 sql 存儲,然後根據 parentId 劃分出定位點級別,這裏就不普及 sql 的用法了,不會的自行百度,這裏要注意判斷經緯度是否爲空:

//獲得行政區
getEqpAreas(result);
//清空數據庫
dbOperatorService.EmptyEQPAreas();
//添加數據庫
dbOperatorService.SaveEQPAreas(areasList);

二、添加百度地圖加載,很簡單,但是這裏要先有思路,根據自己需要展示幾層聚合,設置好地圖縮放的“級別段”,可能有些人會迷糊,這裏簡單普及一下;

1、百度地圖好像一共21級別,級別越大,地圖範圍越小。級別越小,地圖範圍越大。

1級 :10000公里     2級:5000公里     3級:2000公里      4級:1000公里    5級:500公里      6級:200公里       7級:100公里

8級 : 50公里       9級:25公里            10級:20公里         11級:10公里       12級:5公里       13級:2公里         14級:1公里

15級:500米         16級:200米            17級:100米           18級:50米          19級:20米         20級:10米           21級:5米

大概是這個樣子。

而在使用中前8級可以直接忽略,20、21級可以根據自己需求選擇,好了,介紹完地圖級別,就要說什麼叫縮放“級別段”了。

要實現效果圖中 “聚合點” 縮放樣式,只能根據地圖縮放等級來設置,先設置好地圖縮放最小、最大級別,然後縮放深度有幾層,就把地圖展示級別分爲幾個層次,這個層次就叫“級別段”。怎麼劃分就看個人需求了。

2、添加地圖展示,並設置最大、最小級別以及定位,這些就不科普了,不會的自行百度。

baiduMap = mapView.getMap();

//設置默認位置
LatLng latLng = new LatLng(30.280782,120.121143);
status = new MapStatus.Builder().target(latLng).zoom(11).build();

baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(status));

//是否顯示控制地圖等級縮小、放大按鈕
mapView.showZoomControls(false);

//打開定位圖層
baiduMap.setMyLocationEnabled(true);

//聲明 LocationClient 類
client = new LocationClient(getContext());

//配置定位 SDK 參數
location();

//註冊監聽函數
client.registerLocationListener(listener);
//開啓定位
client.start();
//圖片點擊事件,回到固定點
client.requestLocation();

//地圖縮放等級
baiduMap.setMaxAndMinZoomLevel(21, 9);

三、把得到的定位點按等級分別添加到集合中,並且在地圖上添加標記點,這裏只舉一層爲例;

private void setOneMarker() {
    cityAreaList = new ArrayList<>();
    
    // 從 Sql 中取出對應等級的定位點 
    cityAreaList = dbOperatorService.GetEQPAreasOneGrade();

    if (cityAreaList != null && cityAreaList.size() > 0) {
        for (int i = 0; i < cityAreaList.size(); i++) {
            EQCityArea cityArea = cityAreaList.get(i);
            addMarket(cityArea);
        }
        
    } else {
        Toast.makeText(getContext(), "暫無標記點", Toast.LENGTH_SHORT).show();
    }

}

// 標記 Market
private void addMarket(EQCityArea cityArea) {

    if (AppUtil.isNotEmpty(cityArea.getLatitude()) && AppUtil.isNotEmpty(cityArea.getLongitude())) {

        Double lat = Double.valueOf(cityArea.getLatitude()) / 1000000;
        Double lon = Double.valueOf(cityArea.getLongitude()) / 1000000;

        String content = cityArea.getName();

        LatLng latLng = new LatLng(lat, lon);

        // 定位點樣式根據自己需求設置
        View view = LayoutInflater.from(getContext()).inflate(R.layout.maptext_layout, null, false);
        TextView tv_adress = view.findViewById(R.id.tv_adress);
        TextView text = view.findViewById(R.id.text);
        text.setVisibility(View.GONE);
        tv_adress.setText(content);
        BitmapDescriptor descriptorOne = BitmapDescriptorFactory.fromView(view);

        MarkerOptions options = new MarkerOptions().icon(descriptorOne).position(latLng).zIndex(9)
                .animateType(MarkerOptions.MarkerAnimateType.grow);
        Marker markerOne = (Marker) baiduMap.addOverlay(options);
        markerOne.setVisible(false);

        markerOneList.add(markerOne);
        latLngOneList.add(latLng);
    }

第二層、第三層獲取、添加標記同理。

四、很重要、很關鍵!添加地圖狀態發生改變時監聽地圖縮放等級,

baiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {
    @Override
    public void onMapStatusChangeStart(MapStatus mapStatus) {
      
    }

    @Override
    public void onMapStatusChangeStart(MapStatus mapStatus, int i) {
    
    }

    @Override
    public void onMapStatusChange(MapStatus mapStatus) {
      
    }

    @Override
    public void onMapStatusChangeFinish(MapStatus mapStatus) {

    }

五、在 onMapStatusChangeFinish() 方法中獲取當前地圖縮放等級,並且獲取最後一級詳細點位;

1、獲取當前地圖縮放級別以及手機當前屏幕地圖中心點

// 獲得當前地圖等級

zoomSize = baiduMap.getMapStatus().zoom;

Log.e("Start", zoomSize + "");

// 獲取屏幕中心點座標

LatLng latLng = mapStatus.target;

Log.e("", latLng + "");

2、根據自己設定的最大地圖級別進行判斷,這裏根據個人需求,如果聚合最後一級和鏈家一樣要求滑動加載某個點的詳情,就添加是否大於縮放最大級別的判斷,如果只是縮放,只需要添加是否小於縮放最大級別的判斷就行了:

@Override
public void onMapStatusChangeFinish(MapStatus mapStatus) {

    // 獲得當前地圖等級
    zoomSize = baiduMap.getMapStatus().zoom;
    Log.e("Start", zoomSize + "");

    // 獲取屏幕中心點座標
    LatLng latLng = mapStatus.target;
    Log.e("", latLng + "");
    if (zoomSize > MAPSIZE_EIGHTEEN) {  //噹噹前地圖級別大於 18 時
        if (baiduMap.getProjection() != null) {

            //獲取經緯度區域內監控
            getMonitor(latLng);
            //獲取經緯度區域內社會資源
            getResourcesByLatLng(latLng);
        }
    } else if (zoomSize <= MAPSIZE_EIGHTEEN) { //噹噹前地圖級別小於 18 時

        // 如果詳細點點擊有氣泡加上這句去氣泡,如果沒有就不用寫這句
        baiduMap.hideInfoWindow();

        //監控點
        if (markerPoint != null && markerPoint.size() > 0) {
            for (int i = 0; i < markerPoint.size(); i++) {
                markerMoni = markerPoint.get(i);
                markerMoni.setVisible(false);
            }
        }

        //社會資源
        if (resourMarkerList!=null&&resourMarkerList.size()>0){
            for (int i = 0; i < resourMarkerList.size(); i++) {
                resourMaker = resourMarkerList.get(i);
                resourMaker.setVisible(false);
            }
        }


        if (view != null) {
            if (view.getVisibility() == View.VISIBLE) {
                view.setVisibility(View.GONE);
            }
        }

        // 爲了防止異常,把集合清空一下
        if (monitorList != null && monitorList.size() > 0) {
            monitorList.clear();
        }
        if (monitorSet != null && monitorSet.size() > 0) {
            monitorSet.clear();
        }
        if (markerPoint != null && markerPoint.size() > 0) {
            markerPoint.clear();
        }
        if (publicResourceList !=null && publicResourceList.size()>0){
            publicResourceList.clear();
        }

        if (resourceHashSet!=null&&resourceHashSet.size()>0){
            resourceHashSet.clear();
        }

    }

    if (baiduMap.getProjection()!=null){
        //獲取屏幕中心點
        updateMapState();
    }
}

六、在 updateMapState() 方法中通過獲取當前屏幕地圖範圍最大值展示標記點,防止同時標記過多出現屏幕卡頓,並通過判斷地圖級別段對 Marker 的展示進行控制:

1、獲取屏幕地圖範圍

WindowManager manager = getActivity().getWindowManager();

DisplayMetrics metrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(metrics);

//獲取屏幕的寬和高
int width = metrics.widthPixels;
int height = metrics.heightPixels;

Point point = new Point();
point.x = 0;
point.y = 0;
final LatLng llL = baiduMap.getProjection().fromScreenLocation(point);//左上角經緯度
Log.e("", llL + "");

Point ptR = new Point();
ptR.x = width;
ptR.y = height;
final LatLng llR = baiduMap.getProjection().fromScreenLocation(ptR);//右下角經緯度
Log.e("", llR + "");

2,先判斷每一層的定位點是否在屏幕座標範圍內,在內就展示,不在就隱藏。另外根據縮放級別選擇展示哪一層,隱藏哪一層,這裏以第一層和第二層舉個例子:

先把變量給出來,不然不太好懂:

//第一層聚合
Marker markerOne;
private List<EQCityArea> cityAreaList;
private List<Marker> markerOneList = new ArrayList<>();
private List<LatLng> latLngOneList = new ArrayList<>();
//第二層
Marker markerTwo;
private List<EQStreet> streetList;
private List<Marker> markerTwoList = new ArrayList<>();
private List<LatLng> latLngTwoList = new ArrayList<>();

開始進行判斷:

if (zoomSize <= MAPSIZE_TWELVE) { //  小於 12 級別

    if (latLngOneList != null && latLngOneList.size() > 0) {
        for (int i = 0; i < latLngOneList.size(); i++) {
            LatLng latLng = latLngOneList.get(i);
            Double lat = latLng.latitude;
            Double lon = latLng.longitude;

            if (llR.latitude < lat && lat < llL.latitude && llL.longitude < lon && lon < llR.longitude) {
                markerOne = markerOneList.get(i);
                markerOne.setVisible(true);
            } else {
                markerOne = markerOneList.get(i);
                markerOne.setVisible(false);
            }
        }

        if (markerTwoList != null && markerTwoList.size() > 0) {
            for (int i = 0; i < markerTwoList.size(); i++) {
                markerTwo = markerTwoList.get(i);
                markerTwo.setVisible(false);
            }
        }

        if (markerThreeList != null && markerThreeList.size() > 0) {
            for (int i = 0; i < markerThreeList.size(); i++) {
                markerThree = markerThreeList.get(i);
                markerThree.setVisible(false);
            }
        }

    } else { //如果第一層沒數據

        if (latLngTwoList != null && latLngTwoList.size() > 0) {
            for (int i = 0; i < latLngTwoList.size(); i++) {
                LatLng latLng = latLngTwoList.get(i);
                Double lat = latLng.latitude;
                Double lon = latLng.longitude;

                if (llR.latitude < lat && lat < llL.latitude && llL.longitude < lon && lon < llR.longitude) {
                    markerTwo = markerTwoList.get(i);
                    markerTwo.setVisible(true);
                } else {
                    markerTwo = markerTwoList.get(i);
                    markerTwo.setVisible(false);
                }
            }
        }

        if (markerOneList != null && markerOneList.size() > 0) {
            for (int i = 0; i < markerOneList.size(); i++) {
                markerOne = markerOneList.get(i);
                markerOne.setVisible(false);
            }
        }

        if (markerThreeList != null && markerThreeList.size() > 0) {
            for (int i = 0; i < markerThreeList.size(); i++) {
                markerThree = markerThreeList.get(i);
                markerThree.setVisible(false);
            }
        }
    }
} else if (zoomSize > MAPSIZE_TWELVE && zoomSize <= MAPSIZE_FIFTEEN) {// 12 到 15

    if (latLngOneList != null && latLngOneList.size() > 0) {

        if (latLngTwoList != null && latLngTwoList.size() > 0) {
            for (int i = 0; i < latLngTwoList.size(); i++) {
                LatLng latLng = latLngTwoList.get(i);
                Double lat = latLng.latitude;
                Double lon = latLng.longitude;

                if (llR.latitude < lat && lat < llL.latitude && llL.longitude < lon && lon < llR.longitude) {
                    markerTwo = markerTwoList.get(i);
                    markerTwo.setVisible(true);
                } else {
                    markerTwo = markerTwoList.get(i);
                    markerTwo.setVisible(false);
                }
            }
        }

        if (markerOneList != null && markerOneList.size() > 0) {
            for (int i = 0; i < markerOneList.size(); i++) {
                markerOne = markerOneList.get(i);
                markerOne.setVisible(false);
            }
        }

        if (markerThreeList != null && markerThreeList.size() > 0) {
            for (int i = 0; i < markerThreeList.size(); i++) {
                markerThree = markerThreeList.get(i);
                markerThree.setVisible(false);
            }
        }
    } 

七、寫到這裏通過手指縮放屏幕,聚合功能已經可以展示出來了,但是並不完美,作爲程序狗,我們要省事,所以我們還需要再添加一個功能,當用戶點擊最外層聚合點時間直接進入下一層,這裏就要用到 Marker 點擊事件了,這裏如果你只有聚合功能只需要要控制增加地圖層次級別就好了:

baiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
    @Override
    public boolean onMarkerClick(Marker marker) {

        if (zoomSize < 18) {

            zoomSize += 3; // 3 是我的地圖級別段長度,根據自己設置的填寫 
            status = new MapStatus.Builder().zoom(zoomSize).target(marker.getPosition()).build();
            baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(status));

        }
    }
 }

當然大多數功能並不會這麼簡單,往往需求還會讓點擊定位點彈出氣泡:

if (zoomSize < 18) {

    zoomSize += 3;
    status = new MapStatus.Builder().zoom(zoomSize).target(marker.getPosition()).build();
    baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(status));

} else {

    // 填寫自己點擊氣泡要展示的 View

 // 創建 InfoWindow , 傳入 View 、 Latlng 、Y 軸偏移量
    InfoWindow window = new InfoWindow(view, marker.getPosition(), -80);
    baiduMap.showInfoWindow(window);

}

八,最後再來講解一下,怎麼仿鏈家滑動屏幕加載新數據,並且不重複加載老數據,這裏就要用到 Set 集合:

1、先是在設置實體類是添加 

public boolean equals(Object o) {}
public int hashCode() {}

2、先把獲取到的定位點添加到 ArrayList 集合中,再遍歷集合中的定位點是不是在屏幕範圍內,在就添加到 Set 集合中,然後根據 Set 集合添加 Marker:

// 把獲取到的點添加到 ArrayList 集合中
resourceList.add(resource);
// 判斷點是否在屏幕內

WindowManager manager = getActivity().getWindowManager();

DisplayMetrics metrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(metrics);

//獲取屏幕的寬和高
int width = metrics.widthPixels;
int height = metrics.heightPixels;

Point point = new Point();
point.x = 0;
point.y = 0;
llL = baiduMap.getProjection().fromScreenLocation(point);//左上角經緯度
Log.e("", llL + "");

Point ptR = new Point();
ptR.x = width;
ptR.y = height;
llR = baiduMap.getProjection().fromScreenLocation(ptR);//右下角經緯度


Set<MoniPoint> resourceSet = new HashSet<>();

if (resourceList!=null&&resourceList.size()>0){
    for (int i=0;i<resourceList.size();i++){
        Social resource = resourceList.get(i);
        Double lat = Double.valueOf(resource.getLatitude())/1000000;
        Double lon = Double.valueOf(resource.getLongitude())/1000000;

        if (llR.latitude < lat && lat < llL.latitude && llL.longitude < lon && lon < llR.longitude) {

            boolean isPoint = resourceHashSet.contains(resource);
            if (isPoint) {

            } else{
                resourceHashSet.add(resource);
                Log.e("",resourceHashSet.size()+"");

                // 添加繪製 Marker 的方法
                drawResource(resource);
            }
        }
    }
}
private void drawResource(Social resource) {

    publicResourceList.add(resource);

    Double lat = Double.valueOf(resource.getLatitude())/1000000;
    Double lon = Double.valueOf(resource.getLongitude())/1000000;

    LatLng latLng = new LatLng(lat, lon);

    BitmapDescriptor descriptor = BitmapDescriptorFactory.fromResource(R.drawable.icon_shehuizy_map);

    MarkerOptions options = new MarkerOptions().icon(descriptor).position(latLng).zIndex(9)
            .animateType(MarkerOptions.MarkerAnimateType.grow);
    Marker marker = (Marker) baiduMap.addOverlay(options);
    marker.setVisible(true);
    resourMarkerList.add(marker);

    Log.e("",resourMarkerList.size()+"");
}

九、打完收工,第一次寫這麼長的博客,雖然寫的比較亂,但真的用心了!有什麼不懂的可以問我,希望可以幫助那些找不到解決方案的夥伴。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章