API de Google Maps v2 SupportMapFragment dentro de ScrollView: los usuarios no pueden desplazarse verticalmente por el mapa

Estoy tratando de poner un mapa de Google dentro de una vista de desplazamiento, para que el usuario pueda desplazarse hacia abajo para ver el mapa. El problema es que esta vista de desplazamiento se está comiendo todos los eventos de toque vertical, por lo que la experiencia de interfaz de usuario del mapa se vuelve muy extraña.

Sé que en la V1 del mapa de Google, puede anular OnTouch, o configurarOnTouchListener para llamar a requestDisallowInterceptTouchEvent en MotionEvent.ACTION_DOWN. Intenté implementar el truco similar con V2 inútilmente.

Hasta ahora lo he intentado:

  • Anular SupportMapFragment, y dentro deCreateView, establecer un oyente táctil para la Vista
  • llama a .getView () de una instancia de SupportMapFragment y luego a setOnTouchListener
  • Ajustar el diseño relativo o el diseño del marco, enmascarar el fragmento con una vista transparente o una imagen

Ninguno de estos remedió el problema de desplazamiento. ¿Me estoy perdiendo de algo? Si alguien tiene un ejemplo de trabajo de un mapa dentro de la vista de desplazamiento, ¿podría compartir amablemente el ejemplo del código?

Aplica una imagen transparente sobre el fragmento mapview.

     

A continuación, establezca requestDisallowInterceptTouchEvent(true) para ScrollView principal. Cuando el usuario toca la imagen transparente y se mueve, deshabilite el toque en la imagen transparente para MotionEvent.ACTION_DOWN y MotionEvent.ACTION_MOVE para que el fragmento de mapa pueda tomar Touch Events.

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

Esto funcionó para mí. Espero que te ayude …

Me encontré con un problema similar y se me ocurrió una solución de trabajo más general basada en In-Ho Yi y Данаил Димитров respuestas anteriores.

 public class CustomScrollView extends ScrollView { List mInterceptScrollViews = new ArrayList(); 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); } } 

Gracias por las sugerencias,

Después de mucho try-and-error, quitándome el pelo y maldiciendo el monitor y mi pobre teléfono de prueba de Android, me he dado cuenta de que si personalizo ScrollView, invalido on InterceptTouchEvent en el que devolvemos false cuando el evento está en una vista de mapa sin importar qué, entonces el desplazamiento en un mapa ocurre como se esperaba.

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

Este código está en Scala pero entiendes la idea.

Tenga en cuenta que he terminado usando una vista de mapa sin formato (como se muestra en android-sdks \ extras \ google \ google_play_services \ samples \ maps \ src \ com \ example \ mapdemoRawMapViewDemoActivity.java). Supongo que puedes hacer prácticamente lo mismo con fragmentos. Para empezar, nunca me gustaron los fragmentos.

Creo que Google me debe una disculpa.

Tuve el mismo problema, así que aquí es cómo utilicé la solución como código Java por si acaso alguien lo necesita. Solo tiene que establecer el campo mapView cuando lo use.

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

La respuesta aceptada no funcionó en mi caso. La respuesta del invitado no hizo ninguna (pero casi). Si este es el caso para otra persona, intente con esta versión editada de la respuesta del Invitado.

He comentado la altura de la barra de acción si alguien la necesita al calcular el hitbox.

 public class InterceptableScrollView extends ScrollView { List mInterceptScrollViews = new ArrayList(); 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); } } 

Mejora del código si no necesita una imagen transparente nuevamente:

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

La mayoría de las opciones enumeradas anteriormente no funcionaron para mí, pero la siguiente resultó ser una gran solución para mi problema:

Solución Cheese Barons

También tuve que implementar algo diferente para mi implementación, ya que estoy usando el mapa en un fragmento que hace las cosas un poco más complicadas pero más fáciles de poner en práctica.