Статьи

Начало работы с Google Maps для Android: основы

Без сомнения, карты являются одним из самых полезных инструментов для пользователей, когда они включены в приложение. Это первое в серии пособие, посвященное Google Maps v2 для Android. Он будет включать в себя настройку API Карт Google через консоль разработчика Google, включая фрагмент карты в ваших приложениях, отображение местоположения пользователя, добавление маркеров, рисование на карте и некоторые общие методы, которые добавят полезность в ваше приложение. Весь код для этого урока можно найти на GitHub .

Чтобы использовать API Карт Google, необходимо зарегистрировать свое приложение в консоли разработчика Google и включить API. Для этого перейдите в консоль разработчика Google . Если у вас уже есть проект, вы можете пропустить следующий раздел. Если нет, вы можете следовать и создать новый проект для вашего приложения карт.

Чтобы создать новый проект, нажмите синюю кнопку « Создать проект» в верхнем левом углу экрана. Если вы не видите кнопку « Создать проект» , найдите кнопку « Создать пустой проект» .

Create a project button

Это предоставляет вам диалог, который запрашивает название проекта. Для этого урока я создал проект под названием TutsPlusMaps . Существуют некоторые ограничения на то, что вы можете назвать своим проектом, так как только буквы, цифры, кавычки, дефисы, пробелы и восклицательные знаки являются разрешенными символами.

Creating a project dialog window

После нажатия кнопки « Создать » в правом нижнем углу страницы появляется диалоговое окно с индикатором загрузки во время создания проекта.

Когда проект создан или вы выбрали существующий проект, вы попадете на экран обзора проекта. Отсюда вы захотите развернуть APIs и элемент авторизации на левой навигационной панели и нажать на API .

Расположение аутентификации API в nav

Хотя на этом экране есть окно поиска, вы заметите, что Google разместил элементы API Карт в верхней части центральной колонки, чтобы разработчики могли получить к ним доступ. Для этого руководства щелкните элемент под названием « Google Maps Android API» под заголовком « Google Maps APIs» .

Google Maps API item

Вы попадете на экран, где вы можете нажать синюю кнопку Включить API , чтобы включить API Карт для вашего проекта.

Кнопка включения API

После того, как вы включили API Карт, щелкните элемент « Учетные данные» в разделе « API-интерфейсы и авторизация» на боковой панели навигации, чтобы получить ключ для доступа к API-интерфейсу Карт в вашем приложении. Когда вы увидите диалоговое окно Credentials , нажмите синюю кнопку Add Credentials и выберите API Key .

Dialog for selecting the type of credentials to create

Поскольку это приложение для Android, вам нужно выбрать ключ Android в следующем диалоговом окне. Если бы вам нужно было создать одно и то же приложение с использованием карт на разных платформах, вы могли бы создать ключ для каждой платформы.

Диалог для выбора типа ключа для генерации

На следующем экране нажмите Добавить имя пакета и отпечаток пальца . Это обеспечивает два поля: одно для добавления имени пакета, а другое для добавления SHA1 из ключа подписи вашего приложения .

Для этого урока я буду использовать имя пакета com.tutsplus.mapsdemo . Чтобы получить подпись SHA1, вам нужно открыть терминал или командную строку и перейти к расположению ключа подписи вашего приложения. Это может быть либо ключ релиза, либо debug.keystore . Вы можете создать SHA1 с помощью следующей команды:

1
keytool -list -v -keystore debug.keystore
Снимок экрана: вывод терминала при получении ключа SHA1 из хранилища ключей

После того, как вы создали свой ключ SHA1 и ввели его в текстовое поле, нажмите синюю кнопку Создать . Затем вы увидите диалоговое окно, содержащее ключ API, который необходимо добавить в приложение Android для доступа к API Карт.

Диалог, представляющий ваш ключ API Google

На этом этапе вы можете создать исходный проект Android с тем же именем пакета, которое вы использовали для создания ключа API. Как только ваш проект создан, откройте файл build.gradle . Вам необходимо импортировать библиотеку Play Services для карт. В этом примере вам также необходимо импортировать локации из библиотеки Play Services, чтобы установить начальную позицию для вашей карты. Поместите следующие строки в узел dependencies файла build.gradle .

