基于高德地圖實(shí)現(xiàn)Marker聚合效果

??最近,項(xiàng)目有了新的需求,要求地圖上的標(biāo)簽點(diǎn)實(shí)現(xiàn)聚合效果,單純的marker已經(jīng)無(wú)法滿足大量數(shù)據(jù)展示的情況,聚合效果成為大勢(shì)所趨。

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

效果圖

??先來(lái)放一下官網(wǎng)提供的聚合點(diǎn)的demo:https://github.com/amap-demo/android-cluster-marker
,如果只是實(shí)現(xiàn)一種聚合點(diǎn),可以復(fù)制官方的demo代碼,稍微改改就可以用了。如果你們產(chǎn)品有其他需求,比如多種聚合標(biāo)簽點(diǎn),或者實(shí)現(xiàn)普通標(biāo)簽和聚合標(biāo)簽的混排使用,那么可以參考下本文提供的改進(jìn)思路。

  • 發(fā)現(xiàn)問(wèn)題:

首先,我們發(fā)現(xiàn)高德官方提供的demo存在的問(wèn)題:

  1. 當(dāng)存在多種類型聚合標(biāo)簽點(diǎn)時(shí),聚合點(diǎn)的點(diǎn)擊事件沖突問(wèn)題.

這個(gè)問(wèn)題是什么意思呢?舉個(gè)例子:當(dāng)?shù)貓D上我們先后初始化兩種聚合標(biāo)簽點(diǎn)A和B的時(shí)候,不管點(diǎn)擊A還是B類型的聚合點(diǎn),都會(huì)只響應(yīng)B類的點(diǎn)擊事件,即先聲明的聚合標(biāo)簽的點(diǎn)擊事件會(huì)被后聲明的聚合標(biāo)簽的點(diǎn)擊事件"搶占".

  1. 當(dāng)?shù)貓D上存在普通標(biāo)簽(Marker)和聚合標(biāo)簽(Cluster)時(shí),同樣會(huì)產(chǎn)生點(diǎn)擊事件的沖突問(wèn)題.

這個(gè)意思就是,點(diǎn)擊聚合標(biāo)簽仍然會(huì)響應(yīng)普通標(biāo)簽的點(diǎn)擊事件.

  • 分析問(wèn)題:

這兩個(gè)問(wèn)題其實(shí)歸根結(jié)底是一個(gè)問(wèn)題,都是因?yàn)閙arker的點(diǎn)擊事件被搶占,從官方的提供的ClusterOverlay類中代碼我們就可以看出來(lái):

......
amap.setOnCameraChangeListener(this);
amap.setOnMarkerClickListener(this);
......
 //點(diǎn)擊事件
    @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;
    }

??從以上代碼我們可以看出來(lái),聚合處理類ClusterOverlay實(shí)現(xiàn)了AMap.OnMarkerClickListener接口,而當(dāng)我們使用兩個(gè)或者兩個(gè)以上樣式的聚合標(biāo)簽時(shí),我們就會(huì)發(fā)現(xiàn)明明是兩種類型的聚合標(biāo)簽,但是卻觸發(fā)相同的點(diǎn)擊事件。這樣顯然是不符合我們預(yù)期需求的,那么我們?cè)撊绾巫尣煌瑯邮降木酆蠘?biāo)簽A,B,C...各司其職呢?你肯定會(huì)說(shuō),廢話,不然你現(xiàn)在在講啥!咳咳,請(qǐng)?jiān)试S我靜靜~

  • 解決問(wèn)題:

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

ClusterOverlay:

