我在我的项目中使用google maps
。如果标记没有显示在屏幕上,我想将箭头设置为屏幕。我该如何控制屏幕?
答案 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>
结果你应该得到类似的东西: