Android Example : Animate marker on Google map with current location

Hi, guys today we’re going to see how we can animate marker on google map with user current location. In this blog, we’re going to use the Google Map API for google maps. I’m not going to show how you can get Google Map API from Google Console. If you want to see how to get Google MAP API to see this on my blog. So, without further ado let’s dive into AndroidStudio and do some coding.

Android App Setup

First, open your app level build.gradle file and add the dependency for location and google map.

implementation 'com.google.android.gms:play-services-location:15.0.1'
implementation 'com.google.android.gms:play-services-maps:15.0.1'

Now open the Manifest file and add permission for Location and Internet.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

Now add the meta tag in the Manifest file for Google Map API and for play services version.

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="Aalza*******************************qw23" />    // Replace this with your google map api key

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

Add SupportMapFragment to your xml file where you want to show Google Map. Below is the xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/mapFragment"
        class="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageButton
        android:id="@+id/currentLocationImageButton"
        android:layout_width="40dp"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_marginBottom="25dp"
        android:layout_marginEnd="15dp"
        android:contentDescription="@null"
        android:src="@drawable/current_location_vector_icon" />

</RelativeLayout>

As you can see the xml file is much simpler. In here we have SupportMapFragment and current location button. The button is for when user taps on, it navigates to the current location on the map.

Done with the xml file now go to your java file in onCreate method get the SupportMapFragment object and add OnMapReadyCallackBelow is the code for SupportMapFragment.

SupportMapFragment supportMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapFragment);
supportMapFragment.getMapAsync(this);

Below is the overriding method of OnMapReadyCallback in java file.


@Override
public void onMapReady(GoogleMap googleMap) {
    this.googleMap = googleMap;
}

We need to save the instance of GoogleMaps because later we have to set the current location and marker reference on it.

Now we have to see if the user has previously installed GooglePlayServices on his/her mobile. It’s a good approach that you check GooglePlayServices installed or not. Below is the code for checking GooglePlayServices.

@Override
protected void onResume() {
 super.onResume();
 if (isGooglePlayServicesAvailable()) {
 fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
 startCurrentLocationUpdates();
 }
}

You can add this code in the onResume method. isGooglePlayServicesAvailable is our custom method to check that GooglePlayServices available or not. If the user does not have GooglePlayServices then we simply show a Toast else we start current location updates.




Below is the isGooglePlayServicesAvailable method.

private boolean isGooglePlayServicesAvailable() {
    GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
    int status = googleApiAvailability.isGooglePlayServicesAvailable(this);
    if (ConnectionResult.SUCCESS == status)
        return true;
    else {
        if (googleApiAvailability.isUserResolvableError(status))
            Toast.makeText(this, "Please Install google play services to use this application", Toast.LENGTH_LONG).show();
    }
    return false;
}

GoogleApiAvailability is the utility class to check GooglePlayServices availability. If you want to learn more about GoogleApiAvailability class see this link.

Below is the startCurrentLocationUpdates method.

private void startCurrentLocationUpdates() {
    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setInterval(3000);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
            return;
        }
    }
    fusedLocationProviderClient.requestLocationUpdates(locationRequest, mLocationCallback, Looper.myLooper());
}

LocationRequest is the class to tell how much time interval we need after every location and also we can set location Accuracy with the LocationRequest. In here we’re also checking if the app has the location permission or not. If not then we’re asking runtime permission for location else it continues. Now we just need the LocationCallback class object which we’re passing in when requesting location updates.

private final LocationCallback mLocationCallback = new LocationCallback() {

    @Override
    public void onLocationResult(LocationResult locationResult) {
        super.onLocationResult(locationResult);
        if (locationResult.getLastLocation() == null)
            return;
        currentLocation = locationResult.getLastLocation();
        if (firstTimeFlag && googleMap != null) {    
            animateCamera(currentLocation);
            firstTimeFlag = false;
        }
        showMarker(currentLocation);
    }
};

