Getting started with Google Maps for Android

Getting started with Google Maps for Android

The Google Maps Android API consists of a core set of classes that combine to provide mapping capabilities in Android applications. The key elements of a map are as follows:

  • GoogleMap. The main class of the Google Maps Android API. This class is responsible for downloading and displaying map tiles and for displaying and responding to map controls. The GoogleMap object is not created directly by the application but is instead created when MapView or MapFragment instances are created. A reference to the GoogleMap object can be obtained within application code via a call to the getMap() method of a MapView, MapFragment or SupportMapFragment instance.
  • MapView. A subclass of the View class, this class provides the view canvas onto which the map is drawn by the GoogleMap object, allowing a map to be placed in the user interface layout of an activity.
  • SupportFragmentMap. A subclass of the Fragment class, this class allows a map to be placed within a Fragment in an Android layout.
  • Marker. The purpose of the Marker class is to allow locations to be marked on a map. Markers are added to a map by obtaining a reference to the GoogleMap object associated with a map and then making a call to the addMarker() method of that object instance. The position of a marker is defined via Longitude and Latitude. Markers can be configured in a number of ways, including specifying a title, text and an icon. Markers may also be made to be draggable, allowing the user to move the marker to different positions on a map.
  • Shapes. The drawing of lines and shapes on a map is achieved through the use of the Polyline, Polygon and Circle classes.
  • UiSettings. The UiSettings class provides a level of control from within an application of which user interface controls appear on a map. Using this class, for example, the application can control whether or not the zoom, current location and compass controls appear on a map. This class can also be used to configure which touch screen gestures are recognized by the map.
  • My Location Layer. When enabled, the My Location Layer displays a button on the map which, when selected by the user, centers the map on the user’s current geographical location. If the user is stationary, this location is represented on the map by a blue marker. If the user is in motion the location is represented by a chevron indicating the user’s direction of travel.

To use Google Maps you need to create a valid Google Maps API key. The key is free, you can use it with any of your applications that call the Maps API, and it supports an unlimited number of users. You can register your application on the Google Developer Console and enable the API. To do this, go to Get API Key, click GET A KEY and create/enable API for your project.

Next, to test your app you need Android emulator with Google Play services or a compatible Android device that runs Android 2.3 or higher and includes Google Play Store.

If you have Android emulator without Google Play services you will get

W/GooglePlayServicesUtil: Google Play Store is missing

To fix it, create Android emulator in Android Studio with Google APIs, for example, Android 6.0 (with Google APIs).

After that you can get :)

Google Play services out of date. Requires 9683000 but found 8489270

It means that you have installed new version in build.gradle, but device has old one. I fix it by downgrading version in build.gradle to 8+.

So, you have the key and Android emulator with Google Play services.

Place the following lines into the dependencies node of the build.gradle file.

// for real device
// compile 'com.google.android.gms:play-services:9.8.0'
// for emulator
compile 'com.google.android.gms:play-services:8+'
compile 'com.google.android.gms:play-services-location:8+'

Once you have your libraries imported, you can close build.gradle and open your AndroidManifest.xml file. Above the application node, you need to declare that the application uses OpenGL ES 2.0 and define the permissions needed by your application.

<uses-feature android:glEsVersion="0x00020000" android:required="true" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  • INTERNET – if we are connected to Internet or not.
  • ACCESS_FINE_LOCATION - to determine user’s location using GPS. It will give us precise location.
  • ACCESS_COARSE_LOCATION – to determine user’s location using WiFi and mobile. It will give us an approximate location.

Within the application node, you need to add metadata Maps API key.

<meta-data android:name="com.google.android.geo.API_KEY" android:value="API_KEY"/>

Next, you need to extend MainActivity by implementing OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapLongClickListener. SupportMapFragment is used here rather than com.google.android.gms.maps.MapFragment in order to add backwards compatibility before API 12.

  • ConnectionCallbacks and OnConnectionFailedListener are designed to monitor the state of the GoogleApiClient, which is used in this application for getting the user's current location.
  • OnInfoWindowClickListener is triggered when the user clicks on the info window that pops up over a marker on the map.
  • OnMapLongClickListener and OnMapClickListener are triggered when the user either taps or holds down on a portion of the map.
  • OnMarkerClickListener is called when the user clicks on a marker on the map, which typically also displays the info window for that marker.

