如何在滚动视图中设置谷歌地图片段

时间:2015-05-29 08:43:01

标签: android google-maps android-fragments

我想在垂直ScrollView内设置Google地图片段,当我执行地图不垂直缩放时,如何在MapView上覆盖ScrollView上的触摸事件侦听器{{1} }}?

这是我的xml代码:

  <ScrollView  android:layout_height="wrap_content"
    android:layout_width="fill_parent"
     android:layout_above="@id/footer"
    android:layout_below="@id/title_horizontalScrollView">

   ///other things



<fragment 
    android:id="@+id/map"
    android:layout_width="fill_parent"
    android:layout_height="300dp"
    class="com.google.android.gms.maps.SupportMapFragment"
    />

  //other things

9 个答案:

答案 0 :(得分:95)

创建自定义SupportMapFragmet,以便我们可以覆盖其触摸事件:

<强> WorkaroundMapFragment.java

import android.content.Context;
import android.os.Bundle;
import android.widget.FrameLayout;

import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.google.android.gms.maps.SupportMapFragment;

public class WorkaroundMapFragment extends SupportMapFragment {
    private OnTouchListener mListener;

    @Override
    public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle savedInstance) {
        View layout = super.onCreateView(layoutInflater, viewGroup, savedInstance);

        TouchableWrapper frameLayout = new TouchableWrapper(getActivity());

        frameLayout.setBackgroundColor(getResources().getColor(android.R.color.transparent));

        ((ViewGroup) layout).addView(frameLayout,
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

        return layout;
    }

    public void setListener(OnTouchListener listener) {
        mListener = listener;
    }

    public interface OnTouchListener {
        public abstract 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);
        }
    }
}

在上面的类中,我们使用扩展FrameLayout的TouchableWrapper类来拦截触摸事件。还有一个自定义侦听器OnTouchListener将触摸事件分派给处理地图的主要活动MyMapActivity。当触摸事件发生时,将调用dispatchTouchEvent并且监听器mListener将处理它。

然后使用此class="packagename.WorkaroundMapFragment"而不是com.google.android.gms.maps.SupportMapFragment

替换xml中的片段类

然后在您的活动初始化地图中,如下所示:

private GoogleMap mMap;

在onCreate内执行此操作:

  // check if we have got the googleMap already
  if (mMap == null) {
        SupportMapFragment mapFragment = (WorkaroundMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(new OnMapReadyCallback() {
            Override
                public void onMapReady(GoogleMap googleMap)
                    {
                        mMap = googleMap;
                        mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                        mMap.getUiSettings().setZoomControlsEnabled(true);

                        mScrollView = findViewById(R.id.scrollMap); //parent scrollview in xml, give your scrollview id value
                        ((WorkaroundMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
                                .setListener(new WorkaroundMapFragment.OnTouchListener() {
                            @Override
                            public void onTouch()
                                {
                                        mScrollView.requestDisallowInterceptTouchEvent(true);
                                }
                            });
                    }
        });
    }

更新:根据@ javacoder123的答案更新到getMapAsync的答案

答案 1 :(得分:22)

只需制作CustomScrollView.class

public class CustomScrollView extends ScrollView {

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

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            //Log.i("CustomScrollView", "onInterceptTouchEvent: DOWN super false" );
            super.onTouchEvent(ev);
            break;

        case MotionEvent.ACTION_MOVE:
            return false; // redirect MotionEvents to ourself

        case MotionEvent.ACTION_CANCEL:
            // Log.i("CustomScrollView", "onInterceptTouchEvent: CANCEL super false" );
            super.onTouchEvent(ev);
            break;

        case MotionEvent.ACTION_UP:
            //Log.i("CustomScrollView", "onInterceptTouchEvent: UP super false" );
            return false;

        default:
            //Log.i("CustomScrollView", "onInterceptTouchEvent: " + action );
            break;
    }

    return false;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    super.onTouchEvent(ev);
    //Log.i("CustomScrollView", "onTouchEvent. action: " + ev.getAction() );
    return true;
  }
}

然后在xml中

<com.app.ui.views.CustomScrollView
     android:id="@+id/scrollView"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fillViewport="true"
     android:orientation="vertical">
</com.app.ui.views.CustomScrollView>

如果xml beffore无法正常工作......

<com.myproyect.myapp.CustomScrollView
        android:id="@+id/idScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:descendantFocusability="beforeDescendants"
        >
</com.myproyect.myapp.CustomScrollView>

如何使用programmaticaly

CustomScrollView myScrollView = (CustomScrollView) findViewById(R.id.idScrollView);

答案 2 :(得分:4)

我使用了 google map侦听器来解决 google map在滚动视图中的滚动问题

    googleMap?.setOnCameraMoveStartedListener {
        mapView.parent.requestDisallowInterceptTouchEvent(true)

    }

    googleMap?.setOnCameraIdleListener {
        mapView.parent.requestDisallowInterceptTouchEvent(false)

    }

在开始移动地图时使用此功能可禁止父级滚动,而在停止移动地图时可允许滚动。

答案 3 :(得分:2)

我的建议是在处理这种情况时使用较轻版本的谷歌地图。这解决了我的问题。

在片段中添加此属性。

 map:liteMode="true"

我的问题已解决。 我还添加了customScrollView。但放置liteMode属性后我的问题解决了。

答案 4 :(得分:2)

请参考以下答案: https://stackoverflow.com/a/17317176/4837103

它将在mapFragment的相同位置创建一个具有相同mapFragment大小的透明视图,然后调用requestDisallowInterceptTouchEvent(true),以防止其祖先拦截触摸事件。

在mapview片段上方添加透明视图:

<ScrollView>
    ...
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="300dp">
             <FrameLayout
                 android:id="@+id/fl_google_map"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent" />
             <!-- workaround to make google map scrollable -->
             <View
                 android:id="@+id/transparent_touch_panel"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent" />
         </FrameLayout>
</ScrollView>

为该透明视图设置触摸监听器:

    var transparentTouchPanel = view.FindViewById (Resource.Id.transparent_touch_panel);
    transparentTouchPanel.SetOnTouchListener (this);

....

public bool OnTouch (View v, MotionEvent e) {
    switch (e.Action) {
    case MotionEventActions.Up:
        v.Parent.RequestDisallowInterceptTouchEvent (false);
        break;
    default:
        v.Parent.RequestDisallowInterceptTouchEvent (true);
        break;
    }

    return false;
}

我认为RequestDisallowInterceptTouchEvent只能被调用一次,但是显然必须为每个touch事件调用它,这就是为什么必须将它放在onTouchEvent中。父母会将此请求传播给父母的父母。

我尝试为fl_google_map或mapFragment.getView()设置触摸事件监听器。它们都不起作用,因此创建透明的同级视图对我来说是可容忍的解决方法。

我已经尝试过,此解决方案效果很好。如果您不相信我,请查看original post下的评论。我更喜欢这种方式,因为它不需要创建自定义SupportMapFragmet。

答案 5 :(得分:1)

这是Alok Nair's answer的Kotlin版本,我不得不说我是写Kotlin代码的新手,对此表示赞赏。

自定义SupportMapFragmet:

class FocusMapFragment: SupportMapFragment() {
    var listener: OnTouchListener? = null
    lateinit var frameLayout: TouchableWrapper

    override fun onAttach(context: Context) {
        super.onAttach(context)

        frameLayout = TouchableWrapper(context)
        frameLayout.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent))
    }

    override fun onCreateView(inflater: LayoutInflater, viewGroup: ViewGroup?, bundle: Bundle?): View? {
        val layout = super.onCreateView(inflater, viewGroup, bundle)

        val params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        (layout as? ViewGroup)?.addView(frameLayout, params)

        return layout
    }

    interface OnTouchListener {
        fun onTouch()
    }

    inner class TouchableWrapper(context: Context): FrameLayout(context) {
        override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
            when (event?.action) {
                MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP -> listener?.onTouch()
            }
            return super.dispatchTouchEvent(event)
        }
    }
}

