How to handle onTouch event for map in Google Map API v2?
Here is a possible workaround for determining drag start and drag end events:
You have to extend SupportMapFragment
or MapFragment
. In onCreateView()
you have to wrap your MapView
in a customized FrameLayout
(in example below it is the class TouchableWrapper
), in which you intercepts touch events and recognizes whether the map is tapped or not. If your onCameraChange
gets called, just check whether the map view is pressed or not (in example below this is the variable mMapIsTouched
).
Example code:
UPDATE 1:
- return original created view in
getView()
- use
dispatchTouchEvent()
instead ofonInterceptTouchEvent()
Customized FrameLayout:
private class TouchableWrapper extends FrameLayout {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mMapIsTouched = true;
break;
case MotionEvent.ACTION_UP:
mMapIsTouched = false;
break;
}
return super.dispatchTouchEvent(ev);
}
}
In your customized MapFragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mOriginalContentView);
return mTouchView;
}
@Override
public View getView() {
return mOriginalContentView;
}
In your camera change callback method:
private final OnCameraChangeListener mOnCameraChangeListener = new OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition cameraPosition) {
if (!mMapIsTouched) {
refreshClustering(false);
}
}
};
Google Maps Android API v2 - detect touch on map
@ape wrote an answer here on how to intercept the map clicks, but I need to intercept the touches, and then he suggested the following link in a comment of its answer, How to handle onTouch event for map in Google Map API v2?.
That solution seems to be a possible workaround, but the suggested code was incomplete. For this reason I rewrote and tested it, and now it works.
Here it is the working code:
I created the class MySupportMapFragment.java
import com.google.android.gms.maps.SupportMapFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MySupportMapFragment extends SupportMapFragment {
public View mOriginalContentView;
public TouchableWrapper mTouchView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mOriginalContentView);
return mTouchView;
}
@Override
public View getView() {
return mOriginalContentView;
}
}
I even created the class TouchableWrapper.java:
import android.content.Context;
import android.view.MotionEvent;
import android.widget.FrameLayout;
public class TouchableWrapper extends FrameLayout {
public TouchableWrapper(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
MainActivity.mMapIsTouched = true;
break;
case MotionEvent.ACTION_UP:
MainActivity.mMapIsTouched = false;
break;
}
return super.dispatchTouchEvent(event);
}
}
In the layout I declare it this way:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/buttonBar"
class="com.myFactory.myApp.MySupportMapFragment"
/>
Just for test in the main Activity I wrote only the following:
public class MainActivity extends FragmentActivity {
public static boolean mMapIsTouched = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
How to handle touch event on google map (MapFragment)?
You can directly add click listener and get position of touch on Map in form of Location.
map.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { //Do what you want on obtained latLng } });
Touch listener on Google Map Markers Android
All markers in Google Android Maps Api v2 are clickable. You don't need to set any additional properties to your marker. What you need to do - is to register marker click callback to your googleMap and handle click within callback:
public class MarkerDemoActivity extends android.support.v4.app.FragmentActivity
implements OnMarkerClickListener{
private Marker myMarker;
private void setUpMap()
{
.......
googleMap.setOnMarkerClickListener(this);
myMarker = googleMap.addMarker(new MarkerOptions()
.position(latLng)
.title("My Spot")
.snippet("This is my spot!")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));
......
}
@Override
public boolean onMarkerClick(final Marker marker) {
if (marker.equals(myMarker))
{
//handle click here
}
}
}
How can I handle map move end using Google Maps for Android V2?
Here is a possible workaround for determining drag start and drag end events:
You have to extend SupportMapFragment or MapFragment. In onCreateView you have to wrap your MapView in a customized FrameLayout (in example below it is the class "TouchableWrapper"), in which you intercepts touch events and recognizes whether the map is tapped or not. If your "onCameraChange" gets called, just check whether the map view is pressed or not (in example below this is the variable "mMapIsTouched").
Example code:
UPDATE 1:
- return original created view in getView()
- use dispatchTouchEvent() instead of onInterceptTouchEvent()
Customized FrameLayout:
private class TouchableWrapper extends FrameLayout {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mMapIsTouched = true;
break;
case MotionEvent.ACTION_UP:
mMapIsTouched = false;
break;
}
return super.dispatchTouchEvent(ev);
}
}
In your customized MapFragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
mOriginalContentView = super.onCreateView(inflater, parent,
savedInstanceState);
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mOriginalContentView);
return mTouchView;
}
@Override
public View getView() {
return mOriginalContentView;
}
In your camera change callback method:
private final OnCameraChangeListener mOnCameraChangeListener =
new OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition cameraPosition) {
if (!mMapIsTouched) {
refreshClustering(false);
}
}
};
How to disable google maps touch events through a layout? (android)
Add android:clickable="true"
to your legend_popover
layout
Centered zooming a Google Map through a wrapper?
So this is how i managed to do this.
I created two classes for detecting certain events, like a fling, double tap, scroll(drag) and scale(zoom):
public class PinchListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private GoogleMap mGoogleMap;
public PinchListener(GoogleMap googleMap) {
this.mGoogleMap = googleMap;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
double zoom = mGoogleMap.getCameraPosition().zoom;
zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(1.5d);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(mGoogleMap.getCameraPosition().target, (float) zoom);
mGoogleMap.moveCamera(update);
return true;
}
}
public class GestureListener extends GestureDetector.SimpleOnGestureListener {
private GoogleMap mGoogleMap;
private boolean mIsInAnimation;
public GestureListener(GoogleMap googleMap) {
this.mGoogleMap = googleMap;
mIsInAnimation = false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
LatLng target = mGoogleMap.getCameraPosition().target;
Point screenPoint = mGoogleMap.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x + (int) distanceX, screenPoint.y + (int) distanceY);
LatLng mapNewTarget = mGoogleMap.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget, mGoogleMap.getCameraPosition().zoom);
mGoogleMap.moveCamera(update);
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
mGoogleMap.animateCamera(CameraUpdateFactory.zoomIn(), 400, null);
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsInAnimation) return false;
int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
if (velocity < 500) return false;
double k1 = 0.002d; /*exipemental*/
double k2 = 0.002d;/*exipemental*/
LatLng target = mGoogleMap.getCameraPosition().target;
Point screenPoint = mGoogleMap.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x - (int) (velocityX * k1),
screenPoint.y - (int) (velocityY * k1));
LatLng mapNewTarget = mGoogleMap.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget, mGoogleMap.getCameraPosition().zoom);
tryUpdateCamera(update, (int) (velocity * k2));
return true;
}
private void tryUpdateCamera(CameraUpdate update, int animateTime) {
mIsInAnimation = true;
mGoogleMap.animateCamera(update, animateTime, new GoogleMap.CancelableCallback() {
@Override
public void onFinish() {
mIsInAnimation = false;
}
@Override
public void onCancel() {
mIsInAnimation = false;
}
});
}
}
I then initialized a GestureDetector and ScaleGestureDetector in my MainActivity:
ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(this, new PinchListener(mMap));
GestureDetector mGestureDetector = new GestureDetector(this, new GestureListener(mMap));
I had already created a transparent image wrapper in my activity_main to detect all these events and then simulate them on the Google Map below, like this:
<fragment 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"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mapWrapper"
/>
And then finally in my MainActivity i created an OnTouchListener for the wrapper:
mMapWrapper = findViewById(R.id.mapWrapper);
mMapWrapper.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mMode = Mode.DRAG;
mGestureDetector.onTouchEvent(event);
mScaleGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_POINTER_DOWN:
mMode = Mode.ZOOM;
mScaleGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mMode = Mode.NONE;
mGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
if (mMode == Mode.DRAG) {
mGestureDetector.onTouchEvent(event);
} else if (mMode == Mode.ZOOM) {
mScaleGestureDetector.onTouchEvent(event);
}
break;
}
return true;
}
});
In the last step i "feed" the right detector the events that i want. If i had not done that, the zooming would not be completely centered since sometimes onScroll(Drag) events would be called and that would move the map.
Related Topics
How to Check Whether the Sim Card Is Available in an Android Device
Handling Buttons Inside Android Notifications
Disabling Android O Auto-Fill Service for an Application
How to Implement a Setonitemclicklistener Firebaserecyclerviewadapter
Android Textview:"Do Not Concatenate Text Displayed with Settext"
How to Create Tests in Android Studio
Spannablestringbuilder to Create String with Multiple Fonts/Text Sizes etc Example
How to Give Hexagon Shape to Imageview
Android Expandablelistview - Looking for a Tutorial
Cannot Resolve Method Setlatesteventinfo
Android - Supportmapfragment with Googlemaps API 2.0 Giving Illegalargumentexception
How to Call Methods of a Service from Activity
Return Value from Asynctask Class Onpostexecute Method