MapFragment is a map component in an app. This fragment is the simplest way to place a map in an application. It's a wrapper around a MapView of a map to automatically handle the necessary life cycle needs. Being a fragment, this component can be added to an activity's layout file simply with the XML below. Open activity_main.xml from your resources folder and change it so that it includes the fragment as a view.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"



Returning to our MainActivity class, you need to define some global values at the top of the class for use in your application.

private GoogleMap map;
private GoogleApiClient googleApiClient;
private Location currentLocation;

Here googleApiClient and currentLocation are used for getting the user's location for initializing the map camera. Next, you need to create your GoogleApiClient and initiate LocationServices in order to get your user's current location. In the moveToMyLocation method, you initialize the camera and some basic map properties. You start by creating a CameraPosition object through the CameraPosition.Builder, with a target set for the latitude and longitude of your user and a set zoom level.

setMyLocationEnabled adds a button to the top right corner of the MapFragment that automatically moves the camera to your user's location when pressed.

setZoomControlsEnabled adds + and - buttons in the lower right corner, allowing the user to change the map zoom level without having to use gestures. There's a few more interesting things that you can set using UiSettings, such as adding a compass or disabling gestures, which you can find in the UiSettings reference.

Alternatively, the map:uiZoomControls property may be set within the map element of the XML resource file: map:uiZoomControls="false".

The compass may be displayed either via a call to the setCompassEnabled() method of the UiSettings instance, or through XML resources using the map:uiCompass property.

onMarkerClick method creates a generic red marker where the user has tapped. Additional options, such as setting a marker as draggable, can be set through the MarkerOptions object. You can find additional attributes in the official MarkerOptions reference.


Full code of MainActivity

