J'essaie de placer une carte Google dans une vue de défilement afin que l'utilisateur puisse faire défiler d'autres contenus pour voir la carte. Le problème est que cette vue par défilement absorbe tous les événements touchants verticaux, de sorte que l'expérience de la carte de l'interface utilisateur devient très étrange.
Je sais que dans la V1 de Google Map, vous pouvez remplacer OnTouch ou définirOnTouchListener pour appeler requestDisallowInterceptTouchEvent à MotionEvent.ACTION_DOWN. J'ai essayé d'implémenter le même truc avec V2 en vain.
Jusqu'ici j'ai essayé:
Aucune de ces solutions n'a résolu le problème du défilement. Est-ce que j'ai râté quelque chose? Si quelqu'un a un exemple fonctionnel de carte dans la vue défilante, pourriez-vous s'il vous plaît bien vouloir partager un exemple de code?
Appliquez une image transparente sur le fragment 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>
Puis définissez requestDisallowInterceptTouchEvent(true)
pour le ScrollView principal. Lorsque l'utilisateur touche l'image transparente et se déplace, désactivez cette dernière pour MotionEvent.ACTION_DOWN
et MotionEvent.ACTION_MOVE
afin que le fragment de carte puisse prendre des événements tactiles.
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;
}
}
});
Cela a fonctionné pour moi. J'espère que ça vous aide ..
J'ai rencontré un problème similaire et ai proposé une solution de travail plus générale basée sur les réponses d'In-Ho Yi et Данаил Димитров ci-dessus.
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);
}
}
Merci pour vos suggestions,
Après beaucoup d'essais et d'erreurs, enlevant mes cheveux et jurant devant un moniteur et mon mauvais téléphone de test Android, je me suis dit que si je personnalise ScrollView, remplacez onInterceptTouchEvent dans lequel nous retournons false lorsque l'événement est sur une carte, peu importe. quoi, alors le défilement sur une carte se produit comme prévu.
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)
}
}
Ce code est en Scala mais vous voyez l'idée.
Remarque J'ai fini par utiliser une vue de carte brute (comme indiqué dans Android-sdks\extras\google\google_play_services\samples\maps\src\com\example\mapdemoRawMapViewDemoActivity.Java). Je suppose que vous pouvez faire à peu près la même chose avec des fragments, je n’ai jamais aimé les fragments au départ.
Je pense que Google me doit des excuses.
J'avais le même problème. Voici comment j'ai utilisé la solution sous forme de code Java au cas où quelqu'un en aurait besoin. Il vous suffit de définir le champ mapView lors de son utilisation.
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 réponse acceptée n'a pas fonctionné dans mon cas. La réponse de l'invité ne fit ni l'un ni l'autre (mais presque). Si tel est le cas pour quelqu'un d'autre, essayez cette version modifiée de la réponse de l'invité.
J'ai commenté la hauteur de la barre d'action si quelqu'un devait l'utiliser pour calculer la 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);
}
}
Utilisez un fragment de carte Google personnalisé dans votre XML.
Voici le code complet qui a fonctionné pour moi. Si vous avez des questions, s'il vous plaît faites le moi savoir.
Dans votre fichier XML, ajoutez les éléments suivants en tant que fragment de carte
<fragment
Android:id="@+id/map_with_scroll_fix"
Android:name="com.myapplication.maputil.GoogleMapWithScrollFix"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
Voici la classe personnalisée de la carte
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);
}
}
}
Ajoutez ce qui suit dans votre classe d’activité, pour initialiser mapview. C'est tout. Tada :)
((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);
}
});
}
});
Amélioration du code si vous n’avez plus besoin d’être transparent Image
// 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 plupart des options énumérées ci-dessus ne fonctionnaient pas pour moi, mais les solutions suivantes se sont avérées être une excellente solution au problème:
J'ai également dû mettre en œuvre une configuration légèrement différente pour ma mise en œuvre, car j'utilise la carte dans un fragment qui rend les choses un peu plus compliquées mais faciles à utiliser.