1. 程式人生 > >關於百度地圖(四):點聚合及只顯示螢幕範圍內的marker

關於百度地圖(四):點聚合及只顯示螢幕範圍內的marker

繼續來總結百度地圖。
前面我們將marker傳入了地圖中去,但是,隨著專案的深入,需要在地圖上顯示的marker也越來越多,這個時候就必然會出現一個問題:卡頓!
那麼百度地圖也為我們提供了“點聚合”來解決這個問題,不過我們還可以設定地圖只顯示螢幕範圍內的marker來解決這個問題。由於我專案中的marker實在太多了,全部展現出來有幾千個marker,於是兩個一起都用了,結合在一起,效果還是不錯的,但是在marker比較集中的時候,在縮放時由聚合到marker的計算過程還是會有一點卡頓,因為整個螢幕同時有上百個marker從聚合裡散落出來。不知道有沒有大神提出好的方法,歡迎留言!
那麼現在我們開始第一部分——點聚合:
點聚合這一部分百度是開源了,所以下載的sdk裡是沒有這部分程式碼,我們可以去下載demo,從demo裡把clusterutil資料夾全部拷到自己的工作目錄下:
這裡寫圖片描述


然後呢,我們拿到點聚合的管理類:

/**
 * 點聚合功能
 */
public ClusterManager<MyItem> mClusterManager;
// 定義點聚合管理類ClusterManager
   mClusterManager = new ClusterManager<>(context, mBaiduMap,this);
// 設定地圖狀態監聽,當地圖狀態發生改變時,進行點聚合運算
   mBaiduMap.setOnMapStatusChangeListener(mClusterManager);
   mBaiduMap.setOnMarkerClickListener(mClusterManager);//點聚合marker的點選事件

然後是MyItem

/**
     * 每個Marker點,包含Marker點座標以及圖示
     */
    public class MyItem<T extends MapBean> implements ClusterItem<T> {
        private LatLng mPosition;
        private T info;

        @Override
        public LatLng getPosition() {
            return mPosition;
        }

        public
MyItem(LatLng latLng, T info) { mPosition = latLng; this.info = info; } //marker的佈局View:這裡我依舊用上一篇中marker的佈局 @Override public BitmapDescriptor getBitmapDescriptor() { return getView(info); } //每一個點的資訊 @Override public T getMapBean() { return info; } }

然後是getView()方法:

 private <T extends MapBean> BitmapDescriptor getView(T info) {
        ViewHolder viewHolder;
        if (info instanceof DBuildingInfo) {
            //buildingInfo
            if (view == null) {
                view = View.inflate(context, R.layout.item_baidumap_marker, null);
                viewHolder = new ViewHolder();
                viewHolder.tvDbiName = (TextView) view.findViewById(tv_dbiName);
                viewHolder.tvName = (TextView) view.findViewById(R.id.tv_duiName);
                viewHolder.imgMarker = (ImageView) view.findViewById(R.id.img_Marker);
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }
            ...
            //這裡做一些賦值等操作
            ...
            }
            return  BitmapDescriptorFactory.fromView(viewOut);
        }else {
            return  null;
        }
    }

從這裡可以看到,使用點聚合,我不需要再去寫新增單個marker的操作,這些操作在開源的程式碼中,已經為我們做好了,我們只需要把需要新增的marker的經緯度及資訊給它就可以了,所以我們查詢到我們要新增marker的資料後,直接呼叫這個方法:

//單位建築覆蓋物標誌
    public <T extends MapBean> void addMarker(List<T> list) {

        if (list != null && list.size() > 0) {
            List<MyItem> items = new ArrayList<>();
            for (T info : list) {
                String[] ss = info.getMapPosition().split(",");
                LatLng latLng = new LatLng(new Double(ss[1]), new Double(ss[0]));
                items.add(new MyItem(latLng, info));
            }
            mClusterManager.addItems(items);
        }
    }

這樣基本就完成了,裡面的新增marker及點聚合的運算,百度已經做好了。
2.顯示螢幕範圍內的點:
首先,我們要去獲取螢幕的兩個對角點,然後再得到他們的經緯度,這樣我們就能得到一個矩形地圖範圍了

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
int width = outMetrics.widthPixels;
int height = outMetrics.heightPixels;
Point ptL = new Point();
ptL.x = 0;
ptL.y = 0;
final LatLng llL = mBaiduMap.getProjection().fromScreenLocation(ptL);//左上角經緯度
Point ptR = new Point();
ptR.x = width;
ptR.y = height;
final LatLng llR = mBaiduMap.getProjection().fromScreenLocation(ptR);//右下角經緯度

然後只要去判斷該經緯度座標是否在這個螢幕內:

 infos =new ArrayList<>();
 String[] latlngs = info.getDbiMapposition().split(",");
 double lat = new Double(latlngs[1]);
 double lng = new Double(latlngs[0]);
 if (llr.latitude < lat && lat < ll.latitude && ll.longitude < lng && lng < llr.longitude) {
        infos.add(info);
    }

這樣,我們就可以呼叫addmarker()方法了:

 mClusterManager.clearItems();//清理所有的items
 addMarker(buildingInfos);

然後,螢幕移動了之後,我們需要重新去得到移動後的螢幕內的地圖矩形座標及當前範圍內的marker,然後也重新進行點聚合運算,在點聚合類的管理類ClursterManger中,對地圖狀態監聽的回撥裡,我們只需要在finish時候修改:

    @Override
    public void onMapStatusChangeFinish(MapStatus mapStatus) {
        //移動完成後
        mapUtil.updateMapState(mMap);
        cluster();//重新進行點聚合運算
    }

最後,當我們修改了查詢條件時,重新查詢出來的資料,這個時候,我的地圖是沒有移動,沒有縮放的,也就不會有監聽回撥,所有在我們查詢到了資料之後再去執行一邊點聚合的運算,呼叫:

cluster();

MapBean:
用當前marker資料的bean來實現MapBena

public interface MapBean {
    String getMapPosition();//經緯度
    ...
}

然後在點選事件中就可以直接這樣:
點聚合的點選事件

 mapUtil.mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<BaiduMapUtil.MyItem>() {
            @Override
            public boolean onClusterClick(final Cluster<BaiduMapUtil.MyItem> cluster) {
 for (BaiduMapUtil.MyItem item : cluster.getItems()) {//去除myitems
   if (item.getMapBean() instanceof XXXXXInfo) { //判斷取出的這個bean是否是我marker資料的bean
     ...自己的操作...
        }
      }    
   return false;
   }
  });

當item的點選事件

  @Override
    public boolean onClusterItemClick(ClusterItem item) {
    if (item.getMapBean() instanceof DBuildingInfo) {
    ...當前自己的操作...
    }
   return false;
}

由於現在marker是原始碼中加的,那麼有些關於marker的設定,我們可以重新到DefaultClusterRenderer類中的perform(MarkerModifier markerModifier)中根據我們的需求進行設定:

 private void perform(MarkerModifier markerModifier) {
            // Don't show small clusters. Render the markers inside, instead.
            ......
            onBeforeClusterItemRendered(item, markerOptions);
            marker = mClusterManager.getMarkerCollection().addMarker(markerOptions);
            //新增下面
            marker.setZIndex(14);//設定marker在級別
            marker.setAnchor(0.5f,0.5f);//設定marker的中心點
            //新增上面
            markerWithPosition = new MarkerWithPosition(marker);
            ......
       }

以上!
這個方法在marker比較密集的情況下,放大後,在進行聚合運算的時候還是會有點卡頓,哪位老司機有更好的方法?