MapView inside a ScrollView?
Asked Answered
J

10

82

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!

Johannisberger answered 1/7, 2011 at 9:29 Comment(7)
What's your layout? can you post a screenshot of the final layout that is rendered.Hypotenuse
MapView automatically handles the scrolling....Poisoning
I know. I have a ScrollView which contains a lot of different things, including a MapView. The problem is that when I try to scroll inside the mapview, the scrollview will override it.Johannisberger
I was also doing that in my app, but didn't get any solution so i put mapview outside the scroll view and rest of the layout in the scrollview.Poisoning
You can use this template: <LinearLayout><LL><FL><MAP></FL></LL><LL><SV><OtherLayouy></SV></LL>. Also provide the layout_weight value to each LL(LinearLayout) According to your size.Poisoning
Nothing new to other answers but I've created a Gist on how I solved this. gist.github.com/Sottti/890daaeead1bd4784dfce7066a9011aaImpermissible
Check this Answer and let me know if you have any doubts.Calfskin
C
104

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;
}
Confiscable answered 30/7, 2011 at 14:39 Comment(4)
this works for me, but are you experiencing any weird flashing in the layout that the mapview is contained?Semiyearly
@Luizje: the flashing is caused by the background colors you are using for each view. So make sure you have the outermost view using the corect bg color.Semiyearly
thnx @aimango, after some testing i found it by my self.. Lucky it was not that hard as it looks to solve. Thnx for your help :)Desultory
if it doesn't work for you, try to override the dispatchTouchEvent method instead (same implementation, just change the super call at the end). thanks.Encyclical
L
58

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.

Lutist answered 27/10, 2015 at 23:19 Comment(1)
Nice, although requestDisallowInterceptTouchEvent is necessary for MotionEvent.ACTION_DOWN only.Dejadeject
D
21

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"
                />
Devon answered 28/4, 2016 at 8:51 Comment(0)
D
16

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

Deforest answered 6/6, 2016 at 13:47 Comment(0)
I
9

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);
    }
}
Idiographic answered 9/7, 2014 at 1:23 Comment(0)
C
8

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);
        }
}
Ceroplastics answered 24/6, 2015 at 15:0 Comment(1)
Overriding 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
B
4

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)
    }
}
Brasil answered 15/6, 2018 at 8:32 Comment(0)
V
2

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.

Virg answered 5/7, 2012 at 15:0 Comment(0)
R
1

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);
Roister answered 1/5, 2013 at 11:11 Comment(1)
But then after you specified it and the map view would become unscrollableHenryson
O
0

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" />
Obellia answered 23/12, 2023 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.