基于高德地圖實現Marker聚合效果

??最近,項目有了新的需求,要求地圖上的標簽點實現聚合效果,單純的marker已經無法滿足大量數據展示的情況,聚合效果成為大勢所趨。

??目前,網上提供基于高德marker聚合的思路大致差不多,處于雛形階段。高德官方也提供了關于聚合的解決方案,對于緩存和加載效率都做了一些處理,為我們后面的定制奠定了基礎,本文就在高德官方提供的方案基礎上做一些定制化。筆者經過思考后,還是覺得將篇幅分為上下兩部分,前篇主要涉及聚合的基本使用以及針對定制過程中出現的坑進行填補(如多種聚合標簽的沖突,聚合標簽與普通標簽的沖突問題等),下篇則講述如何定制化marker并加載網絡圖片(為了方便描述,marker聚合在下文都稱之為聚合標簽)。看完兩篇文章,大家還可以嘗試將聚合和網絡圖片樣式的marker結合起來,達到更加炫酷的效果。
話不多說,先來看看最終的效果吧:

效果圖

??先來放一下官網提供的聚合點的demo:https://github.com/amap-demo/android-cluster-marker
,如果只是實現一種聚合點,可以復制官方的demo代碼,稍微改改就可以用了。如果你們產品有其他需求,比如多種聚合標簽點,或者實現普通標簽和聚合標簽的混排使用,那么可以參考下本文提供的改進思路。

  • 發現問題:

首先,我們發現高德官方提供的demo存在的問題:

  1. 當存在多種類型聚合標簽點時,聚合點的點擊事件沖突問題.

這個問題是什么意思呢?舉個例子:當地圖上我們先后初始化兩種聚合標簽點A和B的時候,不管點擊A還是B類型的聚合點,都會只響應B類的點擊事件,即先聲明的聚合標簽的點擊事件會被后聲明的聚合標簽的點擊事件"搶占".

  1. 當地圖上存在普通標簽(Marker)和聚合標簽(Cluster)時,同樣會產生點擊事件的沖突問題.

這個意思就是,點擊聚合標簽仍然會響應普通標簽的點擊事件.

  • 分析問題:

這兩個問題其實歸根結底是一個問題,都是因為marker的點擊事件被搶占,從官方的提供的ClusterOverlay類中代碼我們就可以看出來:

......
amap.setOnCameraChangeListener(this);
amap.setOnMarkerClickListener(this);
......
 //點擊事件
    @Override
    public boolean onMarkerClick(Marker arg0) {
        if (mClusterClickListener == null) {
            return true;
        }
       Cluster cluster= (Cluster) arg0.getObject();
        if(cluster!=null){
            mClusterClickListener.onClick(arg0,cluster.getClusterItems());
            return true;
        }
        return false;
    }

??從以上代碼我們可以看出來,聚合處理類ClusterOverlay實現了AMap.OnMarkerClickListener接口,而當我們使用兩個或者兩個以上樣式的聚合標簽時,我們就會發現明明是兩種類型的聚合標簽,但是卻觸發相同的點擊事件。這樣顯然是不符合我們預期需求的,那么我們該如何讓不同樣式的聚合標簽A,B,C...各司其職呢?你肯定會說,廢話,不然你現在在講啥!咳咳,請允許我靜靜~

  • 解決問題:

??顯然,如果我們想讓各種聚合標簽互不干擾,需要集中管理它們的點擊事件,即OnMarkerClickListener。假設我們寫了兩個不同的聚合標簽類ClusterOverlayA和ClusterOverlayB,我們可以將兩個類中OnMarkerClick()方法中的邏輯拿出來,單獨封裝成一個方法,然后放在我們Activity的onMarkerClick()中執行.這里我貼一下修改后的ClusterOverlay類,前方高能,一大波代碼來襲:

ClusterOverlay:

/**
 * Created by yiyi.qi on 16/10/10.
 * 整體設計采用了兩個線程,一個線程用于計算組織聚合數據,一個線程負責處理Marker相關操作
 */
