Anyway you can use a workaround:
disable marker clicks response;
detect touch on map determine nearest marker by yourself.
Of course, you need to store all of the markers in that case.
While the first point is simple:
mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
return true;
}
});
the second is not: even if "empty" onMarkerClick()
return true
markers will always intercept this event.
To get rid of that you can use approach with "touchable wrapper" like in this answer within custom MapFragment
that can intercept touch events before they goes to MapFragment
. When you get touch event you can get screen coordinates of touch. Then you need to find marker with minimal distance from touch location (you also need to convert marke's 'LatLng' position into screen flat coordinates via Projection.toScreenLocation()
method). If founded marker within clickable radius you can process custom onMarkerClick
event, if not - process polygon click.
Something like that:
Class TouchableWrapper
- the core of approach:
public class TouchableWrapper extends FrameLayout {
private static final int CLICK_RADIUS_IN_PIXELS = 25;
private GoogleMap mGoogleMap;
private List<Marker> mMarkers;
public TouchableWrapper(Context context) {
super(context);
}
public void setGoogleMapAndMarkers(GoogleMap googleMap, List<Marker> markers) {
mGoogleMap = googleMap;
mMarkers = markers;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (mGoogleMap == null) return super.dispatchTouchEvent(event);
int screenX = (int) event.getX();
int screenY = (int) event.getY();
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
// find marker nearest to touch position
Projection projection = mGoogleMap.getProjection();
Marker nearestMarker = null;
int minDistanceInPixels = Integer.MAX_VALUE;
for (Marker marker : mMarkers) {
Point markerScreen = projection.toScreenLocation(marker.getPosition());
int distanceToMarker = (int) Math.sqrt((screenX - markerScreen.x) * (screenX - markerScreen.x)
+ (screenY - markerScreen.y) * (screenY - markerScreen.y));
if (distanceToMarker < minDistanceInPixels) {
minDistanceInPixels = distanceToMarker;
nearestMarker = marker;
}
}
// "drop" nearest marker if it is not within radius
if (minDistanceInPixels > CLICK_RADIUS_IN_PIXELS) {
nearestMarker = null;
}
if (nearestMarker != null) {
// decide what to process (marker click or polygon click) here
Toast.makeText(getContext(),
"Clicked on marker " + nearestMarker.getTitle(), Toast.LENGTH_LONG).show();
}
}
return super.dispatchTouchEvent(event);
}
}
You can adjust clickable radius via CLICK_RADIUS_IN_PIXELS
constant value.
Customized MapFragmet
that uses TouchableWrapper
class:
public class TouchableMapFragment extends MapFragment {
public View originalContentView;
public TouchableWrapper touchView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
originalContentView = super.onCreateView(inflater, parent, savedInstanceState);
touchView = new TouchableWrapper(getActivity());
touchView.addView(originalContentView);
return touchView;
}
@Override
public View getView() {
return originalContentView;
}
}
'MainActivity' that uses TouchableMapFragment
:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = MainActivity.class.getSimpleName();
static final LatLng MARKER_1 = new LatLng(50.450311, 30.523730);
static final LatLng MARKER_2 = new LatLng(50.4502, 30.52365);
private GoogleMap mGoogleMap;
private TouchableMapFragment mMapFragment;
private List<Marker> mMarkers = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapFragment = (TouchableMapFragment) getFragmentManager()
.findFragmentById(R.id.map_fragment);
mMapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
// store markers
Marker marker = mGoogleMap.addMarker(new MarkerOptions()
.position(MARKER_1)
.title("Marker 1"));
mMarkers.add(marker);
marker = mGoogleMap.addMarker(new MarkerOptions()
.position(MARKER_2)
.title("Marker 2"));
mMarkers.add(marker);
// pass stored markers to "touchable wrapper"
mMapFragment.touchView.setGoogleMapAndMarkers(mGoogleMap, mMarkers);
// disable marker click processing
mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
return true;
}
});
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(MARKER_1, 14));
}
}
and 'MainActivity' layout
<?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=".activities.MainActivity">
<fragment
android:id="@+id/map_fragment"
android:name="<your.package.name>.TouchableMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Also, in your case you need to pass List<Polygon>
to TouchableWrapper
like List<Marker>
in example above and process polygon click in its dispatchTouchEvent(MotionEvent event)
too.