private void perform(MarkerModifier markerModifier) { // Don't show small clusters. Render the markers inside, instead. if (!shouldRenderAsCluster(cluster)) { for (T item : cluster.getItems()) { Marker marker = mMarkerCache.get(item); MarkerWithPosition markerWithPosition; if (marker == null) { MarkerOptions markerOptions = new MarkerOptions(); if (animateFrom != null) { markerOptions.position(animateFrom); markerOptions.icon(item.getBitmapDescriptor()); } else { markerOptions.position(item.getPosition()); markerOptions.icon(item.getBitmapDescriptor()); } onBeforeClusterItemRendered(item, markerOptions); marker = mClusterManager.getMarkerCollection().addMarker(markerOptions); markerWithPosition = new MarkerWithPosition(marker); mMarkerCache.put(item, marker); if (animateFrom != null) { markerModifier.animate(markerWithPosition, animateFrom, item.getPosition()); } } else { markerWithPosition = new MarkerWithPosition(marker); } onClusterItemRendered(item, marker); newMarkers.add(markerWithPosition); } return; } MarkerOptions markerOptions = new MarkerOptions().position(animateFrom == null ? cluster.getPosition() : animateFrom); onBeforeClusterRendered(cluster, markerOptions); Marker marker = mClusterManager.getClusterMarkerCollection().addMarker(markerOptions); mMarkerToCluster.put(marker, cluster); mClusterToMarker.put(cluster, marker); MarkerWithPosition markerWithPosition = new MarkerWithPosition(marker); if (animateFrom != null) { markerModifier.animate(markerWithPosition, animateFrom, cluster.getPosition()); } onClusterRendered(cluster, marker); newMarkers.add(markerWithPosition); }
@SuppressLint("NewApi") public void run() { if (clusters.equals(DefaultClusterRenderer.this.mClusters)) { mCallback.run(); return; } final MarkerModifier markerModifier = new MarkerModifier(); final float zoom = mMapZoom; final boolean zoomingIn = zoom > mZoom; final float zoomDelta = zoom - mZoom; final Set<MarkerWithPosition> markersToRemove = mMarkers; final LatLngBounds visibleBounds = mProjection.getVisibleRegion().latLngBounds; // TODO: Add some padding, so that markers can animate in from off-screen. // Find all of the existing clusters that are on-screen. These are candidates for // markers to animate from. List<Point> existingClustersOnScreen = null; if (DefaultClusterRenderer.this.mClusters != null && SHOULD_ANIMATE) { existingClustersOnScreen = new ArrayList<Point>(); for (Cluster<T> c : DefaultClusterRenderer.this.mClusters) { if (shouldRenderAsCluster(c) && visibleBounds.contains(c.getPosition())) { Point point = mSphericalMercatorProjection.toPoint(c.getPosition()); existingClustersOnScreen.add(point); } } } // Create the new markers and animate them to their new positions. final Set<MarkerWithPosition> newMarkers = new HashSet<MarkerWithPosition>(); for (Cluster<T> c : clusters) { boolean onScreen = visibleBounds.contains(c.getPosition()); if (zoomingIn && onScreen && SHOULD_ANIMATE) { Point point = mSphericalMercatorProjection.toPoint(c.getPosition()); Point closest = findClosestCluster(existingClustersOnScreen, point); if (closest != null) { LatLng animateTo = mSphericalMercatorProjection.toLatLng(closest); markerModifier.add(true, new CreateMarkerTask(c, newMarkers, animateTo)); } else { markerModifier.add(true, new CreateMarkerTask(c, newMarkers, null)); } } else { markerModifier.add(onScreen, new CreateMarkerTask(c, newMarkers, null)); } } // Wait for all markers to be added. markerModifier.waitUntilFree(); // Don't remove any markers that were just added. This is basically anything that had // a hit in the MarkerCache. markersToRemove.removeAll(newMarkers); // Find all of the new clusters that were added on-screen. These are candidates for // markers to animate from. List<Point> newClustersOnScreen = null; if (SHOULD_ANIMATE) { newClustersOnScreen = new ArrayList<Point>(); for (Cluster<T> c : clusters) { if (shouldRenderAsCluster(c) && visibleBounds.contains(c.getPosition())) { Point p = mSphericalMercatorProjection.toPoint(c.getPosition()); newClustersOnScreen.add(p); } } } // Remove the old markers, animating them into clusters if zooming out. for (final MarkerWithPosition marker : markersToRemove) { boolean onScreen = visibleBounds.contains(marker.position); // Don't animate when zooming out more than 3 zoom levels. // TODO: drop animation based on speed of device & number of markers to animate. if (!zoomingIn && zoomDelta > -3 && onScreen && SHOULD_ANIMATE) { final Point point = mSphericalMercatorProjection.toPoint(marker.position); final Point closest = findClosestCluster(newClustersOnScreen, point); if (closest != null) { LatLng animateTo = mSphericalMercatorProjection.toLatLng(closest); markerModifier.animateThenRemove(marker, marker.position, animateTo); } else { markerModifier.remove(true, marker.marker); } } else { markerModifier.remove(onScreen, marker.marker); } } markerModifier.waitUntilFree(); mMarkers = newMarkers; DefaultClusterRenderer.this.mClusters = clusters; mZoom = zoom; mCallback.run(); }