web-dev-qa-db-fra.com

Comment gérer le déplacement de la carte à l'aide de Google Maps pour Android V2?

Je souhaite géocoder l'adresse dès que le centre de la carte a été modifié.

Comment gérer les mouvements de carte avec la nouvelle version de Google Maps pour Android V2? (Je parle du cas puis l'utilisateur fait glisser la carte au doigt)

40
Alexey Zakharov

Voici une solution de contournement possible pour déterminer les événements de début et de fin de glisser:

Vous devez étendre SupportMapFragment ou MapFragment. Dans onCreateView, vous devez emballer votre MapView dans un FrameLayout personnalisé (dans l'exemple ci-dessous, il s'agit de la classe "TouchableWrapper"), dans lequel vous interceptez les événements tactiles et identifiez si la carte est tapée ou non. Si votre "onCameraChange" est appelé, vérifiez si la vue de carte est activée ou non (dans l'exemple ci-dessous, il s'agit de la variable "mMapIsTouched").

Exemple de code:

MISE À JOUR 1: 

  • retourne la vue originale créée dans getView ()
  • utilisez dispatchTouchEvent () au lieu de onInterceptTouchEvent ()

FrameLayout personnalisé:

private class TouchableWrapper extends FrameLayout {

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mMapIsTouched = true;
                break;
            case MotionEvent.ACTION_UP:
                mMapIsTouched = false;
                break;
        }

        return super.dispatchTouchEvent(ev);

    }

}

Dans votre MapFragment personnalisé:

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

Dans votre méthode de rappel de changement de caméra:

private final OnCameraChangeListener mOnCameraChangeListener = 
        new OnCameraChangeListener() {

    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
        if (!mMapIsTouched) {
            refreshClustering(false);
        }
    }
};
25
AZ13

Découvrez les nouvelles cartes api. 

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

developers.google.com sample

37
punksta

OBSOLÈTE Utilisez plutôt la nouvelle API de cartes. Voir la réponse de punksta.

Après avoir utilisé la solution de AZ13 ci-dessus et rencontré le problème mentionné dans les commentaires, j'ai créé la solution suivante, qui résout le problème de manière plus fiable. Cependant, comme j'utilise un minuteur après l'événement onRelease pour déterminer si la carte est toujours en cours d'animation, cette solution présente un léger retard.

Le code peut être trouvé sur Github via ce lien: https://github.com/MadsFrandsen/MapStateListener

Ma solution peut être utilisée à partir d’une activité de la manière suivante:

new MapStateListener(mMap, mMapFragment, this) {
  @Override
  public void onMapTouched() {
    // Map touched
  }

  @Override
  public void onMapReleased() {
    // Map released
  }

  @Override
  public void onMapUnsettled() {
    // Map unsettled
  }

  @Override
  public void onMapSettled() {
    // Map settled
  }
};
24
Mads Frandsen

Je voudrais essayer un onCameraChangeListener . L'auditeur est appelé chaque fois qu'un mouvement de la caméra est terminé. L'auditeur vous indiquera également le nouvel emplacement. Lors de mes tests, l'auditeur était appelé assez souvent pendant le glissement, peut-être qu'il y avait une meilleure solution.

8
Janusz

À partir de play-services-maps 9.4.0, vous pouvez simplement utiliser GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraMoveListener et GoogleMap.OnCameraIdleListener.

Si, pour une raison quelconque, vous souhaitez utiliser l'ancienne API, devenue obsolète, vous pouvez utiliser onCameraChangeListener. Mais vous devez être conscient de deux choses:

  1. onCameraChange() peut être appelé plusieurs fois lorsque vous faites glisser la carte OR une seule fois (lorsque le déplacement est arrêté).
  2. La position de la caméra lors du dernier appel de onCameraChange() peut être légèrement différente de la position finale de la caméra.

Le code suivant prend en compte les deux problèmes:

