Google Maps API v2 SupportMapFragment inside ScrollView - users cannot scroll the map vertically

后端 未结 9 933
深忆病人
深忆病人 2020-11-27 13:01

I am trying to put a Google map inside a scroll view, so that the user can scroll down other contents to see the map. The problem is that this scroll view is eating up all t

相关标签:
9条回答
  • 2020-11-27 13:43

    Improving code if you do not need transparent Image again:

    // gmap hack for touch and scrollview
            final ScrollView mainScrollView = (ScrollView) rootView.findViewById(R.id.scrollView);
            (rootView.findViewById(R.id.fixTouchMap)).setOnTouchListener(new View.OnTouchListener() {
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    int action = event.getAction();
                    switch (action) {
                        case MotionEvent.ACTION_DOWN:
                            // Disallow ScrollView to intercept touch events.
                            mainScrollView.requestDisallowInterceptTouchEvent(true);
                            // Disable touch on transparent view
                            return false;
    
                        case MotionEvent.ACTION_UP:
                            // Allow ScrollView to intercept touch events.
                            mainScrollView.requestDisallowInterceptTouchEvent(false);
                            return true;
    
                        case MotionEvent.ACTION_MOVE:
                            mainScrollView.requestDisallowInterceptTouchEvent(true);
                            return false;
    
                        default:
                            return true;
                    }
                }
            });
    
    0 讨论(0)
  • 2020-11-27 13:44

    Thank you for suggestions,

    After much try-and-error, pulling off my hairs and swearing at monitor and my poor Android test phone, I've figured that if I customise ScrollView, override onInterceptTouchEvent in which we return false when the event is on a map view no matter what, then the scrolling on a map does happen as expected.

    class MyScrollView(c:Context, a:AttributeSet) extends ScrollView(c,a) {
      val parent = c.asInstanceOf[MyActivity]
      override def onInterceptTouchEvent(ev:MotionEvent):Boolean = {
        var bound:Rect = new Rect()
        parent.mMap.getHitRect(bound)
        if(bound.contains(ev.getX.toInt,ev.getY.toInt))
          false
        else
          super.onInterceptTouchEvent(ev)
      }
    }
    

    This code is in Scala but you get the idea.

    Note I've ended up using a raw map view (as shown in android-sdks\extras\google\google_play_services\samples\maps\src\com\example\mapdemoRawMapViewDemoActivity.java). Guess you can do the pretty much same thing with fragments, I just never liked fragments in the first place.

    I think Google owes me an apology.

    0 讨论(0)
  • 2020-11-27 13:46

    I encountered a similar problem and came up with a more general working solution based on In-Ho Yi and Данаил Димитров answers above.

    public class CustomScrollView extends ScrollView {
    
        List<View> mInterceptScrollViews = new ArrayList<View>();
    
        public CustomScrollView(Context context) {
            super(context);
        }
    
        public CustomScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public void addInterceptScrollView(View view) {
            mInterceptScrollViews.add(view);
        }
    
        public void removeInterceptScrollView(View view) {
            mInterceptScrollViews.remove(view);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
    
            // check if we have any views that should use their own scrolling
            if (mInterceptScrollViews.size() > 0) {
                int x = (int) event.getX();
                int y = (int) event.getY();
                Rect bounds = new Rect();
    
                for (View view : mInterceptScrollViews) {
                    view.getHitRect(bounds);
                    if (bounds.contains(x, y + scrollY)) {
                        //were touching a view that should intercept scrolling
                        return false;
                    }
                }
            }
    
            return super.onInterceptTouchEvent(event);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 13:46

    Use custom Google map fragment in your XML.

    Here is the complete code that worked for me. If you guys have any questions, please let me know.

    In your XML file, add the following as map fragment

    <fragment
            android:id="@+id/map_with_scroll_fix"
            android:name="com.myapplication.maputil.GoogleMapWithScrollFix"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    

    Here is the custom class for map

    package com.myapplication.maputil;
    
    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 GoogleMapWithScrollFix extends SupportMapFragment {
            private OnTouchListener mListener;
    
            @Override
            public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle savedInstance) {
                View layout = super.onCreateView(layoutInflater, viewGroup, savedInstance);
    
                TouchableWrapper touchableWrapper = new TouchableWrapper(getActivity());
    
                touchableWrapper.setBackgroundColor(getResources().getColor(android.R.color.transparent));
    
                ((ViewGroup) layout).addView(touchableWrapper,
                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    
                return layout;
            }
    
            public void setListener(OnTouchListener listener) {
                mListener = listener;
            }
    
            public interface OnTouchListener {
                void onTouch();
            }
    
            public class TouchableWrapper extends FrameLayout {
    
                public TouchableWrapper(Context context) {
                    super(context);
                }
    
                @Override
                public boolean dispatchTouchEvent(MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            mListener.onTouch();
                            break;
                        case MotionEvent.ACTION_UP:
                            mListener.onTouch();
                            break;
                    }
                    return super.dispatchTouchEvent(event);
                }
            }
        }
    

    Add the following in your activity class, to initialize mapview. That's it. Tada :)

    ((GoogleMapWithScrollFix) getSupportFragmentManager()
                    .findFragmentById(R.id.map_with_scroll_fix)).getMapAsync(new OnMapReadyCallback() {
                @Override
                public void onMapReady(GoogleMap googleMap) {
                    ScrollView mScrollView = findViewById(R.id.scrollview); //parent scrollview in xml, give your scrollview id value
                    ((GoogleMapWithScrollFix) getSupportFragmentManager()
                            .findFragmentById(R.id.map_with_scroll_fix)).setListener(new GoogleMapWithScrollFix.OnTouchListener() {
                        @Override
                        public void onTouch() {
                            //Here is the magic happens.
                            //we disable scrolling of outside scroll view here
                            mScrollView.requestDisallowInterceptTouchEvent(true);
                        }
                    });
                }
            });
    
    0 讨论(0)
  • 2020-11-27 13:55

    I had the same issue, so here is how I used the solution as java code just in case anyone needs it. You just have to set the mapView field when using it.

    import com.google.android.gms.maps.MapView;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.ScrollView;
    
    public class ScrollViewWithMap extends ScrollView
    {
        public MapView mapView;
    
        public ScrollViewWithMap(Context context, AttributeSet attrs)
        {
            super(context, attrs);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev)
        {
            if (mapView == null)
                return super.onInterceptTouchEvent(ev);
    
            if (inRegion(ev.getRawX(), ev.getRawY(), mapView))
                return false;
    
            return super.onInterceptTouchEvent(ev);
        }
    
        private boolean inRegion(float x, float y, View v)
        {
            int[] mCoordBuffer = new int[]
            { 0, 0 };
    
            v.getLocationOnScreen(mCoordBuffer);
    
            return mCoordBuffer[0] + v.getWidth() > x && // right edge
                    mCoordBuffer[1] + v.getHeight() > y && // bottom edge
                    mCoordBuffer[0] < x && // left edge
                    mCoordBuffer[1] < y; // top edge
        }
    }
    
    0 讨论(0)
  • 2020-11-27 13:55

    The Kotlin version of @Laksh 's answer: a) Implement View.OnTouchListener and, b) Add the onTouch listener to the transparent view in the activity's onCreate. c) Override the function as follows:

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        when (v?.id) {
            R.id.transparent_image -> {
    
                when (event?.action) {
                    MotionEvent.ACTION_DOWN -> {
                        reportScrollView.requestDisallowInterceptTouchEvent(true)
                        return false
                    }
                    MotionEvent.ACTION_UP -> {
                        reportScrollView.requestDisallowInterceptTouchEvent(false)
                        return true
                    }
                    MotionEvent.ACTION_MOVE -> {
                        reportScrollView.requestDisallowInterceptTouchEvent(true)
                        return false
                    }
                    else -> return true
                }
            }
            else -> {
                return true
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题