代码之家  ›  专栏  ›  技术社区  ›  j2emanue

Android-如何让谷歌地图显示一条多段线,为连续闪点设置动画

  •  1
  • j2emanue  · 技术社区  · 6 年前

    我正在寻找一种在安卓设备上,在谷歌地图上为两个标记之间的点设置动画的方法。
    最后我想要的是两幅图像之间的一行:

    enter image description here

    enter image description here

    假设有一个点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 回复  |  直到 6 年前
        1
  •  1
  •   Andrii Omelchenko    6 年前

    可以在中使用基于MapView的自定义视图画布动画 this answer :

    这种方法需要 MapView custom view , 这将实现:

    • 在MapView画布上绘制;

    • 自定义线样式(圆形而不是简单的线);

    • 将路径绑定到地图的Lat/Lon坐标

    在地图视图上绘制需要替代 dispatchDraw() . setPathEffect() Paint 类,该类允许为“圆戳记”(在 像素),每次“前进”都会重复(也以像素为单位), 诸如此类:

    圆(半径、路径、方向、逆时针方向);mCircleStampPath.close();

    用于将屏幕上的路径绑定到Lat/Lon坐标 Projection.toScreenLocation() 需要,那就需要 GoogleMap 对象,因此自定义视图应该实现 OnMapReadyCallback 对于 postInvalidateDelayed()

    SphericalUtil.interpolate() 从…起 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;
        }
    
    }