在 google 地图 v2 中为标记的旋转设置动画

Animate the rotation of the Marker in google map v2

我正在使用 google 地图 v2。 我在地图上有一个标记,这个标记每时每刻都会改变旋转。 我想为我的制造商的旋转设置动画以平滑旋转。 谁能帮忙

你会想要使用 MarkerOption rotation method

 public MarkerOptions rotation (float rotation)

Sets the rotation of the marker in degrees clockwise about the marker's anchor point. The axis of rotation is perpendicular to the marker. A rotation of 0 corresponds to the default position of the marker. When the marker is flat on the map, the default position is North aligned and the rotation is such that the marker always remains flat on the map. When the marker is a billboard, the default position is pointing up and the rotation is such that the marker is always facing the camera. The default value is 0.

Returns the object for which the method was called, with the new rotation set.

static public void rotateMarker(final Marker marker, final float toRotation, GoogleMap map) {
    final Handler handler = new Handler();
    final long start = SystemClock.uptimeMillis();
    final float startRotation = marker.getRotation();
    final long duration = 1555;

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

            float rot = t * toRotation + (1 -t) * startRotation;

            marker.setRotation(-rot > 180 ? rot/2 : rot);
            if (t < 1.0) {
                // Post again 16ms later.
                handler.postDelayed(this, 16);
            } 
        }
    });
}

我做到了:)

这是我实现的带有标记图像旋转的平滑标记移动(当它设置为 FLAT [重要])。标记被平滑地移动到请求的位置,并且它以正确的方向旋转到请求的度数。例如,从5deg移动到355deg时逆时针移动,当355deg移动到5deg时顺时针移动。

public void animateMarker(final Location location)
{
    if (myMarkerLOC != null) {
        final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
        ValueAnimator valueAnimator = new ValueAnimator();
        final LatLng startPosition = myMarkerLOC.getPosition();
        final float startRotation = myMarkerLOC.getRotation();
        final float angle = 180 - Math.abs(Math.abs(startRotation - location.getBearing()) - 180);
        final float right = WhichWayToTurn(startRotation, location.getBearing());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                try {
                    if (myMarkerLOC == null) // oops... destroying map during animation...
                    {
                        return;
                    }
                    float v = animation.getAnimatedFraction();
                    LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, PositionUtil.toLatLng(location));
                    float rotation = startRotation + right * v * angle;
                    myMarkerLOC.setRotation((float) rotation);
                    myMarkerLOC.setPosition(newPosition);
                } catch (Exception ex) {
                    // I don't care atm..
                }
            }
        });
        valueAnimator.setFloatValues(0, 1);
        valueAnimator.setDuration(300);
        valueAnimator.start();
    }
}
private float WhichWayToTurn(float currentDirection, float targetDirection)
{
    float diff = targetDirection - currentDirection;
    if (Math.abs(diff) == 0)
    {
        return 0;
    }
    if(diff > 180)
    {
        return -1;
    }
    else
    {
        return 1;
    }
}
public interface LatLngInterpolator 
{
    public LatLng interpolate(float fraction, LatLng a, LatLng b);

    public class Linear implements LatLngInterpolator 
    {
        @Override
        public LatLng interpolate(float fraction, LatLng a, LatLng b) 
        {
            double lat = (b.latitude - a.latitude) * fraction + a.latitude;
            double lng = (b.longitude - a.longitude) * fraction + a.longitude;
            return new LatLng(lat, lng);
        }
    }
    public 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);
        }
    }
}

缺少 "toLatLong" 方法的实现:

 public static LatLng toLatLng(final Location location)
 {
    return new LatLng(location.getLatitude(), location.getLongitude());
 }

希望对您有所帮助。

boolean isRotating = false;
public void rotateMarker(final Marker marker, final float toRotation) {
    if(!isRotating) {
        isRotating = true;
        final Handler handler = new Handler();
        final long start = SystemClock.uptimeMillis();
        final float startRotation = marker.getRotation();
        final long duration = 1000;
        float deltaRotation = Math.abs(toRotation - startRotation) % 360;
        final float rotation = (deltaRotation > 180 ? 360 - deltaRotation : deltaRotation) *
                ((toRotation - startRotation >= 0 && toRotation - startRotation <= 180) || (toRotation - startRotation <= -180 && toRotation - startRotation >= -360) ? 1 : -1);

        final LinearInterpolator interpolator = new LinearInterpolator();
        handler.post(new Runnable() {
            @Override
            public void run() {
                long elapsed = SystemClock.uptimeMillis() - start;
                float t = interpolator.getInterpolation((float) elapsed / duration);
                marker.setRotation((startRotation + t * rotation) % 360);
                if (t < 1.0) {
                    // Post again 16ms later.
                    handler.postDelayed(this, 16);
                } else {
                    isRotating = false;
                }
            }
        });
    }
}

这就是我旋转标记的方式,我不知道这是不是最好的代码。 但我认为它会防止错误的轮换。编辑:添加 var isRotating(感谢@Trần Văn Huy)

