В марте 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.