I would like to have a MapView inside a ScrollView, however when I try to scroll the map, the ScrollView takes priority! Is there a way to give the MapView priority when scrolling inside the map, and the ScrollView otherwise?
Thanks!
I would like to have a MapView inside a ScrollView, however when I try to scroll the map, the ScrollView takes priority! Is there a way to give the MapView priority when scrolling inside the map, and the ScrollView otherwise?
Thanks!
I have had a same problem for 10 days, but I got a solution a few minutes ago!! Here is the solution. I made a custom MapView and override onTouchEvent() like this.
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
// Handle MapView's touch events.
super.onTouchEvent(ev);
return true;
}
A better/simpler way to do this without manipulating individual touch events. This will work if you are using MapView:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/**
* Request all parents to relinquish the touch events
*/
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
Full class:
public class CustomMapView extends MapView {
public CustomMapView(Context context) {
super(context);
}
public CustomMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomMapView(Context context, GoogleMapOptions options) {
super(context, options);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/**
* Request all parents to relinquish the touch events
*/
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
}
If you are using a MapFragment then you can put the fragment in a Custom View, and in the dispatchTouchEvent()
make the requestDisallowInterceptTouchEvent
call.
requestDisallowInterceptTouchEvent
is necessary for MotionEvent.ACTION_DOWN
only. –
Dejadeject Make your own map and use it. It works fully for me.
public class CustomMapView extends MapView {
public CustomMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
System.out.println("unlocked");
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_DOWN:
System.out.println("locked");
this.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return super.dispatchTouchEvent(ev);
}}
In your layout xml,
<com.yourpackage.xxxx.utils.CustomMapView
android:id="@+id/customMap"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
/>
For those who want the whole working code . here it is
Custom map view class
public class CustomMapView extends MapView {
private ViewParent mViewParent;
public CustomMapView(Context context) {
super(context);
}
public CustomMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomMapView(Context context, GoogleMapOptions options) {
super(context, options);
}
public void setViewParent(@Nullable final ViewParent viewParent) { //any ViewGroup
mViewParent = viewParent;
}
@Override
public boolean onInterceptTouchEvent(final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (null == mViewParent) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
mViewParent.requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
if (null == mViewParent) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
mViewParent.requestDisallowInterceptTouchEvent(false);
}
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
}
Activity layout xml
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<location.to.your.CustomMapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="250dp"
/>
</ScrollView>
Instantiating the custom map class in your activity or fragment
CustomMapView mapView = (CustomMapView) findViewById(R.id.mapView);
That's it enjoy
You can create a custom MapView like this:
public class CustomMapView extends MapView {
private MapFragment.ControlLock mCallbackControl;
public CustomMapView(Context context) {
this(context, null);
}
public CustomMapView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomMapView(Context context, GoogleMapOptions options) {
super(context, options);
}
public void setCallback(MapFragment.ControlLock callbackControl) {
this.mCallbackControl = callbackControl;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
System.out.println("unlocked");
mCallbackControl.unlock(); /* Interface */
break;
case MotionEvent.ACTION_DOWN:
System.out.println("locked");
mCallbackControl.lock(); /* Interface */
break;
}
return super.dispatchTouchEvent(event);
}
}
I've tried with overriding MapView.onTouchEvent(...), but it didn't work for me. Here is code which works well (overriding MapView.onInterceptTouchEvent(...)):
public class MyMapView extends MapView {
private ViewParent mViewParent;
//add constructors here
public void setViewParent(@Nullable final ViewParent viewParent) { //any ViewGroup
mViewParent = viewParent;
}
@Override
public boolean onInterceptTouchEvent(final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (null == mViewParent) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
mViewParent.requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
if (null == mViewParent) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
mViewParent.requestDisallowInterceptTouchEvent(false);
}
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
}
onInterceptTouchEvent()
worked for me too whereas onTouchEvent()
didn't. But I didn't need the mViewParent though, it seems that requestDisallowInterceptTouchEvent()
is propagated up the view hierarchy. –
Kriskrischer If somebody wants this class in kotlin.
I used dispatchTouchEvent
like suggested by @rotem
class CustomMapView : MapView {
constructor(context: Context):
super(context)
constructor(context: Context, googleMapOptions: GoogleMapOptions):
super(context, googleMapOptions)
constructor(context: Context, attributeSet: AttributeSet):
super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, int: Int):
super(context, attributeSet, int)
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
Log.d("CustomWebView", "touchevent")
when(event?.action) {
MotionEvent.ACTION_UP -> {
Log.d("CustomWebView", "disallow Intercept")
parent.requestDisallowInterceptTouchEvent(false)
}
MotionEvent.ACTION_DOWN -> {
Log.d("CustomWebView", "allow Intercept")
parent.requestDisallowInterceptTouchEvent(true)
}
}
return super.dispatchTouchEvent(event)
}
}
You can simply put the MapView in a Layout itself and override onTouch or set an Click-Listener - easiest way for me since i needed a touch on the whole MapView in my ScrollView.
If you have mapview in scroll view then you have to explicity mention the follwing paramets to the MapView:
mMapView.setClickable(true);
mMapView.setFocusable(true);
mMapView.setDuplicateParentStateEnabled(false);
Extension to Mapbox's MapView in Kotlin (while following code from @fupduck):
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import com.mapbox.maps.MapInitOptions
import com.mapbox.maps.MapView
class CustomMapView : MapView {
constructor(context: Context):
super(context)
constructor(context: Context, initMapOptions: MapInitOptions):
super(context, initMapOptions)
constructor(context: Context, attributeSet: AttributeSet):
super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, int: Int):
super(context, attributeSet, int)
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
Log.d("CustomWebView", "touchevent")
when(event?.action) {
MotionEvent.ACTION_UP -> {
Log.d("CustomWebView", "disallow Intercept")
parent.requestDisallowInterceptTouchEvent(false)
}
MotionEvent.ACTION_DOWN -> {
Log.d("CustomWebView", "allow Intercept")
parent.requestDisallowInterceptTouchEvent(true)
}
}
return super.dispatchTouchEvent(event)
}
}
Which can be easily usable in XML as:
<com.example.PackageName.CustomMapView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/custom_map_view_route"
android:layout_width="match_parent"
android:layout_height="240dp"
mapbox:mapbox_cameraTargetLat="26"
mapbox:mapbox_cameraTargetLng="75"
mapbox:mapbox_cameraZoom="14.0" />
© 2022 - 2024 — McMap. All rights reserved.