private static final int MESSAGE_ID_SAVE_CAMERA_POSITION = 1;
private static final int MESSAGE_ID_READ_CAMERA_POSITION = 2;
private CameraPosition lastCameraPosition;
private Handler handler;
private GoogleMap googleMap;

public void onMapReady(GoogleMap theGoogleMap) {
    googleMap = theGoogleMap;

    handler = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == MESSAGE_ID_SAVE_CAMERA_POSITION) {
                lastCameraPosition = googleMap.getCameraPosition();
            } else if (msg.what == MESSAGE_ID_READ_CAMERA_POSITION) {
                if (lastCameraPosition.equals(googleMap.getCameraPosition())) {
                    Log.d(LOG, "Camera position stable");
                }
            }
        }
    };

    googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
        @Override
        public void onCameraChange(CameraPosition cameraPosition) {
            handler.removeMessages(MESSAGE_ID_SAVE_CAMERA_POSITION);
            handler.removeMessages(MESSAGE_ID_READ_CAMERA_POSITION);
            handler.sendEmptyMessageDelayed(MESSAGE_ID_SAVE_CAMERA_POSITION, 300);
            handler.sendEmptyMessageDelayed(MESSAGE_ID_READ_CAMERA_POSITION, 600);
        }
    });
}
4
Tobus

Je dois animer mon marqueur pour le centrer tant que l'utilisateur fait glisser la carte. Je l'ai implémenté avec Stas Shakirov answer

MapDragListenerFragment.class

public class MapDragListenerFragment extends Fragment implements OnMapReadyCallback, GoogleMap.OnMapLoadedCallback {

    private Context mContext;
    private SupportMapFragment supportMapFragment;
    private Marker centerMarker;
    private LatLng mapCenterLatLng;
    private TextView tvLocationName;
    private Button btnFinalizeDestination;
    private GoogleMap mGoogleMap;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_map_drag_listener, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mContext = getActivity();

