Google Places API 网络服务示例

Google Places API可以用来查找附近的地点。在本教程中,我们将开发一个应用程序,该应用程序显示我们选择的附近地点,以及离我们当前位置的大致距离和时间。我们将在应用程序中使用带有距离矩阵API的Google Places API Web服务。

Google Places API

Google Places API Web Service允许我们根据几个参数来查询位置,例如位置类型、某个位置现在是否开放等。附近的搜索请求是以下形式的HTTP URL:

1https://maps.googleapis.com/maps/api/place/nearbysearch/output?parameters

json为推荐的output,另一个为xml,必选参数为:

1.Key (API密钥) 2.位置 3.rankby=距离 或** 半径** :一个用了,另一个就不能用了。

注意rankby=distance需要指定以下参数之一:

1.名称 :取值范围包括麦当劳、肯德基等。 2.类型 :取值范围为餐厅、咖啡馆等。 3.关键词

可选参数可以是OpenNowPagetoken等,详见this页面。

Google距离矩阵接口

距离矩阵接口用于计算两个或多个点之间的距离和时间。距离矩阵API URL的格式为:

1https://maps.googleapis.com/maps/api/distancematrix/outputFormat?parameters

必选参数为Originsestinationskey。原点-它包含计算旅行距离和时间的起点。我们可以传递由管道(|)分隔的多组坐标。我们还可以传递地址/地点ID而不是坐标,服务会自动将它们转换为经纬度坐标,以计算距离和持续时间。示例代码:

1https://maps.googleapis.com/maps/api/distancematrix/json?origins=Washington,DC&destinations=New+York+City,NY&key=YOUR_API_KEY

可选参数包括:

1.模式 :取值范围为drivingbiclingwalkingtransit 2.避免 :引入通行费室内等路由限制

有关更多详细信息,请访问this页面。

开启API密钥

进入https://console.developers.google.com/,开通以下接口:

1.Google地图距离矩阵接口 2.Google Places API Web服务 3.Android版Google Places API

转到).中显示这些地点我们将根据将在EditText中输入并由空格分隔的类型和名称关键字来搜索地点。例如:餐厅多米诺骨牌 或** 咖啡馆素食者**

Google Places API示例项目结构

Google Places API Web服务示例,Google距离矩阵API,Google Places api该项目由单个活动组成。用于RecclerView的适配器类。一个Model类,它保存每一个RecclerView行的数据。两个POJO类,用于将JSON响应从Google Places API和Distance Matrix API转换为Gson。用于使用Retrofit和终端的APIClient和API接口。

Google Places API示例代码

Build.gradle文件中添加以下依赖项

 1compile 'com.google.android.gms:play-services-location:10.2.1'
 2    compile 'com.google.android.gms:play-services-places:10.2.1'
 3    compile 'com.google.code.gson:gson:2.7'
 4    compile 'com.squareup.retrofit2:retrofit:2.1.0'
 5    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
 6    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
 7    compile 'com.squareup.okhttp3:okhttps:3.4.1'
 8    compile 'io.nlopez.smartlocation:library:3.3.1'
 9    compile 'com.android.support:cardview-v7:25.3.0'
10    compile 'com.android.support:recyclerview-v7:25.3.0'

[library](https://github.com/mrmans0n/smart-location-lib)‘io.nlopez.SmartLocation:库:3.3.1’是一个位置跟踪第三方库,它减少了样板代码。APIClient.java 代码如下:

 1package com.journaldev.nearbyplaces;
 2
 3import java.util.concurrent.TimeUnit;
 4import okhttp3.OkHttpClient;
 5import okhttp3.logging.HttpLoggingInterceptor;
 6import retrofit2.Retrofit;
 7import retrofit2.converter.gson.GsonConverterFactory;
 8
 9public class APIClient {
10
11    private static Retrofit retrofit = null;
12
13    public static final String GOOGLE_PLACE_API_KEY = "ADD_YOUR_API_KEY_HERE";
14
15    public static String base_url = "https://maps.googleapis.com/maps/api/";
16
17    public static Retrofit getClient() {
18
19        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
20        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
21        OkHttpClient client = new OkHttpClient.Builder().readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).addInterceptor(interceptor).build();
22
23        retrofit = null;
24
25        retrofit = new Retrofit.Builder()
26                .baseUrl(base_url)
27                .addConverterFactory(GsonConverterFactory.create())
28                .client(client)
29                .build();
30
31        return retrofit;
32    }
33
34}

ApiInterface.java 代码如下所示

 1package com.journaldev.nearbyplaces;
 2
 3import retrofit2.Call;
 4import retrofit2.http.GET;
 5import retrofit2.http.Query;
 6
 7public interface ApiInterface {
 8
 9    @GET("place/nearbysearch/json?")
10    Call<PlacesPOJO.Root> doPlaces(@Query(value = "type", encoded = true) String type, @Query(value = "location", encoded = true) String location, @Query(value = "name", encoded = true) String name, @Query(value = "opennow", encoded = true) boolean opennow, @Query(value = "rankby", encoded = true) String rankby, @Query(value = "key", encoded = true) String key);
11
12    @GET("distancematrix/json") // origins/destinations:  LatLng as string
13    Call<ResultDistanceMatrix> getDistance(@Query("key") String key, @Query("origins") String origins, @Query("destinations") String destinations);
14}

PlacesPOJO.java 是保存Places API响应的文件。其代码如下所示

 1package com.journaldev.nearbyplaces;
 2
 3import com.google.gson.annotations.SerializedName;
 4import java.io.Serializable;
 5import java.util.ArrayList;
 6import java.util.List;
 7
 8public class PlacesPOJO {
 9
10    public class Root implements Serializable {
11
12        @SerializedName("results")
13        public List<CustomA> customA = new ArrayList<>();
14        @SerializedName("status")
15        public String status;
16    }
17
18    public class CustomA implements Serializable {
19
20        @SerializedName("geometry")
21        public Geometry geometry;
22        @SerializedName("vicinity")
23        public String vicinity;
24        @SerializedName("name")
25        public String name;
26
27    }
28
29    public class Geometry implements Serializable{
30
31        @SerializedName("location")
32        public LocationA locationA;
33
34    }
35
36    public class LocationA implements Serializable {
37
38        @SerializedName("lat")
39        public String lat;
40        @SerializedName("lng")
41        public String lng;
42
43    }
44
45}

ResultDistanceMatrix.java 类保存距离矩阵接口的响应。其代码如下:

 1package com.journaldev.nearbyplaces;
 2
 3import com.google.gson.annotations.SerializedName;
 4
 5import java.util.List;
 6
 7public class ResultDistanceMatrix {
 8    @SerializedName("status")
 9    public String status;
10
11    @SerializedName("rows")
12    public List<InfoDistanceMatrix> rows;
13
14    public class InfoDistanceMatrix {
15        @SerializedName("elements")
16        public List elements;
17
18        public class DistanceElement {
19            @SerializedName("status")
20            public String status;
21            @SerializedName("duration")
22            public ValueItem duration;
23            @SerializedName("distance")
24            public ValueItem distance;
25
26        }
27
28        public class ValueItem {
29            @SerializedName("value")
30            public long value;
31            @SerializedName("text")
32            public String text;
33
34        }
35    }
36}

active_main.xml 文件如下所示

 1<?xml version="1.0" encoding="utf-8"?>
 2<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
 3    xmlns:tools="https://schemas.android.com/tools"
 4    android:layout_width="match_parent"
 5    android:layout_height="match_parent"
 6    android:background="#212121"
 7    tools:context="com.journaldev.nearbyplaces.MainActivity">
 8
 9    <EditText
10        android:id="@+id/editText"
11        android:layout_width="match_parent"
12        android:textColor="@android:color/white"
13        android:textColorHint="@android:color/white"
14        android:text="restaurant mcdonalds"
15        android:hint="type name"
16        android:layout_height="wrap_content"
17        android:layout_alignParentTop="true"
18        android:layout_toLeftOf="@+id/button"
19        android:layout_toStartOf="@+id/button" />
20
21    <Button
22        android:id="@+id/button"
23        android:layout_width="wrap_content"
24        android:layout_height="wrap_content"
25        android:layout_alignParentEnd="true"
26        android:layout_alignParentRight="true"
27        android:text="Search" />
28
29    <android.support.v7.widget.RecyclerView
30        android:id="@+id/recyclerView"
31        android:layout_width="match_parent"
32        android:layout_height="match_parent"
33        android:layout_below="@+id/editText"
34        android:scrollbars="vertical" />
35
36</RelativeLayout>

