Google地图屏幕控制

时间:2017-12-28 09:58:32

标签: android google-maps maps

我在我的项目中使用google maps。如果标记没有显示在屏幕上,我想将箭头设置为屏幕。我该如何控制屏幕?

1 个答案:

答案 0 :(得分:0)

实际上,你的问题的答案是lakshman.pasala评论,但它的实现有点复杂(TLDR)。

因此,获得Google地图屏幕控制的最佳方式是implement custom view,其扩展为MapView级。在这种情况下,您可以完全控制在视图画布上绘制。为此,您应该覆盖dispatchDraw()方法(因为MapView扩展FrameLayout ViewGroup)并在其中实现箭头图。这样的事情:

@Override
public void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    canvas.save();
    drawArrowToMarker(canvas);
    canvas.restore();
}

您需要在每个地图移动/缩放/旋转时通过invalidate()调用它。要检测地图移动/缩放/旋转,您需要GoogleMap(完全GoogleMap.setOnCameraMoveListener()方法)。您可以在自定义MapView类中声明GoogleMap对象并通过setter设置它,但如果自定义MapView类将实现OnMapReadyCallback接口并在onMapReady()回调中获取它,则会更好。您还需要几种实用方法来添加/删除标记,确定段交叉点,方向等。 自定义视图的完整源代码(例如EnhanchedMapView)可以是:

public class EnhanchedMapView extends MapView implements OnMapReadyCallback {

    private final static int ARROW_PADDING = 50;
    private final static double ARROW_ANGLE = Math.PI / 6;
    private final static double ARROW_LENGTH = 100;
    private final static double ARROW_SIZE = 50;

    private OnMapReadyCallback mMapReadyCallback;
    private GoogleMap mGoogleMap;
    private Marker mMarker;
    private Paint mPaintArrow;

    public EnhanchedMapView(@NonNull Context context) {
        super(context);
        init();
    }

    public EnhanchedMapView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public EnhanchedMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public EnhanchedMapView(@NonNull Context context, @Nullable GoogleMapOptions options) {
        super(context, options);
        init();
    }

    @Override
    public void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        canvas.save();
        drawArrowToMarker(canvas);
        canvas.restore();
    }

    private void drawArrowToMarker(Canvas canvas) {
        if (mGoogleMap == null || mMarker == null) {
            return;
        }

        VisibleRegion visibleRegion = mGoogleMap.getProjection().getVisibleRegion();
        LatLngBounds screenBounds = visibleRegion.latLngBounds;

        LatLng mapCenter = screenBounds.getCenter();
        Projection mapProjection = mGoogleMap.getProjection();

        final Point pointMapCenter = mGoogleMap.getProjection().toScreenLocation(mapCenter);

        final Point pointTopLeft = mapProjection.toScreenLocation(visibleRegion.farLeft);
        final Point pointTopRight = mapProjection.toScreenLocation(visibleRegion.farRight);
        final Point pointBottomLeft = mapProjection.toScreenLocation(visibleRegion.nearLeft);
        final Point pointBottomRight = mapProjection.toScreenLocation(visibleRegion.nearRight);
        final Point pointMarker = mapProjection.toScreenLocation(mMarker.getPosition());

        final Point tl = new Point(pointTopLeft.x + ARROW_PADDING, pointTopLeft.y + ARROW_PADDING);
        final Point tr = new Point(pointTopRight.x - ARROW_PADDING, pointTopRight.y + ARROW_PADDING);
        final Point br = new Point(pointBottomRight.x - ARROW_PADDING, pointBottomRight.y - ARROW_PADDING);
        final Point bl = new Point(pointBottomLeft.x + ARROW_PADDING, pointBottomLeft.y - ARROW_PADDING);

        final Point pointIntersection = getBoundsIntersection(tl, tr, br, bl, pointMapCenter, pointMarker);
        if (pointIntersection != null) {
            double angle = Math.atan2(pointMarker.y - pointMapCenter.y, pointMarker.x - pointMapCenter.x);

            int arrowX, arrowY;
            arrowX = (int) (pointIntersection.x - ARROW_LENGTH * Math.cos(angle));
            arrowY = (int) (pointIntersection.y - ARROW_LENGTH * Math.sin(angle));
            canvas.drawLine(pointIntersection.x, pointIntersection.y, arrowX, arrowY, mPaintArrow);

            arrowX = (int) (pointIntersection.x - ARROW_SIZE * Math.cos(angle + ARROW_ANGLE));
            arrowY = (int) (pointIntersection.y - ARROW_SIZE * Math.sin(angle + ARROW_ANGLE));
            canvas.drawLine(pointIntersection.x, pointIntersection.y, arrowX, arrowY, mPaintArrow);

            arrowX = (int) (pointIntersection.x - ARROW_SIZE * Math.cos(angle - ARROW_ANGLE));
            arrowY = (int) (pointIntersection.y - ARROW_SIZE * Math.sin(angle - ARROW_ANGLE));
            canvas.drawLine(pointIntersection.x, pointIntersection.y, arrowX, arrowY, mPaintArrow);
        }
    }

    private void init() {
        setWillNotDraw(false);

        mPaintArrow = new Paint();
        mPaintArrow.setColor(Color.BLUE);
        mPaintArrow.setStrokeWidth(15);
    }

    @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 addMarker(MarkerOptions markerOptions) {
        removeMarker();
        mMarker = mGoogleMap.addMarker(markerOptions);
    }

    public void removeMarker() {
        mGoogleMap.clear();
    }

    private static boolean floatEquals(float f1, float f2) {
        final double EPS = 1e-6;
        return (Math.abs(f1 - f2) < EPS);
    }

    private static Point getBoundIntersection(Point p11, Point p12, Point p21, Point p22) {
        double x, y;
        Point intersectionPoint = null;

        // test intersection with vertical bound
        if (floatEquals(p12.x, p11.x) || floatEquals(p22.x, p21.x)) {
            if (floatEquals(p12.x, p11.x) && floatEquals(p22.x, p21.x) && !floatEquals(p11.x, p21.x)) {
                return null;
            } else {
                if (floatEquals(p12.x, p11.x)) {
                    x = p11.x;
                    y = (x - p21.x) / (p22.x - p21.x) * (p22.y - p21.y) + p21.y;

                    if (x >= Math.min(p21.x, p22.x) && x <= Math.max(p21.x, p22.x)
                            && y >= Math.min(p11.y, p12.y) && y <= Math.max(p11.y, p12.y)) {
                        intersectionPoint = new Point((int) x, (int) y);
                    }
                } else {
                    x = p21.x;
                    y = (x - p11.x) / (p12.x - p11.x) * (p12.y - p11.y) + p11.y;

                    if (x >= Math.min(p11.x, p12.x) && x <= Math.max(p11.x, p12.x)
                            && y >= Math.min(p21.y, p22.y) && y <= Math.max(p21.y, p22.y)) {
                        intersectionPoint = new Point((int) x, (int) y);
                    }
                }
            }
        } else {
            // test intersection with horizontal bound
            if (floatEquals(p12.y, p11.y) || floatEquals(p22.y, p21.y)) {
                if (floatEquals(p12.y, p11.y) && floatEquals(p22.y, p21.y) && !floatEquals(p11.y, p21.y)) {
                    return null;
                } else {
                    if (floatEquals(p12.y, p11.y)) {
                        y = p12.y;
                        x = (y - p21.y) / (p22.y - p21.y) * (p22.x - p21.x) + p21.x;

                        if (x >= Math.min(p11.x, p12.x) && x <= Math.max(p11.x, p12.x)
                                && y >= Math.min(p21.y, p22.y) && y <= Math.max(p21.y, p22.y)) {
                            intersectionPoint = new Point((int) x, (int) y);
                        }
                    } else {
                        y = p21.y;
                        x = (y - p11.y) / (p12.y - p11.y) * (p12.x - p11.x) + p11.x;

                        if (x >= Math.min(p21.x, p22.x) && x <= Math.max(p21.x, p22.x)
                                && y >= Math.min(p11.y, p12.y) && y <= Math.max(p11.y, p12.y)) {
                            intersectionPoint = new Point((int) x, (int) y);
                        }
                    }

                }
            }
        }

        return intersectionPoint;
    }

    private static Point getBoundsIntersection(Point tl, Point tr, Point br, Point bl, Point p1, Point p2) {
        Point intersectionPoint = null;
        if ((intersectionPoint = getBoundIntersection(tl, tr, p1, p2)) != null) {
            return intersectionPoint;
        } else if ((intersectionPoint = getBoundIntersection(tr, br, p1, p2)) != null) {
            return intersectionPoint;
        } else if ((intersectionPoint = getBoundIntersection(br, bl, p1, p2)) != null) {
            return intersectionPoint;
        } else if ((intersectionPoint = getBoundIntersection(bl, tl, p1, p2)) != null) {
            return intersectionPoint;
        }
        return null;
    }

}

你可以用MainActivity这种方式使用它:

public class MainActivity extends AppCompatActivity {

    private static final String MAP_VIEW_BUNDLE_KEY = "MapViewBundleKey";
    static final LatLng KYIV = new LatLng(50.450311, 30.523730);

    private EnhanchedMapView 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 = (EnhanchedMapView) findViewById(R.id.mapview);
        mMapView.onCreate(mapViewBundle);
        mMapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                mMapView.addMarker(new MarkerOptions().position(KYIV).title("Kyiv"));
            }
        });

    }

    @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="{YOUR_PACKAGE_NAME}.MainActivity">

    <{YOUR_PACKAGE_NAME}.EnhanchedMapView
        android:id="@+id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</RelativeLayout>

结果你应该得到类似的东西:

Arrow to marker