Google Maps Android API v2 – Tutorial

Google Maps Android API v2, you can add google maps to your android application. It handles access to Google Maps servers, data downloading, map display, and response to map gestures. API’s can be used for further customizaton such as add markers, polygons, and overlays to a basic map, and to change the user’s view of a particular map area.

[su_carousel source=”media: 379,380,381,382,383,384,385,386,387″ link=”lightbox” width=”900″ height=”500″ mousewheel=”no”]

[su_button url=”http://cs2guru.com/samples/GoogleMapAPIv2.zip” target=”blank” style=”soft” background=”#51d461″ color=”#ffffff” size=”6″ center=”yes” icon=”icon: arrow-circle-o-down”]Download Complete Source Code[/su_button]

1. Configure Google Play Services

For maps api support, Google Play Services library is required to add in project. Refer to Google Play Services Setup.

If you are an eclipse user, Go to menu Window -> Android SDK Manager -> Extras. Install Google Play Service library from there. It will be downloaded to sdk\extras\google\google_play_services\libproject\google-play-services_lib. Import this library project to Eclipse and add as library project for your Google Maps Demo Sample Application (Project->Properties->Android->Add Library).

add_google_play_services_lib

Addition of the Google Play services version to your app’s manifest is required. Edit your application’s AndroidManifest.xml file while creating android application later on, and add the following declaration within the <application> element. This embeds the version of Google Play services that the app was compiled with.

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

 

2. Generating SHA1 using java keytool for your signature key

For getting Google Maps api key, we need to generate SHA-1 fingerprint using java keytool. Open your command prompt and execute the below mentioned command to generate SHA-1 fingerprint. If keytool is not recognized, you can find it in bin directory of installed jre. In my case “C:\Program Files\Java\jre7\bin”.

keytool -list -v -keystore “%USERPROFILE%\.android\debug.keystore” -alias androiddebugkey -storepass android -keypass android

From output note down SHA1 output, “FD:0E:04:E9:99:28:B9:3D:E7:AC:75:AF:6E:2B:F6:E7:CD:EE:CA:96” in my case.

SHA1_keytool

3. Generating Google Maps API Key

We need to obtain api key from Google APIs Console page.. Login using your Google id, you will get option to create a new project if no projects exist. Create new project if required. Navigate to APIs & Auth->APIs. Browse for Google Maps Android API v2 and turn on this api.

maps_api_console

Now navigate to APIs & Auth->Credentials, select Create new key option and opt for Android Key. Input your SHA1 fingerprint and application package name seperated by semi colon(;). It will generate API Key for your android application. Note down this key as it will be required to mention in manifest of android application.

 

android_key_from_console

android_key_from_console2

4. Creating Google Maps Android API v2 Sample Application

Create new Android Application Project. Select Navigation Drawer Activity while selecting for activity type in project wizard as we will user navigation drawer to demonstrate different map options.

 

Project_NavigationDrawerType

 

4.1 Preparing Application Manifest file

We need to add few required permissions and features in our manifest file.
android.permission.INTERNET to download map tiles from Google Maps servers.
android.permission.ACCESS_NETWORK_STATE to determine whether data can be downloaded.
android.permission.WRITE_EXTERNAL_STORAGE to cache map tile data in the device’s external storage area.
android.permission.ACCESS_COARSE_LOCATION to use WiFi or mobile cell data (or both) to determine the device’s location
android.permission.ACCESS_FINE_LOCATION to determine the device’s location to within a very small area.

Make sure to add Google Maps and Google Play services meta data as well.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.androidsrc.googlemapapiv2"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="21" />

    <!-- Google Maps Android API uses OpenGL ES version 2 to render the map -->
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <!-- Used by the API to download map tiles from Google Maps servers. -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- Allows the API to check the connection status in order to determine whether data can be downloaded. -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- Allows the API to cache map tile data in the device's external storage area. -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- Allows the API to use WiFi or mobile cell data (or both) to determine the device's location. -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!-- Allows the API to use the Global Positioning System (GPS) to determine the device's location to within a very small area. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- Google play services used version -->
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <!-- Goolge Maps API Key -->
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyDDuoLcDR6G1-gLf4M6xKXfPeQh_GLCMRU" />
    </application>

</manifest>

4.2 Preparing Layout Files

We will not modify auto generated layout files. activity_main.xml will have DrawerLayout as top-level content view and FrameLayout which will act as container for Google MapView.

<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.androidsrc.googlemapapiv2.MainActivity" >
    <!--
         As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions.
    -->
    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!--
         android:layout_gravity="start" tells DrawerLayout to treat
         this as a sliding drawer on the left side for left-to-right
         languages and on the right side for right-to-left languages.
         If you're not building against API 17 or higher, use
         android:layout_gravity="left" instead.
    -->
    <!--
         The drawer is given a fixed width in dp and extends the full height of
         the container.
    -->
    <fragment
        android:id="@+id/navigation_drawer"
        android:name="com.androidsrc.googlemapapiv2.NavigationDrawerFragment"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        tools:layout="@layout/fragment_navigation_drawer" />

</android.support.v4.widget.DrawerLayout>

 

fragment_navigation_drawer.xml will have ListView to show map options in navigation drawer list.

<ListView 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"
    android:background="#cccc"
    android:choiceMode="singleChoice"
    android:divider="@android:color/transparent"
    android:dividerHeight="0dp"
    tools:context="com.androidsrc.googlemapapiv2.NavigationDrawerFragment" />

4.3 Preparing SRC Files

In auto generated NavigationDrawerFragment.java, define string array for titles of navigation drawers items. We will use navigation drawer selected index to update Google MapView with corresponding options.

	public static final String TYPE_NORMAL = "Normal MapView";
	public static final String TYPE_SATELLITE = "Satellite MapView";
	public static final String TYPE_TERRAIN = "Terrain MapView";
	public static final String TYPE_HYBRID = "Hybrid MapView";
	public static final String TYPE_NORMAL_MARKEROPTIONS = "MapView with Markers";
	public static final String TYPE_NORMAL_ROUTES = "MapView with routes marked";
	public static final String TYPE_NORMAL_CIRCLE = "MapView with circle";
	public static final String TYPE_NORMAL_VIEW_MOVE = "MapView Movement Animation";

	public static final String[] MAP_VIEWS = { TYPE_NORMAL, TYPE_SATELLITE, TYPE_TERRAIN,
			TYPE_HYBRID, TYPE_NORMAL_MARKEROPTIONS, TYPE_NORMAL_ROUTES,
			TYPE_NORMAL_CIRCLE, TYPE_NORMAL_VIEW_MOVE };

Use above defined MAP_VIEW in ArrayAdapter objects on list view. Modify onCreateView as below.

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		mDrawerListView = (ListView) inflater.inflate(
				R.layout.fragment_navigation_drawer, container, false);
		mDrawerListView
				.setOnItemClickListener(new AdapterView.OnItemClickListener() {
					@Override
					public void onItemClick(AdapterView<?> parent, View view,
							int position, long id) {
						selectItem(position);
					}
				});
		mDrawerListView.setAdapter(new ArrayAdapter<String>(getActionBar()
				.getThemedContext(),
				android.R.layout.simple_list_item_activated_1,
				android.R.id.text1, MAP_VIEWS));
		mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
		return mDrawerListView;
	}

