Google Maps Android API v2 - Berührungen auf der Karte erkennen

73

Ich kann kein Beispiel dafür finden, wie die Kartenberührung in der neuen Google Maps API v2 abgefangen wird.

Ich muss wissen, wann der Benutzer die Karte berührt, um einen Thread zu stoppen (die Zentrierung der Karte um meinen aktuellen Standort).

Gaucho
quelle
3
Wenn jemand Ihre Frage beantwortet hat, markieren Sie Ihre Frage als beantwortet. Außerdem sagen Sie explizit "Klicks auf die Karte", sodass Sie nicht auf Affen oder CommonsWare schnappen müssen, um Ihre Gedanken nicht lesen zu können.
Maarten
1
Ich könnte es sogar als beantwortet markieren, aber ich schrieb "map touch", nicht map "click". @ape in einem Kommentar schlug einen anderen Thread vor, der mein Problem löst ( stackoverflow.com/questions/13722869/… ), aber ich kann ihn nicht verwenden, wie ich in Kommentaren geschrieben habe. Ich kann die Lösung für diesen Thread weder für den verknüpften finden. Soll ich eine andere Frage öffnen?
Gaucho
Ihre Antwort sollte eine Antwort sein, die nicht in die Frage eingearbeitet ist. Sie haben es wirklich schwer gemacht, zu folgen. Wenn Ihre eigene Antwort Ihnen am meisten geholfen hat, können Sie sie sogar akzeptieren, um dies für andere zu zeigen.
Kate Gregory
Ich bin neu bei stackOverflow. Ich kann es schaffen!
Gaucho

Antworten:

95

@ape hat hier eine Antwort zum Abfangen der Kartenklicks geschrieben, aber ich muss die Berührungen abfangen. Dann schlug er den folgenden Link in einem Kommentar zu seiner Antwort vor: Wie gehe ich mit dem onTouch-Ereignis für eine Karte in Google Map API v2 um? .

Diese Lösung scheint eine mögliche Problemumgehung zu sein, aber der vorgeschlagene Code war unvollständig. Aus diesem Grund habe ich es umgeschrieben und getestet, und jetzt funktioniert es.

Hier ist der Arbeitscode:

Ich habe die Klasse MySupportMapFragment.java erstellt

import com.google.android.gms.maps.SupportMapFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MySupportMapFragment extends SupportMapFragment {
    public View mOriginalContentView;
    public TouchableWrapper mTouchView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
        mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
        mTouchView = new TouchableWrapper(getActivity());
        mTouchView.addView(mOriginalContentView);
        return mTouchView;
    }

    @Override
    public View getView() {
        return mOriginalContentView;
    }
}

Ich habe sogar die Klasse TouchableWrapper.java erstellt:

import android.content.Context;
import android.view.MotionEvent;
import android.widget.FrameLayout;

public class TouchableWrapper extends FrameLayout {

    public TouchableWrapper(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                  MainActivity.mMapIsTouched = true;
                  break;

            case MotionEvent.ACTION_UP:
                  MainActivity.mMapIsTouched = false;
                  break;
        }
        return super.dispatchTouchEvent(event);
    }
}

Im Layout erkläre ich es so:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/mapFragment"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_alignParentBottom="true"
          android:layout_below="@+id/buttonBar"
          class="com.myFactory.myApp.MySupportMapFragment"
/>

Nur zum Testen in der Hauptaktivität habe ich nur folgendes geschrieben:

public class MainActivity extends FragmentActivity {
    public static boolean mMapIsTouched = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
Gaucho
quelle
3
Abgestimmt, weil der Benutzer das nicht brauchte, aber er fragte. Und genau das brauchte ich :)
Thibault D.
@ Gaucho, ich musste diese Lösung ein wenig verbessern, hauptsächlich durch die Verwendung eines benutzerdefinierten Listeners anstelle einer öffentlichen statischen Variablen. Siehe die Lösung unten.
Dimitar Darazhanski
@ Dimitar: Jetzt habe ich keine Zeit, es zu testen. Ich lasse es dich wissen. Vielen Dank.
Gaucho
tolle Lösung .. :)
Rajshree Tiwari
@ DimitarDarazhanski - Ihre Implementierung funktioniert hervorragend. Vielen Dank! dimitar.me/how-to-detect-a-user-pantouchdrag-on-android-map-v2/…
Adam Hurwitz
48