public class ClusterOverlay {
    private AMap mAMap;
    private Context mContext;
    private List<ClusterItem> mClusterItems;
    private List<Cluster> mClusters;
    private int mClusterSize;
    private ClusterClickListener mClusterClickListener;
    private ClusterRender mClusterRender;
    private ClusterRenderB bClusterRender;
    private AMap.OnMarkerClickListener markerClickListener=new AMap.OnMarkerClickListener() {
        @Override
        public boolean onMarkerClick(Marker arg0) {
            if (mClusterClickListener == null) {
                return true;
            }
            Cluster cluster = (Cluster) arg0.getObject();
            //將marker的位置移動到地圖中心
            LatLng latLng=new LatLng(arg0.getPosition().latitude,arg0.getPosition().longitude);
            mAMap.moveCamera(CameraUpdateFactory.changeLatLng(latLng));
            if (cluster != null) {
                arg0.showInfoWindow();
                Log.e("timeory", "普通標簽infowindow出現了嗎?  " +  arg0.isInfoWindowShown()+ "   "+ arg0.isInfoWindowEnable(), null);
                mClusterClickListener.onClick(arg0, cluster.getClusterItems());
                return true;
            }

            return false;
        }
    };




    private List<Marker> mAddMarkers = new ArrayList<Marker>();
    private double mClusterDistance;
    private LruCache<Integer, BitmapDescriptor> mLruCache;
    private HandlerThread mMarkerHandlerThread = new HandlerThread("addMarker");
    private HandlerThread mSignClusterThread = new HandlerThread("calculateCluster");
    private Handler mMarkerhandler;
    private Handler mSignClusterHandler;
    private float mPXInMeters;
    private boolean mIsCanceled = false;
    private MarkerOptions markerOptions;
    private Cluster mcluster;
    private LatLng latlng1;

    /**
     * 構造函數
     *
     * @param amap
     * @param clusterSize 聚合范圍的大小(指點像素單位距離內的點會聚合到一個點顯示)
     * @param context
     */
    public ClusterOverlay(AMap amap, int clusterSize, Context context) {
        this(amap, null, clusterSize, context);


    }

    /**
     * 構造函數,批量添加聚合元素時,調用此構造函數
     *  默認最多會緩存80張圖片作為聚合顯示元素圖片,根據自己顯示需求和app使用內存情況,可以修改數量
     * @param amap
     * @param clusterItems 聚合元素
     * @param clusterSize
     * @param context
     */
    public ClusterOverlay(AMap amap, List<ClusterItem> clusterItems, int clusterSize, Context context) {
        mLruCache = new LruCache<Integer, BitmapDescriptor>(80) {
            protected void entryRemoved(boolean evicted, Integer key, BitmapDescriptor oldValue, BitmapDescriptor newValue) {
                oldValue.getBitmap().recycle();
            }
        };
        if (clusterItems != null) {
            mClusterItems = clusterItems;
        } else {
            mClusterItems = new ArrayList<ClusterItem>();
        }
        mContext = context;
        mClusters = new ArrayList<Cluster>();
        this.mAMap = amap;
        mClusterSize = clusterSize;
        mPXInMeters = mAMap.getScalePerPixel();
        mClusterDistance = mPXInMeters * mClusterSize;
        initThreadHandler();
        assignClusters();
    }

    /**
     * by moos on 2017/11/13
     * function:聚合刷新,在activity的onCameraChangeFinish()方法中
     */
    public void updateClusters(){
        mPXInMeters = mAMap.getScalePerPixel();
        mClusterDistance = mPXInMeters * mClusterSize;
        assignClusters();
    }

    /**
     * by moos on 2017/11/13
     * func:通過activity中的onMarkerClick()響應聚合點的點擊事件
     * @param arg0
     */
    public void respondClusterClickEvent(Marker arg0){

        Cluster cluster = (Cluster) arg0.getObject();
        //將marker的位置移動到地圖中心
        LatLng latLng=new LatLng(arg0.getPosition().latitude,arg0.getPosition().longitude);
        mAMap.moveCamera(CameraUpdateFactory.changeLatLng(latLng));
        if (cluster != null) {
            arg0.showInfoWindow();
            Log.e("timeory", "普通標簽infowindow出現了嗎?  " +  arg0.isInfoWindowShown()+ "   "+ arg0.isInfoWindowEnable(), null);
            mClusterClickListener.onClick(arg0, cluster.getClusterItems());
        }

    }

