Quel est le meilleur moyen d’animer des marqueurs sur Google Maps à l’aide de l’API v2?
Je travaille sur un jeu centré sur la carte où je dépiste les emplacements des personnes et les affiche sur la carte pour les voir. Alors que les gens bougent, je veux animer un marqueur de son actuel à sa dernière position. Chaque personne a une direction, je dois donc faire pivoter le marqueur de manière appropriée.
Quelle est la meilleure façon de le faire en utilisant la nouvelle API Google Maps?
Certains ingénieurs de Google ont fourni une vidéo de démonstration de Nice avec un exemple de code élégant expliquant comment animer des marqueurs d'un point de départ à un point d'arrivée, pour toutes les versions d'Android:
Le code correspondant est ici:
https://Gist.github.com/broady/6314689
Et une belle vidéo de démonstration de tout cela en action.
ANCIENNE RÉPONSE DÉPRECÉE CI-DESSOUS
Dans la documentation, il est mentionné que les icônes de marqueur ne peuvent pas être modifiées:
Icône
Un bitmap qui est affiché pour le marqueur. Si l'icône n'est pas définie, une icône par défaut est affichée. Vous pouvez spécifier une couleur de remplacement de l'icône par défaut à l'aide de defaultMarker (float). Vous ne pouvez pas changer l'icône une fois que vous avez créé le marqueur.
Documentation de Google Maps API v2
Vous allez devoir suivre des marqueurs spécifiques, en utilisant peut-être une méthode similaire à celle décrite ici: Lier un marqueur à un objet , puis déterminer le marqueur que vous devez mettre à jour. Appelez .remove()
sur le marqueur, puis créez une image pivotée en fonction de la "direction" souhaitée, créez un nouveau marqueur avec cette image et ajoutez le nouveau marqueur à la carte.
Vous n'avez pas besoin "d'effacer" la carte, il suffit de supprimer le marqueur que vous souhaitez modifier, d'en créer un nouveau, puis de le rajouter à la carte.
Malheureusement, la nouvelle API de cartes n’est pas encore très flexible. Espérons que Google continue à l'améliorer.
Exemple d'utilisation pour Réponse de DiscDev (ci-dessus):
LatLng fromLocation = new LatLng(38.5, -100.4); // Whatever Origin coordinates
LatLng toLocation = new LatLng(37.7, -107.7); // Whatever destination coordinates
Marker marker = mMap.addMarker(new MarkerOptions().position(firstLocation));
MarkerAnimation.animateMarkerToICS(marker, toLocation, new LatLngInterpolator.Spherical());
Et pour ceux d'entre vous qui utilisent le GPS/ou tout autre fournisseur de position qui reçoit des mises à jour de position:
Marker ourGlobalMarker;
// We've got a location from some provider of ours, now we can call:
private void updateMarkerPosition(Location newLocation) {
LatLng newLatLng = new LatLng(newLocation.getLatitude(), newLocation.getLongitude());
if(ourGlobalMarker == null) { // First time adding marker to map
ourGlobalMarker = mMap.addMarker(new MarkerOptions().position(newLatLng));
}
else {
MarkerAnimation.animateMarkerToICS(ourGlobalMarker, newLatLng, new LatLngInterpolator.Spherical());
}
}
IMPORTANT:
Dans 1MarkerAnimation.Java
Si la durée de l'animation est définie sur X, .__ et que vous recevez des mises à jour d'emplacement à un taux inférieur à X, plusieurs animations seront déclenchées et vous pourrez voir l'animation du marqueur vaciller un peu (ce qui n'est pas un utilisateur de Nice expérience).
Pour éviter cela, la méthode animationMarkerToICS
(j’ai pris ici animationMarkerToICS
par exemple) devrait ressembler à ceci,
implémentation complète de la méthode:
private static Animator animator; // MAKING ANIMATOR GLOBAL INSTEAD OF LOCAL TO THE STATIC FUNCTION
...
// Ice cream sandwich compatible
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static void animateMarkerToICS(Marker marker, LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
@Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
return latLngInterpolator.interpolate(fraction, startValue, endValue);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
// ADD THIS TO STOP ANIMATION IF ALREADY ANIMATING TO AN OBSOLETE LOCATION
if(animator != null && animator.isRunning()) {
animator.cancel();
animator = null;
}
animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setDuration((long) ANIMATION_DURATION);
animator.start();
}
Prendre plaisir.
Le marqueur a une nouvelle fonction ajoutée à partir de la version 7 de l’API v2. Marker.setIcon , vous pouvez donc utiliser plusieurs icônes pour indiquer la direction.
//Your code
double bearing = 0.0;
bearing = getBearing(new LatLng(
currentPosition.latitude
,currentPosition.longitude),
new LatLng(
nextPosition.latitude,
nextPosition.longitude));
bearing -= 90;
CameraPosition cameraPosition = new CameraPosition
.Builder()
.target(new LatLng(nextPosition.latitude, nextPosition.longitude))
.bearing((float) bearing)
.zoom(ZOOM_LEVEL).build();
mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000, null);
animatedMarker(currentPosition,nextPosition,busMarker);
//Method for finding bearing between two points
private float getBearing(LatLng begin, LatLng end) {
double lat = Math.abs(begin.latitude - end.latitude);
double lng = Math.abs(begin.longitude - end.longitude);
if (begin.latitude < end.latitude && begin.longitude < end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)));
else if (begin.latitude >= end.latitude && begin.longitude < end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 90);
else if (begin.latitude >= end.latitude && begin.longitude >= end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)) + 180);
else if (begin.latitude < end.latitude && begin.longitude >= end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 270);
return -1;
}
private void animatedMarker(final LatLng startPosition,final LatLng nextPosition,final Marker mMarker)
{
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final float durationInMs = 3000;
final boolean hideMarker = false;
handler.post(new Runnable() {
long elapsed;
float t;
float v;
@Override
public void run() {
// Calculate progress using interpolator
elapsed = SystemClock.uptimeMillis() - start;
t = elapsed / durationInMs;
v = interpolator.getInterpolation(t);
LatLng currentPosition = new LatLng(
startPosition.latitude * (1 - t) + nextPosition.latitude * t,
startPosition.longitude * (1 - t) + nextPosition.longitude * t);
mMarker.setPosition(currentPosition);
// Repeat till progress is complete.
if (t < 1) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarker) {
mMarker.setVisible(false);
} else {
mMarker.setVisible(true);
}
}
}
});
}