[Перевод] Исследование Google Play Services: Place Picker & Autocomplete

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

image
С выходом Play Services 7.0, компания Google сделала доступными для реализации в наших приложениях некоторые весьма полезные функции — две из которых включают виджет выбора места (Place Picker UI) и компонент автозаполнения (Autocomplete). Они оба могут помочь значительно улучшить существующие решения в вашем приложении, или даже помочь вам в реализации подобных функций в ваших будущих приложениях. В любом случае, обеспечение родных и чистых решений, в таких часто используемых функциях, очень помогут повысить качество опыта использования, которое предлагает ваше приложение.

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

Вместе с этой статьей, я создал приложение, которое позволило провести эксперимент с этими функциями — вы можете найти код на github по ссылке.

Поскольку вы все еще читаете это здесь, я полагаю, что вы хотите попробовать эти компоненты лично. Итак, до того как вы наденете свою java-шляпу и будете писать код, вам необходимо начать с добавления зависимости play-services-location в ваш build.gradle файл.

compile 'com.google.android.gms:play-services-location:8.1.0'

Примечание: В случае если вам не знаком используемый формат, вам всего лишь необходимо объявить зависимость тех play service которые вы будете использовать. Для более подробной информации рекомендую ознакомиться с данным руководством.

Помимо этого, вам так же потребуется зарегистрировать ключ API в случае использования сервисов местонахождения, что можно сделать в следующем руководстве. Как только вы завершите с регистрацией, вам потребуется добавить ключ API и версию play services в ваш файл манифеста, как показано ниже:

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

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


Виджет выбора места (place picker) — это компонент пользовательского интерфейса, который предоставляет возможность взаимодействовать с интерактивной картой на основе текущего географического расположения устройства. Эта карта может быть использована для выбора расположенных поблизости мест одним из двух способов:

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


Выбор на карте
image


Выбор из списка
image


Очевидный вопрос, — «Зачем нам использовать это?»

  • Время разработки значительно сокращается — Компоненты идут готовыми прямо-из-коробки, это значит, что все функции (карта, места, жизненный цикл, сетевые запросы и т.д.) полностью управляются виджетом. По сравнению с тем как это может быть сделано вручную, время сокращается просто колоссально.
  • Последовательный опыт использования — Использование данного компонента не только Google, а и сторонними разработчиками (как вы) делает опыт пользователей намного легче, поскольку данный компонент им уже знаком, что исключает лишнее время на изучение работы с ним.

Эксплуатационный процесс


Итак, теперь мы знаем с чем мы имеем дело, все выглядит так как будто мы готовы начать. Есть несколько важных операций, всегда присутствующих в жизненном цикле Place Picker'а, с которыми мы сейчас ознакомимся.

  • Открытие Place Picker с помощью метода startActivityForResult(), тот момент когда мы отлавливаем выбранное место, возвращаемое нашей activity.
  • Следующий шаг заключается в решении выбора изначального местоположения на карте. Если место предоставлено заранее — мы используем его, в противном случае будет выбрано текущее положение устройства.
  • Выберет ли пользователь место на карте или из списка мест поблизости, или же закроет place picker не сделав выбор.
  • Как только выбор сделан, place picker завершается и результат (выбранное место) возвращается в нашу activity.


Эксплуатационный процесс Place Picker
image

Реализация


Для начала нам необходимо создать новый экземпляр builder при помощи IntentBuilder из класса Place Picker.

PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();


Используя этот builder, мы можем задать начальные границы широты-доготы, которые будут использоваться экземпляром place picker'а. Как упоминалось ранее, если начальное значение не установлено тогда по-умолчанию наш place picker будет использовать текущее положение устройства как LatLngBounds.

builder.setLatLngBounds(new LatLngBounds(...));


Как только это сделано, мы запускаем picker используя метод startActivityForResult(), передавая нашему builder'у экземпляр place picker'а и код запроса как параметры. Как упоминалось ранее, выбранное место (Place) из Place Picker'а возвращается в нашу activity в onActivityResult, где мы используем код запроса PLACE_PICKER_REQUEST для фильтрования результатов.

startActivityForResult(builder.build(this), PLACE_PICKER_REQUEST);


При вызове метода startActivityForResult, будет открыт виджет place picker'а в новой activity. Здесь пользователь будет делать выбор используя текущее положение или выбор места на основании рекомендаций. Как только place picker будет закрыт, наш onActivityResult будет вызван:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == PLACE_PICKER_REQUEST) {
        if (resultCode == RESULT_OK) {
            Place selectedPlace = PlacePicker.getPlace(data, this);
            // Do something with the place
        }
    }
}


Как показано выше, мы извлекаем наше выбранное место (Place), когда activity возвращает его нам. Делаем мы это следующим образом:

Place selectedPlace = PlacePicker.getPlace(data, this);


В этом месте, если пользователь использует кнопку back для выхода из виджета picker'a без выбора места тогда результат не будет возвращен.

Примечание: Вам будет необходимо наделить ваше приложение следующими правами — ACCESS_FINE_LOCATION для того чтобы использовать Place Picker виджет.