    /**
     * 設置聚合點的點擊事件
     *
     * @param clusterClickListener
     */
    public void setOnClusterClickListener(ClusterClickListener clusterClickListener) {
        mClusterClickListener = clusterClickListener;
    }

    /**
     * 添加一個聚合點
     *
     * @param item
     */
    public void addClusterItem(ClusterItem item) {
        Message message = Message.obtain();
        message.what = SignClusterHandler.CALCULATE_SINGLE_CLUSTER;
        message.obj = item;
        mSignClusterHandler.sendMessage(message);
    }

    /**
     * 共外部調用的接口,設置聚合元素的渲染樣式,不設置則默認為氣泡加數字形式進行渲染
     *
     * @param render
     */
    public void setClusterRenderer(ClusterRender render) {
        mClusterRender = render;
    }
    /**
     * 設置聚合元素的渲染樣式,不設置則默認為氣泡加數字形式進行渲染
     *
     * @param render
     */
    public void setBClusterRenderer(ClusterRenderB render) {
        bClusterRender = render;
    }

    public void onDestroy() {
        mIsCanceled = true;
        mSignClusterHandler.removeCallbacksAndMessages(null);
        mMarkerhandler.removeCallbacksAndMessages(null);
        mSignClusterThread.quit();
        mMarkerHandlerThread.quit();
        for (Marker marker : mAddMarkers) {
            marker.remove();

        }
        if(markerClickListener!=null){
            markerClickListener = null;
        }
        mAddMarkers.clear();
        mLruCache.evictAll();
    }

    /**
     * 清除maker點擊事件,防止搶占
     */
    public void cleanListener(){
        if(markerClickListener!=null){
            markerClickListener = null;
        }
    }

    //初始化Handler
    private void initThreadHandler() {
        mMarkerHandlerThread.start();
        mSignClusterThread.start();
        mMarkerhandler = new MarkerHandler(mMarkerHandlerThread.getLooper());
        mSignClusterHandler = new SignClusterHandler(mSignClusterThread.getLooper());
    }



    /**
     * 將聚合元素添加至地圖上
     */
    private void addClusterToMap(List<Cluster> clusters) {

        ArrayList<Marker> removeMarkers = new ArrayList<>();
        removeMarkers.addAll(mAddMarkers);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        MyAnimationListener myAnimationListener = new MyAnimationListener(removeMarkers);
        for (Marker marker : removeMarkers) {
            marker.setAnimation(alphaAnimation);
            marker.setAnimationListener(myAnimationListener);
            marker.startAnimation();
        }

        for (Cluster cluster : clusters) {
            addSingleClusterToMap(cluster);
        }
    }

    private AlphaAnimation mADDAnimation = new AlphaAnimation(0, 1);

    /**
     * 將單個聚合元素添加至地圖顯示
     *
     * @param cluster
     */
    private void addSingleClusterToMap(Cluster cluster) {
        latlng1 = cluster.getCenterLatLng();
        markerOptions = new MarkerOptions();
        markerOptions
                .anchor(0.5f, 0.5f)
                .icon(getBitmapDes(cluster.getClusterCount()))
                .position(latlng1);
        Marker marker = mAMap.addMarker(markerOptions);
        marker.setAnimation(mADDAnimation);
        marker.setObject(cluster);
        marker.startAnimation();

//        marker.setTitle("你好啊");
//        marker.setSnippet("真的不好");
        cluster.setMarker(marker);
        mcluster = cluster;
        mAddMarkers.add(marker);

    }

    public void newIcon() {
        markerOptions
                .anchor(0.5f, 0.5f)
                .icon(getBitmapDes(mcluster.getClusterCount()))
                .position(latlng1);
    }