然后在xml中更改片段类:

android:name="packagename.FocusMapFragment"

最后,当您初始化地图时:

override fun onMapReady(googleMap: GoogleMap) {
    this.googleMap = googleMap

    (childFragmentManager.findFragmentById(R.id.mapView) as? FocusMapFragment)?.let {
        it.listener = object: FocusMapFragment.OnTouchListener {
            override fun onTouch() {
                scrollView?.requestDisallowInterceptTouchEvent(true)
            }
        }
    }
    // ...
}

我在API 28和29中测试了此代码,我在任何地方都找不到Kotlin版本,因此认为它可能对某些人有用。

答案 6 :(得分:1)

我是这样解决的,加入实现这个类:

public class YourNameFragmentHere extends Fragment implements OnMapReadyCallback, 
GoogleMap.OnCameraMoveStartedListener {...}

进入onMapReady

 @Override
    public void onMapReady(GoogleMap googleMap) {
       // Your code here
       mMap = googleMap;
       mMap.setOnCameraMoveStartedListener(this);
    }

然后我创建了这个函数:

@Override
    public void onCameraMoveStarted(int reason) {

    if (reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE) {
        mScrollView.requestDisallowInterceptTouchEvent(true);

    } else if (reason == GoogleMap.OnCameraMoveStartedListener
            .REASON_API_ANIMATION) {
        mScrollView.requestDisallowInterceptTouchEvent(true);

    } else if (reason == GoogleMap.OnCameraMoveStartedListener
            .REASON_DEVELOPER_ANIMATION) {
        mScrollView.requestDisallowInterceptTouchEvent(true);

    }
}

答案 7 :(得分:0)

<强>更新

@Alok Nair's答案非常好,但.getMap()方法不再适用于Android 9.2以上版本。将getMap方法更改为.getMapAsync并在onMapReady方法中添加代码

更新的解决方案,适用于碎片

if (gMap == null)
        {
          getChildFragmentManager().findFragmentById(R.id.map);
            SupportMapFragment mapFragment = (WorkaroundMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
            mapFragment.getMapAsync(new OnMapReadyCallback()
            {
                @Override
                public void onMapReady(GoogleMap googleMap)
                {
                    gMap = googleMap;
                    gMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                    gMap.getUiSettings().setZoomControlsEnabled(true);

                    mScrollView = mView.findViewById(R.id.advertScrollView); //parent scrollview in xml, give your scrollview id value
                    ((WorkaroundMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
                            .setListener(new WorkaroundMapFragment.OnTouchListener()
                            {
                                @Override
                                public void onTouch()
                                {
                                    mScrollView.requestDisallowInterceptTouchEvent(true);
                                }
                            });
                }
            });
        }

答案 8 :(得分:-10)

Android Document所述,
您可以这样添加:

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:orientation="horizontal" >
<fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>
</ScrollView>