1
2
compile ‘com.google.android.gms:play-services-maps:7.8.0’
compile ‘com.google.android.gms:play-services-location:7.8.0’

После того, как вы импортировали свои библиотеки, вы можете закрыть build.gradle и открыть файл AndroidManifest.xml . Над узлом application необходимо объявить, что приложение использует OpenGL ES 2.0, и определить разрешения, необходимые для вашего приложения.

Обратите внимание, что разрешение ACCESS_FINE_LOCATION требуется только для этой демонстрации, чтобы получить местоположение пользователя для определения того, где карта должна первоначально отображаться. Если у вас есть известное местоположение в вашем собственном приложении, нет необходимости использовать местоположение пользователя.

1
2
3
4
5
6
<uses-feature
    android:glEsVersion=»0x00020000″
    android:required=»true»/>
 
<uses-permission android:name=»android.permission.INTERNET» />
<uses-permission android:name=»android.permission.ACCESS_FINE_LOCATION» />

В узле application вам нужно добавить две части метаданных. Первый информирует приложение об использовании сервисов Play, а второй связывает ключ API Карт с вашим приложением. В следующем фрагменте кода @string/google_api_key — это строковая ссылка на ключ из консоли разработчика Google.

1
2
3
4
5
6
7
<meta-data
    android:name=»com.google.android.gms.version»
    android:value=»@integer/google_play_services_version» />
 
<meta-data
    android:name=»com.google.android.geo.API_KEY»
    android:value=»@string/google_api_key»/>

Как только вы закончите обновление AndroidManifest.xml , закройте файл. Затем вам нужно создать новый класс Java с именем MapFragment , который расширяет SupportMapFragment . SupportMapFragment используется здесь, а не com.google.android.gms.maps.MapFragment , чтобы добавить обратную совместимость перед API 12.

Если ваше приложение не нуждается в поддержке устройств, работающих под управлением более старых версий Android, тогда лучше использовать com.google.android.gms.maps.MapFragment . После создания базового фрагмента вам необходимо реализовать шесть интерфейсов, которые мы будем использовать для этой демонстрации.