        tvLocationName = (TextView) view.findViewById(R.id.tv_location_name);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        FragmentManager fm = getActivity().getSupportFragmentManager();//getChildFragmentManager();//
        supportMapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container);
        if (supportMapFragment == null) {
            //// FIXME: 2/13/2017 crashes at casting to TouchableMapFragment
            supportMapFragment = SupportMapFragment.newInstance();
            fm.beginTransaction().replace(R.id.map_container, supportMapFragment).commit();
        }
        supportMapFragment.getMapAsync(this);

    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        if (googleMap != null) {
            mGoogleMap = googleMap;

            centerMarker = mGoogleMap.addMarker(new MarkerOptions().position(mGoogleMap.getCameraPosition().target)
                    .title("Center of Map")
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.end_green)));

            mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
                @Override
                public void onCameraIdle() {
                    mapCenterLatLng = mGoogleMap.getCameraPosition().target;

                    animateMarker(centerMarker,mapCenterLatLng,false);

                    Toast.makeText(mContext, "The camera has stopped moving.",
                            Toast.LENGTH_SHORT).show();

                    String address = getCompleteAddressString(mapCenterLatLng.longitude,mapCenterLatLng.longitude);
                    tvLocationName.setText(address);
                }
            });
            mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
                @Override
                public void onCameraMoveStarted(int reason) {
                    if (reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE) {
                        ///tvLocationName.setText("Lat " + mapCenterLatLng.latitude + "  Long :" + mapCenterLatLng.longitude);
                        Toast.makeText(mContext, "The user gestured on the map.",
                                Toast.LENGTH_SHORT).show();
                    } else if (reason == GoogleMap.OnCameraMoveStartedListener
                            .REASON_API_ANIMATION) {
                        Toast.makeText(mContext, "The user tapped something on the map.",
                                Toast.LENGTH_SHORT).show();
                    } else if (reason == GoogleMap.OnCameraMoveStartedListener
                            .REASON_DEVELOPER_ANIMATION) {
                        Toast.makeText(mContext, "The app moved the camera.",
                                Toast.LENGTH_SHORT).show();
                    }
                }
            });
            mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
                @Override
                public void onCameraMove() {
                    Toast.makeText(mContext, "The camera is moving.",
                            Toast.LENGTH_SHORT).show();
                }
            });
            mGoogleMap.setOnCameraMoveCanceledListener(new GoogleMap.OnCameraMoveCanceledListener() {
                @Override
                public void onCameraMoveCanceled() {
                    Toast.makeText(mContext, "Camera movement canceled.",
                            Toast.LENGTH_SHORT).show();
                }
            });

            mapCenterLatLng = mGoogleMap.getCameraPosition().target;// it should be done on MapLoaded.

            if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) !=
                    PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) !=
                            PackageManager.PERMISSION_GRANTED) {
                return;
            }
            mGoogleMap.setMyLocationEnabled(true);
            mGoogleMap.animateCamera(CameraUpdateFactory.zoomTo(15));

            mGoogleMap.setOnMapLoadedCallback(this);
            mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
                @Override
                public void onCameraMove() {

                }
            });
        }
    }

    public void animateMarker(final Marker marker, final LatLng toPosition,
                              final boolean hideMarker) {
        final Handler handler = new Handler();
        final long start = SystemClock.uptimeMillis();
        Projection proj = mGoogleMap.getProjection();
        Point startPoint = proj.toScreenLocation(marker.getPosition());
        final LatLng startLatLng = proj.fromScreenLocation(startPoint);
         final long duration = 500;
        final Interpolator interpolator = new LinearInterpolator();
        handler.post(new Runnable() {
            @Override
            public void run() {
                long elapsed = SystemClock.uptimeMillis() - start;
                float t = interpolator.getInterpolation((float) elapsed
                        / duration);
                double lng = t * toPosition.longitude + (1 - t)
                        * startLatLng.longitude;
                double lat = t * toPosition.latitude + (1 - t)
                        * startLatLng.latitude;
                marker.setPosition(new LatLng(lat, lng));
                if (t < 1.0) {
                    // Post again 16ms later.
                    handler.postDelayed(this, 16);
                } else {
                    if (hideMarker) {
                        marker.setVisible(false);
                    } else {
                        marker.setVisible(true);
                    }
                }
            }
        });
    }
}

fragment_map_drag_listener.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical">

    <RelativeLayout
        Android:layout_width="match_parent"
        Android:layout_height="0dp"
        Android:layout_weight="1">

        <fragment
            Android:id="@+id/map_container"
            Android:name="com.google.Android.gms.maps.SupportMapFragment"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent" />

        <ImageView
            Android:id="@+id/iv_center_overlay"
            Android:layout_width="25dp"
            Android:layout_height="25dp"
            Android:visibility="gone"
            Android:layout_centerInParent="true"
            Android:src="@drawable/start_blue" />
    </RelativeLayout>


    <TextView
        Android:id="@+id/tv_location_name"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:padding="4dp"
        Android:text="Location Name" />
</LinearLayout>

MapDragListenerActivity

public class MapDragListenerActivity extends AppCompatActivity {

    private Context mContext;
    private static final String TAG = MapDragListenerFragment.class.getSimpleName();
    private MapDragListenerFragment mapDragListenerFragment;

    private Button selectPlaceBtn;
    public static final int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1219;

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

        mContext = MapDragListenerActivity.this;

        mapDragListenerFragment = new MapDragListenerFragment();
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_container,//where frame_container is a FrameLayout
                        mapDragListenerFragment,
                        MapyFragment.class.getSimpleName()).commit();


        selectPlaceBtn = (Button) findViewById(R.id.btn_select_place);

        selectPlaceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Intent intent = new PlaceAutocomplete.IntentBuilder(
                            PlaceAutocomplete.MODE_FULLSCREEN).build(MapDragListenerActivity.this);
                    startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
                } catch (GooglePlayServicesRepairableException e) {
                    e.printStackTrace();
                } catch (GooglePlayServicesNotAvailableException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE){
            if (resultCode == RESULT_OK) {
                Place place = PlaceAutocomplete.getPlace(mContext, data);
                if(mapDragListenerFragment != null && mapDragListenerFragment.isVisible())
                    mapDragListenerFragment.updateMarkerAtPosition(
                            place.getLatLng() ,place.getName().toString());

                Log.i(TAG, "Place:" + place.toString());
            } else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
                Status status = PlaceAutocomplete.getStatus(mContext, data);
                Log.i(TAG, status.getStatusMessage());
            } else if (requestCode == RESULT_CANCELED) {

            }
        }
    }
}

activity_map_drag_listener.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical">

    <Button
        Android:id="@+id/btn_select_place"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:text="Select Place" />

    <FrameLayout
        Android:id="@+id/frame_container"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

</LinearLayout>
1
Xar E Ahmer
@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView){

    if(event.getAction() == MotionEvent.ACTION_MOVE)
        return true;

    return false;
}
1
Joshua Márquez

La méthode la plus simple consiste à utiliser la méthode setOnCameraIdleListener pour gérer l'état de fin de déplacement de l'écouteur tactile sur votre fragment de carte . Voir l'exemple ci-dessous:

mMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
    @Override
    public void onCameraMoveStarted(int i) {
        mapPin.startAnimation(animZoomOut);
    }
});

mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
    @Override
    public void onCameraIdle() {
        mapPin.startAnimation(animZoomIn);
    }
});
1
TheTeslaa

Vous devez utiliser la caméra au ralenti

 googleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
        @Override
        public void onCameraIdle() {
           //Called when camera movement has ended, there are no pending animations and the user has stopped interacting with the map.
        }
 });
0
Bolling

Solution améliorée avec une classe interne de gestionnaire dans Xamarin Android, basée sur la réponse de Tobus

public void OnMapReady(GoogleMap googleMap)
{
        _googleMap = googleMap;

        if (_googleMap != null)
        {
            _cameraPositionHandler = new CameraPositionlHandler(_googleMap);

            _googleMap.CameraChange += OnCameraChanged; 

        }
}

void OnCameraChanged (object sender, GoogleMap.CameraChangeEventArgs e)
{   
    _cameraPositionHandler.RemoveMessages(MESSAGE_ID_SAVE_CAMERA_POSITION);
    _cameraPositionHandler.RemoveMessages(MESSAGE_ID_READ_CAMERA_POSITION);                 
    _cameraPositionHandler.SendEmptyMessageDelayed(MESSAGE_ID_SAVE_CAMERA_POSITION, 300);
    _cameraPositionHandler.SendEmptyMessageDelayed(MESSAGE_ID_READ_CAMERA_POSITION, 600);

}

Avec la classe intérieure suivante:

    private class CameraPositionlHandler :  Handler 
    {
        private CameraPosition _lastCameraPosition;
        private GoogleMap _googleMap;

        public CameraPositionlHandler (GoogleMap googleMap)
        {
            _googleMap = googleMap;
        }

        public override void HandleMessage(Message msg) 
        {
            if (_googleMap != null) 
            {
                if (msg.What == MESSAGE_ID_SAVE_CAMERA_POSITION) {
                    _lastCameraPosition = _googleMap.CameraPosition;
                } else if (msg.What == MESSAGE_ID_READ_CAMERA_POSITION) {
                    if (_lastCameraPosition.Equals(_googleMap.CameraPosition)) {
                        Console.WriteLine("Camera position stable");
                        //do what you want
                    }
                }
            }
        }
    }
0
iMacX

Je pense que l'événement sur la carte est: map.setOnMapClick ... Mais l'événement glissé est: map.onCameraChangeListener car j'appelle un log.e dans les deux fonctions, et affiché comme onClick view et onDrag view. Donc, juste les utiliser pour vous.

0
nobjta_9x_tq