MainActivity.java will implement NavigationDrawerCallbackslbacks for listening to navigation drawer item selection and OnMapReadyCallback for callback of map ready event which will provide non-null GoogleMap object.

Whenever navigation drawer item is selected, we will create a new instance of com.google.android.gms.maps.MapFragment and call getMapAsync(OnMapReadyCallback callback) to set a callback object which will be triggered when the GoogleMap instance is ready to be used. You should ensure that getMapAsync is called from main thread and callback should also be executed on main thread. In the case where Google Play services is not installed on the user’s device, the callback will not be triggered until the user installs it.

Based upon current index of navigation item, GoogleMapOptions will be used to define map type and toggling other features like compass, zoom gestures and controls etc.

Once we get callback that GoogleMap is ready, you will get object of GoogleMap with which we can customize map view with options like adding marker, drawing lines/circle and camera animations.

Adding Marker on GoogleMap

MarkerOptions can be added at any particular latitude and longitude. Yon can set its title, alpha value, marker icon color etc. Snippet content will be shown in marker info window when marker is clicked. We need to ensure to moveCamera to your coordinates with required zoom level (9 in below example).

	LatLng NYC = new LatLng(40.714, -74.00);
	googleMap.addMarker(
			new MarkerOptions().position(NYC).alpha(0.8f).title("NYC")
					.snippet("AndroidSRC Map Demo")).setIcon(
			BitmapDescriptorFactory
					.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));

	googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(NYC, 9));<br>

