我想在垂直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
答案 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
然后在您的活动初始化地图中,如下所示:
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>