Hier ist eine einfache Lösung, um den Standort basierend auf der Benutzerauswahl zu ermitteln (klicken Sie auf die Option auf der Karte):

googleMap.setOnMapClickListener(new OnMapClickListener() {
  @Override
  public void onMapClick(LatLng arg0) {
    // TODO Auto-generated method stub
    Log.d("arg0", arg0.latitude + "-" + arg0.longitude);
  }
});
Sampath Kumar
quelle
3
Dieser Vorgang funktioniert, wenn Sie die Karte reibungslos berühren, wenn Sie jedoch Karten kaum berühren und das Zoomen beginnt. Aus diesem Grund wird die onMapClick-Methode nicht aufgerufen.
Md. Sajedul Karim
1
@ Md.SajedulKarim Sie können alle Gesten mit googleMap.getUiSettings () deaktivieren. SetAllGesturesEnabled (false); und hören Sie sich diesen Tipp an, nachdem Sie die Gesten wieder aktiviert haben.
Array
2
setOnMapClickListener erkennt nicht. Was soll ich importieren?
ONE_FE
33

Diese und viele weitere Funktionen werden jetzt unterstützt :)

Dies ist der Entwicklerhinweis (Ausgabe 4636):

Mit der Version vom August 2016 wird eine Reihe neuer Listener für Kamerawechsel für Start-, laufende und Endereignisse von Kamerabewegungen eingeführt. Sie können auch sehen, warum sich die Kamera bewegt, ob dies durch Benutzergesten, integrierte API-Animationen oder von Entwicklern gesteuerte Bewegungen verursacht wird. Weitere Informationen finden Sie in der Anleitung zu Kamerawechselereignissen: https://developers.google.com/maps/documentation/android-api/events#camera-change-events

Beachten Sie auch die Versionshinweise: https://developers.google.com/maps/documentation/android-api/releases#august_1_2016

Hier ist ein Code-Snippet von der Dokumentationsseite

