32

I want to implement smooth transition to emulate car marker moving on the map.

Is it possible to animate marker in android map api v2?

Alexey Zakharov
  • 24,694
  • 42
  • 126
  • 197
  • As I understand you need native maps API V2 behavior to move marker from one position to another smoothly? – Paul Annekov Jan 13 '13 at 11:59
  • you may look into this post http://stackoverflow.com/questions/13728041/move-markers-in-google-map-v2-android – Pavel Dudka Apr 02 '13 at 22:08
  • possible duplicate of [How to animate marker when it is added to map on Android?](http://stackoverflow.com/questions/8191582/how-to-animate-marker-when-it-is-added-to-map-on-android) – bummi May 02 '15 at 13:52

4 Answers4

34

None of versions provided worked for me, so I've implemented my custom solution. It provides both - location and rotation animation.

/**
 * Method to animate marker to destination location
 * @param destination destination location (must contain bearing attribute, to ensure
 *                    marker rotation will work correctly)
 * @param marker marker to be animated
 */
public static void animateMarker(Location destination, Marker marker) {
    if (marker != null) {
        LatLng startPosition = marker.getPosition();
        LatLng endPosition = new LatLng(destination.getLatitude(), destination.getLongitude());

        float startRotation = marker.getRotation();

        LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setDuration(1000); // duration 1 second
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override public void onAnimationUpdate(ValueAnimator animation) {
                try {
                    float v = animation.getAnimatedFraction();
                    LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, endPosition);
                    marker.setPosition(newPosition);
                    marker.setRotation(computeRotation(v, startRotation, destination.getBearing()));
                } catch (Exception ex) {
                    // I don't care atm..
                }
            }
        });

        valueAnimator.start();
    }
}

Rotation computation for specified fraction of animation. Marker is rotated in direction which is closer from start to end rotation:

private static float computeRotation(float fraction, float start, float end) {
    float normalizeEnd = end - start; // rotate start to 0
    float normalizedEndAbs = (normalizeEnd + 360) % 360;

    float direction = (normalizedEndAbs > 180) ? -1 : 1; // -1 = anticlockwise, 1 = clockwise
    float rotation;
    if (direction > 0) {
        rotation = normalizedEndAbs;
    } else {
        rotation = normalizedEndAbs - 360;
    }

    float result = fraction * rotation + start;
    return (result + 360) % 360;
} 

And finally Google's LatLngInterpolator:

private interface LatLngInterpolator {
    LatLng interpolate(float fraction, LatLng a, LatLng b);

    class LinearFixed implements LatLngInterpolator {
        @Override
        public LatLng interpolate(float fraction, LatLng a, LatLng b) {
            double lat = (b.latitude - a.latitude) * fraction + a.latitude;
            double lngDelta = b.longitude - a.longitude;
            // Take the shortest path across the 180th meridian.
            if (Math.abs(lngDelta) > 180) {
                lngDelta -= Math.signum(lngDelta) * 360;
            }
            double lng = lngDelta * fraction + a.longitude;
            return new LatLng(lat, lng);
        }
    }
}
skywall
  • 3,956
  • 1
  • 34
  • 52
28

Try out the below code to animate the marker on Google Map V2. You need to use the Interpolator class to apply the animation on the Marker and handle it in the Handler for the animation as below:

   public void animateMarker(final Marker marker, final LatLng toPosition,
        final boolean hideMarker) {
    final Handler handler = new Handler();
    final long start = SystemClock.uptimeMillis();
    Projection proj = mGoogleMapObject.getProjection();
    Point startPoint = proj.toScreenLocation(marker.getPosition());
    final LatLng startLatLng = proj.fromScreenLocation(startPoint);
    final long duration = 500;
    final Interpolator interpolator = new LinearInterpolator();
    handler.post(new Runnable() {
        @Override
        public void run() {
            long elapsed = SystemClock.uptimeMillis() - start;
            float t = interpolator.getInterpolation((float) elapsed
                    / duration);
            double lng = t * toPosition.longitude + (1 - t)
                    * startLatLng.longitude;
            double lat = t * toPosition.latitude + (1 - t)
                    * startLatLng.latitude;
            marker.setPosition(new LatLng(lat, lng));
            if (t < 1.0) {
                // Post again 16ms later.
                handler.postDelayed(this, 16);
            } else {
                if (hideMarker) {
                    marker.setVisible(false);
                } else {
                    marker.setVisible(true);
                }
            }
        }
    });
}
GrIsHu
  • 29,068
  • 10
  • 64
  • 102
  • How exactly do you implement this code? @Anil ? can you get the marker moving along gps coordinates? Please post the answer. – momokjaaaaa Jun 18 '13 at 04:27
  • 2
    Hey, thanks a lot. It works. But marker moves back and forth before start marker animation. Can you please tell me how to fix it. – Sadia Oct 30 '13 at 12:06
  • @GrlsHu I ended up with a code similar to yours but I am not able to animate in actual road it animates in straight line can you tell me how to achieve it – Illegal Argument Nov 20 '13 at 10:37
  • 3
    @IllegalArgument Check out http://ddewaele.github.io/GoogleMapsV2WithActionBarSherlock/part3 which will guide you. – GrIsHu Nov 20 '13 at 11:09
  • @GrIsHu I have done that the problem is that sometimes the markers are placed even above buildings and not on path so I tried to do it using polyline – Illegal Argument Nov 20 '13 at 11:15
  • Should i `not` call builder.include(vehicleLocation); instead ? – Siddharth Aug 22 '16 at 11:05
6

Just implemented a version, try this

