我正在尝试将Google地图放在滚动视图中,以便用户可以向下滚动其他内容以查看地图。问题是这个滚动视图占用了所有垂直触摸事件,因此地图的UI体验变得非常奇怪。
我知道在谷歌地图的V1中,您可以覆盖onTouch或setOnTouchListener以在MotionEvent.ACTION_DOWN上调用requestDisallowInterceptTouchEvent。我试图用V2实现类似的技巧无济于事。
到目前为止,我已经尝试过:
这些都没有解决滚动问题。我在这里错过了什么吗?如果有人在滚动视图中有一个地图的工作示例,请您分享代码示例吗?
答案 0 :(得分:131)
在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_DOWN
和MotionEvent.ACTION_MOVE
透明图像上的触摸,以便地图片段可以进行触摸事件。
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;
}
}
});
这对我有用。希望它可以帮助你..
答案 1 :(得分:15)
我遇到了类似的问题,并提出了一个基于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);
}
}
答案 2 :(得分:9)
感谢您的建议,
经过多次尝试和错误,拔掉我的头发,咒骂显示器和我糟糕的Android测试手机,我想如果我自定义ScrollView,覆盖onInterceptTouchEvent,当事件在地图上时我们返回false无论如何查看,地图上的滚动确实按预期发生。
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中,但您明白了。
注意我最终使用了原始地图视图(如android-sdks \ extras \ google \ google_play_services \ samples \ maps \ src \ com \ example \ mapdemoRawMapViewDemoActivity.java中所示)。猜猜你可以用片段做同样的事情,我从来都不喜欢碎片。
我认为Google欠我一个道歉。
答案 3 :(得分:5)
我有同样的问题,所以这里是我如何使用解决方案作为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
}
}
答案 4 :(得分: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 :(得分:0)
如果您不再需要透明图像,请改进代码:
// 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;
}
}
});
答案 6 :(得分:0)
上面列出的大多数选项对我来说都不起作用,但以下证明对我来说是一个很好的解决方案:
我还必须实现与我的实现略有不同,因为我在片段中使用地图,这使得事情稍微复杂但易于工作。
答案 7 :(得分:0)
在XML中使用自定义Google地图片段。
这是为我工作的完整代码。如果您有任何疑问,请告诉我。
在您的XML文件中,将以下内容添加为地图片段
<fragment
android:id="@+id/map_with_scroll_fix"
android:name="com.myapplication.maputil.GoogleMapWithScrollFix"
android:layout_width="match_parent"
android:layout_height="match_parent" />
这是地图的自定义类
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);
}
}
}
在您的活动类中添加以下内容,以初始化mapview。而已。多田:)
((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);
}
});
}
});
答案 8 :(得分:0)
@Laksh的答案的Kotlin版本: a)实现View.OnTouchListener, b)将onTouch侦听器添加到活动的onCreate的透明视图中。 c)如下重写功能:
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (v?.id) {
R.id.transparent_image -> {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
reportScrollView.requestDisallowInterceptTouchEvent(true)
return false
}
MotionEvent.ACTION_UP -> {
reportScrollView.requestDisallowInterceptTouchEvent(false)
return true
}
MotionEvent.ACTION_MOVE -> {
reportScrollView.requestDisallowInterceptTouchEvent(true)
return false
}
else -> return true
}
}
else -> {
return true
}
}
}