1
2
3
4
5
6
public class MapFragment extends SupportMapFragment implements GoogleApiClient.ConnectionCallbacks,
       GoogleApiClient.OnConnectionFailedListener,
       GoogleMap.OnInfoWindowClickListener,
       GoogleMap.OnMapLongClickListener,
       GoogleMap.OnMapClickListener,
       GoogleMap.OnMarkerClickListener {
  • ConnectionCallbacks и OnConnectionFailedListener предназначены для мониторинга состояния GoogleApiClient , который используется в этом приложении для получения текущего местоположения пользователя.
  • OnInfoWindowClickListener запускается, когда пользователь нажимает на информационное окно, которое всплывает над маркером на карте.
  • OnMapLongClickListener и OnMapClickListener запускаются, когда пользователь либо OnMapClickListener , либо удерживает на части карты.
  • OnMarkerClickListener вызывается, когда пользователь нажимает на маркер на карте, которая обычно также отображает информационное окно для этого маркера.

При появлении запроса нажмите на красную лампочку, которая появляется рядом с именем класса, чтобы добавить методы, необходимые для этих интерфейсов.

После того, как вы создали начальный фрагмент, вы должны сообщить MainActivity , что он должен использовать этот фрагмент. Откройте файл activity_main.xml из папки ресурсов и измените его так, чтобы он включал фрагмент в качестве представления.

01
02
03
04
05
06
07
08
09
10
11
12
<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=»com.tutsplus.mapsdemo.MapFragment»
        android:layout_width=»match_parent»
        android:layout_height=»match_parent»/>
 
</RelativeLayout>

После обновления макета активности вы сможете запустить свое приложение и просмотреть карту Земли, которая полностью уменьшена и сфокусирована на широте 0, долготе 0.

Как должен выглядеть ваш экран, когда Google Maps изначально настроен правильно

Возвращаясь к нашему классу MapFragment , вам нужно определить некоторые глобальные значения в верхней части класса для использования в вашем приложении.

1
2
3
4
5
6
7
8
9
private GoogleApiClient mGoogleApiClient;
private Location mCurrentLocation;
 
private final int[] MAP_TYPES = { GoogleMap.MAP_TYPE_SATELLITE,
                                  GoogleMap.MAP_TYPE_NORMAL,
                                  GoogleMap.MAP_TYPE_HYBRID,
                                  GoogleMap.MAP_TYPE_TERRAIN,
                                  GoogleMap.MAP_TYPE_NONE };
private int curMapTypeIndex = 0;

Здесь mGoogleApiClient и mCurrentLocation используются для получения местоположения пользователя для инициализации камеры карты. MAP_TYPES и curMapTypeIndex используются в примере кода для переключения между различными типами отображения карты. Каждый из типов карт служит разным целям, поэтому один или несколько типов могут подойти для ваших собственных приложений.

GoogleMap.MAP_TYPE_SATELLITE отображает спутниковую GoogleMap.MAP_TYPE_SATELLITE области без названий улиц или надписей.

Пример типа спутниковой карты

GoogleMap.MAP_TYPE_NORMAL показывает общую карту с названиями улиц и метками.

Пример карты нормалей

GoogleMap.MAP_TYPE_HYBRID объединяет спутниковый и обычный режим, отображая спутниковые изображения области со всеми метками.

Пример типа гибридной карты

GoogleMap.MAP_TYPE_TERRAIN похож на карту нормалей, но текстуры добавляются для отображения изменений высоты в окружающей среде. Эти текстуры наиболее заметны, когда карта перетаскивается двумя пальцами.

Пример типа карты местности

GoogleMap.MAP_TYPE_NONE похож на карту нормалей, но не отображает никаких меток или окраски для типа среды в области. Это позволяет отображать трафик и другие наложения на карте.

Example of the None map type

Затем вам нужно создать свой GoogleApiClient и GoogleApiClient LocationServices , чтобы узнать текущее местоположение вашего пользователя. Как я упоминал ранее, если у вас есть заданное местоположение, которое вы хотите отобразить, а не фокусироваться на пользователе, вы можете пропустить, используя LocationServices .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
 
    setHasOptionsMenu(true);
 
    mGoogleApiClient = new GoogleApiClient.Builder( getActivity() )
            .addConnectionCallbacks( this )
            .addOnConnectionFailedListener( this )
            .addApi( LocationServices.API )
            .build();
 
    initListeners();
}

Метод initListeners связывает интерфейсы, которые вы объявили в верхней части класса, с объектом GoogleMap связанным с SupportMapFragment . Вот как выглядит реализация:

1
2
3
4
5
6
private void initListeners() {
    getMap().setOnMarkerClickListener(this);
    getMap().setOnMapLongClickListener(this);
    getMap().setOnInfoWindowClickListener( this );
    getMap().setOnMapClickListener(this);
}

Возможно, вы заметили, что GoogleApiClient и слушатели созданы и связаны с onViewCreated а не с типичным onCreate . Это связано с тем, что объект GoogleMap не был инициализирован при onCreate , поэтому нам нужно дождаться полного создания представления, прежде чем пытаться вызвать getMap , чтобы избежать исключения NullPointerException .

Поскольку вы настроите камеру на карте после того, как местоположение пользователя будет найдено с помощью Play Services, мы будем использовать жизненный цикл Play Services для инициализации нашей карты. Вы можете подключить GoogleApiClient в onStart . Когда клиент подключился, вы можете получить последнее найденное пользователем местоположение и использовать его для наведения камеры на карту.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public void onStart() {
    super.onStart();
    mGoogleApiClient.connect();
}
 
@Override
public void onStop() {
    super.onStop();
    if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ) {
        mGoogleApiClient.disconnect();
    }
}
 
