Google Maps - создание анимации, такой как приложение для захвата, чтобы показать направление пути - PullRequest
0 голосов
/ 26 декабря 2018

Я хотел бы сделать анимацию, которая делает движущийся маркер вдоль пути.

, поэтому я подумал, как сделать это, используя маркер, который немного темнее, чем линия пути.и удаление и обновление своей позиции очень медленно, так что это выглядит как движение.но я просто не думаю, что это то, что происходит в видео.потому что рынок полностью принимает форму пути.это течет отлично.

вот что у меня есть:

fun showLineAtUsersLocation(loc_destination: LatLng) {
    val currentLoc = activity.getCachedCurrentLoc()
        val pattern = Arrays.asList(Dash(), Gap(convertDpToPixel(6).toFloat()))
        val polyLineOptions: PolylineOptions = PolylineOptions()
                .add(currentLoc)
                .add(loc_destination)
                .geodesic(true)
                .pattern(pattern)
                .width(convertDpToPixel(8).toFloat())
        googleMap.addPolyline(polyLineOptions)
}

, но это просто показывает линию наложения от одной точки к другой.не совсем следуя пути.Что я должен искать вместо этого?

Я видел, что у ios есть класс , подходящий для этого GMSStyleSpan для манипулирования изображением.но я не могу найти эквивалент в Android.я не вижу класс span или стиль, я могу получить ссылку на любые идеи по этому поводу?или даже с AnomatedVectorDrawable на маркере вместо

1 Ответ

0 голосов
/ 27 декабря 2018

Для такой (и фактически любой) анимации вы можете использовать анимацию View Canvas.Этот подход требует MapView на основе пользовательского представления , которое реализует:

  • рисование на холсте MapView;

  • настройка стилей линий (круги вместо простой линии);

  • привязка пути к координатам карты в широте и долготе

  • выполнение анимации.

При рисовании поверх MapView необходимо переопределить dispatchDraw().Для настройки стилей линий необходим setPathEffect() метод класса Paint, который позволяет создать путь создания для "круговой печати" (в пикселях), которая будет повторяться при каждом "продвижении" (впикселей), что-то вроде этого:

mCircleStampPath = new Path();
mCircleStampPath.addCircle(0,0, CIRCLE_RADIUS, Path.Direction.CCW);
mCircleStampPath.close();

Для привязки пути на экране к координатам широты / долготы Projection.toScreenLocation() требуется, для которого требуется GoogleMap объектпоэтому пользовательский вид должен реализовывать OnMapReadyCallback для его получения.Для непрерывной анимации можно использовать postInvalidateDelayed().Итак, с полным исходным кодом MapView на основе пользовательских EnhancedMapView:

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 OnMapReadyCallback mMapReadyCallback;
    private GoogleMap mGoogleMap;
    private LatLng mPointA;
    private LatLng mPointB;

    private float mCirclePhase = 0;                                     // amount to offset before the first circle is stamped
    private Path mCircleStampPath;
    private Paint mPaintLine;
    private final Path mPathFromAtoB = new Path();

    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 continuous animation
        postInvalidateDelayed(1000 / FRAMES_PER_SECOND);
    }

    private void drawLineFomAtoB(Canvas canvas) {
        if (mGoogleMap == null || mPointA == null || mPointB == null) {
            return;
        }

        final Projection mapProjection = mGoogleMap.getProjection();
        final Point pointA = mapProjection.toScreenLocation(mPointA);
        final Point pointB = mapProjection.toScreenLocation(mPointB);

        mPathFromAtoB.rewind();
        mPathFromAtoB.moveTo(pointB.x, pointB.y);
        mPathFromAtoB.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(mPathFromAtoB, 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));

        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;
    }

}

MainActivity вроде:

public class MainActivity extends AppCompatActivity {

    private static final String MAP_VIEW_BUNDLE_KEY = "MapViewBundleKey";
    static final LatLng MAIDAN = new LatLng(50.450891, 30.522843);
    static final LatLng SOPHIA = new LatLng(50.452967, 30.514498);

    static final LatLng INITIAL_MAP_CENTER = new LatLng(50.452011, 30.518766);
    static final int INITIAL_ZOOM = 15;

    private GoogleMap mGoogleMap;
    private EnhancedMapView mMapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Bundle mapViewBundle = null;
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAP_VIEW_BUNDLE_KEY);
        }

        mMapView = (EnhancedMapView) findViewById(R.id.mapview);
        mMapView.onCreate(mapViewBundle);
        mMapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                mGoogleMap = googleMap;
                mMapView.setPoints(MAIDAN, SOPHIA);
                mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(INITIAL_MAP_CENTER, INITIAL_ZOOM));
            }
        });

    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        Bundle mapViewBundle = outState.getBundle(MAP_VIEW_BUNDLE_KEY);
        if (mapViewBundle == null) {
            mapViewBundle = new Bundle();
            outState.putBundle(MAP_VIEW_BUNDLE_KEY, mapViewBundle);
        }

        mMapView.onSaveInstanceState(mapViewBundle);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mMapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mMapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mMapView.onStop();
    }
    @Override
    protected void onPause() {
        mMapView.onPause();
        super.onPause();
    }
    @Override
    protected void onDestroy() {
        mMapView.onDestroy();
        super.onDestroy();
    }
    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mMapView.onLowMemory();
    }

}

и activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.test.just.googlemapsgeneral.views.EnhancedMapView
        android:id="@+id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</RelativeLayout>

вы получили что-то вроде:

animated line

NB!Вы должны использовать Path вместо рисования линий.И это только подход, а не полное решение.

...