Drawing lines on GoogleMap

PolylineOptions can be used to draw lines between different coordinates, you can define color for your polyline. Below example will show rectangular shape in blue color.

	// Instantiates a new Polyline object and adds points to define a
	// rectangle
	PolylineOptions rectOptions = new PolylineOptions()
			.add(new LatLng(37.35, -122.0))
			.add(new LatLng(37.45, -122.0))
			.add(new LatLng(37.45, -122.2))
                        .add(new LatLng(37.35, -122.2))
			.add(new LatLng(37.35, -122.0)).color(Color.BLUE);

	// Get back the mutable Polyline
	Polyline polyline = googleMap.addPolyline(rectOptions);

	googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.35, -122.0), 9));

Drawing circle on GoogleMap with specified radius

CircleOptions is used to draw circle around particular coordinate in map view. You can specify circle radius in meters and stroke color of circle boundary.

	// Instantiates a new CircleOptions object and defines the center and radius
	CircleOptions circleOptions = new CircleOptions()
			.center(new LatLng(37.4, -122.1)).radius(1000)
			.strokeColor(Color.GREEN); // In meters

	// Get back the mutable Circle
	Circle circle = googleMap.addCircle(circleOptions);
	googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.4, -122.1), 15));

Animating camera from location to another

GoogleMap provides camera api’s like moveCamera() to move it to specific location and animateCamera() for camera animations like zoom in/out or to a specific CameraPosition.

	LatLng SYDNEY = new LatLng(-33.88, 151.21);
	LatLng MOUNTAIN_VIEW = new LatLng(37.4, -122.1);

	// Move the camera instantly to Sydney with a zoom of 15.
	googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 15));

	// Zoom in, animating the camera.
	googleMap.animateCamera(CameraUpdateFactory.zoomIn());

	// Zoom out to zoom level 10, animating with a duration of 2
	// seconds.
	googleMap.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);

	// Construct a CameraPosition focusing on Mountain View and animate
	// the camera to that position.
	CameraPosition cameraPosition = new CameraPosition.Builder()
			.target(MOUNTAIN_VIEW) // Sets the center of the map to Mountain View
			.zoom(17) // Sets the zoom
			.bearing(90) // Sets the orientation of the camera to east
			.tilt(30) // Sets the tilt of the camera to 30 degrees
			.build(); // Creates a CameraPosition from the builder

	googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

 

Final code for MainActivity.java will be as below after adding all the functionality.

package com.androidsrc.googlemapapiv2;

import android.app.ActionBar;
import android.app.Activity;
import android.app.FragmentManager;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.widget.DrawerLayout;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

