Je n'arrive pas à trouver un exemple d'interception de la carte sur la nouvelle API Google Maps v2.
J'ai besoin de savoir quand l'utilisateur touche la carte pour arrêter un fil (le centrage de la carte autour de ma position actuelle).
@ape a écrit ici une réponse sur la façon d'intercepter les clics sur la carte, mais je dois intercepter les touches, puis il a suggéré le lien suivant dans un commentaire de sa réponse, Comment gérer un événement onTouch pour la carte dans Google Map API v2 ? .
Cette solution semble être une solution de contournement possible, mais le code suggéré était incomplet. Pour cette raison, je l'ai réécrit et testé, et maintenant cela fonctionne.
Ici c'est le code de travail:
J'ai créé la classe MySupportMapFragment.Java
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;
}
}
J'ai même créé la classe TouchableWrapper.Java:
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);
}
}
Dans la mise en page, je le déclare de cette façon:
<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"
/>
Juste pour tester dans l'activité principale, je n'ai écrit que ce qui suit:
public class MainActivity extends FragmentActivity {
public static boolean mMapIsTouched = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Voici une solution simple pour obtenir la localisation en fonction de la sélection de l'utilisateur (Cliquez sur l'option sur la carte)
googleMap.setOnMapClickListener(new OnMapClickListener() {
@Override
public void onMapClick(LatLng arg0) {
// TODO Auto-generated method stub
Log.d("arg0", arg0.latitude + "-" + arg0.longitude);
}
});
Cette fonctionnalité et beaucoup d'autres sont maintenant supportées :)
voici la note du développeur (numéro 4636):
La version d'août 2016 présente un ensemble de nouveaux écouteurs de changement de caméra pour les événements de début, de déroulement et de fin de mouvement de la caméra. Vous pouvez également voir pourquoi la caméra bouge, qu'elle soit provoquée par des gestes de l'utilisateur, des animations d'API intégrées ou des mouvements contrôlés par le développeur. Pour plus d'informations, consultez le guide des événements de changement de caméra: https://developers.google.com/maps/documentation/Android-api/events#camera-change-events
Consultez également les notes de publication: https://developers.google.com/maps/documentation/Android-api/releases#august_1_2016
voici un extrait de code de la page de documentation
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();
}
}
J'ai créé un FrameLayout vide superposé au MapFragment dans la présentation. J'ai ensuite défini onTouchListener sur cette vue pour que je sache quand la carte a été touchée, mais renvoie la valeur false pour que le contact soit transmis à la carte.
<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.
}
});
Gaucho a une excellente réponse et, compte tenu des nombreux votes positifs, j’ai pensé qu’il faudrait peut-être une autre implémentation:
J'en avais besoin pour utiliser un auditeur afin que je puisse réagir au toucher et ne pas avoir à le vérifier en permanence.
Je mets tous dans une classe qui peut être utilisée comme ceci:
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
}
}
});
où le mapfragment doit être de type TouchSupportMapFragment et dans le layout xml, cette ligne est requise:
<fragment class="de.bjornson.maps.TouchSupportMapFragment"
...
Voici la classe:
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);
}
}
}
Voir ce lien. Implémentez l'interface et indiquez la méthode onMapClick()
ou celle qui vous convient et définissez la variable onMapClickListener
sur la bonne implémentation.
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
}
}
// 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
}
}
});
Pour Mono amoureux:
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);
}
}
}
@Gaucho MySupportMapFragment sera évidemment utilisé par un autre segment ou activité (où il pourrait y avoir plus d'éléments de vue que le fragment de carte). Alors, comment peut-on envoyer cet événement au fragment suivant où il doit être utilisé? Avons-nous besoin de réécrire une interface pour le faire?
J'ai une solution plus simple diferent à la TouchableWrapper
et cela fonctionne avec la dernière version de play-services-maps:10.0.1
. Cette solution utilise uniquement les événements de cartes et n'utilise pas de vues personnalisées. N'utilise pas de fonctions obsolètes et aura probablement un support pour plusieurs versions.
Tout d'abord, vous avez besoin d'une variable d'indicateur qui stocke si la carte est déplacée par une animation ou par une entrée utilisateur (cela suppose que chaque mouvement de caméra qui n'est pas déclenché par une animation est déclenché par l'utilisateur)
GoogleMap googleMap;
boolean movedByApi = false;
Votre parfum ou votre activité doit implémenter GoogleMap.OnMapReadyCallback
, GoogleMap.CancelableCallback
public class ActivityMap extends Activity implements OnMapReadyCallback, GoogleMap.CancelableCallback{
...
}
et cela vous oblige à implémenter les méthodes onMapReady
, onFinish
, onCancel
. Et l'objet googleMap dans onMapReady
doit définir un eventlistener pour le déplacement de la caméra.
@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;
}
Et enfin, mieux si vous créez une fonction générique pour déplacer la carte
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);
}
ou juste chaque fois que vous déplacez la carte, activez le drapeau avant l'animation
movedByApi = true;
googleMap.animateCamera(cu, this);
J'espère que ça aide!