Android - 如何使 google 地图显示一条多段线,使连续闪烁的点动画化
Android - How to make google Maps display a polyline that animates sequenctial flashing dots
我正在寻找一种方法来为 android 设备中 google 地图上两个标记之间的点设置动画。
所以我最后想要的是两张图片之间的下一行:
它会像这个典型的 google 折线实现一样使用:
假设有一个点 A 和一个点 B。如果我将用户引导到点 B,那么这条线将以动画形式从点 A 到点 B,这样用户就知道要朝这个方向走。
为了实现这一点,我想我可以从多段线中取出点并将它们删除并添加回去
迅速地。所以假设我在多段线中有 5 个点,我会删除位置 1,然后将其放回原处,然后删除位置 2,然后放回原处,以模拟此动画。
但它不起作用。一旦设置了 hte 折线,我似乎无法更改它。你有什么建议吗?
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. 我认为一个点将是点之一。
你可以使用基于MapView的自定义视图View Canvas animationlike in :
This approach requires
MapView
-based
custom
view,
that implements:
drawing over the MapView canvas;
customizing line styles (circles instead of a simple line);
binding path to Lat/Lon coordinates of map
performing animation.
Drawing over the MapView needs to override dispatchDraw()
.
Customizing line styles needs
setPathEffect()
method of
Paint
class that allows to create create path for "circle stamp" (in
pixels), which will repeated every "advance" (in pixels too),
something like that:
mCircleStampPath = new Path(); mCircleStampPath.addCircle(0,0,
CIRCLE_RADIUS, Path.Direction.CCW); mCircleStampPath.close();
For binding path on screen to Lat/Lon coordinates
Projection.toScreenLocation()
needed, that requires
GoogleMap
object so custom view should implements OnMapReadyCallback
for
receive it. For continuous animation
postInvalidateDelayed()
can be used.
但不是直接从 A 点绘制路径到 B 点,而是从 A 点到 C 点绘制从 A 到 B 的动画。要获取 C 点的当前位置,您可以使用 SphericalUtil.interpolate()
from Google Maps Android API Utility Library。类似的东西:
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;
}
}
注意!这只是想法,没有经过完整测试的代码。
我正在寻找一种方法来为 android 设备中 google 地图上两个标记之间的点设置动画。
所以我最后想要的是两张图片之间的下一行:
它会像这个典型的 google 折线实现一样使用:
假设有一个点 A 和一个点 B。如果我将用户引导到点 B,那么这条线将以动画形式从点 A 到点 B,这样用户就知道要朝这个方向走。
为了实现这一点,我想我可以从多段线中取出点并将它们删除并添加回去 迅速地。所以假设我在多段线中有 5 个点,我会删除位置 1,然后将其放回原处,然后删除位置 2,然后放回原处,以模拟此动画。
但它不起作用。一旦设置了 hte 折线,我似乎无法更改它。你有什么建议吗?
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. 我认为一个点将是点之一。
你可以使用基于MapView的自定义视图View Canvas animationlike in
This approach requires
MapView
-based custom view, that implements:
drawing over the MapView canvas;
customizing line styles (circles instead of a simple line);
binding path to Lat/Lon coordinates of map
performing animation.
Drawing over the MapView needs to override
dispatchDraw()
. Customizing line styles needssetPathEffect()
method ofPaint
class that allows to create create path for "circle stamp" (in pixels), which will repeated every "advance" (in pixels too), something like that:mCircleStampPath = new Path(); mCircleStampPath.addCircle(0,0, CIRCLE_RADIUS, Path.Direction.CCW); mCircleStampPath.close();
For binding path on screen to Lat/Lon coordinates
Projection.toScreenLocation()
needed, that requiresGoogleMap
object so custom view should implementsOnMapReadyCallback
for receive it. For continuous animationpostInvalidateDelayed()
can be used.
但不是直接从 A 点绘制路径到 B 点,而是从 A 点到 C 点绘制从 A 到 B 的动画。要获取 C 点的当前位置,您可以使用 SphericalUtil.interpolate()
from Google Maps Android API Utility Library。类似的东西:
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;
}
}
注意!这只是想法,没有经过完整测试的代码。