public class MainActivity extends Activity implements
        NavigationDrawerFragment.NavigationDrawerCallbacks, OnMapReadyCallback {

    public static final String[] MAP_VIEWS = {
            NavigationDrawerFragment.TYPE_NORMAL,
            NavigationDrawerFragment.TYPE_SATELLITE,
            NavigationDrawerFragment.TYPE_TERRAIN,
            NavigationDrawerFragment.TYPE_HYBRID,
            NavigationDrawerFragment.TYPE_NORMAL_MARKEROPTIONS,
            NavigationDrawerFragment.TYPE_NORMAL_ROUTES,
            NavigationDrawerFragment.TYPE_NORMAL_CIRCLE,
            NavigationDrawerFragment.TYPE_NORMAL_VIEW_MOVE};

    /**
     * Fragment managing the behaviors, interactions and presentation of the
     * navigation drawer.
     */
    private NavigationDrawerFragment mNavigationDrawerFragment;

    /**
     * Used to store the last screen title. For use in
     * {@link #restoreActionBar()}.
     */
    private CharSequence mTitle;

    private int selectedDrawerIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager()
                .findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.
        mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));

    }

    @Override
    public void onNavigationDrawerItemSelected(int position) {

        selectedDrawerIndex = position;
        // update the main content by replacing fragments
        FragmentManager fragmentManager = getFragmentManager();
        MapFragment mapFragment = MapFragment
                .newInstance(getMapOptions(position));
        // call getMapAsync() to set OnMapReadyCallback on map fragment
        mapFragment.getMapAsync(this);
        fragmentManager.beginTransaction().replace(R.id.container, mapFragment)
                .commit();

        mTitle = MAP_VIEWS[selectedDrawerIndex];
        restoreActionBar();
    }

    private GoogleMapOptions getMapOptions(int position) {
        GoogleMapOptions options = new GoogleMapOptions();

        options.compassEnabled(true).zoomGesturesEnabled(true)
                .zoomControlsEnabled(true).mapToolbarEnabled(true)
                .rotateGesturesEnabled(true).scrollGesturesEnabled(true);

        switch (position) {
            case 0:
                options.mapType(GoogleMap.MAP_TYPE_NORMAL);

                break;
            case 1:
                options.mapType(GoogleMap.MAP_TYPE_SATELLITE);

                break;
            case 2:
                options.mapType(GoogleMap.MAP_TYPE_TERRAIN);

                break;
            case 3:
                options.mapType(GoogleMap.MAP_TYPE_HYBRID);

                break;
            default:
                options.mapType(GoogleMap.MAP_TYPE_NORMAL);

                break;
        }
        return options;
    }

    public void restoreActionBar() {
        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        // Add markers once map is ready
        if (selectedDrawerIndex == 4) // TYPE_NORMAL_MARKEROPTIONS
        {

            LatLng NYC = new LatLng(40.714, -74.00);
            googleMap.addMarker(
                    new MarkerOptions().position(NYC).alpha(0.8f).title("NYC")
                            .snippet("AndroidSRC Map Demo")).setIcon(
                    BitmapDescriptorFactory
                            .defaultMarker(BitmapDescriptorFactory.HUE_AZURE));

            googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(NYC, 9));

            LatLng BROOKLYN = new LatLng(40.598, -73.944);
            googleMap
                    .addMarker(
                            new MarkerOptions().position(BROOKLYN)
                                    .title("BROOKLYN Draggable")
                                    .draggable(true))
                    .setIcon(
                            BitmapDescriptorFactory
                                    .defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
        } else if (selectedDrawerIndex == 5) // TYPE_NORMAL_ROUTES
        {
            // Instantiates a new Polyline object and adds points to define a
            // rectangle
            PolylineOptions rectOptions = new PolylineOptions()
                    .add(new LatLng(37.35, -122.0))
                    .add(new LatLng(37.45, -122.0)) // North of the previous
                    // point, but at the same
                    // longitude
                    .add(new LatLng(37.45, -122.2)) // Same latitude, and 30km
                    // to the west
                    .add(new LatLng(37.35, -122.2)) // Same longitude, and 16km
                    // to the south
                    .add(new LatLng(37.35, -122.0)).color(Color.BLUE); // Closes
            // the
            // polyline.

            // Get back the mutable Polyline
            Polyline polyline = googleMap.addPolyline(rectOptions);

            googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(
                    37.35, -122.0), 9));
        } else if (selectedDrawerIndex == 6) // TYPE_NORMAL_CIRCLE
        {
            // Instantiates a new CircleOptions object and defines the center
            // and radius
            CircleOptions circleOptions = new CircleOptions()
                    .center(new LatLng(37.4, -122.1)).radius(1000)
                    .strokeColor(Color.GREEN); // In meters

            // Get back the mutable Circle
            Circle circle = googleMap.addCircle(circleOptions);
            googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(
                    37.4, -122.1), 15));
        } else if (selectedDrawerIndex == 7) // TYPE_NORMAL_CAMERA
        {
            LatLng SYDNEY = new LatLng(-33.88, 151.21);
            LatLng MOUNTAIN_VIEW = new LatLng(37.4, -122.1);

            // Move the camera instantly to Sydney with a zoom of 15.
            googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 15));

            // Zoom in, animating the camera.
            googleMap.animateCamera(CameraUpdateFactory.zoomIn());

            // Zoom out to zoom level 10, animating with a duration of 2
            // seconds.
            googleMap.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);

            // Construct a CameraPosition focusing on Mountain View and animate
            // the camera to that position.
            CameraPosition cameraPosition = new CameraPosition.Builder()
                    .target(MOUNTAIN_VIEW) // Sets the center of the map to
                    // Mountain View
                    .zoom(17) // Sets the zoom
                    .bearing(90) // Sets the orientation of the camera to east
                    .tilt(30) // Sets the tilt of the camera to 30 degrees
                    .build(); // Creates a CameraPosition from the builder

            googleMap.animateCamera(CameraUpdateFactory
                    .newCameraPosition(cameraPosition));
        } else // for other maps move camera to NYC
        {
            googleMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(
                    40.714, -74.00)));
        }
    }
}