下面给出了MainActivity.java类代码。

  1package com.journaldev.nearbyplaces;
  2
  3import android.annotation.TargetApi;
  4import android.content.DialogInterface;
  5import android.content.pm.PackageManager;
  6import android.location.Location;
  7import android.os.Build;
  8import android.support.v7.app.AlertDialog;
  9import android.support.v7.app.AppCompatActivity;
 10import android.os.Bundle;
 11import android.support.v7.widget.LinearLayoutManager;
 12import android.support.v7.widget.RecyclerView;
 13import android.view.View;
 14import android.widget.Button;
 15import android.widget.EditText;
 16import android.widget.Toast;
 17import com.google.android.gms.maps.model.LatLng;
 18import java.util.ArrayList;
 19import java.util.List;
 20
 21import io.nlopez.smartlocation.OnLocationUpdatedListener;
 22import io.nlopez.smartlocation.SmartLocation;
 23import retrofit2.Call;
 24import retrofit2.Callback;
 25import retrofit2.Response;
 26
 27import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
 28import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 29
 30public class MainActivity extends AppCompatActivity {
 31
 32    private ArrayList<String> permissionsToRequest;
 33    private ArrayList<String> permissionsRejected = new ArrayList<>();
 34    private ArrayList<String> permissions = new ArrayList<>();
 35    private final static int ALL_PERMISSIONS_RESULT = 101;
 36    List<StoreModel> storeModels;
 37    ApiInterface apiService;
 38
 39    String latLngString;
 40    LatLng latLng;
 41
 42    RecyclerView recyclerView;
 43    EditText editText;
 44    Button button;
 45    List<PlacesPOJO.CustomA> results;
 46
 47    @Override
 48    protected void onCreate(Bundle savedInstanceState) {
 49        super.onCreate(savedInstanceState);
 50        setContentView(R.layout.activity_main);
 51
 52        permissions.add(ACCESS_FINE_LOCATION);
 53        permissions.add(ACCESS_COARSE_LOCATION);
 54
 55        permissionsToRequest = findUnAskedPermissions(permissions);
 56
 57        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 58
 59            if (permissionsToRequest.size() > 0)
 60                requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
 61            else {
 62                fetchLocation();
 63            }
 64        } else {
 65            fetchLocation();
 66        }
 67
 68        apiService = APIClient.getClient().create(ApiInterface.class);
 69
 70        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
 71
 72        recyclerView.setNestedScrollingEnabled(false);
 73        recyclerView.setHasFixedSize(true);
 74
 75        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
 76        recyclerView.setLayoutManager(layoutManager);
 77
 78        editText = (EditText) findViewById(R.id.editText);
 79        button = (Button) findViewById(R.id.button);
 80
 81        button.setOnClickListener(new View.OnClickListener() {
 82            @Override
 83            public void onClick(View v) {
 84                String s = editText.getText().toString().trim();
 85                String[] split = s.split("\\s+");
 86
 87                if (split.length != 2) {
 88                    Toast.makeText(getApplicationContext(), "Please enter text in the required format", Toast.LENGTH_SHORT).show();
 89                } else
 90                    fetchStores(split[0], split[1]);
 91            }
 92        });
 93
 94    }
 95
 96    private void fetchStores(String placeType, String businessName) {
 97
 98        /**
 99         * For Locations In India McDonalds stores aren't returned accurately
100         */
101
102        //Call<PlacesPOJO.Root> call = apiService.doPlaces(placeType, latLngString,"\""+ businessName +"\"", true, "distance", APIClient.GOOGLE_PLACE_API_KEY);
103
104        Call<PlacesPOJO.Root> call = apiService.doPlaces(placeType, latLngString, businessName, true, "distance", APIClient.GOOGLE_PLACE_API_KEY);
105        call.enqueue(new Callback<PlacesPOJO.Root>() {
106            @Override
107            public void onResponse(Call<PlacesPOJO.Root> call, Response<PlacesPOJO.Root> response) {
108                PlacesPOJO.Root root = response.body();
109
110                if (response.isSuccessful()) {
111
112                    if (root.status.equals("OK")) {
113
114                        results = root.customA;
115                        storeModels = new ArrayList<>();
116                        for (int i = 0; i < results.size(); i++) {
117
118                            if (i == 10)
119                                break;
120                            PlacesPOJO.CustomA info = results.get(i);
121
122                            fetchDistance(info);
123
124                        }
125
126                    } else {
127                        Toast.makeText(getApplicationContext(), "No matches found near you", Toast.LENGTH_SHORT).show();
128                    }
129
130                } else if (response.code() != 200) {
131                    Toast.makeText(getApplicationContext(), "Error " + response.code() + " found.", Toast.LENGTH_SHORT).show();
132                }
133
134            }
135
136            @Override
137            public void onFailure(Call<PlacesPOJO.Root> call, Throwable t) {
138                // Log error here since request failed
139                call.cancel();
140            }
141        });
142
143    }
144
145    private ArrayList<String> findUnAskedPermissions(ArrayList<String> wanted) {
146        ArrayList<String> result = new ArrayList<>();
147
148        for (String perm : wanted) {
149            if (!hasPermission(perm)) {
150                result.add(perm);
151            }
152        }
153
154        return result;
155    }
156
157    private boolean hasPermission(String permission) {
158        if (canMakeSmores()) {
159            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
160                return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
161            }
162        }
163        return true;
164    }
165
166    private boolean canMakeSmores() {
167        return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
168    }
169
170    @TargetApi(Build.VERSION_CODES.M)
171    @Override
172    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
173
174        switch (requestCode) {
175
176            case ALL_PERMISSIONS_RESULT:
177                for (String perms : permissionsToRequest) {
178                    if (!hasPermission(perms)) {
179                        permissionsRejected.add(perms);
180                    }
181                }
182
183                if (permissionsRejected.size() > 0) {
184
185                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
186                        if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
187                            showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
188                                    new DialogInterface.OnClickListener() {
189                                        @Override
190                                        public void onClick(DialogInterface dialog, int which) {
191                                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
192                                                requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
193                                            }
194                                        }
195                                    });
196                            return;
197                        }
198                    }
199
200                } else {
201                    fetchLocation();
202                }
203
204                break;
205        }
206
207    }
208
209    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
210        new AlertDialog.Builder(MainActivity.this)
211                .setMessage(message)
212                .setPositiveButton("OK", okListener)
213                .setNegativeButton("Cancel", null)
214                .create()
215                .show();
216    }
217
218    private void fetchLocation() {
219
220        SmartLocation.with(this).location()
221                .oneFix()
222                .start(new OnLocationUpdatedListener() {
223                    @Override
224                    public void onLocationUpdated(Location location) {
225                        latLngString = location.getLatitude() + "," + location.getLongitude();
226                        latLng = new LatLng(location.getLatitude(), location.getLongitude());
227                    }
228                });
229    }
230
231    private void fetchDistance(final PlacesPOJO.CustomA info) {
232
233        Call<ResultDistanceMatrix> call = apiService.getDistance(APIClient.GOOGLE_PLACE_API_KEY, latLngString, info.geometry.locationA.lat + "," + info.geometry.locationA.lng);
234        call.enqueue(new Callback<ResultDistanceMatrix>() {
235            @Override
236            public void onResponse(Call<ResultDistanceMatrix> call, Response<ResultDistanceMatrix> response) {
237
238                ResultDistanceMatrix resultDistance = response.body();
239                if ("OK".equalsIgnoreCase(resultDistance.status)) {
240
241                    ResultDistanceMatrix.InfoDistanceMatrix infoDistanceMatrix = resultDistance.rows.get(0);
242                    ResultDistanceMatrix.InfoDistanceMatrix.DistanceElement distanceElement = infoDistanceMatrix.elements.get(0);
243                    if ("OK".equalsIgnoreCase(distanceElement.status)) {
244                        ResultDistanceMatrix.InfoDistanceMatrix.ValueItem itemDuration = distanceElement.duration;
245                        ResultDistanceMatrix.InfoDistanceMatrix.ValueItem itemDistance = distanceElement.distance;
246                        String totalDistance = String.valueOf(itemDistance.text);
247                        String totalDuration = String.valueOf(itemDuration.text);
248
249                        storeModels.add(new StoreModel(info.name, info.vicinity, totalDistance, totalDuration));
250
251                        if (storeModels.size() == 10 || storeModels.size() == results.size()) {
252                            RecyclerViewAdapter adapterStores = new RecyclerViewAdapter(results, storeModels);
253                            recyclerView.setAdapter(adapterStores);
254                        }
255
256                    }
257
258                }
259
260            }
261
262            @Override
263            public void onFailure(Call<ResultDistanceMatrix> call, Throwable t) {
264                call.cancel();
265            }
266        });
267
268    }
269}