/**
 * Created by yiyi.qi on 16/10/10.
 * 整體設(shè)計(jì)采用了兩個(gè)線程,一個(gè)線程用于計(jì)算組織聚合數(shù)據(jù),一個(gè)線程負(fù)責(zé)處理Marker相關(guān)操作
 */
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的位置移動(dòng)到地圖中心
            LatLng latLng=new LatLng(arg0.getPosition().latitude,arg0.getPosition().longitude);
            mAMap.moveCamera(CameraUpdateFactory.changeLatLng(latLng));
            if (cluster != null) {
                arg0.showInfoWindow();
                Log.e("timeory", "普通標(biāo)簽infowindow出現(xiàn)了嗎?  " +  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;

    /**
     * 構(gòu)造函數(shù)
     *
     * @param amap
     * @param clusterSize 聚合范圍的大?。ㄖ更c(diǎn)像素單位距離內(nèi)的點(diǎn)會(huì)聚合到一個(gè)點(diǎn)顯示)
     * @param context
     */
    public ClusterOverlay(AMap amap, int clusterSize, Context context) {
        this(amap, null, clusterSize, context);


    }

    /**
     * 構(gòu)造函數(shù),批量添加聚合元素時(shí),調(diào)用此構(gòu)造函數(shù)
     *  默認(rèn)最多會(huì)緩存80張圖片作為聚合顯示元素圖片,根據(jù)自己顯示需求和app使用內(nèi)存情況,可以修改數(shù)量
     * @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:通過(guò)activity中的onMarkerClick()響應(yīng)聚合點(diǎn)的點(diǎn)擊事件
     * @param arg0
     */
    public void respondClusterClickEvent(Marker arg0){

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

    }

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

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

    /**
     * 共外部調(diào)用的接口,設(shè)置聚合元素的渲染樣式,不設(shè)置則默認(rèn)為氣泡加數(shù)字形式進(jìn)行渲染
     *
     * @param render
     */
    public void setClusterRenderer(ClusterRender render) {
        mClusterRender = render;
    }
    /**
     * 設(shè)置聚合元素的渲染樣式,不設(shè)置則默認(rèn)為氣泡加數(shù)字形式進(jìn)行渲染
     *
     * @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點(diǎn)擊事件,防止搶占
     */
    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);

    /**
     * 將單個(gè)聚合元素添加至地圖顯示
     *
     * @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();
        //地圖獲取轉(zhuǎn)換器, 獲取可視區(qū)域, 獲取可視區(qū)域的四個(gè)點(diǎn)形成的經(jīng)緯度范圍, 得到一個(gè)經(jīng)緯度范圍
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        for (ClusterItem clusterItem : mClusterItems) {
            if (mIsCanceled) {
                return;
            }
            LatLng latlng = clusterItem.getPosition();//聚合元素的地理位置
            if (visibleBounds.contains(latlng)) {//如果這個(gè)范圍包含這個(gè)聚合元素的地理位置
                Cluster cluster = getCluster(latlng, mClusters);//根據(jù)這個(gè)位置和聚合物的集合, 獲得一個(gè)聚合器
                if (cluster != null) {
                    cluster.addClusterItem(clusterItem);//往聚合器中添加聚合點(diǎn)
                } else {
                    cluster = new Cluster(latlng);
                    mClusters.add(cluster);
                    cluster.addClusterItem(clusterItem);
                }

            }
        }

        //復(fù)制一份數(shù)據(jù),規(guī)避同步
        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);
    }

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

    /**
     * 在已有的聚合基礎(chǔ)上,對(duì)添加的單個(gè)元素進(jìn)行聚合
     *
     * @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);

        }
    }

    /**
     * 根據(jù)一個(gè)點(diǎn)獲取是否可以依附的聚合點(diǎn),沒(méi)有則返回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;
    }


    /**
     * 獲取每個(gè)聚合點(diǎn)的繪制樣式
     */
    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);
            // 應(yīng)用字體
            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("當(dāng)前縮放級(jí)別是==" + 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));   //根據(jù)數(shù)量設(shè)置背景樣式
            } else {
                //textView.setBackgroundResource(R.mipmap.act_map_evelopetwo);
                //2017/09/25修改 換圖標(biāo)
                textView.setBackgroundResource(R.mipmap.act_map_blue_icon_bg);
            }
            bitmapDescriptor = BitmapDescriptorFactory.fromView(textView);
            mLruCache.put(num, bitmapDescriptor);    //把聚合點(diǎn)數(shù)量,樣式鍵值對(duì)形式存入集合中

        }
        return bitmapDescriptor;
    }

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

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


    }




//-----------------------輔助內(nèi)部類用---------------------------------------------

    /**
     * marker漸變動(dòng)畫(huà),動(dòng)畫(huà)結(jié)束后將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;
            }
        }
    }

    /**
     * 處理聚合點(diǎn)算法線程
     */
    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;
            }
        }
    }
}

以上代碼大家可以根據(jù)自己具體需要修改,接下來(lái)是聚合標(biāo)簽類Cluster代碼,幾乎沒(méi)改(滑稽):

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接口了,用來(lái)獲取聚合標(biāo)簽的相關(guān)信息:

ClusterItem:


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

}

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

