I am trying to crate a app like ola/uber. I want to move the icon and rotate when road turn left or right. I am using following code.
private void rotateMarker(final Marker marker, final float toRotation) {
if(!isMarkerRotating) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = marker.getRotation();
final long duration = 1000;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
isMarkerRotating = true;
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
float rot = t * toRotation + (1 - t) * startRotation;
marker.setRotation(-rot > 180 ? rot / 2 : rot);
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
isMarkerRotating = false;
}
}
});
}
}
To calculate bearing:
currentLocation = location;
if(previousLocaton!=null){
previousLocaton = tempLocation;
tempLocation = currentLocation;
Log.d("previousLocaton=====> ",""+previousLocaton);
Log.d("currentLocation=====> ",""+currentLocation);
bearing = previousLocaton.bearingTo(currentLocation) ;
}else{
previousLocaton = location;
tempLocation = location;
}
To set the bearing:
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng).zoom(14).bearing(bearing).build();
To rotate the marker I call roateMarker method in onLocationChanged changed method:
currLocationMarker = mMap.addMarker(markerOptions);
rotateMarker(currLocationMarker,bearing);
Now my icon is rotating. But google map also get rotating. I want rotate icon alone. I refer the following link for animate and move the marker. Link 1. Please let me any idea to solve my issue.
there is simple method available for marker
marker.rotation(float value)
Sets the rotation of the marker in degrees clockwise about the marker's anchor point. The axis of rotation is perpendicular to the marker. A rotation of 0 corresponds to the default position of the marker. When the marker is flat on the map, the default position is North aligned and the rotation is such that the marker always remains flat on the map. When the marker is a billboard, the default position is pointing up and the rotation is such that the marker is always facing the camera. The default value is 0.
To rotate only the marker set rotation to marker using setRotation(float) method.
static public void rotateMarker(final Marker marker, final float toRotation) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = marker.getRotation();
final long duration = 1000;
final Interpolator interpolator = new LinearInterpolator();
L.d("Bearing: "+toRotation);
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
float rot = t * toRotation + (1 - t) * startRotation;
marker.setRotation(-rot > 180 ? rot / 2 : rot);
if (t < 1.0) {
// Post again 10ms later.
handler.postDelayed(this, 10);
}
}
});
}
Try this :
public void animateMarker(final LatLng toPosition, final LatLng startLatLng,
final boolean hideMarker) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(d_marker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
// final CameraPosition newcameraPosition = null;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
Location prevLoc = new Location("service Provider");
prevLoc.setLatitude(startLatLng.latitude);
prevLoc.setLongitude(startLatLng.longitude);
Location newLoc = new Location("service Provider");
newLoc.setLatitude(toPosition.latitude);
newLoc.setLongitude(toPosition.longitude);
System.out.println("Locations ---- " + prevLoc + "-" + newLoc);
float bearing = prevLoc.bearingTo(newLoc);
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ 1000);
double lng = t * toPosition.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * toPosition.latitude + (1 - t)
* startLatLng.latitude;
d_marker.setPosition(new LatLng(lat, lng));
d_marker.setRotation(bearing);
d_marker.setFlat(true);
// googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(newcameraPosition));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarker) {
d_marker.setVisible(false);
} else {
d_marker.setVisible(true);
}
}
}
});
}
Try Below method:
public void animateMarker(final LatLng toPosition,
final boolean hideMarker) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = googleMap.getProjection();
Point startPoint = proj.toScreenLocation(cabMarker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
// final CameraPosition newcameraPosition = null;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
Location prevLoc = new Location("service Provider");
prevLoc.setLatitude(startLatLng.latitude);
prevLoc.setLongitude(startLatLng.longitude);
Location newLoc = new Location("service Provider");
newLoc.setLatitude(toPosition.latitude);
newLoc.setLongitude(toPosition.longitude);
System.out.println("Locations ---- " + prevLoc + "-" + newLoc);
float bearing = prevLoc.bearingTo(newLoc);
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ 1000);
double lng = t * toPosition.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * toPosition.latitude + (1 - t)
* startLatLng.latitude;
cabMarker.setPosition(new LatLng(lat, lng));
cabMarker.setRotation(bearing);
// googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(newcameraPosition));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
if (hideMarker) {
cabMarker.setVisible(false);
} else {
cabMarker.setVisible(true);
}
}
}
});
}
Related
I am using this code to animate the marker to kinda replicate the movement of a vehicle on the google maps but the marker shakes vigorously when marker.setPosition is called in the handler function. Code is below:
public static void animateMarker(final GoogleMap map, final Marker marker, final LatLng toPosition) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(marker.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;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
handler.postDelayed(this, 16);
}
}
});
}
Try using the AccelerateDecelerateInterpolator, an interpolator whose rate of change starts and ends slowly but accelerates through the middle, instead of LinearInterpolator as used in this Google video tutorial. The marker animation was seamless. Here's the snippet:
public class MarkerAnimation {
static void animateMarkerToGB(final Marker marker, final LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
final LatLng startPosition = marker.getPosition();
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final float durationInMs = 3000;
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);
marker.setPosition(latLngInterpolator.interpolate(v, startPosition, finalPosition));
// Repeat till progress is complete.
if (t < 1) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
}
Full code [here](https://gist.github.com/broady/6314689).
I am facing an issue right now I am developing an Android app similar to Uber or lyft but the problem that I am facing is the car is moving on the map smoothly but sometimes it is revolving at 360 degree at one position, I need to stop that please help, here is my animation code.
private void animateMarkr(double laat, double lnng,Location prevLocation)
{
final LatLng toPosition=new LatLng(laat,lnng);
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = mGoogleMap.getProjection();
Point startPoint = proj.toScreenLocation(carMarker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 3000;
final boolean hideMarker=false;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
LatLng pre=carMarker.getPosition();
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;
// carMarker.setRotation(getBearing(pre,new LatLng(lat,lng)));
carMarker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 20);
} else {
if (hideMarker) {
carMarker.setVisible(false);
} else {
carMarker.setVisible(true);
}
}
}
});
}
Here is my Rotation Code
private void rotateMarker(Location location,Location preLocation){
Location prevLocation = new Location(LocationManager.GPS_PROVIDER);
prevLocation.setLatitude(carMarker.getPosition().latitude);
prevLocation.setLongitude(carMarker.getPosition().longitude);
final float toRotation = prevLocation.bearingTo(location);
performRotaion(toRotation);
}
private void performRotaion(final float toRotation){
if(!isMarkerRotating) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = carMarker.getRotation();
final float totalDegree=0;
final long duration = 1000;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
isMarkerRotating = true;
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
float rot = t toRotation + (1 - t) startRotation;
if(carMarker != null){
carMarker.setAnchor(0.5f, 0.5f);
// if(rot<0.0f && rot>-355.0f) {
carMarker.setRotation(-rot > 180 ? rot / 2 : rot);
carMarker.setFlat(true);
}
if (t < 1.0) {
handler.postDelayed(this, 16);
} else {
isMarkerRotating = false;
}
}
});
}
}
// method call
rotateMarker(location,previcsLocation);
animateMarkr(location.getLatitude(), location.getLongitude(),previcsLocation);
I have implemented the check to stop rotation if the degree is 360 also I have checked if the degree is more than 180 degree than divide the same and move the car on that angle. But nothing works
Before calling these functions
rotateMarker(location,previcsLocation);
animateMarkr(location.getLatitude(), location.getLongitude(),previcsLocation);
in your code. You'll have to add a check for the new (Latitude,Logitude) which comes through the server and marker's (Latitude,Logitude) if both values are same then you should avoid to call the above functions.
Only you have to add this condition in it.
LatLng new_location = new LatLng(data.getLatitude(), data.getLongitude());
//new_location is coming through the server
LatLng current_location = marker.getPosition();
//this is the marker's location
if (current_location != new_location) {
if (current_location.equals(new_location)) {
Log.d(TAG, "LatLng " + "---------current_location-" + current_location);
Log.d(TAG, "LatLng " + "---------new_location-" + new_location);
return;
}
rotateMarker(location,previcsLocation);
animateMarkr(location.getLatitude(), location.getLongitude(),previcsLocation);
}
It worked in my project.
I want to implement smooth transition to emulate car marker moving on the map. From current postion to selected position.
Even if I suppose that you hadn't try anything, I give you a great method to achieve that .. just because I'm in a good mood
public static void animateMarker(final GoogleMap map, final Marker marker, final LatLng toPosition,
final boolean hideMarker) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.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);
}
}
}
});
}
Enjoy it!
i am using google maps v2.
i have a marker on map, this marker changes rotation every while.
I want to animate the rotation of my maker to rotate smoothly.
Can anyone help please
That's mine implementation of smooth marker movement with maker image rotation (when it's set to FLAT [important]). The marker is moved to the requested location smoothly plus it's rotated to the requested degree with a proper direction. For example, when moving from 5deg to 355deg it moves anticlockwise, when 355deg to 5deg it moves clockwise.
public void animateMarker(final Location location)
{
if (myMarkerLOC != null) {
final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
ValueAnimator valueAnimator = new ValueAnimator();
final LatLng startPosition = myMarkerLOC.getPosition();
final float startRotation = myMarkerLOC.getRotation();
final float angle = 180 - Math.abs(Math.abs(startRotation - location.getBearing()) - 180);
final float right = WhichWayToTurn(startRotation, location.getBearing());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate(ValueAnimator animation)
{
try {
if (myMarkerLOC == null) // oops... destroying map during animation...
{
return;
}
float v = animation.getAnimatedFraction();
LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, PositionUtil.toLatLng(location));
float rotation = startRotation + right * v * angle;
myMarkerLOC.setRotation((float) rotation);
myMarkerLOC.setPosition(newPosition);
} catch (Exception ex) {
// I don't care atm..
}
}
});
valueAnimator.setFloatValues(0, 1);
valueAnimator.setDuration(300);
valueAnimator.start();
}
}
private float WhichWayToTurn(float currentDirection, float targetDirection)
{
float diff = targetDirection - currentDirection;
if (Math.abs(diff) == 0)
{
return 0;
}
if(diff > 180)
{
return -1;
}
else
{
return 1;
}
}
public interface LatLngInterpolator
{
public LatLng interpolate(float fraction, LatLng a, LatLng b);
public class Linear implements LatLngInterpolator
{
#Override
public LatLng interpolate(float fraction, LatLng a, LatLng b)
{
double lat = (b.latitude - a.latitude) * fraction + a.latitude;
double lng = (b.longitude - a.longitude) * fraction + a.longitude;
return new LatLng(lat, lng);
}
}
public class LinearFixed implements LatLngInterpolator
{
#Override
public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.latitude - a.latitude) * fraction + a.latitude;
double lngDelta = b.longitude - a.longitude;
// Take the shortest path across the 180th meridian.
if (Math.abs(lngDelta) > 180) {
lngDelta -= Math.signum(lngDelta) * 360;
}
double lng = lngDelta * fraction + a.longitude;
return new LatLng(lat, lng);
}
}
}
Missing implementation of "toLatLong" method:
public static LatLng toLatLng(final Location location)
{
return new LatLng(location.getLatitude(), location.getLongitude());
}
I hope that helps.
boolean isRotating = false;
public void rotateMarker(final Marker marker, final float toRotation) {
if(!isRotating) {
isRotating = true;
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = marker.getRotation();
final long duration = 1000;
float deltaRotation = Math.abs(toRotation - startRotation) % 360;
final float rotation = (deltaRotation > 180 ? 360 - deltaRotation : deltaRotation) *
((toRotation - startRotation >= 0 && toRotation - startRotation <= 180) || (toRotation - startRotation <= -180 && toRotation - startRotation >= -360) ? 1 : -1);
final LinearInterpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
marker.setRotation((startRotation + t * rotation) % 360);
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
isRotating = false;
}
}
});
}
}
This is how I rotate the marker, I don't know this is the best code or not.
But I think it will prevent wrong rotation. EDIT: Add var isRotating (Thanks #Trần Văn Huy)
Update Explanation:
static public void rotateMarker(final Marker marker, final float toRotation, GoogleMap map) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = marker.getRotation();
final long duration = 1555;
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);
float rot = t * toRotation + (1 -t) * startRotation;
marker.setRotation(-rot > 180 ? rot/2 : rot);
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
}
i have managed to do it :)
With #Bhagaskara Liancer answer.Add var isMarkerRotating with help marker rotate smoothly.
private boolean isMarkerRotating = false;
public void rotateMarker(final Marker marker, final float toRotation) {
if(!isMarkerRotating){
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = marker.getRotation();
final long duration = 1000;
float deltaRotation = Math.abs(toRotation - startRotation) % 360;
final float rotation = (deltaRotation > 180 ? 360 - deltaRotation : deltaRotation) * ((toRotation - startRotation >= 0 && toRotation - startRotation <= 180)
|| (toRotation - startRotation <=-180 && toRotation- startRotation>= -360) ? 1 : -1);
final LinearInterpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
isMarkerRotating = true;
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
marker.setRotation((startRotation + t* rotation)%360);
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}else {
isMarkerRotating = false;
}
}
});
}
}
Here is the simple example
You can use the following MarkerAnimation class for animating marker with rotation.
import android.graphics.Point;
import android.location.Location;
import android.os.Handler;
import android.os.SystemClock;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import com.gogrocerycart.settings.Constants;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.Projection;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
/**
* Created by Vinil Chandran on 7/6/18.
*/
public class MarkerAnimation {
private static Location fromPosition;
private static float angle = 0;
public static void move(GoogleMap mMap, final Marker marker, final Location toPosition) {
if (fromPosition != null && marker != null && toPosition != null) {
final Handler handlerRotation = new Handler();
final long startAngle = SystemClock.uptimeMillis();
final float startRotation = marker.getRotation();
final long durationRotation = 300;
final Interpolator interpolatorRotation = new LinearInterpolator();
float bearing = fromPosition.bearingTo(toPosition);
Print.e("Bearing:" + bearing);
angle = bearing<0?(360+bearing):bearing;
angle = angle%360f;
Print.e("Angle:" + angle);
handlerRotation.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - startAngle;
float t = interpolatorRotation.getInterpolation((float) elapsed / durationRotation);
float rot = t * angle + (1 - t) * startRotation;
float mAngle = -rot > 180 ? rot / 2 : rot;
marker.setRotation(mAngle);
if (t < 1.0) {
handlerRotation.postDelayed(this, 16);
} else {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection projection = mMap.getProjection();
Point startPoint = projection.toScreenLocation(marker.getPosition());
final LatLng startLatLng = projection.fromScreenLocation(startPoint);
final long duration = Constants.LOCATION_REQUEST_INTERVAL;
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.getLongitude() + (1 - t)
* startLatLng.longitude;
double lat = t * toPosition.getLatitude() + (1 - t)
* startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
}
}
});
}
fromPosition = toPosition;
}
}
Just call the following code for moving the marker when the location is changed.
if (carMarker != null) {
MarkerAnimation.move(mMap,carMarker, location);
}
Had the same problem, in my case I used ValueAnimator
static public void rotateMarker(final Marker marker, final float toRotation) {
ValueAnimator animator = ValueAnimator.ofFloat(marker.getRotation(), toRotation);
animator.setDuration(duration);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float rotate = Float.parseFloat(animation.getAnimatedValue().toString());
marker.setRotation(rotate);
}
});
animator.start();
}
The answer from Alexandr Kolesnik seems to be the most simple.
Here's a Kotlin extension version of it.
You can pass in an interpolator to avoid a little thrash.
fun Marker.animateRotation(toRotation: Float, interpolator: TimeInterpolator? = AccelerateDecelerateInterpolator()) {
val animator = ValueAnimator.ofFloat(rotation, toRotation)
animator.duration = 500
animator.interpolator = interpolator
animator.addUpdateListener {
rotation = it.animatedValue as? Float ?: return#addUpdateListener
}
animator.start()
}
You would want to use MarkerOption rotation method
public MarkerOptions rotation (float rotation)
Sets the rotation of the marker in degrees clockwise about the
marker's anchor point. The axis of rotation is perpendicular to the
marker. A rotation of 0 corresponds to the default position of the
marker. When the marker is flat on the map, the default position is
North aligned and the rotation is such that the marker always remains
flat on the map. When the marker is a billboard, the default position
is pointing up and the rotation is such that the marker is always
facing the camera. The default value is 0.
Returns the object for which the method was called, with the new
rotation set.
I am using android maps v2works fine I can add and remove markers onLong Touch on locations.
Problem:
I would like to drop the marker slowly into the touched location i.e. I want the user to see the marker floating from the top of the screen to the point where it is dropped(touched location).
Currently; the marker just appears on the touched location such that you have to lift you finger to see that it has been dropped. It would be nice to see it coming from the top of the screen.
Thanks.
You can achieve this with a code similar to this (untested):
final LatLng target = ...;
final long duration = 400;
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = map.getProjection();
Point startPoint = proj.toScreenLocation(target);
startPoint.y = 0;
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
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 * target.longitude + (1 - t) * startLatLng.longitude;
double lat = t * target.latitude + (1 - t) * startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 10ms later.
handler.postDelayed(this, 10);
} else {
// animation ended
}
}
});
I have combined the MaciejGórski's approach with the code from this gist. In addition, added a bounce effect.
public class MyBounceInterpolator implements android.view.animation.Interpolator {
double mAmplitude = 1;
double mFrequency = 10;
public MyBounceInterpolator(double amplitude, double frequency) {
mAmplitude = amplitude;
mFrequency = frequency;
}
public float getInterpolation(float time) {
double amplitude = mAmplitude;
if (amplitude == 0) { amplitude = 0.05; }
// The interpolation curve equation:
// -e^(-time / amplitude) * cos(frequency * time) + 1
//
// View the graph live: https://www.desmos.com/calculator/6gbvrm5i0s
return (float) (-1 * Math.pow(Math.E, -time/ mAmplitude) * Math.cos(mFrequency * time) + 1);
}
}
void dropMarker(final Marker marker, GoogleMap map) {
final LatLng finalPosition = new LatLng(marker.getPosition().latitude, marker.getPosition().longitude);
Projection projection = map.getProjection();
Point startPoint = projection.toScreenLocation(finalPosition);
startPoint.y = 0;
final LatLng startLatLng = projection.fromScreenLocation(startPoint);
final Interpolator interpolator = new MyBounceInterpolator(0.11, 4.6);
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
#Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
float t = interpolator.getInterpolation(fraction);
double lng = t * finalPosition.longitude + (1 - t) * startLatLng.longitude;
double lat = t * finalPosition.latitude + (1 - t) * startLatLng.latitude;
return new LatLng(lat, lng);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setDuration(400);
animator.start();
}
It works great, but it seemed to me that sometimes the marker remained one step far from the target, so I added just one more line:
if (t < 1.0) {
// Post again 10ms later.
handler.postDelayed(this, 50);
} else {
// animation ended
marker.setPosition(target);
}
Hope it helps.
I've applied your way but have an issue that is the markers position on map is not correct. I think command below calculate is not correct. So after animation finished then final result is not same as original Lat,Lng.
double lng = t * target.longitude + (1 - t) * startLatLng.longitude;
double lat = t * target.latitude + (1 - t) * startLatLng.latitude;