web-dev-qa-db-fra.com

Comment animer le marqueur dans Android map api V2?

Je souhaite implémenter une transition en douceur pour émuler le marqueur de voiture se déplaçant sur la carte.

Est-il possible d'animer un marqueur dans Android map api v2?

29
Alexey Zakharov

Essayez le code ci-dessous pour animer le marqueur sur Google Map V2. Vous devez utiliser la classe Interpolator pour appliquer l'animation sur le marqueur et la gérer dans le gestionnaire de l'animation comme ci-dessous:

   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);
                }
            }
        }
    });
}
26
GrIsHu

Aucune des versions fournies ne fonctionnait pour moi, j'ai donc implémenté ma solution personnalisée. Il fournit à la fois - l'emplacement et l'animation de rotation.

/**
 * 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();
    }
}

Calcul de rotation pour la fraction d'animation spécifiée. Le marqueur est tourné dans une direction plus proche du début à la fin de la 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;
} 

Et enfin LatLngInterpolator de Google:

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);
        }
    }
}
23
skywall

Je viens d'implémenter une version, essayez ceci

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();
    } 

La classe LatLngInterpolator est pré-écrite par les gars de Google que vous pouvez utiliser comme suit:

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)));
    }
}
}

Instanciez ensuite un objet de la classe MarkerAnimation et appelez la méthode comme ceci:

 MarkerAnimation.animateLine(TripPoints,map,MovingMarker,context); 
4
Chinmoy Panda