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

Posted

技术标签:

【中文标题】如何在滚动视图中设置谷歌地图片段【英文标题】:how to set google map fragment inside scroll view 【发布时间】:2015-08-12 01:17:24 【问题描述】:

我想在垂直ScrollView 内设置一个谷歌地图片段,当我这样做时地图不会垂直缩放,我如何覆盖MapView 上的触摸事件侦听器而不是ScrollView 的侦听器?

这是我的 xml 代码:

  <ScrollView  android:layout_
    android:layout_
     android:layout_above="@id/footer"
    android:layout_below="@id/title_horizontalScrollView">

   ///other things



<fragment 
    android:id="@+id/map"
    android:layout_
    android:layout_
    class="com.google.android.gms.maps.SupportMapFragment"
    />

  //other things

【问题讨论】:

检查这个答案 :) ***.com/a/17317176/3215911 应该会有帮助! 我添加了透明图像,但仍然无法垂直缩放谷歌地图视图 【参考方案1】:

创建一个自定义的SupportMapFragment,以便我们可以覆盖它的触摸事件:

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

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

然后用这个class="packagename.WorkaroundMapFragment"而不是com.google.android.gms.maps.SupportMapFragment替换xml中的片段类

然后在你的activity中初始化map如下:

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

【讨论】:

OMG :D 它的工作非常感谢你,你是最棒的 嗨,它有效!唯一的更正:WorkaroundMapFragment.java 中的 getColor 现在已弃用,因此我使用了 ContextCompat.getColor(getActivity().getApplicationContext(), android.R.color.transparent)。 准确!非常感谢楼主! @AlokNair 我已经使用了你的答案,它在滚动视图中运行良好,但现在我有需要检测 TouchableWrapper 的 ACTION_UP 的情况,它不会被调用。我需要从 touchableWrapper 的触摸事件中返回 true 吗? .你能帮忙吗?? 很好的答案!它工作正常。但是我不明白getMap()方法是什么,从哪里来的!我只是在面向事件的方法里面写了mMap = googleMaponMapReady(GoogleMap googleMap)【参考方案2】:

只要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_
     android:layout_
     android:fillViewport="true"
     android:orientation="vertical">
</com.app.ui.views.CustomScrollView>

如果之前的 xml 不起作用,请更改为这个...

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

如何以编程方式使用

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

【讨论】:

这解决了我在 ScrollView 中的 MapView 问题(它位于可滑动标签片段的片段中)。我之前尝试在 MapView 中覆盖 onTouchEvent,但没有成功。你拯救了我的一天! 很棒的解决方案。我不知道它为什么起作用。谁能解释一下? 覆盖运动事件,智能一个 这个问题似乎比@Alok Nair 建议的要容易。【参考方案3】:

我使用 google maps listeners 解决了 google map 在滚动视图中的滚动问题

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

    

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

    

当你使用开始移动地图时使用它,它不允许父滚动,当你停止移动地图时,它允许滚动。

【讨论】:

【参考方案4】:

这是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 版本,所以我认为它可能对某人有用。

【讨论】:

【参考方案5】:

请参考这个答案: https://***.com/a/17317176/4837103

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

ma​​pview 片段上添加透明视图:

<ScrollView>
    ...
         <FrameLayout
             android:layout_
             android:layout_>
             <FrameLayout
                 android:id="@+id/fl_google_map"
                 android:layout_
                 android:layout_ />
             <!-- workaround to make google map scrollable -->
             <View
                 android:id="@+id/transparent_touch_panel"
                 android:layout_
                 android:layout_ />
         </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 可以只被调用一次,但显然它必须为每个触摸事件调用,这就是为什么它必须放在 onTouchEvent 中。并且父级会将这个请求传播给父级的父级。

我尝试为 fl_google_map 或 mapFragment.getView() 设置触摸事件侦听器。它们都不起作用,所以创建一个透明的兄弟视图对我来说是一个可以容忍的解决方法。

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

【讨论】:

【参考方案6】:

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

在片段中添加这个属性。

 map:liteMode="true"

我的问题解决了。 除此之外,我还添加了 customScrollView。但在放置 liteMode 属性后,我的问题解决了。

【讨论】:

我使用的是 androidx.core.widget.NestedScrollView,添加这个解决了我的地图显示为灰色而里面没有任何内容的问题。【参考方案7】:

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

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

    

【讨论】:

【参考方案8】:

更新

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

【讨论】:

【参考方案9】:

正如Android Document 建议的那样, 你可以这样添加:

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_ >
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_
        android:layout_
        android:layout_weight="3"
        android:orientation="horizontal" >
<fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_
        android:layout_
        />
</LinearLayout>
</ScrollView>

【讨论】:

我也无法滚动我的地图并垂直缩放,因为滚动视图会在地图上滚动 我希望你提到了提到的文件。将地图片段放在滚动视图中并不是一个好习惯。请让我知道你为什么这样做。这背后有什么原因吗?我在没有添加滚动视图的情况下做了同样的事情。如果您想观看代码,请告诉我。 @RiteshGajera 如果你有一个很长的表格并且想要在它的中心有所有功能的地图怎么办。那你不使用滚动视图吗?? 没有,我还没用。但是如果没有滚动视图,您可以通过 SupportMapFragment 实现相同的功能。 Alok,你可以在谷歌地图上执行或绑定触摸事件。像: googleMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() @Override public void onMapLongClick(LatLng point) //在这里做你的事情.. );这是我在一项研发任务中所做的。

以上是关于如何在滚动视图中设置谷歌地图片段的主要内容,如果未能解决你的问题,请参考以下文章

片段更改时如何隐藏地图视图(谷歌地图v2)

谷歌地图在片段android

Android从地图片段底部拖动回收器视图

如何在片段中嵌入谷歌地图?

如何在Android的谷歌地图片段中更改默认的蓝色圆形位置图标?

谷歌地图视图如何在地图缩放时获取原始点或框进行注释?