i am using google maps v2. i have a marker on map, this marker changes rotation every while. I want to animate the rotation of my maker to rotate smoothly. Can anyone help please
问题:
回答1:
That's mine implementation of smooth marker movement with maker image rotation (when it's set to FLAT [important]). The marker is moved to the requested location smoothly plus it's rotated to the requested degree with a proper direction. For example, when moving from 5deg to 355deg it moves anticlockwise, when 355deg to 5deg it moves clockwise.
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);
}
}
}
Missing implementation of "toLatLong" method:
public static LatLng toLatLng(final Location location)
{
return new LatLng(location.getLatitude(), location.getLongitude());
}
I hope that helps.
回答2:
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);
}
}
});
}
i have managed to do it :)
回答3:
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;
}
}
});
}
}
This is how I rotate the marker, I don't know this is the best code or not. But I think it will prevent wrong rotation. EDIT: Add var isRotating (Thanks @Trần Văn Huy)
回答4:
With @Bhagaskara Liancer answer.Add var isMarkerRotating with help marker rotate smoothly.
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;
}
}
});
}
}
回答5:
Here is the simple example You can use the following MarkerAnimation class for animating marker with rotation.
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;
}
}
Just call the following code for moving the marker when the location is changed.
if (carMarker != null) {
MarkerAnimation.move(mMap,carMarker, location);
}
回答6:
You would want to use 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.
回答7:
Had the same problem, in my case I used 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();
}