接著,就是我們的RegionItem類了,實(shí)現(xiàn)了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中根據(jù)需求添加我們的聚合標(biāo)簽了:

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

                List<ClusterItem> items = new ArrayList<ClusterItem>();//普通用戶
               
                List<ClusterItem> bItems = new ArrayList<ClusterItem>();//企業(yè)用戶
            
                 //loge("附近的點(diǎn)有===" + 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")) {//是企業(yè)用戶的數(shù)據(jù)
                        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));//放入所有企業(yè)用戶數(shù)據(jù)
                    }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("企業(yè)用戶有==" + bItems.size());

                if ( items.size() > 0) {
    
                    if (mClusterOverlay == null) {
                        //覆蓋物的集合,
                        mClusterOverlay = new ClusterOverlay(mAMap, items, dp2px(getApplicationContext(), clusterRadius), getApplicationContext());   //地圖對(duì)象,集合,聚合范圍,上下文傳到指定方法實(shí)現(xiàn)
                    } else {
                        mClusterOverlay.onDestroy();   //銷毀之前的覆蓋物集合
                        mClusterOverlay = null;
                        mClusterOverlay = new ClusterOverlay(mAMap, items, dp2px(getApplicationContext(), clusterRadius), getApplicationContext());
                    }
                    //渲染和聚合點(diǎn)點(diǎn)擊事件監(jiān)聽(tīng)
                    mClusterOverlay.setClusterRenderer(MapActivity.this);      //getdrawable 方法實(shí)現(xiàn)
                    mClusterOverlay.setOnClusterClickListener(MapActivity.this);    //onclick方法實(shí)現(xiàn)
                }
                
                if (  bItems.size() > 0) {
                    //商家用戶數(shù)據(jù)
                    loge("設(shè)置企業(yè)標(biāo)簽點(diǎn)擊事件");
                    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());
                    }
                    //渲染和聚合點(diǎn)點(diǎn)擊事件監(jiān)聽(tīng)
                    bClusterOverlay.setClusterRenderer(this);
                    bClusterOverlay.setOnClusterClickListener(MapActivity.this);    //onBclick 方法實(shí)現(xiàn)
                }

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

            }
        }.start();


    }

這里大家可能看得很煩,沒(méi)錯(cuò),看得很煩就對(duì)了??!因?yàn)檫@一部分完全沒(méi)必要看...只是為了篩選后臺(tái)不同的用戶數(shù)據(jù)而已。記得之前我提到的ClusterItem類中g(shù)etUserType()方法以及實(shí)現(xiàn)這個(gè)接口的類RegionItem中定義的userType變量嗎?它的作用就是用來(lái)區(qū)分不同的聚合標(biāo)簽,以上代碼的03代表商家用戶,其余類型默認(rèn)普通用戶。

最后,就是我們的重頭戲了,集中處理多種聚合標(biāo)簽的點(diǎn)擊事件:

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("是普通用戶聚合標(biāo)簽");
                            mClusterOverlay.respondClusterClickEvent(marker);//響應(yīng)聚合點(diǎn)的點(diǎn)擊事件    
                        }else {
                            loge("是商家用戶聚合標(biāo)簽");
                            bClusterOverlay.respondClusterClickEvent(marker);//響應(yīng)聚合點(diǎn)的點(diǎn)擊事件
                        }
                    }
                    return true;
                }
            });
            
            ......
            
    //聚合標(biāo)簽的點(diǎn)擊事件
    @Override
    public void onClick(Marker marker, List<ClusterItem> clusterItems) {
        loge("點(diǎn)擊了普通用戶");
        
    }
    
    @Override
    public void onBClick(Marker marker, List<ClusterItem> clusterItems) {
        loge("點(diǎn)擊了商家用戶");
        
    }

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

獲取marker附加信息

看到這,很多人一定豁然開(kāi)朗,沒(méi)錯(cuò)!還有個(gè)對(duì)應(yīng)的方法marker.setObject(Object obj)。在ClusterOverlay中,我們可以找到如下代碼:

/**
     * 將單個(gè)聚合元素添加至地圖顯示
     *
     * @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);

    }

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

??這樣我們就解決多種聚合標(biāo)簽點(diǎn)擊沖突的問(wèn)題了,至于如何解決聚合標(biāo)簽與普通Marker點(diǎn)擊事件沖突,其實(shí)大同小異,這里就不獻(xiàn)丑了,相信對(duì)于大家來(lái)說(shuō)沒(méi)什么問(wèn)題。下一篇文章將為大家提供marker加載網(wǎng)絡(luò)圖片的完美解決方案,請(qǐng)拭目以待!

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

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

Android集中營(yíng)

個(gè)人博客傳送門:www.moos.club

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容