Google Maps Android API v2 - detect touch on map
Solution 1
@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);
}
}
Solution 2
Here is a simple solution to get the location based on user selection (click option on map):
googleMap.setOnMapClickListener(new OnMapClickListener() {
@Override
public void onMapClick(LatLng arg0) {
// TODO Auto-generated method stub
Log.d("arg0", arg0.latitude + "-" + arg0.longitude);
}
});
Solution 3
This feature and many more are now supported :)
this is the developer note(Issue 4636) :
The August 2016 release introduces a set of new camera change listeners for camera motion start, ongoing, and end events. You can also see why the camera is moving, whether it's caused by user gestures, built-in API animations or developer-controlled movements. For details, see the guide to camera change events: https://developers.google.com/maps/documentation/android-api/events#camera-change-events
Also, see the release notes: https://developers.google.com/maps/documentation/android-api/releases#august_1_2016
here is a code snippet from the documentation page
public class MyCameraActivity extends FragmentActivity implements
OnCameraMoveStartedListener,
OnCameraMoveListener,
OnCameraMoveCanceledListener,
OnCameraIdleListener,
OnMapReadyCallback {
private GoogleMap mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_camera);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap map) {
mMap = map;
mMap.setOnCameraIdleListener(this);
mMap.setOnCameraMoveStartedListener(this);
mMap.setOnCameraMoveListener(this);
mMap.setOnCameraMoveCanceledListener(this);
// Show Sydney on the map.
mMap.moveCamera(CameraUpdateFactory
.newLatLngZoom(new LatLng(-33.87365, 151.20689), 10));
}
@Override
public void onCameraMoveStarted(int reason) {
if (reason == OnCameraMoveStartedListener.REASON_GESTURE) {
Toast.makeText(this, "The user gestured on the map.",
Toast.LENGTH_SHORT).show();
} else if (reason == OnCameraMoveStartedListener
.REASON_API_ANIMATION) {
Toast.makeText(this, "The user tapped something on the map.",
Toast.LENGTH_SHORT).show();
} else if (reason == OnCameraMoveStartedListener
.REASON_DEVELOPER_ANIMATION) {
Toast.makeText(this, "The app moved the camera.",
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onCameraMove() {
Toast.makeText(this, "The camera is moving.",
Toast.LENGTH_SHORT).show();
}
@Override
public void onCameraMoveCanceled() {
Toast.makeText(this, "Camera movement canceled.",
Toast.LENGTH_SHORT).show();
}
@Override
public void onCameraIdle() {
Toast.makeText(this, "The camera has stopped moving.",
Toast.LENGTH_SHORT).show();
}
}
Solution 4
I created an empty FrameLayout layered over top of the MapFragment in the layout. I then set an onTouchListener on this view so I know when the map has been touched but return false so that the touch gets passed on to the map.
<FrameLayout
android:id="@+id/map_touch_layer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
mapTouchLayer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Utils.logDebug(TAG, "Map touched!");
timeLastTouched = System.currentTimeMillis();
return false; // Pass on the touch to the map or shadow layer.
}
});
Solution 5
Gaucho has a great answer, and seeing the many upvotes I figured there might be some need for another implementation:
I needed it to use a listener so I can react on the touch and do not have to check it constantly.
I put all in one class that can be used like this:
mapFragment.setNonConsumingTouchListener(new TouchSupportMapFragment.NonConsumingTouchListener() {
@Override
public void onTouch(MotionEvent motionEvent) {
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// map is touched
break;
case MotionEvent.ACTION_UP:
// map touch ended
break;
default:
break;
// use more cases if needed, for example MotionEvent.ACTION_MOVE
}
}
});
where the mapfragment needs to be of type TouchSupportMapFragment and in the layout xml this line is needed:
<fragment class="de.bjornson.maps.TouchSupportMapFragment"
...
Here is the class:
package de.bjornson.maps;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.google.android.gms.maps.SupportMapFragment;
public class TouchSupportMapFragment extends SupportMapFragment {
public View mOriginalContentView;
public TouchableWrapper mTouchView;
private NonConsumingTouchListener mListener;
@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;
}
public void setNonConsumingTouchListener(NonConsumingTouchListener listener) {
mListener = listener;
}
public interface NonConsumingTouchListener {
boolean onTouch(MotionEvent motionEvent);
}
public class TouchableWrapper extends FrameLayout {
public TouchableWrapper(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (mListener != null) {
mListener.onTouch(event);
}
return super.dispatchTouchEvent(event);
}
}
}
Gaucho
Updated on November 07, 2020Comments
-
Gaucho over 3 years
I can't find an example on how to intercept the map touch on the new Google Maps API v2.
I need to know when the user touches the map in order to stop a thread (the centering of the map around my current location).
-
Gaucho over 11 yearsThank you very much ndsmyter for the answer. The onMapClick intercepts when you tap on the map, but it doesn't work when you move the finger on the map. I need to intercept not only the map click, but even the map pan. Do you know how to do?
-
Gaucho over 11 yearsMap Touch is not the "map Click", so the question is not answered. I need to intercept the map move due to user touch on the map and i can't find a working way to intercept this action. I think that i can't use the setOnCameraChangeListener cause i still use the animateCamera method to update the camera location in my code, then i just need a listener to intercept the touch on the map during the pan of the map.
-
adarsh over 11 yearsI think you need the
onMarkerDragListener
? developers.google.com/maps/documentation/android/reference/com/… -
Gaucho over 11 yearsDear @ape , the onMarkerDragListener intercepts the drag of a marker, not the pan of a map without markers. I need to get an interrupt when the user touch the map to pan.
-
Gaucho over 11 years@ndsmyter , i forgot to mention your name, could you please read my comments? thank you.
-
adarsh over 11 yearsandroid-coding.blogspot.in/2011/08/… Maybe this is useful?
-
Gaucho over 11 yearsdear @ape, thank you for your reply but that is good only for api v.1 but it is deprecated. I'm using api v.2
-
adarsh over 11 yearsOkay I guess this helps? stackoverflow.com/questions/13722869/…
-
Gaucho over 11 yearsthat seems to be a possible workaround, i just can't write it down correctly. if you help me and write down the working code i give to you the solution. I wrote it down this way but i get errors on getActivity: public class MyMapFragment extends MapFragment{ 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()); ...continues..
-
Gaucho over 11 years@Alexey Zakharov probably still solved the problem, cause approved the answer of your linked thread.
-
Thibault D. about 11 yearsVoted up, because that's not what the user needed but that's what he asked. And that's what I needed :)
-
Dimitar Darazhanski about 11 years@Gaucho, I had to improve a bit on that solution, mainly by using a custom listener instead of using a public static variable. See the solution below.
-
Gaucho almost 11 years@Dimitar: now I don't have time to test it. I'll let you know. Thank you.
-
Md. Sajedul Karim over 8 yearsThis process works when you touch on map smoothly but when you touch on maps more hardly and it start zooming, for this reason onMapClick method will not called.
-
Oleg Novosad about 8 years@Md.SajedulKarim you can disable all gestures with googleMap.getUiSettings().setAllGesturesEnabled(false); and then listen to that tap, after re-enable the gestures.
-
Hamzeh Soboh over 7 yearsThis simply will NOT work. Returning false on
ACTION_DOWN
will causeACTION_UP
to be interrupted andonTouch
will NOT be invoked for it. -
ONE_FE over 7 yearssetOnMapClickListener does not recognize. What should I import?
-
Tom Bevelander over 6 yearsIf users touch the map during an animation, this does not work.
-
Rajshree Tiwari over 6 yearsgreat solution.. :)
-
AdamHurwitz about 6 years@DimitarDarazhanski - Your implementation works great. Thank you! dimitar.me/how-to-detect-a-user-pantouchdrag-on-android-map-v2/…
-
Nirmal over 5 yearsShort & simple with great clue
-
Taslim Oseni over 3 yearsPerfect! Worked like a charm! At least for my use case.
-
Parth Patel about 3 yearsThis is the easier and good solution. Thanks!