如何在android map api V2中为标记设置动画?
Posted
技术标签:
【中文标题】如何在android map api V2中为标记设置动画?【英文标题】:How to animate marker in android map api V2? 【发布时间】:2012-12-02 02:35:03 【问题描述】:我想实现平滑过渡以模拟汽车标记在地图上的移动。
是否可以在 android map api v2 中为标记设置动画?
【问题讨论】:
据我了解,您需要原生地图 API V2 行为才能将标记从一个位置顺利移动到另一个位置? 你可以看看这个帖子***.com/questions/13728041/… How to animate marker when it is added to map on Android?的可能重复 【参考方案1】:提供的版本都不适合我,因此我实施了我的自定义解决方案。它同时提供位置和旋转动画。
/**
* 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();
指定动画部分的旋转计算。标记从开始到结束旋转的方向旋转:
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;
最后是谷歌的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);
【讨论】:
我正在尝试实现这个我如何将轴承添加到目的地?,你有最终项目的github上的任何来源吗? @FrankOdoom 您应该通过setBearing(float)
或bearingTo(Location)
方法在传递的Location destination
参数上正确设置方位。
这是我找到的最佳答案。完全没有闪烁。
如何设置方位
@M.Yogeshwaran 在此讨论中查找两条消息【参考方案2】:
尝试使用以下代码为 Google Map V2 上的标记设置动画。
您需要使用Interpolator
类将动画应用到Marker 上,并在动画的Handler 中进行处理,如下所示:
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); );
【讨论】:
非常感谢。这非常有帮助。 你是如何实现这段代码的? @阿尼尔?你能让标记沿着gps坐标移动吗?请发布答案。 嘿,非常感谢。有用。但是标记在开始标记动画之前来回移动。你能告诉我如何解决它。 @GrlsHu 我最终得到了一个与你的代码相似的代码,但我无法在直线动画的实际道路上制作动画,你能告诉我如何实现它 @IllegalArgument 查看ddewaele.github.io/GoogleMapsV2WithActionBarSherlock/part3,它将为您提供指导。【参考方案3】:刚刚实现了一个版本,试试这个
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 类是由 Google 人员预先编写的,您可以按如下方式使用:
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)));
然后实例化一个 MarkerAnimation 类的对象,调用方法如下:
MarkerAnimation.animateLine(TripPoints,map,MovingMarker,context);
【讨论】:
【参考方案4】:您只需要添加这个类并传递一个您可以使用 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);
【讨论】:
以上是关于如何在android map api V2中为标记设置动画?的主要内容,如果未能解决你的问题,请参考以下文章
Google android maps api v2 始终显示标记标题
在 Google Maps API v2 Android 中添加多个标记
Google Maps Android API v2,错误的标记位置