In this tutorial, we have used MapFragment to show GoogleMap view programmatically. We can define MapFragment in our layout as well and use it similar to other fragment. We can find fragment using its id with FragmentManager and carry on same tasks as above. We just need to ensure that android:name attribute defines full class path of MapFragment as below. We can define map attributes as well. In order to use these custom attributes within your XML layout file, you must add the map namespace declaration as below.

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:name="com.google.android.gms.maps.MapFragment"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    map:cameraBearing="112.5"
    map:cameraTargetLat="-33.796923"
    map:cameraTargetLng="150.922433"
    map:cameraTilt="30"
    map:cameraZoom="13"
    map:mapType="normal"
    map:uiCompass="false"
    map:uiRotateGestures="true"
    map:uiScrollGestures="false"
    map:uiTiltGestures="true"
    map:uiZoomControls="false"
    map:uiZoomGestures="true"/>

You may also like...

3 Responses

  1. NAUSHAD MADAKIYA says:

    HI
    When i make it from google documentation i got error for invalid api & your code also give same error
    can you give some suggestion ?

    • AndroidSRC . says:

      Dear Naushad,

      As per LogCat output, it could be one of the two possibilities.

      1. At Console side, Google Maps Android API v2 is not turned ON. For reflecting changes, make sure you perform a complete uninstall and install of application.

      2. Maps API key is generated from combination of SHA1 and package name. For the same reason, application apk must be signed with same key as what we used for generating SHA-1.

      You must ensure that SHA-1 fingerprint at Eclipse->Windows->Preferences->Android->Build should be same as SHA1 used to generate Maps API Key i.e. apk must be signed with same key. Or else you need to export Android Application signed with same key and install this signed apk.

      For same reason, APK created from our sample code is now signed with debug key of your eclipse. So it gives you authorization failure.

      Hopefully, it should resolve your problem.

      I have uploaded apk signed with key used for registering key of sample application. You can check if it is running without problem.

      http://cs2guru.com/samples/GoogleMapAPIv2_APK.zip

      • NAUSHAD MADAKIYA says:

        I Followed Same Steps But My Problem Was Package Name In Src & In Manifest Was Different And I Was Adding Package Name From Src Instead Of Manifest File

        Thankyou For Response & Clear Solution 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *