我希望能够使用 android sdk 在 mapbox 地图上手动绘制复杂的形状。我继承了地图视图类并覆盖了 ondraw 事件,但不幸的是,我绘制的任何内容都被地图本身过度绘制。
例如,我需要能够在其他复杂形状中绘制带有菱形边框的多边形。这在使用自定义磁贴提供程序并覆盖 ondraw 的 GoogleMaps 中我可以做到。
这是迄今为止我对 mapbox 的唯一代码:
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint stroke = new Paint();
stroke.setColor(Color.BLACK);
stroke.setStyle(Paint.Style.STROKE);
stroke.setStrokeWidth(5);
stroke.setAntiAlias(true);
canvas.drawLine(0f,0f,1440f,2464f,stroke);
}
您可以通过两种方式做您想做的事:
1)正如您所建议的:“继承
MapView
类并覆盖onDraw()
事件”。但是MapView
extendsFrameLayout
这是ViewGroup
,所以你应该覆盖dispatchDraw()
而不是onDraw()
.这种方法需要自定义视图,它扩展
MapView
并实现:绘制在
MapView
;自定义线条样式(“菱形而不是简单的线条”);
结合道路
Lat/Lon
的坐标MapView
。对于绘制在
MapView
You should override 上dispatchDraw()
,例如像这样:@Override public void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); canvas.save(); drawDiamondsPath(canvas); canvas.restore(); }
对于自定义线条样式,您可以使用类的setPathEffect()方法
Paint
。为此,您应该为“钻石图章”(以像素为单位)创建路径,这将重复每个“前进”(也以像素为单位):mPathDiamondStamp = new Path(); mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0); mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2); mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0); mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2); mPathDiamondStamp.close(); mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0); mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2); mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0); mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2); mPathDiamondStamp.close(); mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD); mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mDiamondPaint.setColor(Color.BLUE); mDiamondPaint.setStrokeWidth(2); mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE); mDiamondPaint.setStyle(Paint.Style.STROKE); mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE));
(在这种情况下,有 2 个
Path
- 第一个(顺时针)用于外边界,第二个(逆时针)用于“钻石”透明“孔”的内边界。有关屏幕结合路径
Lat/Lon
的坐标MapView
,您应该具有MapboxMap
的对象MapView
-因为这getMapAsync()
和onMapReady()
应该重写:@Override public void getMapAsync(OnMapReadyCallback callback) { mMapReadyCallback = callback; super.getMapAsync(this); } @Override public void onMapReady(MapboxMap mapboxMap) { mMapboxMap = mapboxMap; if (mMapReadyCallback != null) { mMapReadyCallback.onMapReady(mapboxMap); } }
比您可以在“纬度/经度到屏幕”转换中使用它:
mBorderPath = new Path(); LatLng firstBorderPoint = mBorderPoints.get(0); PointF firstScreenPoint = mMapboxMap.getProjection().toScreenLocation(firstBorderPoint); mBorderPath.moveTo(firstScreenPoint.x, firstScreenPoint.y); for (int ixPoint = 1; ixPoint < mBorderPoints.size(); ixPoint++) { PointF currentScreenPoint = mMapboxMap.getProjection().toScreenLocation(mBorderPoints.get(ixPoint)); mBorderPath.lineTo(currentScreenPoint.x, currentScreenPoint.y); }
完整源代码:
自定义 DrawMapView.java
public class DrawMapView extends MapView implements OnMapReadyCallback{ private float DIAMOND_WIDTH = 42; private float DIAMOND_HEIGHT = 18; private float DIAMOND_ADVANCE = 1.5f * DIAMOND_WIDTH; // spacing between each stamp of shape private float DIAMOND_PHASE = DIAMOND_WIDTH / 2; // amount to offset before the first shape is stamped private float DIAMOND_BORDER_WIDTH = 6; // width of diamond border private Path mBorderPath; private Path mPathDiamondStamp; private Paint mDiamondPaint; private OnMapReadyCallback mMapReadyCallback; private MapboxMap mMapboxMap = null; private List<LatLng> mBorderPoints; public DrawMapView(@NonNull Context context) { super(context); init(); } public DrawMapView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public DrawMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public DrawMapView(@NonNull Context context, @Nullable MapboxMapOptions options) { super(context, options); init(); } public void setBorderPoints(List<LatLng> borderPoints) { mBorderPoints = borderPoints; } @Override public void getMapAsync(OnMapReadyCallback callback) { mMapReadyCallback = callback; super.getMapAsync(this); } @Override public void onMapReady(MapboxMap mapboxMap) { mMapboxMap = mapboxMap; if (mMapReadyCallback != null) { mMapReadyCallback.onMapReady(mapboxMap); } } @Override public void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); canvas.save(); drawDiamondsPath(canvas); canvas.restore(); } private void drawDiamondsPath(Canvas canvas) { if (mBorderPoints == null || mBorderPoints.size() == 0) { return; } mBorderPath = new Path(); LatLng firstBorderPoint = mBorderPoints.get(0); PointF firstScreenPoint = mMapboxMap.getProjection().toScreenLocation(firstBorderPoint); mBorderPath.moveTo(firstScreenPoint.x, firstScreenPoint.y); for (int ixPoint = 1; ixPoint < mBorderPoints.size(); ixPoint++) { PointF currentScreenPoint = mMapboxMap.getProjection().toScreenLocation(mBorderPoints.get(ixPoint)); mBorderPath.lineTo(currentScreenPoint.x, currentScreenPoint.y); } mPathDiamondStamp = new Path(); mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0); mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2); mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0); mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2); mPathDiamondStamp.close(); mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0); mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2); mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0); mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2); mPathDiamondStamp.close(); mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD); mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mDiamondPaint.setColor(Color.BLUE); mDiamondPaint.setStrokeWidth(2); mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE); mDiamondPaint.setStyle(Paint.Style.STROKE); mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE)); canvas.drawPath(mBorderPath, mDiamondPaint); } private void init() { mBorderPath = new Path(); mPathDiamondStamp = new Path(); } }
ActivityMain.java
public class MainActivity extends AppCompatActivity { private DrawMapView mapView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MapboxAccountManager.start(this, getString(R.string.access_token)); setContentView(R.layout.activity_main); mapView = (DrawMapView) findViewById(R.id.mapView); mapView.onCreate(savedInstanceState); mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(MapboxMap mapboxMap) { mapView.setBorderPoints(Arrays.asList(new LatLng(-36.930129, 174.958843), new LatLng(-36.877860, 174.978108), new LatLng(-36.846373, 174.901841), new LatLng(-36.829215, 174.814659), new LatLng(-36.791326, 174.779337), new LatLng(-36.767680, 174.823242))); } }); } @Override public void onResume() { super.onResume(); mapView.onResume(); } @Override public void onPause() { super.onPause(); mapView.onPause(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public void onLowMemory() { super.onLowMemory(); mapView.onLowMemory(); } @Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); } }
活动_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:mapbox="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ua.com.omelchenko.mapboxlines.MainActivity"> <ua.com.omelchenko.mapboxlines.DrawMapView android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height="match_parent" mapbox:center_latitude="-36.841362" mapbox:center_longitude="174.851110" mapbox:style_url="@string/style_mapbox_streets" mapbox:zoom="10"/> </RelativeLayout>
最后,您应该得到如下结果:
并且您应该考虑一些“特殊情况”,例如,如果路径的所有点都在地图的当前视图之外,则上面没有线,甚至线应该与地图的视图交叉并且应该可见。
2)(更好的方法)创建,并与其他行并发布地图自定义样式为他们(特别是看看节“处理图像线条图案”)。您可以为此使用Mapbox Studio。在这种方法中,所有“特殊情况”和性能问题都在 Mabpox 端解决。