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

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

这是我的 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

【问题讨论】:

标签: android google-maps android-fragments


【解决方案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_width="match_parent"
     android:layout_height="match_parent"
     android:fillViewport="true"
     android:orientation="vertical">
</com.app.ui.views.CustomScrollView>

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

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

如何以编程方式使用

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://stackoverflow.com/a/17317176/4837103

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

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

      <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 可以只被调用一次,但显然它必须为每个触摸事件调用,这就是为什么它必须放在 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_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>
            

            【讨论】:

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