В марте 2015 года Google выпустила API Google Places для Android в составе сервисов Google Play. Этот API позволяет разработчикам получать доступ к большому количеству информации из Google, чтобы предоставить пользователям опыт, адаптированный к их текущему местоположению, используя имена и информацию о местах, а не набор координат.
В этом руководстве вы узнаете, как представить своим пользователям компонент «Выбор мест», использовать API «Места», чтобы угадать текущее место пользователя, выполнить поиск места по его идентификатору и позволить пользователю ввести текстовое поле, чтобы представить их. с прогнозирующими результатами.
1. Начало настройки
Если у вас еще нет открытого ключа API Android, вам нужно будет создать открытый ключ Google API для приложений Android. Вы можете сделать это, посетив Консоль разработчиков Google . Инструкции по созданию ключа на основе вашего сертификата подписи и имени пакета доступны в документации Google и выходят за рамки этой статьи.
Когда вы создали ключ, найдите API Адресов и установите его включенным. Некоторые вызовы API Адресов ограничены количеством запросов, которые можно отправить за 24 часа. На момент написания, учетная запись без платежного профиля может отправлять до 1000 запросов, а учетная запись с платежным профилем может отправлять 150 000 запросов. Если вам требуется больше, вы можете отправить запрос на увеличение этого лимита, как описано в документации по лимитам использования .
Когда ключ API готов к использованию, настало время начать работу над демонстрационным проектом. Создайте проект в Android Studio и установите минимальную поддерживаемую версию SDK как минимум 9. Это минимальное требование для использования Google Play Services.
Как только Android Studio создаст шаблонный проект Hello World , откройте файл build.gradle и в узле dependencies добавьте требуемую зависимость Play Services 7.0. Это самая последняя на момент написания статьи, но вы можете проверить последнюю версию, проверив документацию Google .
|
1
2
3
4
5
|
dependencies {
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
compile ‘com.android.support:appcompat-v7:22.0.0’
compile ‘com.google.android.gms:play-services:7.0.0’
}
|
Затем откройте AndroidManifest.xml , добавьте необходимые разрешения для проекта и укажите, что приложение требует OpenGL версии 2.
|
1
2
3
4
5
6
|
<uses-permission android:name=»com.google.android.providers.gsf.permission.READ_GSERVICES»/>
<uses-permission android:name=»android.permission.ACCESS_FINE_LOCATION» />
<uses-feature
android:glEsVersion=»0x00020000″
android:required=»true»/>
|
Последнее, что вам нужно сделать в манифесте, — это добавить два <meta-data> чтобы установить версию gms и ключ API для приложения в <application> .
|
1
2
3
4
5
6
|
<meta-data
android:name=»com.google.android.geo.API_KEY»
android:value=»@string/google_api_key» />
<meta-data
android:name=»com.google.android.gms.version»
android:value=»@integer/google_play_services_version» />
|
Когда вы закончите с манифестом, вы готовы начать писать код. Поскольку это компонент Play Services, вам нужно будет инициализировать свой GoogleApiClient и подключать / отключать его в течение жизненного цикла вашей Activity . Мы делаем это в onCreate , onStart и onStop класса Activity .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Override
protected void onCreate( Bundle savedInstanceState ) {
//—Snippet
mGoogleApiClient = new GoogleApiClient
.Builder( this )
.enableAutoManage( this, 0, this )
.addApi( Places.GEO_DATA_API )
.addApi( Places.PLACE_DETECTION_API )
.addConnectionCallbacks( this )
.addOnConnectionFailedListener( this )
.build();
}
@Override
protected void onStart() {
super.onStart();
if( mGoogleApiClient != null )
mGoogleApiClient.connect();
}
@Override
protected void onStop() {
if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
|
2. Использование виджета Place Picker
Виджет «Выбор места» — это компонент пользовательского интерфейса, предоставляемый Play Services, который позволяет пользователю видеть карту своего окружения. Компонент включает в себя список ближайших мест, которые могут быть использованы вашим приложением. Используя этот компонент, вы сможете следовать стандартному дизайну, с которым ваши пользователи будут знать, как взаимодействовать, и при этом сэкономить время на разработку.

Чтобы использовать средство выбора места, вам нужно создать намерение и прослушать результат действия, чтобы получить место, выбранное пользователем. Следующий метод показывает, как вы будете запускать это действие.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
private void displayPlacePicker() {
if( mGoogleApiClient == null || !mGoogleApiClient.isConnected() )
return;
PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
try {
startActivityForResult( builder.build( getApplicationContext() ), PLACE_PICKER_REQUEST );
} catch ( GooglePlayServicesRepairableException e ) {
Log.d( «PlacesAPI Demo», «GooglePlayServicesRepairableException thrown» );
} catch ( GooglePlayServicesNotAvailableException e ) {
Log.d( «PlacesAPI Demo», «GooglePlayServicesNotAvailableException thrown» );
}
}
|
PlacePicker.IntentBuilder используется для создания Intent который будет использоваться для запуска Place Picker. У него также есть метод setLatLngBounds , который позволяет вам разместить географическую границу от юго-западного угла до северо-восточного угла для управления областью поиска.
Intent может быть построено с использованием метода build из PlacePicker.IntentBuilder и запущено с использованием метода startActivityForResult из вашей Activity . Следует отметить, что при использовании метода build есть возможность генерировать исключение GooglePlayServicesRepairableException или GooglePlayServicesNotAvailableException , поэтому их следует проверять на использование стандартного блока try / catch и обрабатывать их корректно, если они происходят.
Если пользователь выбирает местоположение из списка выбора мест, этот объект Place упаковывается в Intent и отправляется обратно в вызывающую Activity . Используя метод PlacePicker.getPlace , вы можете извлечь данные Place из возвращенного Intent .
|
1
2
3
4
5
|
protected void onActivityResult( int requestCode, int resultCode, Intent data ) {
if( requestCode == PLACE_PICKER_REQUEST && resultCode == RESULT_OK ) {
displayPlace( PlacePicker.getPlace( data, this ) );
}
}
|
После извлечения объекта Place его можно рассматривать как объект модели для отображения или использования в приложении.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
private void displayPlace( Place place ) {
if( place == null )
return;
String content = «»;
if( !TextUtils.isEmpty( place.getName() ) ) {
content += «Name: » + place.getName() + «\n»;
}
if( !TextUtils.isEmpty( place.getAddress() ) ) {
content += «Address: » + place.getAddress() + «\n»;
}
if( !TextUtils.isEmpty( place.getPhoneNumber() ) ) {
content += «Phone: » + place.getPhoneNumber();
}
mTextView.setText( content );
}
|

3. Нахождение текущего места пользователя
Еще одна интересная особенность API Places заключается в том, что вы можете использовать его, чтобы угадать, находится ли пользователь в списке. API также обеспечит вероятность того, что вы сможете принимать обоснованные решения о том, как ваше приложение должно взаимодействовать с пользователем. Следует отметить, что это одна из функций API, которая требует запроса на выделенные вам средства.
Чтобы определить место пользователя, вам потребуется использовать метод Places.PlacesDetectionApi.getCurrentPlace чтобы создать PendingIntent который возвращается с объектом PlaceLikelihoodBuffer . Используя ResultCallBack , вы можете взять первое и, скорее всего, место из буфера и использовать его в своем приложении.
Если вашему приложению нужна дополнительная информация, вы можете извлечь другие элементы PlaceLikelihood из буфера, просматривая его. Вероятность того, что это место находится там, где в данный момент находится пользователь, передается обратно в каждый объект PlaceLikelihood в виде значения с плавающей запятой от 0,0 до 1,0 , 1,0 почти гарантированное совпадение. Не забудьте вызвать release для PlaceLikelihoodBuffer чтобы избежать утечек памяти.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
private void guessCurrentPlace() {
PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi.getCurrentPlace( mGoogleApiClient, null );
result.setResultCallback( new ResultCallback<PlaceLikelihoodBuffer>() {
@Override
public void onResult( PlaceLikelihoodBuffer likelyPlaces ) {
PlaceLikelihood placeLikelihood = likelyPlaces.get( 0 );
String content = «»;
if( placeLikelihood != null && placeLikelihood.getPlace() != null && !TextUtils.isEmpty( placeLikelihood.getPlace().getName() ) )
content = «Most likely place: » + placeLikelihood.getPlace().getName() + «\n»;
if( placeLikelihood != null )
content += «Percent change of being there: » + (int) ( placeLikelihood.getLikelihood() * 100 ) + «%»;
mTextView.setText( content );
likelyPlaces.release();
}
});
}
|

4. Прогнозирование мест
Следующая и самая сложная тема, которую мы рассмотрим в этом руководстве, — это прогнозирование и отображение мест для пользователя при вводе поискового запроса. Опять же, этот вызов API также учитывает ограничения использования API. Однако, это неоценимо для того, чтобы сделать ваше приложение более полезным.

В этой части руководства вы будете использовать AutoCompleteTextView и пользовательский адаптер в приложении, чтобы ввести название места для прогнозов. Практически вся работа выполняется в адаптере. Однако нам потребуется передать ссылку на GoogleApiClient адаптеру для доступа к API.
Это можно сделать с помощью стандартного обратного вызова onConnected , и мы можем удалить экземпляр клиента в onStop где mAdapter — это экземпляр нашего пользовательского класса Adapter , AutoCompleteAdapter .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Override
protected void onStop() {
if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ) {
mAdapter.setGoogleApiClient( null );
mGoogleApiClient.disconnect();
}
super.onStop();
}
@Override
public void onConnected( Bundle bundle ) {
if( mAdapter != null )
mAdapter.setGoogleApiClient( mGoogleApiClient );
}
|
Чтобы инициировать вызов API всякий раз, когда пользователь вводит новую букву в AutoCompleteTextView , необходимо переопределить метод getFilter объекта ArrayAdapter . Этот метод запускается всякий раз, когда пользователь изменяет содержимое представления, связанного с адаптером. Позволяет изменить содержимое адаптера AutoCompleteTextView . В следующем примере constraints — это содержимое представления.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
if( mGoogleApiClient == null || !mGoogleApiClient.isConnected() ) {
Toast.makeText( getContext(), «Not connected», Toast.LENGTH_SHORT ).show();
return null;
}
clear();
displayPredictiveResults( constraint.toString() );
return null;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
};
}
|
Метод displayPredictiveResults — это место, где происходит фактическое взаимодействие с API. Есть несколько различных объектов, которые можно сделать, чтобы настроить ваши прогнозы.
Первый — это объект LatLngBounds который создает квадратную границу от юго-западной точки до северо-восточной точки для локализации запроса. Если вместо инициализированного объекта LatLngBounds передается значение null , то географические ограничения на запрос не накладываются.
|
1
|
LatLngBounds bounds = new LatLngBounds( new LatLng( 39.906374, -105.122337 ), new LatLng( 39.949552, -105.068779 ) );
|
Второй объект, который можно создать для настройки запроса, — это фильтр для запроса API. Фильтр для вызова Places AutoCompletePredictions представляет собой список объектов Integer представляющих фильтры различных типов. На данный момент только один тип фильтра может быть применен к запросу. Приемлемые значения можно найти в документации . Если список Integer пуст или пропущен ноль, возвращаются все типы результатов.
Когда вы будете готовы сделать запрос, вы можете использовать метод Places.GeoDataApi.getAutocompletePredictions чтобы вернуть PendingIntent , который может быть связан с ResultCallback для отображения возвращаемой информации.
Важно отметить, что пользовательский объект, представляющий объекты AutoCompletePrediction из буфера, используется для хранения данных в ArrayAdapter . В противном случае исключение IllegalArgumentsException будет выдано сразу после освобождения буфера, что крайне важно для предотвращения утечки памяти.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
private void displayPredictiveResults( String query )
{
//Southwest corner to Northeast corner.
LatLngBounds bounds = new LatLngBounds( new LatLng( 39.906374, -105.122337 ), new LatLng( 39.949552, -105.068779 ) );
//Filter: https://developers.google.com/places/supported_types#table3
List<Integer> filterTypes = new ArrayList<Integer>();
filterTypes.add( Place.TYPE_ESTABLISHMENT );
Places.GeoDataApi.getAutocompletePredictions( mGoogleApiClient, query, bounds, AutocompleteFilter.create( filterTypes ) )
.setResultCallback (
new ResultCallback<AutocompletePredictionBuffer>() {
@Override
public void onResult( AutocompletePredictionBuffer buffer ) {
if( buffer == null )
return;
if( buffer.getStatus().isSuccess() ) {
for( AutocompletePrediction prediction : buffer ) {
//Add as a new item to avoid IllegalArgumentsException when buffer is released
add( new AutoCompletePlace( prediction.getPlaceId(), prediction.getDescription() ) );
}
}
//Prevent memory leak by releasing buffer
buffer.release();
}
}, 60, TimeUnit.SECONDS );
}
|
Содержимое в AutoCompleteAdapter отображается с использованием макета android.R.layout.simple_list_item_1 и стандартного шаблона getView в getView .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
ViewHolder holder;
if( convertView == null ) {
holder = new ViewHolder();
convertView = LayoutInflater.from( getContext() ).inflate( android.R.layout.simple_list_item_1, parent, false );
holder.text = (TextView) convertView.findViewById( android.R.id.text1 );
convertView.setTag( holder );
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText( getItem( position ).getDescription() );
return convertView;
}
|
Когда элемент выбирается из этого списка, идентификатор выбранного Place передается в onItemClickedListener и ищется для отображения.
5. Поиск места по идентификатору
Последняя часть этого руководства посвящена поиску объекта Place на основе его идентификатора. Это работает аналогично другим вызовам API, создав PendingIntent и взаимодействуя с возвращенным буфером для получения места. Как и другие объекты буфера, с которыми вы работали, PlaceBuffer должен вызывать release чтобы избежать любых утечек памяти.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private void findPlaceById( String id ) {
if( TextUtils.isEmpty( id ) || mGoogleApiClient == null || !mGoogleApiClient.isConnected() )
return;
Places.GeoDataApi.getPlaceById( mGoogleApiClient, id ) .setResultCallback( new ResultCallback<PlaceBuffer>() {
@Override
public void onResult(PlaceBuffer places) {
if( places.getStatus().isSuccess() ) {
Place place = places.get( 0 );
displayPlace( place );
mPredictTextView.setText( «» );
mAdapter.clear();
}
//Release the PlaceBuffer to prevent a memory leak
places.release();
}
} );
}
|
Вывод
API Places — это мощный инструмент для информирования ваших приложений о местоположении пользователя и предоставления им контекстной информации. В этом руководстве вы узнали, как использовать компонент «Выбор места», угадать место пользователя, представить ему прогнозирующие результаты при поиске и найти место на основе заданного идентификатора. В дополнение к темам, рассмотренным здесь, также можно представить новые места в Google, чтобы помочь расширить информацию, к которой имеет доступ API.