How to Handle Ontouch Event for Map in Google Map API V2

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 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);
}
}
};

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



Leave a reply



Submit