You see in here we’re saving user location in our currentLocation object because we need the location for when user tap on current location button. After that, we check if firstTimeFlag is a true and googleMap instance not null. I’m checking this condition because when the app opens we need to animate GoogleMaps to user current location. This if condition true only first time.

Below is the code for animateCamera method.

private void animateCamera(@NonNull Location location) {
    LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
    googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(getCameraPositionWithBearing(latLng)));
}

Below is the code for showMarker method.

private void showMarker(@NonNull Location currentLocation) {
    LatLng latLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
    if (currentLocationMarker == null)
        currentLocationMarker = googleMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.defaultMarker()).position(latLng));
    else
        MarkerAnimation.animateMarkerToGB(currentLocationMarker, latLng, LatLngInterpolator.Spherical());
}

Note: This is the main part of our blog. In here first, we check if the currentLocationMarker equal to null. Then we simply add a new marker on the map. If not then we call our AnimatingMarkerHelper class to animate the marker from the previous location to user new location.

Below is the code of AnimatingMarkerHelper.java class.

public class MarkerAnimation {

    public 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 = 2000;

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

In the animateMarkerToGB method, we’re passing LatLngInterpolator instance. Below is the LatLngInterpolator class.

public interface LatLngInterpolator {

    LatLng interpolate(float fraction, LatLng a, LatLng b);

    class Spherical implements LatLngInterpolator {

        /* From github.com/googlemaps/android-maps-utils */
        @Override
        public LatLng interpolate(float fraction, LatLng from, LatLng to) {
            // http://en.wikipedia.org/wiki/Slerp
            double fromLat = toRadians(from.latitude);
            double fromLng = toRadians(from.longitude);
            double toLat = toRadians(to.latitude);
            double toLng = toRadians(to.longitude);
            double cosFromLat = cos(fromLat);
            double cosToLat = cos(toLat);

            // Computes Spherical interpolation coefficients.
            double angle = computeAngleBetween(fromLat, fromLng, toLat, toLng);
            double sinAngle = sin(angle);
            if (sinAngle < 1E-6) {
                return from;
            }
            double a = sin((1 - fraction) * angle) / sinAngle;
            double b = sin(fraction * angle) / sinAngle;

            // Converts from polar to vector and interpolate.
            double x = a * cosFromLat * cos(fromLng) + b * cosToLat * cos(toLng);
            double y = a * cosFromLat * sin(fromLng) + b * cosToLat * sin(toLng);
            double z = a * sin(fromLat) + b * sin(toLat);

            // Converts interpolated vector back to polar.
            double lat = atan2(z, sqrt(x * x + y * y));
            double lng = atan2(y, x);
            return new LatLng(toDegrees(lat), toDegrees(lng));
        }

        private double computeAngleBetween(double format, double fromLng, double toLat, double toLng) {
            // Haversine's formula
            double dLat = fromLat - toLat;
            double dLng = fromLng - toLng;
            return 2 * asin(sqrt(pow(sin(dLat / 2), 2) +
                    cos(fromLat) * cos(toLat) * pow(sin(dLng / 2), 2)));
        }
    }
}

Now it’s a good approach that you stopped the location updates in the onStop method. Below is the code how to stop location updates.

@Override
protected void onStop() {
    super.onStop();
    if (fusedLocationProviderClient != null)
        fusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
}

That’s it, this is the complete example of how to animate marker on google maps. For complete source code of the above example see it on GitHub. If you guy’s want to learn how to show current location on GoogleMapStreetView see this example.

I hope this blog gives you a good understanding of how to animate a marker on google map. If you’ve any queries please do comment below.

Thank you for being here and keep reading…

4 COMMENTS

    • Yes, it did work if there’s no internet. To recheck I download the source from my GitHub repo and test it out. There’s a video which I recorded when testing. You can see in the video I have no Internet or Wifi only GPS when recording.
      One more thing, when the first time you open. You need to connect to the Internet or Wifi after that it won’t be necessary.

LEAVE A REPLY

Please enter your comment!
Please enter your name here