@Override
public void onConnected(Bundle bundle) {
    mCurrentLocation = LocationServices
        .FusedLocationApi
        .getLastLocation( mGoogleApiClient );
 
    initCamera( mCurrentLocation );
}

В методе initCamera вы инициализируете камеру и некоторые основные свойства карты. Вы начинаете с создания объекта CameraPosition через CameraPosition.Builder , с целью, установленной для широты и долготы вашего пользователя, и с установленным уровнем масштабирования.

Наклон и направление используются здесь в их значениях по умолчанию, чтобы показать, что они являются доступными вариантами. Если у вас есть объект CameraPosition , вы можете анимировать камеру в этой позиции с помощью CameraUpdateFactory .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private void initCamera( Location location ) {
    CameraPosition position = CameraPosition.builder()
            .target( new LatLng( location.getLatitude(),
                location.getLongitude() ) )
            .zoom( 16f )
            .bearing( 0.0f )
            .tilt( 0.0f )
            .build();
 
    getMap().animateCamera( CameraUpdateFactory
            .newCameraPosition( position ), null );
 
    getMap().setMapType( MAP_TYPES[curMapTypeIndex] );
    getMap().setTrafficEnabled( true );
    getMap().setMyLocationEnabled( true );
    getMap().getUiSettings().setZoomControlsEnabled( true );
}

В конце этого метода вы заметите, что последние четыре строки устанавливают некоторые свойства для карты. Вы устанавливаете тип карты, как описано ранее в этом учебном пособии, и включаете наложения потоков трафика в первых двух строках. setMyLocationEnabled добавляет кнопку в верхний правый угол MapFragment которая автоматически перемещает камеру в местоположение вашего пользователя при нажатии.

Наконец, вызов setZoomControlsEnabled добавляет кнопки « и « в нижнем правом углу, позволяя пользователю изменять уровень масштабирования карты без использования жестов. Есть еще несколько интересных вещей, которые вы можете установить с помощью UiSettings , таких как добавление компаса или отключение жестов, которые вы можете найти в справочной документации по Android .

Одна из наиболее часто используемых функций карты включает в себя указание мест с маркерами. Поскольку для добавления маркера требуются широта и долгота, вам нужно использовать OnMapClickListener чтобы позволить пользователю выбрать точку на карте для размещения объекта Marker .

1
2
3
4
5
6
7
8
9
@Override
public void onMapClick(LatLng latLng) {
 
    MarkerOptions options = new MarkerOptions().position( latLng );
    options.title( getAddressFromLatLng( latLng ) );
 
    options.icon( BitmapDescriptorFactory.defaultMarker() );
    getMap().addMarker( options );
}

Этот метод создает общий красный маркер, где пользователь нажал. Дополнительные параметры, такие как установка маркера как перетаскиваемого, могут быть установлены с MarkerOptions объекта MarkerOptions . Вы можете найти дополнительные атрибуты в официальной справочной документации по Android . Если вы хотите изменить цвет маркера, вы можете вызвать BitmapDescriptorFactory.defaultMarker при добавлении значка в MarkerOptions . Метод defaultMarker принимает значение с плавающей точкой, которое определяет оттенок. Оттенок может быть установлен вручную или как предопределенное статическое значение из BitmapDescriptorFactory . Следует отметить, что addMarker возвращает объект Marker , который можно сохранить для ручного удаления определенных маркеров, если это необходимо.

Generic markers placed on the map

Если вы хотите избежать использования общих цветных штырьков для маркеров местоположения, вы можете установить растровое изображение в качестве значка на объекте MarkerOptions . Чтобы продемонстрировать это, вы переопределяете метод onMapLongClick чтобы он использовал значок приложения из папки ресурсов в качестве Marker когда пользователь долго нажимает на карту.

01
02
03
04
05
06
07
08
09
10
11
@Override
public void onMapLongClick(LatLng latLng) {
    MarkerOptions options = new MarkerOptions().position( latLng );
    options.title( getAddressFromLatLng( latLng ) );
 
    options.icon( BitmapDescriptorFactory.fromBitmap(
            BitmapFactory.decodeResource( getResources(),
                    R.mipmap.ic_launcher ) ) );
 
    getMap().addMarker( options );
}

