diff options
4 files changed, 175 insertions, 12 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java index f3573c78ba..7eec8710a7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java @@ -9,6 +9,8 @@ import android.support.annotation.Nullable; import android.support.v4.util.LongSparseArray; import android.view.View; +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; @@ -19,6 +21,7 @@ import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.annotations.PolylineOptions; +import com.mapbox.services.commons.geojson.Feature; import java.util.ArrayList; import java.util.List; @@ -36,15 +39,21 @@ import java.util.List; */ class AnnotationManager { + private static final String LAYER_ID_SHAPE_ANNOTATIONS = "com.mapbox.annotations.shape."; + private final MapView mapView; private final IconManager iconManager; private final InfoWindowManager infoWindowManager = new InfoWindowManager(); private final MarkerViewManager markerViewManager; private final LongSparseArray<Annotation> annotationsArray; private final List<Marker> selectedMarkers = new ArrayList<>(); + private final List<String> shapeAnnotationIds = new ArrayList<>(); private MapboxMap mapboxMap; private MapboxMap.OnMarkerClickListener onMarkerClickListener; + private MapboxMap.OnPolygonClickListener onPolygonClickListener; + private MapboxMap.OnPolylineClickListener onPolylineClickListener; + private Annotations annotations; private Markers markers; private Polygons polygons; @@ -107,6 +116,9 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); } + } else { + // instanceOf Polygon/Polyline + shapeAnnotationIds.remove(annotation.getId()); } annotations.removeBy(annotation); } @@ -123,6 +135,9 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); } + } else { + // instanceOf Polygon/Polyline + shapeAnnotationIds.remove(annotation.getId()); } } annotations.removeBy(annotationList); @@ -142,6 +157,9 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); } + } else { + // instanceOf Polygon/Polyline + shapeAnnotationIds.remove(annotation.getId()); } } annotations.removeAll(); @@ -195,11 +213,17 @@ class AnnotationManager { // Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { - return polygons.addBy(polygonOptions, mapboxMap); + Polygon polygon = polygons.addBy(polygonOptions, mapboxMap); + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId()); + return polygon; } List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { - return polygons.addBy(polygonOptionsList, mapboxMap); + List<Polygon> polygonList = polygons.addBy(polygonOptionsList, mapboxMap); + for (Polygon polygon : polygonList) { + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId()); + } + return polygonList; } void updatePolygon(Polygon polygon) { @@ -215,11 +239,17 @@ class AnnotationManager { // Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { - return polylines.addBy(polylineOptions, mapboxMap); + Polyline polyline = polylines.addBy(polylineOptions, mapboxMap); + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId()); + return polyline; } List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { - return polylines.addBy(polylineOptionsList, mapboxMap); + List<Polyline> polylineList = polylines.addBy(polylineOptionsList, mapboxMap); + for (Polyline polyline : polylineList) { + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId()); + } + return polylineList; } void updatePolyline(Polyline polyline) { @@ -231,11 +261,18 @@ class AnnotationManager { } // TODO Refactor from here still in progress - void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { onMarkerClickListener = listener; } + void setOnPolygonClickListener(@Nullable MapboxMap.OnPolygonClickListener listener) { + onPolygonClickListener = listener; + } + + void setOnPolylineClickListener(@Nullable MapboxMap.OnPolylineClickListener listener) { + onPolylineClickListener = listener; + } + void selectMarker(@NonNull Marker marker) { if (selectedMarkers.contains(marker)) { return; @@ -330,11 +367,37 @@ class AnnotationManager { // boolean onTap(PointF tapPoint) { + ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint); + long shapeAnnotationId = new ShapeAnnotationHitResolver(mapboxMap).execute(shapeAnnotationHit); + if (shapeAnnotationId >= 0) { + handleClickForShapeAnnotation(shapeAnnotationId); + } + MarkerHit markerHit = getMarkerHitFromTouchArea(tapPoint); - long markerId = new MarkerHitResolver(markerViewManager, mapboxMap.getProjection()).execute(markerHit); + long markerId = new MarkerHitResolver(mapboxMap).execute(markerHit); return markerId >= 0 && isClickHandledForMarker(markerId); } + private ShapeAnnotationHit getShapeAnnotationHitFromTap(PointF tapPoint) { + float touchTargetSide = Mapbox.getApplicationContext().getResources().getDimension(R.dimen.mapbox_eight_dp); + RectF tapRect = new RectF( + tapPoint.x - touchTargetSide, + tapPoint.y - touchTargetSide, + tapPoint.x + touchTargetSide, + tapPoint.y + touchTargetSide + ); + return new ShapeAnnotationHit(tapRect, shapeAnnotationIds.toArray(new String[shapeAnnotationIds.size()])); + } + + private void handleClickForShapeAnnotation(long shapeAnnotationId) { + Annotation annotation = getAnnotation(shapeAnnotationId); + if (annotation instanceof Polygon && onPolygonClickListener != null) { + onPolygonClickListener.onPolygonClick((Polygon) annotation); + } else if (annotation instanceof Polyline && onPolylineClickListener != null) { + onPolylineClickListener.onPolylineClick((Polyline) annotation); + } + } + private MarkerHit getMarkerHitFromTouchArea(PointF tapPoint) { int averageIconWidthOffset = iconManager.getAverageIconWidth() / 2; int averageIconHeightOffset = iconManager.getAverageIconHeight() / 2; @@ -356,7 +419,7 @@ class AnnotationManager { } if (!handledDefaultClick) { - setMarkerSelectionState(marker); + toggleMarkerSelectionState(marker); } return true; } @@ -365,7 +428,7 @@ class AnnotationManager { return onMarkerClickListener != null && onMarkerClickListener.onMarkerClick(marker); } - private void setMarkerSelectionState(Marker marker) { + private void toggleMarkerSelectionState(Marker marker) { if (!selectedMarkers.contains(marker)) { selectMarker(marker); } else { @@ -373,6 +436,20 @@ class AnnotationManager { } } + private static class ShapeAnnotationHitResolver { + + private MapboxMap mapboxMap; + + ShapeAnnotationHitResolver(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + } + + public long execute(ShapeAnnotationHit shapeHit) { + List<Feature> features = mapboxMap.queryRenderedFeatures(shapeHit.tapPoint, shapeHit.layerIds); + return features.isEmpty() ? -1 : Long.valueOf(features.get(0).getId()); + } + } + private static class MarkerHitResolver { private final MarkerViewManager markerViewManager; @@ -388,9 +465,9 @@ class AnnotationManager { private long closestMarkerId = -1; - MarkerHitResolver(@NonNull MarkerViewManager markerViewManager, @NonNull Projection projection) { - this.markerViewManager = markerViewManager; - this.projection = projection; + MarkerHitResolver(@NonNull MapboxMap mapboxMap) { + this.markerViewManager = mapboxMap.getMarkerViewManager(); + this.projection = mapboxMap.getProjection(); } public long execute(MarkerHit markerHit) { @@ -443,6 +520,16 @@ class AnnotationManager { } } + private static class ShapeAnnotationHit { + private final RectF tapPoint; + private final String[] layerIds; + + ShapeAnnotationHit(RectF tapRect, String[] layerIds) { + this.tapPoint = tapRect; + this.layerIds = layerIds; + } + } + private static class MarkerHit { private final RectF tapRect; private final List<Marker> markers; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 18bcd4953d..6233efa966 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1460,6 +1460,28 @@ public final class MapboxMap { } /** + * Sets a callback that's invoked when the user clicks on a polygon. + * + * @param listener The callback that's invoked when the user clicks on a polygon. + * To unset the callback, use null. + */ + @UiThread + public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) { + annotationManager.setOnPolygonClickListener(listener); + } + + /** + * Sets a callback that's invoked when the user clicks on a polyline. + * + * @param listener The callback that's invoked when the user clicks on a polyline. + * To unset the callback, use null. + */ + @UiThread + public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) { + annotationManager.setOnPolylineClickListener(listener); + } + + /** * <p> * Selects a marker. The selected marker will have it's info window opened. * Any other open info windows will be closed unless isAllowConcurrentMultipleOpenInfoWindows() @@ -2151,6 +2173,34 @@ public final class MapboxMap { } /** + * Interface definition for a callback to be invoked when the user clicks on a polygon. + * + * @see MapboxMap#setOnPolygonClickListener(OnPolygonClickListener) + */ + public interface OnPolygonClickListener { + /** + * Called when the user clicks on a polygon. + * + * @param polygon The polygon the user clicked on. + */ + void onPolygonClick(@NonNull Polygon polygon); + } + + /** + * Interface definition for a callback to be invoked when the user clicks on a polyline. + * + * @see MapboxMap#setOnPolylineClickListener(OnPolylineClickListener) + */ + public interface OnPolylineClickListener { + /** + * Called when the user clicks on a polyline. + * + * @param polyline The polyline the user clicked on. + */ + void onPolylineClick(@NonNull Polyline polyline); + } + + /** * Interface definition for a callback to be invoked when the user clicks on an info window. * * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java index b51d717f33..fecfe2a842 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java @@ -2,9 +2,11 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation; import android.graphics.Color; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; +import android.widget.Toast; import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.PolygonOptions; @@ -77,6 +79,18 @@ public class PolygonActivity extends AppCompatActivity implements OnMapReadyCall @Override public void onMapReady(MapboxMap map) { mapboxMap = map; + + map.setOnPolygonClickListener(new MapboxMap.OnPolygonClickListener() { + @Override + public void onPolygonClick(@NonNull Polygon polygon) { + Toast.makeText( + PolygonActivity.this, + "You clicked on polygon with id = " + polygon.getId(), + Toast.LENGTH_SHORT + ).show(); + } + }); + polygon = mapboxMap.addPolygon(new PolygonOptions() .addAll(STAR_SHAPE_POINTS) .fillColor(BLUE_COLOR)); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java index 0aaa6127f4..b9dc39e5d4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java @@ -67,8 +67,20 @@ public class PolylineActivity extends AppCompatActivity { mapView.onCreate(savedInstanceState); mapView.getMapAsync(new OnMapReadyCallback() { @Override - public void onMapReady(@NonNull MapboxMap mapboxMap) { + public void onMapReady(@NonNull final MapboxMap mapboxMap) { PolylineActivity.this.mapboxMap = mapboxMap; + + mapboxMap.setOnPolylineClickListener(new MapboxMap.OnPolylineClickListener() { + @Override + public void onPolylineClick(@NonNull Polyline polyline) { + Toast.makeText( + PolylineActivity.this, + "You clicked on polygon with id = " + polyline.getId(), + Toast.LENGTH_SHORT + ).show(); + } + }); + polylines = mapboxMap.addPolylines(polylineOptions); } }); |