使用百度地图的点聚合功能

百度地图的demo中,已经提供了点聚合功能。

一、先大体了解下,主要关注点聚合里面的两个类:

1.ClusterItem接口

这个就是地图上一个一个独立的标记点。这个接口提供两个方法需要实现:
一个是提供marker的位置:

        LatLng getPosition();         

一个是提供marker的图标:

        BitmapDescriptor getBitmapDescriptor();  

我们自定义的点MyItem必须继承ClusterItem这个接口,才能被ClusterManager管理。

2,ClusterManager

顾名思义就是Cluster的管理类,其中有设置相应的点击事件。
聚合点的点击监听:

mClusterManager.setOnClusterClickListener(new OnClusterClickListener<BaiduNearbyActivity.MyItem>(){

聚合点中单项的点击监听:

mClusterManager.setOnClusterItemClickListener(new OnClusterItemClickListener<BaiduNearbyActivity.MyItem>(){

百度地图的demo中,是在MarkerClusterDemo.java中演示了点聚合功能,不过比较简单,不能满足实际应用的需求。

二、在其基础功能之上,做了如下改进:

1,给标记点传递数据;
2,实现聚合点的点击功能,点击后在地图上展开聚合点的内容;
3,聚合的起始数目修改,支持2个点也能聚合;
4,聚合的范围调整,避免聚合点图标的互相覆盖;
5,实现地图状态变化的监听;

1,给标记点传递数据;

其实就是新建一个类MyItem,继承ClusterItem接口,并增加数据成员来传递数据;
主要就是添加了一个Bundle成员,实现了Bundle的设置及使用的逻辑;
我在Bundle中也只是添加了一个字符串,在选择图标时(getBitmapDescriptor()),用于判断,可以选择不同的图标展示在地图上。
下面是MyItem类的实现:

    /**
     * 每个Marker点,包含Marker点坐标以及图标
     */
    public class MyItem implements ClusterItem {
        private final LatLng mPosition;
        private Bundle mBundle;

        public MyItem(LatLng latLng) {
            mPosition = latLng;
            mBundle = null;
        }
        public MyItem(LatLng latLng, Bundle bundle) {
            mPosition = latLng;
            mBundle = bundle;
        }
        @Override
        public LatLng getPosition() {
            return mPosition;
        }

        @Override
        public BitmapDescriptor getBitmapDescriptor() {
            int iconId = R.drawable.icon_gcoding;
            if(mBundle!=null){
                if("001".contentEquals(mBundle.getString("index"))) {
                    iconId = R.drawable.icon_marka;
                } else if("002".contentEquals(mBundle.getString("index"))) 

{
                    iconId = R.drawable.icon_markb;
                }
            }

            return BitmapDescriptorFactory                   .fromResource(iconId);//R.drawable.icon_gcoding);
        }

        public Bundle getBundle(){
            return mBundle;
        }

    }

在点击ClusterItem时使用Toast显示一下:

        mClusterManager.setOnClusterItemClickListener(new ClusterManager.OnClusterItemClickListener<MyItem>() {
            @Override
            public boolean onClusterItemClick(MyItem item) {
                String showText = "点击单个Item";
                if(item.getBundle()!=null) {
                    showText += " index="+item.getBundle().getString("index");
                }
                Toast.makeText(MarkerClusterDemo.this,
                        showText, Toast.LENGTH_SHORT).show();

                return false;
            }
        });

2,实现点击功能,点击后在地图上展开聚合点的内容;

点击功能,demo中已经实现了,可是,点击聚合点,如何放大到恰好包含那些点?
这里用到了LatLngBounds类来进行地理范围管理,具体实现如下:

       mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<MyItem>() {
            @Override
            public boolean onClusterClick(Cluster<MyItem> cluster) {
                Toast.makeText(MarkerClusterDemo.this,
                        "有" + cluster.getSize() + "个点", Toast.LENGTH_SHORT).show();

                List<MyItem> items = (List<MyItem>) cluster.getItems();
                LatLngBounds.Builder builder2 = new LatLngBounds.Builder();
                int i=0;
                for(MyItem myItem : items){
                    builder2 = builder2.include(myItem.getPosition());
                    Log.i("map","log: i="+ i++ +" pos="+myItem.getPosition().toString());
                }

                LatLngBounds latlngBounds = builder2.build();
                MapStatusUpdate u = MapStatusUpdateFactory.newLatLngBounds(latlngBounds,mMapView.getWidth(),mMapView.getHeight());
                mBaiduMap.animateMapStatus(u);
                Log.i("map","log: mBaiduMap.animateMapStatus(u)");

                return false;
            }
        });

对于聚合点的点击操作,需要注意进行传递:
ClusterManager.java中:

    public boolean onMarkerClick(Marker marker) {
//        return false;
        return getMarkerManager().onMarkerClick(marker);
    }

3,聚合的起始数目修改:4->1;

原demo中,要至少5个点才能聚合,而实际使用时,我可不能这样来实现,只要有两个点靠近了,也是要聚合的。
修改这个文件:

com\baidu\mapapi\clusterutil\clustering\view\DefaultClusterRenderer.java

其中有个MIN_CLUSTER_SIZE,修改4->1:

private static final int MIN_CLUSTER_SIZE = 1;//4

查看判断逻辑,也就是使用MIN_CLUSTER_SIZE的地方:

    /**
     * Determine whether the cluster should be rendered as individual markers or a cluster.
     */
    protected boolean shouldRenderAsCluster(Cluster<T> cluster) {
        return cluster.getSize() > MIN_CLUSTER_SIZE;
    }

4,聚合的范围调整,避免聚合点图标的互相覆盖;

运行时发现,点聚合后还是有点互相覆盖的问题,这样就应该有个聚合点负责的范围值,我通过测试,发现修改MAX_DISTANCE_AT_ZOOM有效,在这个文件中:

com\baidu\mapapi\clusterutil\clustering\algo\NonHierarchicalDistanceBasedAlgorithm.java

修改 NonHierarchicalDistanceBasedAlgorithm 类中的:

public static final int MAX_DISTANCE_AT_ZOOM = 300; // essentially 100 dp ->300dp

这样基本上就不再有重叠问题了。

5,实现地图变化的监听;

在实际使用过程中,发现一个问题,就是进行两次设置地图状态改变的监听,第一次设置的,就不管用了。

原来的代码中已经设置了一次监听:

mBaiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {

对点聚合功能,又设置了一次监听:

// 设置地图监听,当地图状态发生改变时,进行点聚合运算
mBaiduMap.setOnMapStatusChangeListener(mClusterManager);

然后,第一次设置的监听中的onMapStatusChangeFinish()走不到了。
感觉是对同一个BaiduMap,只允许设置一次地图状态监听:

setOnMapStatusChangeListener()

当时挺犯愁的:总不能添加了点聚合功能,导致原先的部分功能失效吧。

后来是在查看ClusterManager类的定义时发现了解决方法:

public class ClusterManager<T extends ClusterItem> implements
        BaiduMap.OnMapStatusChangeListener, BaiduMap.OnMarkerClickListener {

原来,ClusterManager类里面已经有了nMapStatusChangeListener这个接口,可以由我们来实现这个接口中的方法:

    public void onMapStatusChangeStart(MapStatus mapStatus) {

    public void onMapStatusChangeFinish(MapStatus mapStatus) {

接下来又遇到一个问题:原来是在activity中直接响应地图改变,访问的变量都是本地的,现在转移到ClusterManager这个类中了,如何实现原来的动作呢?

最开始的想法,是直接传递context下来,然后强制转换类型,去调用原Activity的方法,应该也可以实现,可是,如果ClusterManager这个类被别的activity调用,这个强制转换就会带来问题了。

所以,还是使用一个handler,来进行消息的传递,在activity中来实现相应的响应动作。
这样,能很好的适应不同调用环境。

具体实现方法:
在ClusterManager类中定义两个变量:

    Handler handler;
    int result; //msg.what

添加一个对外的接口,用于设置这两个值:

    public void setHandler(Handler handler, int result){
        this.handler = handler;
        this.result = result;
    }

然后,在地图状态发生变化完成时,使用handler发送消息:

    @Override
    public void onMapStatusChangeFinish(MapStatus mapStatus) {

        Log.i("ClusterManger","onMapStatusChangeFinish");
        Message message = handler.obtainMessage(result);
        message.obj = mapStatus;
        handler.sendMessage(message);

    }

在activity中调用setHandler进行设置:

    mClusterManager.setHandler(handler, MAP_STATUS_CHANGE); //设置handler

然后实现对消息的处理:

    private final  int MAP_STATUS_CHANGE = 100;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MAP_STATUS_CHANGE:
                    MapStatus mapStatus = (MapStatus) msg.obj;
                    if(mapStatus!=null){
                        Log.i("MarkerClusterDemo", "mapStatus="+mapStatus.toString());
                        // to do :  判断地图状态,进行相应处理
                    }
                    break;
                default:
                    break;
            }
        }
    };

有图有真相:
这里写图片描述
第一张图,是都聚合为一个点的情况;
第二张图,是点击一次聚合点后的情况,进行地图展开;
第三张图,是再次点击聚合点后的情况,对新聚合点进行地图展开;
第四张图,进行缩放后,对比不同的标记点图标,并且点击了一个标记点;

demo下载地址:

http://download.csdn.net/detail/lintax/9737954

参考:

http://blog.csdn.net/javine/article/details/50186023
http://blog.csdn.net/u010635353/article/details/52386097

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值