Skip to content

Commit

Permalink
Nightly commit
Browse files Browse the repository at this point in the history
  • Loading branch information
carltonwhitehead authored and Carlton Whitehead committed Apr 24, 2013
1 parent 587cb2b commit 3e7f1e9
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/**
* @author Carlton Whitehead
*/
package com.twotoasters.clusterkraf;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.Animator.AnimatorListener;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;

/**
*
*/
class ClusterTransitionsAnimation implements AnimatorListener, AnimatorUpdateListener {

private final WeakReference<GoogleMap> mapRef;
private final WeakReference<Options> optionsRef;
private final WeakReference<Host> hostRef;

private HashMap<ClusterTransition, Marker> markersByTransition;
private State state;

private Marker[] markers;

ClusterTransitionsAnimation(GoogleMap map, Options options, Host host) {
mapRef = new WeakReference<GoogleMap>(map);
optionsRef = new WeakReference<Options>(options);
hostRef = new WeakReference<Host>(host);
}

void animate(ArrayList<ClusterTransition> transitions) {
if (state == null) {
Options options = optionsRef.get();
Host host = hostRef.get();
if (options != null && host != null) {
state = new State(transitions);
ObjectAnimator animator = ObjectAnimator.ofFloat(state, "value", 0f, 1f);
animator.addListener(this);
animator.addUpdateListener(this);
animator.setDuration(optionsRef.get().getTransitionDuration());
host.onClusterTransitionStarting();
animator.start();
}
}
}

private class State {

private final ArrayList<ClusterTransition> transitions;

private float value;

private State(ArrayList<ClusterTransition> transitions) {
this.transitions = transitions;
}

public void setValue(float value) {
this.value = value;
}

public ArrayList<ClusterTransition> getTransitions() {
return transitions;
}

private int getCount() {
return transitions.size();
}

private LatLng[] getPositions() {
LatLng[] positions = new LatLng[transitions.size()];
int i = 0;
for (ClusterTransition transition : transitions) {
LatLng start = transition.getOriginRelevantInputPointsCluster().getMapPosition();
LatLng end = transition.getDestinationClusterPoint().getMapPosition();
double currentLat = start.latitude + (value * (end.latitude - start.latitude));
double currentLon = start.longitude + (value * (end.longitude - start.longitude));
positions[i++] = new LatLng(currentLat, currentLon);
}
return positions;
}
}

/*
* (non-Javadoc)
*
* @see com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener#
* onAnimationUpdate(com.nineoldandroids.animation.ValueAnimator)
*/
@Override
public void onAnimationUpdate(ValueAnimator animator) {
LatLng[] positions = state.getPositions();
for (int i = 0; i < markers.length; i++) {
markers[i].setPosition(positions[i]);
}
}

/*
* (non-Javadoc)
*
* @see
* com.nineoldandroids.animation.Animator.AnimatorListener#onAnimationCancel
* (com.nineoldandroids.animation.Animator)
*/
@Override
public void onAnimationCancel(Animator animator) {
// TODO Auto-generated method stub

}

/*
* (non-Javadoc)
*
* @see
* com.nineoldandroids.animation.Animator.AnimatorListener#onAnimationEnd
* (com.nineoldandroids.animation.Animator)
*/
@Override
public void onAnimationEnd(Animator animator) {
state = null;
Host host = hostRef.get();
if (host != null) {
host.onClusterTransitionFinished();
}
}

/*
* (non-Javadoc)
*
* @see
* com.nineoldandroids.animation.Animator.AnimatorListener#onAnimationRepeat
* (com.nineoldandroids.animation.Animator)
*/
@Override
public void onAnimationRepeat(Animator animator) {
// TODO Auto-generated method stub

}

/*
* (non-Javadoc)
*
* @see
* com.nineoldandroids.animation.Animator.AnimatorListener#onAnimationStart
* (com.nineoldandroids.animation.Animator)
*/
@Override
public void onAnimationStart(Animator animator) {
GoogleMap map = mapRef.get();
if (map != null) {
markers = new Marker[state.getCount()];
markersByTransition = new HashMap<ClusterTransition, Marker>(state.getCount());
ArrayList<ClusterTransition> transitions = state.getTransitions();
for (ClusterTransition transition : transitions) {
// TODO: plot transition marker
}
}
}

interface Host {
void onClusterTransitionStarting();

void onClusterTransitionFinished();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
*/
package com.twotoasters.clusterkraf;

import java.lang.ref.WeakReference;

import android.os.Handler;

import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener;
import com.google.android.gms.maps.model.CameraPosition;

Expand All @@ -11,8 +15,67 @@
*/
class ClusteringOnCameraChangeListener implements OnCameraChangeListener {

private final WeakReference<Host> hostRef;

private Handler handler = new Handler();

private static final int CALLBACK_DELAY_MILLISECONDS = 100;
private CallbackRunnable pendingCallbackRunnable;

private boolean dirty = false;

public ClusteringOnCameraChangeListener(Host host) {
hostRef = new WeakReference<Host>(host);
}

@Override
public void onCameraChange(CameraPosition newPosition) {
if(dirty == false) {
Host host = hostRef.get();
if(host != null) {
dirty = true;
if(pendingCallbackRunnable == null) {
pendingCallbackRunnable = new CallbackRunnable(this);
handler.postDelayed(pendingCallbackRunnable, CALLBACK_DELAY_MILLISECONDS);
} else {
handler.removeCallbacks(pendingCallbackRunnable);
handler.postDelayed(pendingCallbackRunnable, CALLBACK_DELAY_MILLISECONDS);
}
}
}
}

private void onRunnableCallback() {
pendingCallbackRunnable = null;
Host host = hostRef.get();
if(host != null) {
host.onClusteringCameraChange();
}
}

public void setDirty(boolean dirty) {
this.dirty = dirty;
}

interface Host {
void onClusteringCameraChange();
}

private static class CallbackRunnable implements Runnable {

private final WeakReference<ClusteringOnCameraChangeListener> listenerRef;

private CallbackRunnable(ClusteringOnCameraChangeListener listener) {
this.listenerRef = new WeakReference<ClusteringOnCameraChangeListener>(listener);
}

@Override
public void run() {
ClusteringOnCameraChangeListener listener = listenerRef.get();
if(listener != null) {
listener.onRunnableCallback();
}
}

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ public class ClusterkrafMapHelper {

private final WeakReference<GoogleMap> mapRef;
private final Options options;
private final ClusterTransitionsAnimation transitionsAnimation;

private InnerCallbackListener innerCallbackListener = new InnerCallbackListener();

private final ArrayList<InputPoint> points = new ArrayList<InputPoint>();
private final ArrayList<Marker> markers = new ArrayList<Marker>();
private final HashMap<Marker, ClusterPoint> clusterPointsByMarker = new HashMap<Marker, ClusterPoint>();
private ArrayList<ClusterTransition> clusterTransitions;
private ArrayList<ClusterPoint> currentClusters;

/**
* construct a Clusterkraf instance to manage your map with customized
Expand All @@ -31,6 +36,7 @@ public class ClusterkrafMapHelper {
public ClusterkrafMapHelper(GoogleMap map, Options options) {
this.mapRef = new WeakReference<GoogleMap>(map);
this.options = options;
this.transitionsAnimation = new ClusterTransitionsAnimation(map, options, innerCallbackListener);
}

public void add(InputPoint inputPoint) {
Expand All @@ -54,7 +60,7 @@ void clear() {
// we avoid GoogleMap.clear() because the current SDK leaks custom
// bitmaps, see:
// http://code.google.com/p/gmaps-api-issues/issues/detail?id=4703
for(Marker marker : markers) {
for (Marker marker : markers) {
marker.remove();
}
markers.clear();
Expand All @@ -64,4 +70,73 @@ void clear() {

// TODO: support removing individual markers by InputPoint

private void buildClusters() {
switch(options.getMarkerDrawingStrategy()) {
case WITHIN_BOUNDS_ONLY:
buildClustersWithinBoundsOnly();
break;
}
}

private void buildClustersWithinBoundsOnly() {
GoogleMap map = mapRef.get();
if (map != null) {
ArrayList<ClusterPoint> previousClusters = currentClusters;
ClustersBuilder builder = new ClustersBuilder(map, options, previousClusters);
builder.addAll(points);
currentClusters = builder.build();

if (currentClusters != null && previousClusters != null) {
ClusterTransition.Builder ctb = new ClusterTransition.Builder(map, previousClusters);
for (ClusterPoint currentClusterPoint : currentClusters) {
ctb.add(currentClusterPoint);
}
clusterTransitions = ctb.build();
}
}
}

private void updateClustersAndTransition() {

}

private class InnerCallbackListener implements ClusteringOnCameraChangeListener.Host, ClusterTransitionsAnimation.Host {

private final ClusteringOnCameraChangeListener clusteringOnCameraChangeListener = new ClusteringOnCameraChangeListener(this);

@Override
public void onClusteringCameraChange() {
switch(options.getMarkerDrawingStrategy()) {
case WITHIN_BOUNDS_ONLY:
updateClustersAndTransition();
break;
}
}

/*
* (non-Javadoc)
*
* @see com.twotoasters.clusterkraf.ClusterTransitionsAnimation.Host#
* onClusterTransitionStarting()
*/
@Override
public void onClusterTransitionStarting() {
clusteringOnCameraChangeListener.setDirty(true);
}

/*
* (non-Javadoc)
*
* @see com.twotoasters.clusterkraf.ClusterTransitionsAnimation.Host#
* onClusterTransitionFinished()
*/
@Override
public void onClusterTransitionFinished() {
clusteringOnCameraChangeListener.setDirty(false);
// TODO: draw markers at new location and hide remnants of
// transition
}

}

}

0 comments on commit 3e7f1e9

Please sign in to comment.