Вы, наверное, заметили, что метод getAddressFromLatLng используется в обоих методах click. Это вспомогательный метод, который берет LatLng и запускает его через Geocoder для получения адреса улицы. В последних двух примерах мы используем этот метод для отображения адреса улицы при касании маркера.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private String getAddressFromLatLng( LatLng latLng ) {
    Geocoder geocoder = new Geocoder( getActivity() );
 
    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;
}
Пример изображений, используемых в качестве маркеров

У объекта GoogleMap есть набор методов, которые позволяют легко рисовать фигуры и размещать изображения на карте. Чтобы нарисовать простой круг, вам нужно всего лишь создать объект CircleOptions , установить радиус и расположение центра, а также определить цвет и размер обводки / заливки.

Если у вас есть объект CircleOptions , вы можете вызвать addCircle чтобы нарисовать определенный круг поверх карты. Как и при размещении маркеров, объекты, нарисованные на карте, возвращают объект типа нарисованного элемента, поэтому на него можно ссылаться позже, если это необходимо.

01
02
03
04
05
06
07
08
09
10
11
12
private void drawCircle( LatLng location ) {
    CircleOptions options = new CircleOptions();
    options.center( location );
    //Radius in meters
    options.radius( 10 );
    options.fillColor( getResources()
        .getColor( R.color.fill_color ) );
    options.strokeColor( getResources()
        .getColor( R.color.stroke_color ) );
    options.strokeWidth( 10 );
    getMap().addCircle(options);
}
Illustration of a circle drawn programmatically on the map

Чтобы нарисовать другую замкнутую форму, вы можете взять несколько точек LatLng и создать объект PolygonOptions . Как вы можете видеть ниже, PolygonOptions создаются аналогично CircleOptions . Вместо того, чтобы использовать метод center и radius , вы используете add со списком точек. Затем вы можете вызвать addPolygon чтобы нарисовать форму. Для этого примера вы просто рисуете треугольник на карте.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private void drawPolygon( LatLng startingLocation ) {
    LatLng point2 = new LatLng( startingLocation.latitude + .001,
        startingLocation.longitude );
    LatLng point3 = new LatLng( startingLocation.latitude,
        startingLocation.longitude + .001 );
 
    PolygonOptions options = new PolygonOptions();
    options.add( startingLocation, point2, point3 );
 
    options.fillColor( getResources()
        .getColor( R.color.fill_color ) );
    options.strokeColor( getResources()
        .getColor( R.color.stroke_color ) );
    options.strokeWidth( 10 );
 
    getMap().addPolygon( options );
}
Example of a polygon drawn on the map

Последний тип рисования, о котором вы узнаете, — это добавление изображения в качестве наложения на карту. Наложения могут быть полезны, если у вас есть нарисованная карта для области, которую вы хотите отобразить поверх обычного типа карты. Это может быть достигнуто путем создания GroundOverlayOptions с заданным местоположением, шириной и высотой и изображением, которое вы хотите использовать в качестве BitmapDescriptor .

В следующем методе вы рисуете значок запуска для приложения как наложение на плитки карты.

01
02
03
04
05
06
07
08
09
10
private void drawOverlay( LatLng location, int width, int height ) {
    GroundOverlayOptions options = new GroundOverlayOptions();
    options.position( location, width, height );
 
    options.image( BitmapDescriptorFactory
            .fromBitmap( BitmapFactory
                .decodeResource( getResources(),
                    R.mipmap.ic_launcher ) ) );
    getMap().addGroundOverlay( options );
}

Из этого руководства вы узнали, как создать ключ API и включить Google Maps для Android. Вы также узнали о классе MapFragment и некоторых основных функциях, которые можно активировать для карты.

Вы узнали, как размещать маркеры, прислушиваться к взаимодействию с картой и рисовать на карте для отображения дополнительной информации.

В следующем уроке из этой серии вы узнаете, как наложить View на MapFragment , как взаимодействовать с картами уровней внутри помещений и как показать вид улиц своим пользователям.