Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created September 5, 2013 01:25
Show Gist options
  • Save slightfoot/6444918 to your computer and use it in GitHub Desktop.
Save slightfoot/6444918 to your computer and use it in GitHub Desktop.
Example of synchronized animated markers on Android 2.3 and up. (Android without Object Animators or JB's Choreographer.)
package com.demondevelopers.example.markers;
import java.util.HashSet;
import java.util.Iterator;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
public class MainActivity extends FragmentActivity
{
private SyncedMapFragment mMapFragment;
private GoogleMap mMap;
private Marker mMarker;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mMapFragment = new SyncedMapFragment();
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, mMapFragment)
.commit();
}
@Override
protected void onResumeFragments()
{
super.onResumeFragments();
mMap = mMapFragment.getMap();
mMap.setOnMapClickListener(mMapClickListener);
}
private GoogleMap.OnMapClickListener mMapClickListener = new GoogleMap.OnMapClickListener()
{
private LatLngInterpolator mLatLngInterpolator;
@Override
public void onMapClick(LatLng point)
{
if(mMarker != null){
mMapFragment.animateMarkerToGB(mMarker, point, mLatLngInterpolator, 1500);
}
else{
mLatLngInterpolator = new LatLngInterpolator.Linear();
mMarker = mMap.addMarker(new MarkerOptions().position(point));
}
}
};
public static class SyncedMapFragment extends SupportMapFragment
{
private MapViewWrapper mWrapper;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mWrapper = new MapViewWrapper(getActivity());
mWrapper.addView(super.onCreateView(inflater, container, savedInstanceState));
return mWrapper;
}
public void animateMarkerToGB(Marker marker, LatLng finalPosition, LatLngInterpolator latLngInterpolator,
long duration)
{
if(mWrapper == null){
throw new IllegalStateException("MapFragment view not yet created.");
}
mWrapper.animateMarkerToGB(marker, finalPosition, latLngInterpolator, duration);
}
public static class MapViewWrapper extends FrameLayout
{
private HashSet<MarkerAnimation> mAnimations = new HashSet<MarkerAnimation>();
public MapViewWrapper(Context context)
{
super(context);
setWillNotDraw(false);
}
public void animateMarkerToGB(Marker marker, LatLng finalPosition, LatLngInterpolator latLngInterpolator,
long duration)
{
mAnimations.add(new MarkerAnimation(marker, finalPosition, latLngInterpolator, duration));
invalidate();
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
boolean shouldPost = false;
Iterator<MarkerAnimation> iterator = mAnimations.iterator();
while(iterator.hasNext()){
MarkerAnimation markerAnimation = iterator.next();
if(markerAnimation.animate()){
shouldPost = true;
}
else{
iterator.remove();
}
}
if(shouldPost){
postInvalidateOnAnimation();
}
}
@SuppressLint("NewApi")
public void postInvalidateOnAnimation()
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
super.postInvalidateOnAnimation();
}
else{
ViewCompat.postInvalidateOnAnimation(this);
}
}
private static class MarkerAnimation
{
final static Interpolator sInterpolator = new AccelerateDecelerateInterpolator();
private final Marker mMarker;
private final LatLngInterpolator mLatLngInterpolator;
private final LatLng mStartPosition;
private final LatLng mFinalPosition;
private final long mDuration;
private final long mStartTime;
public MarkerAnimation(Marker marker, LatLng finalPosition, LatLngInterpolator latLngInterpolator,
long duration)
{
mMarker = marker;
mLatLngInterpolator = latLngInterpolator;
mStartPosition = marker.getPosition();
mFinalPosition = finalPosition;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
}
public boolean animate()
{
// Calculate progress using interpolator
long elapsed = AnimationUtils.currentAnimationTimeMillis() - mStartTime;
float t = elapsed / (float)mDuration;
float v = sInterpolator.getInterpolation(t);
mMarker.setPosition(mLatLngInterpolator.interpolate(v, mStartPosition, mFinalPosition));
// Repeat till progress is complete.
return (t < 1);
}
@Override
public int hashCode()
{
// So we only get one animation for the same marker in our HashSet
return mMarker.hashCode();
}
@Override
public boolean equals(Object o)
{
if(o instanceof Marker){
return mMarker.equals(o);
}
return super.equals(o);
}
}
}
}
}
@akoul889
Copy link

akoul889 commented Sep 3, 2014

Hey, I was trying your code but I can not seem to find the LatLngInterpolator Can you please share the code for this Interpolator

@akoul889
Copy link

akoul889 commented Sep 3, 2014

Never mind found it here
https://gist.github.com/broady/6314689

@dakaugu
Copy link

dakaugu commented Dec 28, 2014

What is the advantage of animating multiple markers this way, compared to Google's way?

@KevalPatel
Copy link

I am getting error on this line...
final static Interpolator sInterpolator = new AccelerateDecelerateInterpolator();
In markerAnimation class, saying - Incompatible Types...

@Akash35p
Copy link

please give one example project i didn't understand Interpolator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment