首页 文章

ScrollView中的Google Maps API v2 SupportMapFragment - 用户无法垂直滚动 Map

提问于
浏览
50

我正在尝试将Google Map 放在滚动视图中,以便用户可以向下滚动其他内容以查看 Map . 问题是这个滚动视图占用了所有垂直触摸事件,因此 Map 的UI体验变得非常奇怪 .

我知道在谷歌 Map 的V1中,您可以覆盖onTouch或setOnTouchListener以在MotionEvent.ACTION_DOWN上调用requestDisallowInterceptTouchEvent . 我试图用V2实现类似的技巧无济于事 .

到目前为止,我尝试过:

  • 覆盖SupportMapFragment,并在onCreateView中为View设置触摸侦听器

  • 调用SupportMapFragment实例的.getView(),然后调用setOnTouchListener

  • 环绕相对布局或框架布局,使用透明视图或图像视图遮罩片段

这些都没有解决滚动问题 . 我在这里错过了什么吗?如果有人在滚动视图中有一个 Map 的工作示例,请您分享代码示例吗?

8 回答

  • 0

    上面列出的大多数选项对我来说都不起作用,但以下证明对我来说是一个很好的解决方案:

    Cheese Barons Solution

    我也必须实现与我的实现略有不同,因为我在片段中使用 Map ,这使得事情稍微复杂但易于工作 .

  • 15

    在mapview片段上应用透明图像 .

    <RelativeLayout
        android:id="@+id/map_layout"
        android:layout_width="match_parent"
        android:layout_height="300dp">
    
        <fragment
            android:id="@+id/mapview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="-100dp"
            android:layout_marginBottom="-100dp"
            android:name="com.google.android.gms.maps.MapFragment"/>
    
        <ImageView
            android:id="@+id/transparent_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@color/transparent" />
    
    </RelativeLayout>
    

    然后为主ScrollView设置 requestDisallowInterceptTouchEvent(true) . 当用户触摸透明图像并移动时,禁用透明图像上的触摸 MotionEvent.ACTION_DOWNMotionEvent.ACTION_MOVE ,以便 Map 片段可以进行触摸事件 .

    ScrollView mainScrollView = (ScrollView) findViewById(R.id.main_scrollview);
    ImageView transparentImageView = (ImageView) findViewById(R.id.transparent_image);
    
    transparentImageView.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;
            }   
        }
    });
    

    这对我有用 . 希望它可以帮助你..

  • 128

    我遇到了类似的问题,并提出了一个基于In-Ho Yi和上面的ДанаилДимитров答案的更通用的工作解决方案 .

    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

    谢谢你的建议,

    经过多次尝试和错误,拔掉我的头发,咒骂显示器和我糟糕的Android测试手机,我想如果我自定义ScrollView,重写onInterceptTouchEvent,当事件在 Map 视图上时我们返回false什么,然后 Map 上的滚动确实按预期发生 .

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

    这段代码在Scala中,但你明白了 .

    注意我最终使用了原始 Map 视图(如android-sdks \ extras \ google \ google_play_services \ samples \ maps \ src \ com \ example \ mapdemoRawMapViewDemoActivity.java中所示) . 猜猜你可以用片段做同样的事情,我从来没有喜欢片段 .

    我认为Google欠我一个道歉 .

  • 0

    我有同样的问题,所以这里是我如何使用解决方案作为Java代码,以防任何人需要它 . 您只需在使用时设置mapView字段 .

    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
        }
    }
    
  • 1

    接受的答案在我的案例中不起作用 . 客人的回答既没有(但差不多) . 如果是其他人的情况,请尝试访客回答的这个编辑版本 .

    如果有人在计算hitbox时需要使用它,我已经注释掉了操作栏的高度 .

    public class InterceptableScrollView extends ScrollView {
    
        List<View> mInterceptScrollViews = new ArrayList<View>();
    
        public InterceptableScrollView(Context context) {
            super(context);
        }
    
        public InterceptableScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public InterceptableScrollView(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);
        }
    
        private int getRelativeTop(View myView) {
            if (myView.getParent() == this)
                return myView.getTop();
            else
                return myView.getTop() + getRelativeTop((View) myView.getParent());
        }
        private int getRelativeLeft(View myView) {
            if (myView.getParent() == this)
                return myView.getLeft();
            else
                return myView.getLeft() + getRelativeLeft((View) myView.getParent());
        }
    
        @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();
    
    
                /*
                int actionBarHeight = 0;
    
                TypedValue tv = new TypedValue();
                if (getContext().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
                {
                    actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
                }
                */
    
                int viewLocationY = 0;
                int viewLocationX = 0;
                int relativeTop = 0;
                int relativeLeft = 0;
    
                for (View view : mInterceptScrollViews) {
    
                    relativeTop = getRelativeTop((View) view.getParent());
                    relativeLeft = getRelativeLeft((View) view.getParent());
                    viewLocationY = relativeTop - getScrollY();
                    viewLocationX = relativeLeft - getScrollX();
    
                    if (view.getHeight() + viewLocationY > y && y > viewLocationY && view.getWidth() + viewLocationX > x && x > viewLocationX)
                    {
                        return false;
                    }
                }
            }
    
            return super.onInterceptTouchEvent(event);
        }
    }
    
  • 5

    如果您不再需要透明图像,请改进代码:

    // 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;
                    }
                }
            });
    
  • 9

    在XML中使用自定义Google Map 片段 .

    这是完整的代码,对我有用 . 如果您有任何疑问,请告诉我 .

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

相关问题