在上面的代码中,我们首先请求运行时权限,然后使用SmartLocation库获取当前位置。准备好之后,我们将EditText中的第一个单词传递到类型中,并将第二个单词传递到fetchStores()方法的name参数中,该方法最终调用Google Places API Web服务。我们将搜索结果限制为10个。对于每个结果,我们在fetchDistance()方法中计算到商店的距离和时间。一旦完成了对所有商店的操作,我们将使用StoreModel.java数据类填充Recical erViewAdapter.java类中的数据。StoreModel.java 代码如下:

 1package com.journaldev.nearbyplaces;
 2
 3public class StoreModel {
 4
 5    public String name, address, distance, duration;
 6
 7    public StoreModel(String name, String address, String distance, String duration) {
 8
 9        this.name = name;
10        this.address = address;
11        this.distance = distance;
12        this.duration = duration;
13    }
14
15}

下面的XML给出了RecillerView的每一行的布局:store_list_row.xml

 1<?xml version="1.0" encoding="utf-8"?>
 2<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
 3    android:layout_width="match_parent"
 4    android:layout_height="wrap_content"
 5    android:layout_marginBottom="@dimen/activity_horizontal_margin"
 6    android:orientation="vertical">
 7
 8    <android.support.v7.widget.CardView xmlns:card_view="https://schemas.android.com/apk/res-auto"
 9        android:id="@+id/card_view"
10        android:layout_width="match_parent"
11        android:layout_height="wrap_content"
12        card_view:cardCornerRadius="0dp"
13        card_view:cardElevation="5dp">
14
15        <LinearLayout
16            android:layout_width="match_parent"
17            android:layout_height="wrap_content"
18            android:orientation="vertical"
19            android:padding="5dp">
20
21            <TextView
22                android:id="@+id/txtStoreName"
23                android:layout_width="wrap_content"
24                android:layout_height="wrap_content"
25                android:paddingBottom="5dp"
26                android:textColor="#212121" />
27
28            <TextView
29                android:id="@+id/txtStoreAddr"
30                android:layout_width="wrap_content"
31                android:layout_height="wrap_content"
32                android:paddingBottom="5dp"
33                android:textColor="#212121" />
34
35            <TextView
36                android:id="@+id/txtStoreDist"
37                android:layout_width="wrap_content"
38                android:layout_height="wrap_content"
39                android:paddingBottom="5dp" />
40
41        </LinearLayout>
42
43    </android.support.v7.widget.CardView>
44
45</LinearLayout>

下面给出了 ClerViewAdapter.java 代码。

 1public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
 2
 3    private List<PlacesPOJO.CustomA> stLstStores;
 4    private List<StoreModel> models;
 5
 6    public RecyclerViewAdapter(List<PlacesPOJO.CustomA> stores, List<StoreModel> storeModels) {
 7
 8        stLstStores = stores;
 9        models = storeModels;
10    }
11
12    @Override
13    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
14        final View view = LayoutInflater.from(parent.getContext())
15                .inflate(R.layout.store_list_row, parent, false);
16
17        return new MyViewHolder(view);
18    }
19
20    @Override
21    public void onBindViewHolder(MyViewHolder holder, int position) {
22
23        holder.setData(stLstStores.get(holder.getAdapterPosition()), holder, models.get(holder.getAdapterPosition()));
24    }
25
26    @Override
27    public int getItemCount() {
28        return Math.min(5, stLstStores.size());
29    }
30
31    public class MyViewHolder extends RecyclerView.ViewHolder {
32
33        TextView txtStoreName;
34        TextView txtStoreAddr;
35        TextView txtStoreDist;
36        StoreModel model;
37
38        public MyViewHolder(View itemView) {
39            super(itemView);
40
41            this.txtStoreDist = (TextView) itemView.findViewById(R.id.txtStoreDist);
42            this.txtStoreName = (TextView) itemView.findViewById(R.id.txtStoreName);
43            this.txtStoreAddr = (TextView) itemView.findViewById(R.id.txtStoreAddr);
44
45        }
46
47        public void setData(PlacesPOJO.CustomA info, MyViewHolder holder, StoreModel storeModel) {
48
49            this.model = storeModel;
50
51            holder.txtStoreDist.setText(model.distance + "\n" + model.duration);
52            holder.txtStoreName.setText(info.name);
53            holder.txtStoreAddr.setText(info.vicinity);
54
55        }
56
57    }
58}

Google Places API示例应用程序的运行输出如下:Google Places API,Google Distance Matrix API,Google Near Places Example android注:Places API对于麦当劳和一些食品连锁店是不准确的,特别是对于印度的位置。一种解决方法是在参数name中传递双引号中的值,例如:

1Call call = apiService.doPlaces(placeType, latLngString,"\""+ businessName +"\"", true, "distance", APIClient.GOOGLE_PLACE_API_KEY);

我的位置如下所示,输出如下:Android Neighbor Places Using Google Places api这将结束本教程。您可以从下面的链接下载最终的Google Places API示例项目。

下载Google Places API示例Project

Published At
Categories with 技术
Tagged with
comments powered by Disqus