I'm working on an app, similar to uber, didi, etc. I have a problem when making the animations of the vehicles (going from point A to point B), I found this code on Internet and it works quite well:
public void animateMarker(final LatLng startPosition, final LatLng toPosition,final boolean hideMarke) {
final Handler handler = new Handler();
final Marker m = map.addMarker(new MarkerOptions()
.position(startPosition)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.bus2))
.title("Camión"));
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(m.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 5000;
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;
m.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarke) {
m.setVisible(false);
} else {
m.setVisible(true);
}
}
}
});
markers_animations.add(m);
}
I have this method that is in charge of passing it the positions of a list with all the coordinates that I require for the Polylines:
private void controlAnimaciones(List<LatLng> ruta) {
for (int i=0; i<ruta.size()-1; i++) {
if (i<ruta.size()) {
animateMarker3(ruta.get(i), ruta.get(i+1), true);
}
}
}
It does what I expected it to do, if it moves the marker from point A to point B, but, just by iterating the list, I don't know how to explain it, there are many markers that move only from one element of the list to the next and after that they stop. What I want to do is to achieve that a single marker can move through all the points of the list, I have been trying in some ways with the code I got from the internet, to try to understand it, but I have not had much success. How could I do it?
Currently in your posted code, animateMarker
creates a marker for each "segment" of the polyline - it starts and stops the movement of the marker along the one segment, and it does this asynchronously. This would have the effect of a marker created (simultaneously) at the start of every segment and each one animated in parallel (nearly). Not what you want.
So you have two things to change:
Easy way to do the above is to modify the animateMarker
to accept the list of points rather than a single point. The list of points is your polyline (ruta
).
I made some comments where the method was modified from your original.
public void animateMarker(List<LatLng> pts,final boolean hideMarker) {
// Simple check to make sure there are enough points in the list.
if (pts.size() <= 1) {
// need at least two points.
return;
}
final Handler handler = new Handler();
// Use first point in list as start.
final Marker m = mMap.addMarker(new MarkerOptions()
.position(pts.get(0))
.title("Camión"));
Projection proj = mMap.getProjection();
Point startPoint = proj.toScreenLocation(m.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 5000;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
// start at first segment
private int segment = 0;
// initial start time
long start = SystemClock.uptimeMillis();
@Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
// Use next point in list as destination
double lng = t * pts.get(segment+1).longitude + (1 - t)
* pts.get(segment).longitude;
double lat = t * pts.get(segment+1).latitude + (1 - t)
* pts.get(segment).latitude;
m.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
// check if to move to next segment (or done)
else if (segment < (pts.size()-2)) {
// move to next segment
segment++;
start = SystemClock.uptimeMillis();
handler.postDelayed(this,16);
} else {
if (hideMarker) {
m.setVisible(false);
} else {
m.setVisible(true);
}
}
}
});
markers_animations.add(m);
}
And to call the animation with your list just modify your
private void controlAnimaciones(List<LatLng> ruta) {
animateMarker(ruta, true);
}
And this is the result.
(Note that the "velocity" of the marker movement is dependent on the length of the segment. Since the duration is fixed per segment, longer segments would make the marker appear to move faster. You could change this to a constant velocity for any segment by changing duration
as a function of the distance between the two points.)
Note that the animaterMarker
supports multiple animations naturally without modification. So in this example the map onMapClickListener
invokes controlAnimaciones
on every map click which was done every few seconds for demonstration purposes.
Hope this helps!