一、先大体了解下,主要关注点聚合里面的两个类:
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