Android - 如何让谷歌地图显示一条动画连续闪烁点的折线
Posted
技术标签:
【中文标题】Android - 如何让谷歌地图显示一条动画连续闪烁点的折线【英文标题】:Android - How to make google Maps display a polyline that animates sequenctial flashing dots 【发布时间】:2019-05-20 22:41:10 【问题描述】:我正在寻找一种方法来为 android 设备中谷歌地图上两个标记之间的点设置动画。 所以我最终想要的是两个图像之间的以下行:
它会像这个典型的谷歌折线实现一样使用:
假设有一个点 A 和一个点 B。如果我将用户引导到点 B,那么该线会从 A 点动画到 B 点,因此用户知道朝这个方向走。
为了实现这一点,我想我可以从折线中取出点并将它们删除并重新添加 迅速。所以假设我在折线中有 5 个点,我会删除位置 1 ,然后将其放回原处,然后删除位置 2 并将其放回,以模拟此动画。
但它不起作用。一旦设置了多段线,似乎我无法更改它。你有什么建议吗?
val dotPattern = Arrays.asList(Dot(), Gap(convertDpToPixel(7).toFloat()))
val polyLineOptions: PolylineOptions = PolylineOptions()
.add(usersLocation)
.add(users_destination)
.pattern(dotPattern)
.width(convertDpToPixel(6).toFloat())
dottedPolyLine = googleMap.addPolyline(polyLineOptions)
dottedPolyLine?.points?.removeAt(1) // 这里作为一个测试,如果我的想法我尝试删除一个点,但它看起来这里的一个点表示当前位置或目的地,所以总是有 2。我认为一个点是其中一个点。
【问题讨论】:
【参考方案1】:您可以在this answer 中使用基于 MapView 的自定义视图 View Canvas 动画:
这种方法需要 基于
MapView
custom view, 实现:在 MapView 画布上绘图;
自定义线条样式(圆形而不是简单的线条);
地图纬度/经度坐标的绑定路径
执行动画。
在 MapView 上绘制需要覆盖
dispatchDraw()
。 自定义线型需求setPathEffect()
的方法Paint
允许为“圆形印章”创建路径的类(在 像素),它将重复每个“前进”(也以像素为单位), 类似的东西:mCircleStampPath = new Path(); mCircleStampPath.addCircle(0,0, CIRCLE_RADIUS,路径.方向.逆时针); mCircleStampPath.close();
用于将屏幕上的路径绑定到纬度/经度坐标
Projection.toScreenLocation()
需要,这需要GoogleMap
对象所以自定义视图应该实现OnMapReadyCallback
收到它。对于连续动画postInvalidateDelayed()
可以使用。
但不是直接从 A 点到 B 点绘制路径,而是从 A 点到 C 点的动画从 A 到 B 点。要获取 C 点的当前位置,您可以使用 Google Maps Android API Utility Library 中的 SphericalUtil.interpolate()
。类似的东西:
public class EnhancedMapView extends MapView implements OnMapReadyCallback
private static final float CIRCLE_RADIUS = 10;
private static final float CIRCLE_ADVANCE = 3.5f * CIRCLE_RADIUS; // spacing between each circle stamp
private static final int FRAMES_PER_SECOND = 30;
private static final int ANIMATION_DURATION = 10000;
private OnMapReadyCallback mMapReadyCallback;
private GoogleMap mGoogleMap;
private LatLng mPointA;
private LatLng mPointB;
private LatLng mPointC;
private float mCirclePhase = 0; // amount to offset before the first circle is stamped
private Path mCircleStampPath;
private Paint mPaintLine;
private final Path mPathFromAtoC = new Path();
private long mStartTime;
private long mElapsedTime;
public EnhancedMapView(@NonNull Context context)
super(context);
init();
public EnhancedMapView(@NonNull Context context, @Nullable AttributeSet attrs)
super(context, attrs);
init();
public EnhancedMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
public EnhancedMapView(@NonNull Context context, @Nullable GoogleMapOptions options)
super(context, options);
init();
@Override
public void dispatchDraw(Canvas canvas)
super.dispatchDraw(canvas);
canvas.save();
drawLineFomAtoB(canvas);
canvas.restore();
// perform one shot animation
mElapsedTime = System.currentTimeMillis() - mStartTime;
if (mElapsedTime < ANIMATION_DURATION)
postInvalidateDelayed(1000 / FRAMES_PER_SECOND);
private void drawLineFomAtoB(Canvas canvas)
if (mGoogleMap == null || mPointA == null || mPointB == null)
return;
// interpolate current position
mPointC = SphericalUtil.interpolate(mPointA, mPointB, (float) mElapsedTime / (float)ANIMATION_DURATION);
final Projection mapProjection = mGoogleMap.getProjection();
final Point pointA = mapProjection.toScreenLocation(mPointA);
final Point pointC = mapProjection.toScreenLocation(mPointC);
mPathFromAtoC.rewind();
mPathFromAtoC.moveTo(pointC.x, pointC.y);
mPathFromAtoC.lineTo(pointA.x, pointA.y);
// change phase for circles shift
mCirclePhase = (mCirclePhase < CIRCLE_ADVANCE)
? mCirclePhase + 1.0f
: 0;
mPaintLine.setPathEffect(new PathDashPathEffect(mCircleStampPath, CIRCLE_ADVANCE, mCirclePhase, PathDashPathEffect.Style.ROTATE));
canvas.drawPath(mPathFromAtoC, mPaintLine);
private void init()
setWillNotDraw(false);
mCircleStampPath = new Path();
mCircleStampPath.addCircle(0,0, CIRCLE_RADIUS, Path.Direction.CCW);
mCircleStampPath.close();
mPaintLine = new Paint();
mPaintLine.setColor(Color.BLACK);
mPaintLine.setStrokeWidth(1);
mPaintLine.setStyle(Paint.Style.STROKE);
mPaintLine.setPathEffect(new PathDashPathEffect(mCircleStampPath, CIRCLE_ADVANCE, mCirclePhase, PathDashPathEffect.Style.ROTATE));
// start animation
mStartTime = System.currentTimeMillis();
postInvalidate();
@Override
public void getMapAsync(OnMapReadyCallback callback)
mMapReadyCallback = callback;
super.getMapAsync(this);
@Override
public void onMapReady(GoogleMap googleMap)
mGoogleMap = googleMap;
mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener()
@Override
public void onCameraMove()
invalidate();
);
if (mMapReadyCallback != null)
mMapReadyCallback.onMapReady(googleMap);
public void setPoints(LatLng pointA, LatLng pointB)
mPointA = pointA;
mPointB = pointB;
注意!这只是想法,不是经过完整测试的代码。
【讨论】:
你的工作是天才。感谢你的帮助。我需要的一件事。我不希望这条线过多地接触标记。一旦线到达标记,它就不应该再显示了。我怎么能控制它?我希望有一个 z-index 来使标记出现在线上。你有什么建议?我只需要这条线不要越过标记的顶部。 @j2emanue 感谢,但我只是 Android 工程师 :) 由于直接在 View 画布上绘图,因此无法考虑 Google Maps z-index。可能的解决方法 - 将标记 LatLng 转换为屏幕坐标(通过mapProjection.toScreenLocation(marker.getPosition());
)并在drawLineFomAtoB(canvas);
调用后在画布上手动绘制标记。标记可以像this 问题的答案一样绘制为位图。或者您可以在 A 点稍靠后和 B 点之前画线,以避免与标记相交。以上是关于Android - 如何让谷歌地图显示一条动画连续闪烁点的折线的主要内容,如果未能解决你的问题,请参考以下文章
如何在Android上使用谷歌地图或mapquest动态突出显示道路的一侧(或在旁边画一条线)