public class MyCameraActivity extends FragmentActivity implements
        OnCameraMoveStartedListener,
        OnCameraMoveListener,
        OnCameraMoveCanceledListener,
        OnCameraIdleListener,
        OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_camera);

        SupportMapFragment mapFragment =
            (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap map) {
        mMap = map;

        mMap.setOnCameraIdleListener(this);
        mMap.setOnCameraMoveStartedListener(this);
        mMap.setOnCameraMoveListener(this);
        mMap.setOnCameraMoveCanceledListener(this);

        // Show Sydney on the map.
        mMap.moveCamera(CameraUpdateFactory
                .newLatLngZoom(new LatLng(-33.87365, 151.20689), 10));
    }

    @Override
    public void onCameraMoveStarted(int reason) {

        if (reason == OnCameraMoveStartedListener.REASON_GESTURE) {
            Toast.makeText(this, "The user gestured on the map.",
                           Toast.LENGTH_SHORT).show();
        } else if (reason == OnCameraMoveStartedListener
                                .REASON_API_ANIMATION) {
            Toast.makeText(this, "The user tapped something on the map.",
                           Toast.LENGTH_SHORT).show();
        } else if (reason == OnCameraMoveStartedListener
                                .REASON_DEVELOPER_ANIMATION) {
            Toast.makeText(this, "The app moved the camera.",
                           Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onCameraMove() {
        Toast.makeText(this, "The camera is moving.",
                       Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCameraMoveCanceled() {
        Toast.makeText(this, "Camera movement canceled.",
                       Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCameraIdle() {
        Toast.makeText(this, "The camera has stopped moving.",
                       Toast.LENGTH_SHORT).show();
    }
}
A. Alqadomi
quelle
9

Ich habe ein leeres FrameLayout erstellt, das über dem MapFragment im Layout liegt. Ich habe dann einen onTouchListener für diese Ansicht festgelegt, damit ich weiß, wann die Karte berührt wurde, aber false zurückgebe, damit die Berührung an die Karte weitergegeben wird.

<FrameLayout
    android:id="@+id/map_touch_layer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

mapTouchLayer.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Utils.logDebug(TAG, "Map touched!");
            timeLastTouched = System.currentTimeMillis();
            return false; // Pass on the touch to the map or shadow layer.
        }
    });
Flyview
quelle
1
Dies wird einfach NICHT funktionieren. Die Rückgabe von false on ACTION_DOWNführt ACTION_UPzu einer Unterbrechung und onTouchwird NICHT dafür aufgerufen.
Hamzeh Soboh
Kurz und einfach mit großem Hinweis
Nirmal
7

https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener

Siehe diesen Link. Implementieren Sie die Schnittstelle und geben Sie die onMapClick() Methode oder was auch immer Sie benötigen ein und stellen Sie onMapClickListenerdie richtige Implementierung ein.

public class YourActivity extends Activity implements OnMapClickListener {
    @Override
    protected void onCreate(Bundle icicle) { 
        super.onCreate(icicle);
        ...
        my_map.setOnMapClickListener(this)        
        ...
    }

    public void onMapClick (LatLng point) {
        // Do Something
    }
}
adarsh
quelle
Vielen Dank ndsmyter für die Antwort. Der onMapClick fängt ab, wenn Sie auf die Karte tippen, funktioniert jedoch nicht, wenn Sie den Finger auf der Karte bewegen. Ich muss nicht nur den Kartenklick abfangen, sondern auch den Kartenschwenk. Weißt du wie es geht?
Gaucho
2
Map Touch ist nicht der "Map Click", daher wird die Frage nicht beantwortet. Ich muss die Kartenbewegung abfangen, weil der Benutzer die Karte berührt, und ich kann keinen funktionierenden Weg finden, um diese Aktion abzufangen. Ich denke, dass ich den setOnCameraChangeListener nicht verwenden kann, da ich immer noch die animateCamera-Methode verwende, um den Kamerastandort in meinem Code zu aktualisieren. Dann brauche ich nur einen Listener, um die Berührung der Karte während des Schwenkens der Karte abzufangen.
Gaucho
Ich denke du brauchst das onMarkerDragListener? Entwickler.google.com/maps/documentation/android/reference/com/…
Adarsh
Lieber @ape, der onMarkerDragListener fängt das Ziehen eines Markers ab, nicht das Schwenken einer Karte ohne Marker. Ich brauche eine Unterbrechung, wenn der Benutzer die Karte berührt, um zu schwenken.
Gaucho
2
Okay, ich denke das hilft? stackoverflow.com/questions/13722869/…
adarsh
7

Gaucho hat eine großartige Antwort, und angesichts der vielen positiven Stimmen, die ich vermutet habe, könnte eine weitere Implementierung erforderlich sein:

Ich brauchte es, um einen Listener zu verwenden, damit ich auf die Berührung reagieren kann und es nicht ständig überprüfen muss.

Ich habe alles in eine Klasse eingeordnet, die so verwendet werden kann:

mapFragment.setNonConsumingTouchListener(new TouchSupportMapFragment.NonConsumingTouchListener() {
    @Override
    public void onTouch(MotionEvent motionEvent) {
        switch (motionEvent.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                // map is touched
                break;
            case MotionEvent.ACTION_UP:
                // map touch ended
                break;
            default:
                break;
            // use more cases if needed, for example MotionEvent.ACTION_MOVE
        }
    }
});

Wo das Mapfragment vom Typ TouchSupportMapFragment sein muss und in der Layout-XML diese Zeile benötigt wird:

<fragment class="de.bjornson.maps.TouchSupportMapFragment"
...

Hier ist die Klasse:

package de.bjornson.maps;

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 TouchSupportMapFragment extends SupportMapFragment {
    public View mOriginalContentView;
    public TouchableWrapper mTouchView;
    private NonConsumingTouchListener mListener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
        mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
        mTouchView = new TouchableWrapper(getActivity());
        mTouchView.addView(mOriginalContentView);
        return mTouchView;
    }

    @Override
    public View getView() {
        return mOriginalContentView;
    }

    public void setNonConsumingTouchListener(NonConsumingTouchListener listener) {
        mListener = listener;
    }

    public interface NonConsumingTouchListener {
        boolean onTouch(MotionEvent motionEvent);
    }

    public class TouchableWrapper extends FrameLayout {

        public TouchableWrapper(Context context) {
            super(context);
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if (mListener != null) {
                mListener.onTouch(event);
            }
            return super.dispatchTouchEvent(event);
        }
    }
}
Björn Kechel
quelle
2
  // Initializing
    markerPoints = new ArrayList<LatLng>();

    // Getting reference to SupportMapFragment of the activity_main
    SupportMapFragment sfm = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);

    // Getting Map for the SupportMapFragment
    map = sfm.getMap();

    // Enable MyLocation Button in the Map
    map.setMyLocationEnabled(true);

    // Setting onclick event listener for the map
    map.setOnMapClickListener(new OnMapClickListener() {

        @Override
        public void onMapClick(LatLng point) {

            // Already two locations
            if(markerPoints.size()>1){
                markerPoints.clear();
                map.clear();
            }

            // Adding new item to the ArrayList
            markerPoints.add(point);

            // Creating MarkerOptions
            MarkerOptions options = new MarkerOptions();

            // Setting the position of the marker
            options.position(point);


            if(markerPoints.size()==1){
                options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
            }else if(markerPoints.size()==2){
                options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
            }

            // Add new marker to the Google Map Android API V2
            map.addMarker(options);

            // Checks, whether start and end locations are captured
            if(markerPoints.size() >= 2){
                LatLng origin = markerPoints.get(0);
                LatLng dest = markerPoints.get(1);

            //Do what ever you want with origin and dest
            }
        }
    });
Pratibha Sarode
quelle
1

Für Mono- Liebhaber:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Android.Gms.Maps;

namespace apcurium.MK.Booking.Mobile.Client.Controls
{
    public class TouchableMap : SupportMapFragment
    {
        public View mOriginalContentView;

        public TouchableWrapper Surface;

        public override View OnCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
        {
            mOriginalContentView = base.OnCreateView(inflater, parent, savedInstanceState);
            Surface = new TouchableWrapper(Activity);
            Surface.AddView(mOriginalContentView);
            return Surface;
        }

        public override View View
        {
            get
            {
                return mOriginalContentView;
            }
        }
    }

    public class TouchableWrapper: FrameLayout {

        public event EventHandler<MotionEvent> Touched;

        public TouchableWrapper(Context context) :
        base(context)
        {
        }

        public TouchableWrapper(Context context, IAttributeSet attrs) :
        base(context, attrs)
        {
        }

        public TouchableWrapper(Context context, IAttributeSet attrs, int defStyle) :
        base(context, attrs, defStyle)
        {
        }

        public override bool DispatchTouchEvent(MotionEvent e)
        {
            if (this.Touched != null)
            {
                this.Touched(this, e);
            }

            return base.DispatchTouchEvent(e);
        }
    }
}
Léon Pelletier
quelle
1

Ich habe eine einfachere Lösung als die TouchableWrapperund dies funktioniert mit der letzten Version von play-services-maps:10.0.1. Diese Lösung verwendet nur die Kartenereignisse und keine benutzerdefinierten Ansichten. Verwendet keine veralteten Funktionen und wird wahrscheinlich mehrere Versionen unterstützen.

Zuerst benötigen Sie eine Flag-Variable, die speichert, ob die Karte durch eine Animation oder durch Benutzereingaben verschoben wird (dieser Code setzt voraus, dass jede Kamerabewegung, die nicht durch eine Animation ausgelöst wird, vom Benutzer ausgelöst wird).

GoogleMap googleMap;
boolean movedByApi = false;

Ihr Fragment oder Ihre Aktivität muss umgesetzt werden GoogleMap.OnMapReadyCallback,GoogleMap.CancelableCallback

public class ActivityMap extends Activity implements OnMapReadyCallback, GoogleMap.CancelableCallback{
    ...
}

und diese Kräfte , um die Methoden zu implementieren onMapReady, onFinish, onCancel. Und das googleMap-Objekt in onMapReadymuss einen Ereignislistener für die Kamerabewegung festlegen

@Override
public void onMapReady(GoogleMap mMap) {
    //instantiate the map
    googleMap = mMap;

    [...]  // <- set up your map

    googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
        @Override
        public void onCameraMove() {
            if (movedByApi) {
                Toast.makeText(ActivityMap.this, "Moved by animation", Toast.LENGTH_SHORT).show();

                [...] // <-- do something whe you want to handle api camera movement
            } else {
                Toast.makeText(ActivityMap.this, "Moved by user", Toast.LENGTH_SHORT).show();

                [...] // <-- do something whe you want to handle user camera movement
            }
        }
    });
}
@Override
public void onFinish() {
    //is called when the animation is finished
    movedByApi = false;
}
@Override
public void onCancel() {
    //is called when the animation is canceled (the user drags the map or the api changes to a ne position)
    movedByApi = false;
}

Und schließlich ist es besser, wenn Sie eine generische Funktion zum Verschieben der Karte erstellen

public void moveMapPosition(CameraUpdate cu, boolean animated){
    //activate the flag notifying that the map is being moved by the api
    movedByApi = true;
    //if its not animated, just do instant move
    if (!animated) {
        googleMap.moveCamera(cu);
        //after the instant move, clear the flag
        movedByApi = false;
    }
    else
        //if its animated, animate the camera
        googleMap.animateCamera(cu, this);
}

oder aktivieren Sie jedes Mal, wenn Sie die Karte verschieben, die Flagge vor der Animation

movedByApi = true;
googleMap.animateCamera(cu, this);

Ich hoffe das hilft!

Sander Rito
quelle
Wenn Benutzer während einer Animation die Karte berühren, funktioniert dies nicht.
Tom Bevelander
1

Ich habe die Idee aus der akzeptierten Antwort übernommen und sie verbessert, indem ich sie in Kotlin konvertiert und Konstruktoren hinzugefügt habe, mit denen der berührbare Wrapper im Markup deklariert werden kann, und eine einstellbare Rückrufeigenschaft für die Berührungserkennung verwendet habe, um die Kopplung direkt mit der Aktivität zu entfernen, die dies ermöglicht es kann leichter wiederverwendet werden:

class TouchableWrapper : FrameLayout {

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    var onTouch: ((event :MotionEvent) -> Unit)? = null

    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        onTouch?.invoke(event)
        return super.dispatchTouchEvent(event)
    }
}

Dann in Ihrem Layout:

    <com.yourpackage.views.TouchableWrapper
        android:id="@+id/viewMapWrapper"
        android:layout_height="match_parent"
        android:layout_width="match_parent">
        <fragment
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:id="@+id/map"
                  tools:context=".MapsActivity"
                  android:name="com.google.android.gms.maps.SupportMapFragment"/>
    </com.yourpackage.views.TouchableWrapper>

Richten Sie dann Ihren Rückruf wie folgt ein:

        findViewById<TouchableWrapper>(R.id.viewMapWrapper)
            .onTouch = {
            if (MotionEvent.ACTION_DOWN == it.action) {
                  //Handle touch down on the map
            }
        }
Ian Newson
quelle
0

@Gaucho MySupportMapFragment wird offensichtlich von einer anderen Farcement oder Aktivität verwendet (bei der möglicherweise mehr Ansichtselemente als das Kartenfragment vorhanden sind). Wie kann man dieses Ereignis an das nächste Fragment senden, in dem es verwendet werden soll? Müssen wir dazu erneut eine Schnittstelle schreiben?

user2201332
quelle