更新说明:

使用@Bhagaskara Liancer answer.Add var isMarkerRotating 帮助标记平滑旋转。

private boolean isMarkerRotating = false;
public void rotateMarker(final Marker marker, final float toRotation) {
    if(!isMarkerRotating){
        final Handler handler = new Handler();
        final long start = SystemClock.uptimeMillis();
        final float startRotation = marker.getRotation();
        final long duration = 1000;
        float deltaRotation = Math.abs(toRotation - startRotation) % 360;
        final float rotation = (deltaRotation > 180 ? 360 - deltaRotation : deltaRotation) * ((toRotation - startRotation >= 0 && toRotation - startRotation <= 180)
                || (toRotation - startRotation <=-180 && toRotation- startRotation>= -360) ? 1 : -1);

        final LinearInterpolator interpolator = new LinearInterpolator();
        handler.post(new Runnable() {
            @Override
            public void run() {
                isMarkerRotating = true;
                long elapsed = SystemClock.uptimeMillis() - start;
                float t = interpolator.getInterpolation((float) elapsed / duration);
                marker.setRotation((startRotation + t* rotation)%360);
                if (t < 1.0) {
                    // Post again 16ms later.
                    handler.postDelayed(this, 16);
                }else {
                    isMarkerRotating = false;
                }
            }
        });
    }

}

这是一个简单的例子 您可以使用以下 MarkerAnimation class 为带旋转的标记设置动画。

import android.graphics.Point;
import android.location.Location;
import android.os.Handler;
import android.os.SystemClock;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;

import com.gogrocerycart.settings.Constants;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.Projection;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;

/**
 * Created by Vinil Chandran on 7/6/18.
 */

public class MarkerAnimation {
    private static Location fromPosition;
    private static float angle = 0;
    public static void move(GoogleMap mMap, final Marker marker, final Location toPosition) {
        if (fromPosition != null && marker != null && toPosition != null) {
            final Handler handlerRotation = new Handler();
            final long startAngle = SystemClock.uptimeMillis();
            final float startRotation = marker.getRotation();
            final long durationRotation = 300;
            final Interpolator interpolatorRotation = new LinearInterpolator();
            float bearing = fromPosition.bearingTo(toPosition);
            Print.e("Bearing:" + bearing);
            angle = bearing<0?(360+bearing):bearing;
            angle = angle%360f;
            Print.e("Angle:" + angle);
            handlerRotation.post(new Runnable() {
                @Override
                public void run() {
                    long elapsed = SystemClock.uptimeMillis() - startAngle;
                    float t = interpolatorRotation.getInterpolation((float) elapsed / durationRotation);
                    float rot = t * angle + (1 - t) * startRotation;
                    float mAngle = -rot > 180 ? rot / 2 : rot;
                    marker.setRotation(mAngle);

                    if (t < 1.0) {
                        handlerRotation.postDelayed(this, 16);
                    } else {
                        final Handler handler = new Handler();
                        final long start = SystemClock.uptimeMillis();
                        Projection projection = mMap.getProjection();
                        Point startPoint = projection.toScreenLocation(marker.getPosition());
                        final LatLng startLatLng = projection.fromScreenLocation(startPoint);
                        final long duration = Constants.LOCATION_REQUEST_INTERVAL;
                        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.getLongitude() + (1 - t)
                                        * startLatLng.longitude;
                                double lat = t * toPosition.getLatitude() + (1 - t)
                                        * startLatLng.latitude;
                                marker.setPosition(new LatLng(lat, lng));
                                if (t < 1.0) {
                                    // Post again 16ms later.
                                    handler.postDelayed(this, 16);
                                }
                            }
                        });
                    }
                }
            });
        }
        fromPosition = toPosition;
    }
}

当位置发生变化时,只需调用以下代码移动标记即可。

if (carMarker != null) {
      MarkerAnimation.move(mMap,carMarker, location);
}

有同样的问题,我用的是ValueAnimator

    static public void rotateMarker(final Marker marker, final float toRotation) {

    ValueAnimator animator = ValueAnimator.ofFloat(marker.getRotation(), toRotation);
    animator.setDuration(duration);
    animator.setInterpolator(new AccelerateDecelerateInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float rotate = Float.parseFloat(animation.getAnimatedValue().toString());
            marker.setRotation(rotate);
        }
    });
    animator.start();
}

Alexandr Kolesnik 的回答似乎是最简单的。 这是它的 Kotlin 扩展版本。
您可以传入一个插值器以避免出现小问题。

fun Marker.animateRotation(toRotation: Float, interpolator: TimeInterpolator? = AccelerateDecelerateInterpolator()) {
    val animator = ValueAnimator.ofFloat(rotation, toRotation)
    animator.duration = 500
    animator.interpolator = interpolator
    animator.addUpdateListener {
        rotation = it.animatedValue as? Float ?: return@addUpdateListener
    }

    animator.start()
}