public class MarkerAnimation {
static GoogleMap map;
ArrayList<LatLng> _trips = new ArrayList<>() ;
Marker _marker;
LatLngInterpolator _latLngInterpolator = new LatLngInterpolator.Spherical();

public void animateLine(ArrayList<LatLng> Trips,GoogleMap map,Marker  marker,Context current){
    _trips.addAll(Trips);
    _marker = marker;

animateMarker();
}

    public void animateMarker() {
        TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
            @Override
            public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
                return _latLngInterpolator.interpolate(fraction, startValue, endValue);
            }
        };
        Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");

        ObjectAnimator animator = ObjectAnimator.ofObject(_marker, property, typeEvaluator, _trips.get(0));

        //ObjectAnimator animator = ObjectAnimator.o(view, "alpha", 0.0f);
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationCancel(Animator animation) {
                //  animDrawable.stop();
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                //  animDrawable.stop();
            }

            @Override
            public void onAnimationStart(Animator animation) {
                //  animDrawable.stop();
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //  animDrawable.stop();
                if (_trips.size() > 1) {
                    _trips.remove(0);
                    animateMarker();
                }
            }
        });

        animator.setDuration(300);
        animator.start();
    } 

LatLngInterpolator class is pre-written by Google guys which you can use as follows:

public interface LatLngInterpolator {

public LatLng interpolate(float fraction, LatLng a, LatLng b);

public class Spherical implements LatLngInterpolator {
    @Override
    public LatLng interpolate(float fraction, LatLng from, LatLng to) {
        // http://en.wikipedia.org/wiki/Slerp
        double fromLat = toRadians(from.latitude);
        double fromLng = toRadians(from.longitude);
        double toLat = toRadians(to.latitude);
        double toLng = toRadians(to.longitude);
        double cosFromLat = cos(fromLat);
        double cosToLat = cos(toLat);

        // Computes Spherical interpolation coefficients.
        double angle = computeAngleBetween(fromLat, fromLng, toLat, toLng);
        double sinAngle = sin(angle);
        if (sinAngle < 1E-6) {
            return from;
        }
        double a = sin((1 - fraction) * angle) / sinAngle;
        double b = sin(fraction * angle) / sinAngle;

        // Converts from polar to vector and interpolate.
        double x = a * cosFromLat * cos(fromLng) + b * cosToLat * cos(toLng);
        double y = a * cosFromLat * sin(fromLng) + b * cosToLat * sin(toLng);
        double z = a * sin(fromLat) + b * sin(toLat);

        // Converts interpolated vector back to polar.
        double lat = atan2(z, sqrt(x * x + y * y));
        double lng = atan2(y, x);
        return new LatLng(toDegrees(lat), toDegrees(lng));
    }

    private double computeAngleBetween(double fromLat, double fromLng, double toLat, double toLng) {
        // Haversine's formula
        double dLat = fromLat - toLat;
        double dLng = fromLng - toLng;
        return 2 * asin(sqrt(pow(sin(dLat / 2), 2) +
                cos(fromLat) * cos(toLat) * pow(sin(dLng / 2), 2)));
    }
}
}

Then instantiate an object of the MarkerAnimation class and call the method like this:

 MarkerAnimation.animateLine(TripPoints,map,MovingMarker,context); 
Chinmoy Panda
  • 935
  • 8
  • 7
0

You just need to add this class and pass a location which you can easily get by using Fusedlocationproviderclient.

    public class LocationMoveAnim {

        public static void startAnimation(final Marker marker, final GoogleMap googleMap, final LatLng startPosition,
                                         final LatLng endPosition,final GoogleMap.CancelableCallback callback) {
          ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
          int duration = 500;
          valueAnimator.setDuration(duration);
          final LatLngInterpolatorNew latLngInterpolator = new LatLngInterpolatorNew.LinearFixed();
          valueAnimator.setInterpolator(new LinearInterpolator());
          valueAnimator.addUpdateListener(valueAnimator1 -> {
            float v = valueAnimator1.getAnimatedFraction();
            LatLng newPos = latLngInterpolator.interpolate(v, startPosition, endPosition);
            marker.setPosition(newPos);
            marker.setAnchor(0.5f, 0.5f);
            marker.setRotation((float) bearingBetweenLocations(startPosition, endPosition));
            googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(newPos,20F));
            callback.onFinish();
          });
          valueAnimator.start();
       }

         private static double bearingBetweenLocations(LatLng latLng1,LatLng latLng2) {

           double PI = 3.14159;
           double lat1 = latLng1.latitude * PI / 180;
           double long1 = latLng1.longitude * PI / 180;
           double lat2 = latLng2.latitude * PI / 180;
           double long2 = latLng2.longitude * PI / 180;
           double dLon = (long2 - long1);
           double y = Math.sin(dLon) * Math.cos(lat2);
           double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
           double brng = Math.atan2(y, x);
           brng = Math.toDegrees(brng);
           brng = (brng + 360) % 360;

           return brng;
        }

        public interface LatLngInterpolatorNew {
           LatLng interpolate(float fraction, LatLng a, LatLng b);
           class LinearFixed implements LatLngInterpolatorNew {
            @Override
            public LatLng interpolate(float fraction, LatLng a, LatLng b) {
                double lat = (b.latitude - a.latitude) * fraction + a.latitude;
                double lngDelta = b.longitude - a.longitude;
                // Take the shortest path across the 180th meridian.
                if (Math.abs(lngDelta) > 180) {
                    lngDelta -= Math.signum(lngDelta) * 360;
                }
                double lng = lngDelta * fraction + a.longitude;
                return new LatLng(lat, lng);
            }
        }
    }
}
Ole Pannier
  • 3,208
  • 9
  • 22
  • 33
Mohak Shah
  • 518
  • 3
  • 12