2015年3月,Google发布了Android版Places API,作为Google Play服务的一部分。 该API使开发人员可以从Google访问大量信息,从而通过使用地点的名称和信息(而不是一组坐标)为用户提供针对其当前位置的量身定制的体验。
在本教程中,您将学习如何向用户展示Place Picker组件,如何使用Places API猜测用户的当前位置,通过其ID搜索位置,以及允许用户在文本字段中键入内容以显示它们具有预测结果。
1.设置
如果您还没有Android公共API密钥,则需要为Android应用程序创建公共Google API密钥。 您可以通过访问Google的开发人员控制台进行此操作 。 Google的文档中提供了基于签名证书和程序包名称创建密钥的说明 ,这些说明不在本文的讨论范围之内。
创建密钥后,搜索Places API并将其设置为enabled。 在每24小时内可以发送多少请求的情况下,对Places API的某些调用受到限制。 在撰写本文时,不具有计费配置文件的帐户最多可以发送1,000个请求,而具有计费配置文件的帐户可以发送150,000个请求。 如果您需要更多,则可以提交请求以按照“ 使用限制”文档中的说明增加此限制。
准备使用API密钥后,就可以开始进行演示项目了。 在Android Studio中创建一个项目,并将支持的最低SDK版本设置为至少9。这是使用Google Play服务的最低要求。
Android Studio创建Hello World模板项目后,打开build.gradle文件,并在“ dependencies
节点下,添加所需的Play Services 7.0依赖项。 这是撰写本文时的最新版本,但是您可以通过查看Google文档来验证最新版本。
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。
您在清单中要做的最后一件事是添加两个
标签,以在
标签内为该
设置gms版本和API密钥。
完成清单后,就可以开始编写代码了。 由于这是Play Services的组件,因此您需要初始化GoogleApiClient
并在Activity
的生命周期内进行连接/断开连接。 我们在Activity
类的onCreate
, onStart
和onStop
方法中执行此操作。
@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.使用“地点选择器”小部件
地点选择器小部件是Play服务提供的用户界面组件,可让用户查看其周围区域的地图。 该组件包括您的应用可以使用的附近地点的列表。 通过使用此组件,您可以遵循一个标准设计,您的用户将知道如何与之交互,同时可以节省开发时间。
要使用位置选择器,您需要创建一个意图并监听Activity
结果以检索用户选择的位置。 以下方法显示了如何启动此Activity
。
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
。 它还有一个可用的方法setLatLngBounds
,它使您可以放置从西南角到东北角的地理边界,以控制搜索区域。
可以使用PlacePicker.IntentBuilder
的build
方法来build
PlacePicker.IntentBuilder
并使用Activity
的startActivityForResult
方法来启动Intent
。 应当注意,使用build
方法确实有可能GooglePlayServicesRepairableException
或GooglePlayServicesNotAvailableException
异常,因此应使用标准try / catch块检查这些异常,并在发生异常时对其进行适当处理。
如果用户从位置选择器列表中选择一个位置,则该Place
对象将被打包到一个Intent
并发送回调用方Activity
。 使用PlacePicker.getPlace
方法,可以从返回的Intent
提取Place
数据。
protected void onActivityResult( int requestCode, int resultCode, Intent data ) {
if( requestCode == PLACE_PICKER_REQUEST && resultCode == RESULT_OK ) {
displayPlace( PlacePicker.getPlace( data, this ) );
}
}
提取Place
对象后,可以将其视为模型对象以在您的应用程序中显示或使用。
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.查找用户的当前位置
Places API的另一个有趣功能是,您可以使用它来猜测用户当前是否在列出的位置。 API也将提供一种可能性,以便您可以就应用程序应如何与用户交互做出明智的决定。 应该注意的是,这是API的功能之一,需要针对您分配的使用情况提出请求。
要检测用户的位置,您将需要使用Places.PlacesDetectionApi.getCurrentPlace
方法创建一个PendingIntent
,该对象将随PlaceLikelihoodBuffer
对象一起返回。 使用ResultCallBack
,您可以从缓冲区中取出第一个(也是最有可能的)位置,并在应用程序中使用它。
如果您的应用需要更多信息,则可以通过循环从缓冲区中提取其他PlaceLikelihood
项目。 在每个PlaceLikelihood
对象中,该位置是用户当前所在位置的可能性作为从0.0到1.0的浮点值传回,其中1.0几乎可以保证匹配。 不要忘记在PlaceLikelihoodBuffer
上调用release
,以避免任何内存泄漏。
private void guessCurrentPlace() {
PendingResult result = Places.PlaceDetectionApi.getCurrentPlace( mGoogleApiClient, null );
result.setResultCallback( new ResultCallback() {
@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
的引用GoogleApiClient
给适配器,以使其能够访问API。
这可以在标准的GoogleApiClient
回调onConnected
,我们可以在onStop
中删除客户端的实例,其中mAdapter
是我们的自定义Adapter
类AutoCompleteAdapter
的实例。
@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 );
}
要在用户在AutoCompleteTextView
输入新字母时触发API调用,您需要重写ArrayAdapter
的getFilter
方法。 每当用户更改与适配器关联的视图的内容时,都会触发此方法。 它允许您更改AutoCompleteTextView
适配器的内容。 在以下示例中, constraints
是视图的内容。
@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
对象,该对象创建一个从西南点到东北点的正方形边界以定位查询。 如果传递了null
而不是初始化的LatLngBounds
对象,则不会对查询施加任何地理限制。
LatLngBounds bounds = new LatLngBounds( new LatLng( 39.906374, -105.122337 ), new LatLng( 39.949552, -105.068779 ) );
您可以创建以自定义查询的第二个对象是API请求的过滤器。 Places
AutoCompletePredictions
调用的过滤器是代表不同类型过滤器的Integer
对象的列表。 此时,只能将一种过滤器类型应用于查询。 可接受的值可以在文档中找到。 如果Integer
列表为空或传递了null
,则返回所有结果类型。
准备好发出请求后,可以使用Places.GeoDataApi.getAutocompletePredictions
方法返回PendingIntent
,可以将其与ResultCallback
关联以显示返回的信息。
重要的是要注意,代表来自缓冲区的AutoCompletePrediction
对象的自定义对象用于将数据存储在ArrayAdapter
。 否则,一旦释放缓冲区,就会抛出IllegalArgumentsException
异常,这对于避免内存泄漏至关重要。
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 filterTypes = new ArrayList();
filterTypes.add( Place.TYPE_ESTABLISHMENT );
Places.GeoDataApi.getAutocompletePredictions( mGoogleApiClient, query, bounds, AutocompleteFilter.create( filterTypes ) )
.setResultCallback (
new ResultCallback() {
@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
标准ViewHolder模式显示。
@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;
}
当从该列表中单击一个项目时,所选位置的ID Place
传递到onItemClickedListener
并搜索以显示。
5.通过ID搜索地点
本教程的最后一部分将介绍根据其ID查找Place
对象。 通过创建PendingIntent
并与返回的缓冲区进行交互以检索位置,此工作与其他API调用类似。 像您使用过的其他缓冲区对象一样, PlaceBuffer
必须调用release
以避免任何内存泄漏。
private void findPlaceById( String id ) {
if( TextUtils.isEmpty( id ) || mGoogleApiClient == null || !mGoogleApiClient.isConnected() )
return;
Places.GeoDataApi.getPlaceById( mGoogleApiClient, id ) .setResultCallback( new ResultCallback() {
@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();
}
} );
}
结论
Places API是一个功能强大的工具,可让您的应用知道用户的位置,从而为他们提供上下文信息。 在本教程中,您学习了如何使用“位置选择器”组件,猜测用户的位置,在搜索时向他们显示预测结果以及根据给定的ID查找位置。 除了此处介绍的主题之外,还可以向Google提交新的地点,以帮助扩展API可以访问的信息。
翻译自: https://code.tutsplus.com/articles/google-play-services-using-the-places-api--cms-23715