time

Getting started with Google Maps for Android

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.

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.

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"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/map"
        android:name="me.proft.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

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.

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.

android_map.png

Full code of MainActivity

public class MainActivity extends FragmentActivity implements OnMapReadyCallback,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        GoogleMap.OnMarkerClickListener,
        GoogleMap.OnMapLongClickListener {

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

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

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

        googleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    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.", 
                Toast.LENGTH_LONG).show();
            return;
        }

        currentLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);

        map.setMyLocationEnabled(true);
        map.getUiSettings().setZoomControlsEnabled(true);
        map.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
            @Override
            public boolean onMyLocationButtonClick() {
                moveToMyLocation();
                return false;
            }
        });

        moveToMyLocation();
    }

    @Override
    public void onConnectionSuspended(int i) {}

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}

    @Override
    public void onMapLongClick(LatLng latLng) {
        MarkerOptions options = new MarkerOptions().position(latLng);
        options.title(getAddressFromLatLng(latLng));

        options.icon(BitmapDescriptorFactory.defaultMarker());
        map.addMarker(options);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        map = googleMap;
        LatLng latLng = new LatLng(49.2400, 28.4811);
        MarkerOptions marker = new MarkerOptions()
            .position(latLng)
            .title("Vinnytsia")
            .snippet("My hometown!");
        map.addMarker(marker);
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 18));
        map.setOnMarkerClickListener(this);
        map.setOnMapLongClickListener(this);
    }

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

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

        return address;
    }

    @Override
    public boolean onMarkerClick(Marker marker) {
        marker.showInfoWindow();
        return true;
    }


    @Override
    protected void onStart() {
        super.onStart();
        googleApiClient.connect();
    }

    @Override
    protected void onStop() {
        super.onStop();

        if( googleApiClient != null && googleApiClient.isConnected() ) {
            googleApiClient.disconnect();
        }
    }

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

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

Useful links

comments powered by Disqus