    private void calculateClusters() {
        mIsCanceled = false;
        mClusters.clear();
        //地圖獲取轉換器, 獲取可視區域, 獲取可視區域的四個點形成的經緯度范圍, 得到一個經緯度范圍
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        for (ClusterItem clusterItem : mClusterItems) {
            if (mIsCanceled) {
                return;
            }
            LatLng latlng = clusterItem.getPosition();//聚合元素的地理位置
            if (visibleBounds.contains(latlng)) {//如果這個范圍包含這個聚合元素的地理位置
                Cluster cluster = getCluster(latlng, mClusters);//根據這個位置和聚合物的集合, 獲得一個聚合器
                if (cluster != null) {
                    cluster.addClusterItem(clusterItem);//往聚合器中添加聚合點
                } else {
                    cluster = new Cluster(latlng);
                    mClusters.add(cluster);
                    cluster.addClusterItem(clusterItem);
                }

            }
        }

        //復制一份數據,規避同步
        List<Cluster> clusters = new ArrayList<Cluster>();
        clusters.addAll(mClusters);
        Message message = Message.obtain();
        message.what = MarkerHandler.ADD_CLUSTER_LIST;
        message.obj = clusters;
        if (mIsCanceled) {
            return;
        }
        mMarkerhandler.sendMessage(message);
    }

    /**
     * 對點進行聚合
     */
    private void assignClusters() {
        mIsCanceled = true;
        mSignClusterHandler.removeMessages(SignClusterHandler.CALCULATE_CLUSTER);
        mSignClusterHandler.sendEmptyMessage(SignClusterHandler.CALCULATE_CLUSTER);
    }

    /**
     * 在已有的聚合基礎上,對添加的單個元素進行聚合
     *
     * @param clusterItem
     */
    private void calculateSingleCluster(ClusterItem clusterItem) {
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        LatLng latlng = clusterItem.getPosition();
        if (!visibleBounds.contains(latlng)) {
            return;
        }
        Cluster cluster = getCluster(latlng, mClusters);
        if (cluster != null) {
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.UPDATE_SINGLE_CLUSTER;

            message.obj = cluster;
            mMarkerhandler.removeMessages(MarkerHandler.UPDATE_SINGLE_CLUSTER);
            mMarkerhandler.sendMessageDelayed(message, 5);


        } else {

            cluster = new Cluster(latlng);
            mClusters.add(cluster);
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.ADD_SINGLE_CLUSTER;
            message.obj = cluster;
            mMarkerhandler.sendMessage(message);

        }
    }

    /**
     * 根據一個點獲取是否可以依附的聚合點,沒有則返回null
     *
     * @param latLng
     * @return
     */
    private Cluster getCluster(LatLng latLng, List<Cluster> clusters) {
        for (Cluster cluster : clusters) {
            LatLng clusterCenterPoint = cluster.getCenterLatLng();
            double distance = AMapUtils.calculateLineDistance(latLng, clusterCenterPoint);
            if (distance < mClusterDistance) {
                return cluster;
            }
        }

        return null;
    }


    /**
     * 獲取每個聚合點的繪制樣式
     */
    private BitmapDescriptor getBitmapDes(int num) {

        //加載字體
        Typeface typeFace = Typeface.createFromAsset(mContext.getAssets(), "fonts/A-OTF-ShinGoPr6N-Ultra.otf");

        BitmapDescriptor bitmapDescriptor = mLruCache.get(num);
        if (bitmapDescriptor == null) {
            TextView textView = new TextView(mContext);
            // 應用字體
            textView.setTypeface(typeFace);
            if (num < 10) {
                String tile = String.valueOf(num);

                textView.setText(tile);
            }else if(num==1){
                textView.setText("");
            }else {
                float zoom = mAMap.getCameraPosition().zoom;
                //loge("當前縮放級別是==" + zoom);
                if (zoom == 19.0 || zoom == 20.0) {
                    String tile = String.valueOf(num);
                    textView.setText(tile);
                }
            }
            textView.setGravity(Gravity.CENTER);
            textView.setTextColor(Color.parseColor("#ffffff"));
            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
            if (mClusterRender != null && mClusterRender.getDrawAble(num) != null) {
                textView.setBackgroundDrawable(mClusterRender.getDrawAble(num));   //根據數量設置背景樣式
            } else {
                //textView.setBackgroundResource(R.mipmap.act_map_evelopetwo);
                //2017/09/25修改 換圖標
                textView.setBackgroundResource(R.mipmap.act_map_blue_icon_bg);
            }
            bitmapDescriptor = BitmapDescriptorFactory.fromView(textView);
            mLruCache.put(num, bitmapDescriptor);    //把聚合點數量,樣式鍵值對形式存入集合中

        }
        return bitmapDescriptor;
    }

    /**
     * 更新已加入地圖聚合點的樣式
     */
    private void updateCluster(Cluster cluster) {

        Marker marker = cluster.getMarker();
        marker.setIcon(getBitmapDes(cluster.getClusterCount()));


    }




//-----------------------輔助內部類用---------------------------------------------

    /**
     * marker漸變動畫,動畫結束后將Marker刪除
     */
    class MyAnimationListener implements Animation.AnimationListener {
        private List<Marker> mRemoveMarkers;

        MyAnimationListener(List<Marker> removeMarkers) {
            mRemoveMarkers = removeMarkers;
        }

        @Override
        public void onAnimationStart() {

        }

        @Override
        public void onAnimationEnd() {
            for (Marker marker : mRemoveMarkers) {
                marker.remove();
            }
            mRemoveMarkers.clear();
        }
    }

    /**
     * 處理market添加,更新等操作
     */
    class MarkerHandler extends Handler {

        static final int ADD_CLUSTER_LIST = 0;

        static final int ADD_SINGLE_CLUSTER = 1;

        static final int UPDATE_SINGLE_CLUSTER = 2;

        MarkerHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {

            switch (message.what) {
                case ADD_CLUSTER_LIST:
                    List<Cluster> clusters = (List<Cluster>) message.obj;
                    addClusterToMap(clusters);
                    break;
                case ADD_SINGLE_CLUSTER:
                    Cluster cluster = (Cluster) message.obj;
                    addSingleClusterToMap(cluster);
                    break;
                case UPDATE_SINGLE_CLUSTER:
                    Cluster updateCluster = (Cluster) message.obj;
                    updateCluster(updateCluster);
                    break;
            }
        }
    }

    /**
     * 處理聚合點算法線程
     */
    class SignClusterHandler extends Handler {
        static final int CALCULATE_CLUSTER = 0;
        static final int CALCULATE_SINGLE_CLUSTER = 1;

        SignClusterHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {
            switch (message.what) {
                case CALCULATE_CLUSTER:
                    calculateClusters();
                    break;
                case CALCULATE_SINGLE_CLUSTER:
                    ClusterItem item = (ClusterItem) message.obj;
                    mClusterItems.add(item);
                    Log.e("cluster_overlay", "calculate single cluster");
                    calculateSingleCluster(item);
                    break;
            }
        }
    }
}

以上代碼大家可以根據自己具體需要修改,接下來是聚合標簽類Cluster代碼,幾乎沒改(滑稽):

Cluster:

public class Cluster {


    private LatLng mLatLng;
    private List<ClusterItem> mClusterItems;
    private Marker mMarker;


    public Cluster(LatLng latLng) {

        mLatLng = latLng;
        mClusterItems = new ArrayList<ClusterItem>();
    }

    public Cluster(List<ClusterItem> clusterItems){
        mClusterItems = new ArrayList<>() ;
        mClusterItems = clusterItems;
    }

    public void addClusterItem(ClusterItem clusterItem) {
        mClusterItems.add(clusterItem);
    }

    public int getClusterCount() {
        return mClusterItems.size();
    }


    public LatLng getCenterLatLng() {
        return mLatLng;
    }

    public void setMarker(Marker marker) {
        mMarker = marker;
    }

    public Marker getMarker() {
        return mMarker;
    }

    public List<ClusterItem> getClusterItems() {
        return mClusterItems;
    }
}

然后就是ClusterItem接口了,用來獲取聚合標簽的相關信息:

ClusterItem:


public interface ClusterItem {
    /**
     * 返回聚合元素的地理位置
     *
     * @return
     */
    LatLng getPosition();
    String getUserType();

}

有人可能會問:getUserType()方法干啥用的?客官,別急,先接著往下看,很快就知道它的用處了.

接著,就是我們的RegionItem類了,實現了ClusterItem接口:

RegionItem:

public class RegionItem implements ClusterItem {
    private LatLng mLatLng;
    private String userType;



    public String getUserType() {
        return userType;
    }

    public RegionItem(LatLng latLng, String userType) {
        this.mLatLng=latLng;
        this.userType = userType;
    }

    @Override
    public LatLng getPosition() {
        // TODO Auto-generated method stub
        return mLatLng;
    }

}

最后,就是要在Activity中根據需求添加我們的聚合標簽了:

private ClusterOverlay mClusterOverlay;//普通用戶聚合覆蓋物的對象
private BClusterOverlay bClusterOverlay;//企業用戶聚合覆蓋物的對象
/**
     * by moos on 2017/11/15
     * func:添加并顯示聚合點,增加點擊和繪制事件
     */
    
    private void addItems() {
        new Thread() {
            public void run() {

                List<ClusterItem> items = new ArrayList<ClusterItem>();//普通用戶
               
                List<ClusterItem> bItems = new ArrayList<ClusterItem>();//企業用戶
            
                 //loge("附近的點有===" + imageNearBean.getData().getExhibitionList().size());
                for (int i = 0; i < imageNearBean.getData().getExhibitionList().size(); i++) {

                    double lat = imageNearBean.getData().getExhibitionList().get(i).getLatitude();
                    double lon = imageNearBean.getData().getExhibitionList().get(i).getLongitude();

                    if (imageNearBean.getData().getExhibitionList().get(i).getUserType() != null &&
                            imageNearBean.getData().getExhibitionList().get(i).getUserType().equals("03")) {//是企業用戶的數據
                        LatLng latLng = new LatLng(lat, lon, false);
                        RegionItem regionItem = new RegionItem(latLng,  imageNearBean.getData().getExhibitionList().get(i).getUserType());//添加元素
                        bItems.add(regionItem);
                        allBussinessBean.add(imageNearBean.getData().getExhibitionList().get(i));//放入所有企業用戶數據
                    }else { //是普通用戶
                        LatLng latLng = new LatLng(lat, lon, false);
                        RegionItem regionItem = new RegionItem(latLng, imageNearBean.getData().getExhibitionList().get(i).getOriginalId(), imageNearBean.getData().getExhibitionList().get(i).getUserType());//添加元素
                        items.add(regionItem);
                    }

                }

                log.e("普通用戶有==" + items.size());
                log.e("企業用戶有==" + bItems.size());

                if ( items.size() > 0) {
    
                    if (mClusterOverlay == null) {
                        //覆蓋物的集合,
                        mClusterOverlay = new ClusterOverlay(mAMap, items, dp2px(getApplicationContext(), clusterRadius), getApplicationContext());   //地圖對象,集合,聚合范圍,上下文傳到指定方法實現
                    } else {
                        mClusterOverlay.onDestroy();   //銷毀之前的覆蓋物集合
                        mClusterOverlay = null;
                        mClusterOverlay = new ClusterOverlay(mAMap, items, dp2px(getApplicationContext(), clusterRadius), getApplicationContext());
                    }
                    //渲染和聚合點點擊事件監聽
                    mClusterOverlay.setClusterRenderer(MapActivity.this);      //getdrawable 方法實現
                    mClusterOverlay.setOnClusterClickListener(MapActivity.this);    //onclick方法實現
                }
                
                if (  bItems.size() > 0) {
                    //商家用戶數據
                    loge("設置企業標簽點擊事件");
                    if (bClusterOverlay == null) {
                        //覆蓋物的集合
                        bClusterOverlay = new BClusterOverlay(mAMap, bItems, dp2px(getApplicationContext(), clusterRadius), getApplicationContext());
                    } else {
                        bClusterOverlay.onDestroy();
                        bClusterOverlay = null;
                        bClusterOverlay = new BClusterOverlay(mAMap, bItems, dp2px(getApplicationContext(), clusterRadius), getApplicationContext());
                    }
                    //渲染和聚合點點擊事件監聽
                    bClusterOverlay.setClusterRenderer(this);
                    bClusterOverlay.setOnClusterClickListener(MapActivity.this);    //onBclick 方法實現
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        addMarkersToMap();//添加普通marker到地圖
                    }
                });

            }
        }.start();


    }

這里大家可能看得很煩,沒錯,看得很煩就對了??!因為這一部分完全沒必要看...只是為了篩選后臺不同的用戶數據而已。記得之前我提到的ClusterItem類中getUserType()方法以及實現這個接口的類RegionItem中定義的userType變量嗎?它的作用就是用來區分不同的聚合標簽,以上代碼的03代表商家用戶,其余類型默認普通用戶。

最后,就是我們的重頭戲了,集中處理多種聚合標簽的點擊事件:

mAMap.setOnCameraChangeListener(new AMap.OnCameraChangeListener() {
                @Override
                public void onCameraChange(CameraPosition cameraPosition) {
                    //LatLng latLng = cameraPosition.target;
                    
                }

                @Override
                public void onCameraChangeFinish(CameraPosition cameraPosition) {

                    mClusterOverlay.updateClusters();//刷新
                    bClusterOverlay.updateClusters();
                }
            });
            
            
            mAMap.setOnMarkerClickListener(new AMap.OnMarkerClickListener() {
                @Override
                public boolean onMarkerClick(Marker marker) {
                    //mClusterOverlay.cleanListener();
                    Cluster cluster = (Cluster) marker.getObject();
                    if(cluster!=null && cluster.getClusterCount()>0){
                        if(cluster.getClusterItems().get(0).getUserType().equals("03")){
                            loge("是普通用戶聚合標簽");
                            mClusterOverlay.respondClusterClickEvent(marker);//響應聚合點的點擊事件    
                        }else {
                            loge("是商家用戶聚合標簽");
                            bClusterOverlay.respondClusterClickEvent(marker);//響應聚合點的點擊事件
                        }
                    }
                    return true;
                }
            });
            
            ......
            
    //聚合標簽的點擊事件
    @Override
    public void onClick(Marker marker, List<ClusterItem> clusterItems) {
        loge("點擊了普通用戶");
        
    }
    
    @Override
    public void onBClick(Marker marker, List<ClusterItem> clusterItems) {
        loge("點擊了商家用戶");
        
    }

代碼有點多,具體業務邏輯代碼就不貼了,估計看了也會比較的煩,大家只需要看onMarkerClick()方法中的邏輯就OK了。一般情況下,我們需要點擊不同的marker后,獲取該marker應該對應的數據,那么該如何獲取呢?為了快點結束戰斗,這里我就不賣關子了,高德marker類中有一方法getObject(),官方是這樣描述的:

獲取marker附加信息

看到這,很多人一定豁然開朗,沒錯!還有個對應的方法marker.setObject(Object obj)。在ClusterOverlay中,我們可以找到如下代碼:

/**
     * 將單個聚合元素添加至地圖顯示
     *
     * @param cluster
     */
    private void addSingleClusterToMap(Cluster cluster) {
        latlng1 = cluster.getCenterLatLng();
        markerOptions = new MarkerOptions();
        markerOptions
                .anchor(0.5f, 0.5f)
                .icon(getBitmapDes(cluster.getClusterCount()))
                .position(latlng1);
        Marker marker = mAMap.addMarker(markerOptions);
        marker.setAnimation(mADDAnimation);
        marker.setObject(cluster);
        marker.startAnimation();
        cluster.setMarker(marker);
        mcluster = cluster;
        mAddMarkers.add(marker);

    }

看看上面這塊代碼,我們的確發現了marker.setObject(cluster)方法,cluster里面有什么?有ClusterItem呀!看到這,我們就發現,前面的getUsertype()方法不是正好派上用場了嘛!添加聚合標簽的時候,我們給每個marker貼上了"名牌",當我們點擊聚合標簽的時候,拿到附加的信息并找到這個名牌(userType),這樣不就可以輕而易舉"撕掉名牌"了嗎!??

??這樣我們就解決多種聚合標簽點擊沖突的問題了,至于如何解決聚合標簽與普通Marker點擊事件沖突,其實大同小異,這里就不獻丑了,相信對于大家來說沒什么問題。下一篇文章將為大家提供marker加載網絡圖片的完美解決方案,請拭目以待!

??最后,大家有任何問題或者建議,歡迎留言或者加群討論,謝謝.

代碼地址:https://github.com/Moosphan/AMapMarker-master

Android集中營

個人博客傳送門:www.moos.club

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

推薦閱讀更多精彩內容