Темы оформления

Чтобы оставаться в одной теме оформления с вашим приложением, по-умолчанию Place Picker виджет будет использовать цвета вашего приложения:

  • colorPrimary — Будет использован как цвет текста и стрелки назад в action bar приложения
  • colorPrimaryDark — Будет использован как цвет заднего плана для панели action bar


Будьте внимательны и перепроверьте установленные цветовые атрибуты темы.

Сигналы Beacon


С выходом play services 7.8, виджет place picker так же может использовать сигналы от расположенных рядом маяков (beacons) для определения текущего местоположения устройства. Работает это благодаря PlaceId обнаруженного маяка (beacon) вместе с остальными обнаруженными сигналами, что доступны устройству (сеть, wifi и т.д.). На основании собранных геоданых будет построен список рекомендаций для отображения пользователю как ‘Места поблизости’ в компоненте Place Picker.
Place Autocomplete — новый компонент который может быть использован для возвращения списка рекомендуемых мест поблизости на основании поискового запроса, где результат смещен от текущего местоположения устройства. Ранее для достижение данного результата было необходимо вызывать API мест на прямую, но теперь то же самое может быть выполнено посредством обертки GoogleApiClient автоматически.

Для начала мы обязаны создать новый экземпляр класса GoogleApiClient. Для этого мы будем использовать GoogleApiClient builder, где нам так же необходимо указать, что мы желаем использовать Places API:

mGoogleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this, 0, this)
        .addApi(Places.GEO_DATA_API)
        .build();


Как результат поискового запроса так и текущее положение будут делать запрос к Auto Complete API используя этот экземпляр.

Так как в данный момент RxJava является частью моего рабочего процесса, я решил реализовать это с помощью observables. Я предлагаю вам посмотреть завершенный класс чтобы разобраться в коде:

  • Мы начинаем использование Places API для извлечения списка автозаполнения предсказаний основанных на предоставленных поисковых запросах и границах широты-долготы.
PendingResult<AutocompletePredictionBuffer> results =
        Places.GeoDataApi.getAutocompletePredictions(
                             mGoogleApiClient, query, bounds, null);


Примечание: Обеспечение экземпляра LatLngBounds не ограничивает результаты этими границами. Результаты будут смещены в предоставленных пределах, это означает, что местам поблизости будет присвоен больший приоритет.

Метод PendingResult используется для получения данных от Google Play Services посредством обращения к API. Мы делаем это с помощью метода await() чтобы дать PendingResult некоторый промежуток времени для возвращения нам данных.

AutocompletePredictionBuffer autocompletePredictions = 
                                results.await(60, TimeUnit.SECONDS);


Как только мы дождались результата, мы можем проверить статус нашего DataBuffer для проверки успешности запроса:

final Status status = autocompletePredictions.getStatus();
if (!status.isSuccess()) {
    autocompletePredictions.release();
    subscriber.onError(null);
} else {
    for (AutocompletePrediction place : autocompletePredictions) {
        subscriber.onNext(
                new AutocompletePlace(
                        place.getPlaceId(),
                        place.getDescription()
                ));
    }
    autocompletePredictions.release();
    subscriber.onCompleted();
}


Если запрос завершен успешно, мы возвращаем набор объектов AutocompletePrediction, которые в теории являются минимальным объектом Place. Так как этого будет не достаточно для отображения полезной информации пользователю, мы используем placeId для получения полного объекта Place с API. Это осуществляется с помощью связывания запросов в наш observable поток, где мы получаем place таким образом:

final PendingResult<PlaceBuffer> placeResult = 
               Places.GeoDataApi.getPlaceById(mGoogleApiClient, id);
placeResult.setResultCallback(new ResultCallback<PlaceBuffer>() {
    @Override
    public void onResult(PlaceBuffer places) {
        if (!places.getStatus().isSuccess()) {
            places.release();
            subscriber.onError(new Throwable(...));
        } else {
            subscriber.onNext(
                          PointOfInterest.fromPlace(places.get(0)));                
            places.close();                            
            subscriber.onCompleted();
        }
    }
});


Примечание: Не забудьте про вызов release для экземпляра PlaceBuffer.

Эти результаты Place далее будут возвращены activity и отображены в результате поиска recyclerview. Дальше работает подобно примеру Place Picker, когда предсказания autocomplete выбраны из списка происходит возврат к MainActivity и отображение в списке сохраненных Places.

Выводы


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

Хотя стандартный MapView все еще используется во множестве разных ситуаций (в основном для более тонкой настройки и повышенной функциональности, Place Picker будет полезен в случае, когда вы не нуждаетесь в каких-либо расширенных возможностях. То же самое относиться и к компоненту автозаполнения (Autocomplete), опять же, вы можете самостоятельно реализовать вызов API для получения рекомендаций на основе геоданных, но используя GoogleApiClient это сделать на много проще. Говоря это, я считаю, что компонентов предоставленных в Play Services будет достаточно для решения большинства повседневных задач.

© Habrahabr.ru