public class MainActivity extends FragmentActivity implements OnMapReadyCallback,
        GoogleMap.OnMapLongClickListener {

    private GoogleMap map;
    private GoogleApiClient googleApiClient;
    private Location currentLocation;

    protected void onCreate(Bundle savedInstanceState) {

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()

        googleApiClient = new GoogleApiClient.Builder(this)

    public void onConnected(@Nullable Bundle bundle) {
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION ) 
            != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(MainActivity.this, "Please allow ACCESS_COARSE_LOCATION persmission.", 

        currentLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);

        map.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
            public boolean onMyLocationButtonClick() {
                return false;


    public void onConnectionSuspended(int i) {}

    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}

    public void onMapLongClick(LatLng latLng) {
        MarkerOptions options = new MarkerOptions().position(latLng);


    public void onMapReady(GoogleMap googleMap) {
        map = googleMap;
        LatLng latLng = new LatLng(49.2400, 28.4811);
        MarkerOptions marker = new MarkerOptions()
            .snippet("My hometown!");
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18));

    private String getAddressFromLatLng(LatLng latLng) {
        Geocoder geocoder = new Geocoder(MainActivity.this);

        String address = "";
        try {
            address = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1)
        } catch (IOException e ) {

        return address;

    public boolean onMarkerClick(Marker marker) {
        return true;

    protected void onStart() {

    protected void onStop() {

        if( googleApiClient != null && googleApiClient.isConnected() ) {

    public void moveToMyLocation() {
        if (currentLocation != null) {
            CameraPosition position = CameraPosition.builder()
                    .target(new LatLng(currentLocation.getLatitude(),

            map.animateCamera(CameraUpdateFactory.newCameraPosition(position), null);
        } else {
            Toast.makeText(this, "Can not get user location!", Toast.LENGTH_LONG).show();

Don’t like the default Android pins? You can also create a marker with a custom icon as the pin. Go back to onMapLongClick() and add the following line of code after the MarkerOptions instantiation:

options.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_user_location)));

Download custom pins named ic_user_location from this link and unzip it to res/mipmap.

You can also change the type of the MAP dynamically by making a call to the setMapType() method of the corresponding GoogleMap object, passing through one of the following values:

  • GoogleMap.MAP_TYPE_NONE. An empty grid with no mapping tiles displayed.
  • GoogleMap.MAP_TYPE_NORMAL. The standard view consisting of the classic road map.
  • GoogleMap.MAP_TYPE_SATELLITE. Displays the satellite imagery of the map region.
  • GoogleMap.MAP_TYPE_HYBRID. Displays satellite imagery with the road maps superimposed.
  • GoogleMap.MAP_TYPE_TERRAIN. Displays topographical information such as contour lines and colors.

Handling map gesture interaction

The Google Maps Android API is capable of responding to a number of different user interactions. These interactions can be used to change the area of the map displayed, the zoom level and even the angle of view (such that a 3D representation of the map area is displayed for certain cities).

Map zooming gestures

Support for gestures relating to zooming in and out of a map may be enabled or disabled using the setZoomControlsEnabled() method of the UiSettings object associated with the GoogleMap instance. For example, the following code enables zoom gestures for our example map:

import com.google.android.gms.maps.UiSettings;
UiSettings mapSettings;
mapSettings = mMap.getUiSettings();

The same result can be achieved within an XML resource file by setting the map:uiZoomGestures property to true or false. When enabled, zooming will occur when the user makes pinching gestures on the screen. Similarly, a double tap will zoom in whilst a two finger tap will zoom out. One finger zooming gestures, on the other hand, are performed by tapping twice but not releasing the second tap and then sliding the finger up and down on the screen to zoom in and out respectively.

Map scrolling/panning gestures

A scrolling, or panning gesture allows the user to move around the map by dragging the map around the screen with a single finger motion. Scrolling gestures may be enabled within code via a call to the setScrollGesturesEnabled() method of the UiSettings instance:

UiSettings mapSettings;
mapSettings = mMap.getUiSettings(); 

Alternatively, scrolling on a map instance may be enabled in an XML resource layout file using the map:uiScrollGestures property.

Map tilt gestures

Tilt gestures allow the user to tilt the angle of projection of the map by placing two fingers on the screen and moving them up and down to adjust the tilt angle. Tilt gestures may be enabled or disabled via a call to the setTiltGesturesEnabled() method of the UiSettings instance, for example:

UiSettings mapSettings;
mapSettings = mMap.getUiSettings(); 

Tilt gestures may also be enabled and disabled using the map:uiTiltGestures property in an XML layout resource file.

Map rotation gestures

By placing two fingers on the screen and rotating then in a circular motion, the user may rotate the orientation of a map when map rotation gestures are enabled. This gesture support is enabled and disabled in code via a call to the setRotateGesturesEnabled() method of the UiSettings instance, for example:

UiSettings mapSettings;
mapSettings = mMap.getUiSettings(); 

Rotation gestures may also be enabled or disabled using the map:uiRotateGestures property in an XML layout resource file.

My location button

My location button will be used to move map to your current location. This button can be shown / hidden by calling setMyLocationButtonEnabled(boolean) function.


Toolbar buttons

By default, a toolbar appears at the bottom right of the map when a user taps a marker. The toolbar gives the user quick access to the Google Maps mobile app. You can enable and disable the toolbar by calling setMapToolbarEnabled(boolean).


Drawing on the map

The GoogleMap object has a set of methods that make it easy to draw shapes and place images onto the map. To draw a simple circle, you only need to create a CircleOptions object, set a radius and center location, and define the stroke/fill colors and size.

Once you have a CircleOptions object, you can call addCircle to draw the defined circle on top of the map. Just like when placing markers, objects that are drawn on the map return an object of the drawn item type so it can be referenced later if needed.

private void drawCircle(LatLng loc) {
    CircleOptions options = new CircleOptions();

    // radius in meters

I added drawCircle() to onMapReady() method after map.setOnMapLongClickListener(this);.

To draw a different closed-off shape, you can take multiple LatLng points and create a PolygonOptions object. As you can see below, PolygonOptions are created in a similar fashion to CircleOptions. Instead of using a center and radius method, you use add with a list of points. You can then call addPolygon to draw the shape. For this example, you simply draw a triangle onto the map.

private void drawPolygon(LatLng startPoint) {
    LatLng point2 = new LatLng(startPoint.latitude + .001, startPoint.longitude);
    LatLng point3 = new LatLng(startPoint.latitude, startPoint.longitude + .001);

    PolygonOptions options = new PolygonOptions();
    options.add(startPoint, point2, point3);



Marker Clustering

By clustering your markers, you can put a large number of markers on a map without making the map hard to read.

Clustering on Google Maps is really simple because there is an awesome library that helps us do this. The Google Maps Android API Utility Library.

Add the following dependency to your app's Gradle build file:

dependencies {
    compile 'com.google.maps.android:android-maps-utils:0.5+'

The ClusterManager helps you manage multiple markers at different zoom levels. This means you can put a large number of markers on a map without making the map hard to read. When a user views the map at a high zoom level, the individual markers show on the map. When the user zooms out to a lower zoom level, the markers gather together into clusters, to make viewing the map easier.

The first thing you need to do is create a new model object that implements the ClusterItem interface. This model needs to implement the getPosition, getTitle, getSnippet methods from the ClusterItem.

public class PinCluster implements ClusterItem {
    private LatLng position;
    private String title;
    private String snippet;

    public PinCluster(LatLng latLng, String title, String snippet) {
        this.position = latLng;
        this.title = title;
        this.snippet = snippet;

    public LatLng getPosition() {
        return position;

    public String getTitle() {
        return title;

    public String getSnippet() {
        return snippet;

When you initialize your map, you need to create a ClusterManager, associate it with your GoogleMap, and add your LatLng positions as ClusterMarkerLocations to the ClusterManager for the utility to know what to cluster. Take a look at the following implementation to better understand how this works.

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback,
        GoogleApiClient.OnConnectionFailedListener {

    private GoogleMap map;
    private GoogleApiClient googleApiClient;
    private List<PinCluster> pins;
    private ClusterManager<PinCluster> clusterManager;
    private Activity activity = MainActivity.this;

    public void onMapReady(GoogleMap googleMap) {
        map = googleMap;
        pins = new ArrayList<>();
        clusterManager = new ClusterManager<PinCluster>(activity, map);

        map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
            public void onMapLoaded() {
                LatLng centerLatLng = new LatLng(49.2400, 28.4811);

                map.moveCamera(CameraUpdateFactory.newLatLngZoom(centerLatLng, 18));

                pins.add(new PinCluster(centerLatLng, "My town", "Vinnica"));

                LatLngBounds.Builder builder = new LatLngBounds.Builder();

                for (int i = 0; i < 20; i++) {
                    LatLng rndLatLng = randomMarker(centerLatLng);
                    pins.add(new PinCluster(rndLatLng, "Title " + i, "Snippet " + i));

                LatLngBounds bounds = builder.build();
                int width = getResources().getDisplayMetrics().widthPixels;
                int padding = (int) (width * 0.05);

                CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);



At this point, you should be able to render default markers that cluster.


In order use customized markers you will need to extend DefaultClusterRenderer and build your own renderer class:

public class PinRenderer extends DefaultClusterRenderer<PinCluster> {
    Context context;
    int pinWith = 100;
    int pinHeight = 110;

    public PinRenderer(Context context, ClusterManager<PinCluster> clusterManager, GoogleMap map) {
        super(context, map, clusterManager);
        this.context = context;

    protected void onBeforeClusterItemRendered(PinCluster myItem, MarkerOptions markerOptions) {
        // Customize the marker here

    protected void onBeforeClusterRendered(Cluster<PinCluster> cluster, MarkerOptions markerOptions) {
        // Customize the cluster here
        IconGenerator iconGenerator = new IconGenerator(context.getApplicationContext());
        iconGenerator.setBackground(ContextCompat.getDrawable(context, R.drawable.pin));

        LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View iconView = inflater.inflate(R.layout.map_cluster_view, null, false);

        Bitmap bg = iconGenerator.makeIcon(String.valueOf(cluster.getSize()));
        Bitmap resizedBitmap = Bitmap.createScaledBitmap(bg, pinWith, pinHeight, false);


Following is map_cluster_view.xml.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

        android:gravity="center" />




How to disable gestures and events in map

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

    map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
        public boolean onMarkerClick(Marker marker) {
            return true;

How to set bounds in map

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

    map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
        public void onMapLoaded() {
            final LatLngBounds.Builder builder = new LatLngBounds.Builder();

            LatLng latLng1 = new LatLng(49.442403, 28.147860);
            map.addMarker(new MarkerOptions().position(latLng1));

            LatLng latLng2 = new LatLng(49.677570, 27.683688);
            map.addMarker(new MarkerOptions().position(latLng2));

            LatLng latLng3 = new LatLng(49.996429, 28.741122);
            map.addMarker(new MarkerOptions().position(latLng3));

            LatLngBounds bounds = builder.build();

            int width = getResources().getDisplayMetrics().widthPixels;
            int padding = (int) (width * 0.05);

            CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);

How to generate random marker nearby another marker

private void randomMarker(LatLng startLatLng) {
    Random rand = new Random();
    float minF = 0.0010f;
    float maxF = 0.0099f;
    float incLat = rand.nextFloat() * (maxF - minF) + minF;
    float incLng = rand.nextFloat() * (maxF - minF) + minF;

    int minI = -1;
    int maxI = 1;
    int range = maxI - minI + 1;
    int signLat = rand.nextInt(range) + minI;
    int signLng = rand.nextInt(range) + minI;
    signLat = signLat == 0 ? 1 : signLat;
    signLng = signLng == 0 ? 1 : signLng;

    incLat = incLat * signLat;
    incLng = incLng * signLng;

    LatLng ll = new LatLng(startLatLng.latitude + incLat, startLatLng.longitude + incLng);

    MarkerOptions options = new MarkerOptions()

How to add marker on Map click

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

    LatLng latLng = new LatLng(49.2349067, 28.399594);
    map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 13));

    map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
        public void onMapClick(LatLng latLng) {
            Geocoder geocoder = new Geocoder(activity);
            List<Address> list;
            try {
                list = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
            } catch (IOException e) {

            Address address = list.get(0);

            if (marker != null) {

            MarkerOptions options = new MarkerOptions()
                    .position(new LatLng(latLng.latitude, latLng.longitude));
            marker = map.addMarker(options);

How to sort list of LatLng objects by distance to some location

Sort list of Person items

ArrayList<Person> items = getPersons();
LatLng startLocation = new LatLng(49.2400, 28.4811);

Collections.sort(items, new Comparator<Person>(){
    public int compare(Person o1, Person o2){
        float distance1 = o1.getDistance(startLocation);
        float distance2 = o2.getDistance(startLocation);

        if(distance1 == distance2)
            return 0;
        return distance1 < distance2 ? -1 : 1;

Snippet of getDistance method of Person object

public float getDistance(LatLng to) {
    float distance = 0;
    if (to != null) {
        Location loc1 = new Location("");

        Location loc2 = new Location("");

        distance = loc1.distanceTo(loc2);
    return distance;

How to change the position of a marker

MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(50,6)));
Marker marker = map.addMarker(markerOptions);
marker.setPosition(new LatLng(50,5));

How to create custom marker

By default map marker color will be red. Google maps provides some set of predefined colored icons for the marker.


Apart from maps native marker icons, you can use own image to show as a marker. You can load the icon from any kind of supported sources.

  • fromAsset(String assetName) - loading from assets folder.
  • fromBitmap(Bitmap image) - loading bitmap image.
  • fromFile(String path) - loading from file.
  • fromResource(int resourceId) - loading from drawable resource.
public BitmapDescriptor getMapIcon(int resID, int widthPx, int heightPx) {
    //int resID = getResources().getIdentifier(iconName, "drawable", getPackageName());

    Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), resID);
    Bitmap resizedBitmap = Bitmap.createScaledBitmap(imageBitmap, widthPx, heightPx, false);
    return BitmapDescriptorFactory.fromBitmap(resizedBitmap);

MarkerOptions markerOptions = new MarkerOptions()
    .position(new LatLng(50,5))
    .icon(getMapIcon(R.drawable.girl1, 150, 150));

Moving Camera to a Location with Animation

You may want to move camera to a particular position. Google maps provides set of functions to achieve this.

map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 17));

How to calculate distance between two location

First approach

Location loc1 = new Location("One");

Location loc2 = new Location("Two");

float distanceInMeters = loc1.distanceTo(loc2);

Second approach

float[] distanceInMeters = new float[1];
Location.distanceBetween(lat1, lon1, lat2, lon2, distanceInMeters);

Both approaches return the same result.

How to create custom InfoWindow

First, you should define new InfoWindowAdapter

map.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
    public View getInfoWindow(Marker arg0) {
        return null;

    public View getInfoContents(Marker arg0) {
        View v = null;
        try {
            v = getLayoutInflater().inflate(R.layout.custom_infowindow, null);
            TextView tvTitle = (TextView) v.findViewById(R.id.tvTitle);
        } catch (Exception e) {
        return v;

Second, if you want some action on click you should define OnInfoWindowClickListener

map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
    public void onInfoWindowClick(Marker marker) {
        Toast.makeText(activity, "Selected", Toast.LENGTH_SHORT).show();

Third, define custom layout

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

        android:layout_height="wrap_content" />

        android:layout_height="wrap_content" />

If you want InfoWindow with custom action you can use InteractiveInfoWindowAndroid.

Useful links

comments powered by Disqus