├── .gitignore ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── itangqi │ │ └── me │ │ └── mygoogleplaces │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── itangqi │ │ └── me │ │ └── mygoogleplaces │ │ ├── MainActivity.java │ │ └── PlaceAutocompleteAdapter.java │ └── res │ ├── drawable │ └── ic_location.png │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.ap_ 3 | 4 | # files for the dex VM 5 | *.dex 6 | 7 | # Java class files 8 | *.class 9 | 10 | # generated files 11 | bin/ 12 | gen/ 13 | 14 | # Local configuration file (sdk path, etc) 15 | local.properties 16 | 17 | # Proguard folder generated by Eclipse 18 | proguard/ 19 | 20 | # Ignore gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Eclipse project files 25 | .classpath 26 | .project 27 | .settings/ 28 | 29 | # Intellij project files 30 | *.iml 31 | *.ipr 32 | *.iws 33 | .idea/ 34 | 35 | # Mac system files 36 | .DS_Store 37 | 38 | *.keystore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MyGooglePlaces 2 | ========== 3 | 4 |

5 | 最近由于项目的原因,接触到了 Google Places 的使用与开发,学习并在项目中实现了简单的地点定位与自动补全功能。在整个实践过程中,发现除了官方文档外,网上相关的中文教程寥寥无几且质量都不高,遂决定自己写篇入门指南(其实也没多少干货啦),希望可以帮助到,有这方面需求的同学们,欢迎大家 Fork 与 Star 。 6 |

7 | 8 | screenshot 9 | 10 | 11 | **Demo is still in development so more features will be added soon** 12 | 13 | - [Usage](#usage) 14 | - [Download](#download) 15 | - [Documentation](#documentation) 16 | - [Thanks to](#thanks-to) 17 | - [Contact Me](#contact-me) 18 | - [License](#license) 19 | 20 | # Usage 21 | 22 | ### Dependency - 依赖 23 | - Java Development Kit (JDK) 8 + 24 | - com.android.tools.build:gradle:1.3.0 25 | - Android SDK 26 | - Android SDK Build-tools 22.0.1 27 | 28 | 29 | ### Build - 构建 30 | 31 | git clone https://github.com/tangqi92/MyGooglePlaces.git 32 | 33 | 用最新的IntelliJ IDE导入工程(Import Project),然后等待IDE下载gradle和依赖包即可 34 | 35 | This project uses the Gradle build system. To build this project, use the "gradlew build" command or use "Import Project" in Android Studio. 36 | 37 | 对了,是时候告别 Eclipse 了,因为你值得拥有更好的,赶紧投入 [Android Studio](https://developer.android.com/sdk/index.html) 的怀抱吧:D 38 | 39 | 40 | # Download 41 | 直接下载:[MyGooglePlaces-1.0.0.apk](https://github.com/tangqi92/MyGooglePlaces/releases/download/v1.0/mygoogleplaces-1.0.0.apk) 42 | 43 | # Documentation 44 | 45 | 项目主页:[Google Places for Android 入门指南](http://imtangqi.com/2015/08/09/google-places-for-android-guide/) 46 | 47 | # Thanks to 48 | 49 | 再次感谢:[https://github.com/googlesamples/android-play-places](https://github.com/googlesamples/android-play-places) 50 | 51 | # Contact Me 52 | 53 | - Weibo:[@Tang](http://weibo.com/qiktang) 54 | - Gmail:[imtangqi#gmail.com](mailto:imtangqi@gmail.com "欢迎与我联系") 55 | - Blog: [http://imtangqi.com](http://imtangqi.com) 56 | 57 | # License 58 | 59 |
60 | The MIT License (MIT)
61 | 
62 | Copyright (c) 2015 Qi Tang
63 | 
64 | Permission is hereby granted, free of charge, to any person obtaining a copy
65 | of this software and associated documentation files (the "Software"), to deal
66 | in the Software without restriction, including without limitation the rights
67 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
68 | copies of the Software, and to permit persons to whom the Software is
69 | furnished to do so, subject to the following conditions:
70 | 
71 | The above copyright notice and this permission notice shall be included in all
72 | copies or substantial portions of the Software.
73 | 
74 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
75 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
76 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
77 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
78 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
79 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
80 | SOFTWARE.
81 | 
82 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.ap_ 3 | 4 | # files for the dex VM 5 | *.dex 6 | 7 | # Java class files 8 | *.class 9 | 10 | # generated files 11 | bin/ 12 | gen/ 13 | 14 | # Local configuration file (sdk path, etc) 15 | local.properties 16 | 17 | # Proguard folder generated by Eclipse 18 | proguard/ 19 | 20 | # Ignore gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Eclipse project files 25 | .classpath 26 | .project 27 | .settings/ 28 | 29 | # Intellij project files 30 | *.iml 31 | *.ipr 32 | *.iws 33 | .idea/ 34 | 35 | # Mac system files 36 | .DS_Store 37 | 38 | *.keystore -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "itangqi.me.mygoogleplaces" 9 | minSdkVersion 16 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:22.2.1' 25 | compile 'com.google.android.gms:play-services:7.5.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/tangqi/android-dev/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/itangqi/me/mygoogleplaces/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package itangqi.me.mygoogleplaces; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 23 | 24 | 27 | 28 | 29 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/itangqi/me/mygoogleplaces/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package itangqi.me.mygoogleplaces; 18 | 19 | import android.content.res.Resources; 20 | import android.net.Uri; 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.text.Html; 24 | import android.text.Spanned; 25 | import android.util.Log; 26 | import android.view.View; 27 | import android.widget.AdapterView; 28 | import android.widget.AutoCompleteTextView; 29 | import android.widget.Button; 30 | import android.widget.TextView; 31 | import android.widget.Toast; 32 | 33 | import com.google.android.gms.common.ConnectionResult; 34 | import com.google.android.gms.common.api.GoogleApiClient; 35 | import com.google.android.gms.common.api.PendingResult; 36 | import com.google.android.gms.common.api.ResultCallback; 37 | import com.google.android.gms.location.places.Place; 38 | import com.google.android.gms.location.places.PlaceBuffer; 39 | import com.google.android.gms.location.places.PlaceLikelihoodBuffer; 40 | import com.google.android.gms.location.places.Places; 41 | import com.google.android.gms.maps.model.LatLng; 42 | import com.google.android.gms.maps.model.LatLngBounds; 43 | 44 | public class MainActivity extends AppCompatActivity 45 | implements GoogleApiClient.OnConnectionFailedListener { 46 | /** 47 | * GoogleApiClient wraps our service connection to Google Play Services and provides access 48 | * to the user's sign in state as well as the Google's APIs. 49 | */ 50 | private static final int GOOGLE_API_CLIENT_ID = 0; 51 | 52 | protected GoogleApiClient mGoogleApiClient; 53 | 54 | private PlaceAutocompleteAdapter mAdapter; 55 | 56 | private AutoCompleteTextView mAutocompleteView; 57 | 58 | private TextView mPlaceDetailsText; 59 | 60 | private TextView mPlaceDetailsAttribution; 61 | 62 | private Button mCurrentLocation; 63 | 64 | private static final String TAG = "MainActivity"; 65 | 66 | private static final LatLngBounds BOUNDS_GREATER_SYDNEY = new LatLngBounds( 67 | new LatLng(-34.041458, 150.790100), new LatLng(-33.682247, 151.383362)); 68 | 69 | @Override 70 | protected void onCreate(Bundle savedInstanceState) { 71 | super.onCreate(savedInstanceState); 72 | 73 | // Construct a GoogleApiClient for the {@link Places#GEO_DATA_API} using AutoManage 74 | // functionality, which automatically sets up the API client to handle Activity lifecycle 75 | // events. If your activity does not extend FragmentActivity, make sure to call connect() 76 | // and disconnect() explicitly. 77 | mGoogleApiClient = new GoogleApiClient.Builder(this) 78 | .enableAutoManage(this, GOOGLE_API_CLIENT_ID /* clientId */, this) 79 | .addApi(Places.GEO_DATA_API) 80 | .addApi(Places.PLACE_DETECTION_API) 81 | .build(); 82 | 83 | setContentView(R.layout.activity_main); 84 | 85 | // Retrieve the AutoCompleteTextView that will display Place suggestions. 86 | mAutocompleteView = (AutoCompleteTextView) 87 | findViewById(R.id.autocomplete_places); 88 | 89 | // Register a listener that receives callbacks when a suggestion has been selected 90 | mAutocompleteView.setOnItemClickListener(mAutocompleteClickListener); 91 | 92 | // Retrieve the TextViews that will display details and attributions of the selected place. 93 | mPlaceDetailsText = (TextView) findViewById(R.id.place_details); 94 | mPlaceDetailsAttribution = (TextView) findViewById(R.id.place_attribution); 95 | 96 | // CurrentLocation 97 | mCurrentLocation = (Button) findViewById(R.id.btn_current_location); 98 | mCurrentLocation.setOnClickListener(mOnClickListener); 99 | 100 | // Set up the adapter that will retrieve suggestions from the Places Geo Data API that cover 101 | // the entire world. 102 | mAdapter = new PlaceAutocompleteAdapter(this, android.R.layout.simple_list_item_1, 103 | mGoogleApiClient, BOUNDS_GREATER_SYDNEY, null); 104 | mAutocompleteView.setAdapter(mAdapter); 105 | } 106 | 107 | private View.OnClickListener mOnClickListener = new View.OnClickListener() { 108 | @Override 109 | public void onClick(View view) { 110 | PendingResult result = Places.PlaceDetectionApi 111 | .getCurrentPlace(mGoogleApiClient, null); 112 | result.setResultCallback(new ResultCallback() { 113 | @Override 114 | public void onResult(PlaceLikelihoodBuffer likelyPlaces) { 115 | if (!likelyPlaces.getStatus().isSuccess()) { 116 | // Request did not complete successfully 117 | Log.e(TAG, "Place query did not complete. Error: " + likelyPlaces.getStatus().toString()); 118 | likelyPlaces.release(); 119 | return; 120 | } 121 | String placeName = String.format("%s", likelyPlaces.get(0).getPlace().getName()); 122 | String placeAttributuion = String.format("%s", likelyPlaces.get(0).getPlace().getAddress()); 123 | mPlaceDetailsText.setText(placeName); 124 | mPlaceDetailsAttribution.setText(placeAttributuion); 125 | likelyPlaces.release(); 126 | } 127 | }); 128 | } 129 | }; 130 | 131 | /** 132 | * Listener that handles selections from suggestions from the AutoCompleteTextView that 133 | * displays Place suggestions. 134 | * Gets the place id of the selected item and issues a request to the Places Geo Data API 135 | * to retrieve more details about the place. 136 | * 137 | * @see com.google.android.gms.location.places.GeoDataApi#getPlaceById(com.google.android.gms.common.api.GoogleApiClient, 138 | * String...) 139 | */ 140 | private AdapterView.OnItemClickListener mAutocompleteClickListener 141 | = new AdapterView.OnItemClickListener() { 142 | @Override 143 | public void onItemClick(AdapterView parent, View view, int position, long id) { 144 | /* 145 | Retrieve the place ID of the selected item from the Adapter. 146 | The adapter stores each Place suggestion in a PlaceAutocomplete object from which we 147 | read the place ID. 148 | */ 149 | final PlaceAutocompleteAdapter.PlaceAutocomplete item = mAdapter.getItem(position); 150 | final String placeId = String.valueOf(item.placeId); 151 | Log.i(TAG, "Autocomplete item selected: " + item.description); 152 | 153 | /* 154 | Issue a request to the Places Geo Data API to retrieve a Place object with additional 155 | details about the place. 156 | */ 157 | PendingResult placeResult = Places.GeoDataApi 158 | .getPlaceById(mGoogleApiClient, placeId); 159 | placeResult.setResultCallback(mUpdatePlaceDetailsCallback); 160 | Log.i(TAG, "Called getPlaceById to get Place details for " + item.placeId); 161 | } 162 | }; 163 | 164 | /** 165 | * Callback for results from a Places Geo Data API query that shows the first place result in 166 | * the details view on screen. 167 | */ 168 | private ResultCallback mUpdatePlaceDetailsCallback 169 | = new ResultCallback() { 170 | @Override 171 | public void onResult(PlaceBuffer places) { 172 | if (!places.getStatus().isSuccess()) { 173 | // Request did not complete successfully 174 | Log.e(TAG, "Place query did not complete. Error: " + places.getStatus().toString()); 175 | places.release(); 176 | return; 177 | } 178 | // Get the Place object from the buffer. 179 | final Place place = places.get(0); 180 | 181 | // Format details of the place for display and show it in a TextView. 182 | mPlaceDetailsText.setText(formatPlaceDetails(getResources(), place.getName(), 183 | place.getId(), place.getAddress(), place.getPhoneNumber(), 184 | place.getWebsiteUri())); 185 | 186 | // Display the third party attributions if set. 187 | final CharSequence thirdPartyAttribution = places.getAttributions(); 188 | if (thirdPartyAttribution == null) { 189 | mPlaceDetailsAttribution.setVisibility(View.GONE); 190 | } else { 191 | mPlaceDetailsAttribution.setVisibility(View.VISIBLE); 192 | mPlaceDetailsAttribution.setText(Html.fromHtml(thirdPartyAttribution.toString())); 193 | } 194 | 195 | Log.i(TAG, "Place details received: " + place.getName()); 196 | 197 | places.release(); 198 | } 199 | }; 200 | 201 | private static Spanned formatPlaceDetails(Resources res, CharSequence name, String id, 202 | CharSequence address, CharSequence phoneNumber, Uri websiteUri) { 203 | Log.e(TAG, res.getString(R.string.place_details, name, id, address, phoneNumber, 204 | websiteUri)); 205 | return Html.fromHtml(res.getString(R.string.place_details, name, id, address, phoneNumber, 206 | websiteUri)); 207 | 208 | } 209 | 210 | /** 211 | * Called when the Activity could not connect to Google Play services and the auto manager 212 | * could resolve the error automatically. 213 | * In this case the API is not available and notify the user. 214 | * 215 | * @param connectionResult can be inspected to determine the cause of the failure 216 | */ 217 | @Override 218 | public void onConnectionFailed(ConnectionResult connectionResult) { 219 | 220 | Log.e(TAG, "onConnectionFailed: ConnectionResult.getErrorCode() = " 221 | + connectionResult.getErrorCode()); 222 | 223 | // TODO(Developer): Check error code and notify the user of error state and resolution. 224 | Toast.makeText(this, 225 | "Could not connect to Google API Client: Error " + connectionResult.getErrorCode(), 226 | Toast.LENGTH_SHORT).show(); 227 | MainActivity.this.finish(); 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /app/src/main/java/itangqi/me/mygoogleplaces/PlaceAutocompleteAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package itangqi.me.mygoogleplaces; 18 | 19 | import android.content.Context; 20 | import android.util.Log; 21 | import android.widget.ArrayAdapter; 22 | import android.widget.Filter; 23 | import android.widget.Filterable; 24 | import android.widget.Toast; 25 | 26 | import com.google.android.gms.common.api.GoogleApiClient; 27 | import com.google.android.gms.common.api.PendingResult; 28 | import com.google.android.gms.common.api.Status; 29 | import com.google.android.gms.location.places.AutocompleteFilter; 30 | import com.google.android.gms.location.places.AutocompletePrediction; 31 | import com.google.android.gms.location.places.AutocompletePredictionBuffer; 32 | import com.google.android.gms.location.places.Places; 33 | import com.google.android.gms.maps.model.LatLngBounds; 34 | 35 | import java.util.ArrayList; 36 | import java.util.Iterator; 37 | import java.util.concurrent.TimeUnit; 38 | 39 | public class PlaceAutocompleteAdapter 40 | extends ArrayAdapter implements Filterable { 41 | 42 | private static final String TAG = "PlaceAutocompleteAdapter"; 43 | /** 44 | * Current results returned by this adapter. 45 | */ 46 | private ArrayList mResultList; 47 | 48 | /** 49 | * Handles autocomplete requests. 50 | */ 51 | private GoogleApiClient mGoogleApiClient; 52 | 53 | /** 54 | * The bounds used for Places Geo Data autocomplete API requests. 55 | */ 56 | private LatLngBounds mBounds; 57 | 58 | /** 59 | * The autocomplete filter used to restrict queries to a specific set of place types. 60 | */ 61 | private AutocompleteFilter mPlaceFilter; 62 | 63 | /** 64 | * Initializes with a resource for text rows and autocomplete query bounds. 65 | * 66 | * @see ArrayAdapter#ArrayAdapter(Context, int) 67 | */ 68 | public PlaceAutocompleteAdapter(Context context, int resource, GoogleApiClient googleApiClient, 69 | LatLngBounds bounds, AutocompleteFilter filter) { 70 | super(context, resource); 71 | mGoogleApiClient = googleApiClient; 72 | mBounds = bounds; 73 | mPlaceFilter = filter; 74 | } 75 | 76 | /** 77 | * Sets the bounds for all subsequent queries. 78 | */ 79 | public void setBounds(LatLngBounds bounds) { 80 | mBounds = bounds; 81 | } 82 | 83 | /** 84 | * Returns the number of results received in the last autocomplete query. 85 | */ 86 | @Override 87 | public int getCount() { 88 | return mResultList.size(); 89 | } 90 | 91 | /** 92 | * Returns an item from the last autocomplete query. 93 | */ 94 | @Override 95 | public PlaceAutocomplete getItem(int position) { 96 | return mResultList.get(position); 97 | } 98 | 99 | /** 100 | * Returns the filter for the current set of autocomplete results. 101 | */ 102 | @Override 103 | public Filter getFilter() { 104 | Filter filter = new Filter() { 105 | @Override 106 | protected FilterResults performFiltering(CharSequence constraint) { 107 | FilterResults results = new FilterResults(); 108 | // Skip the autocomplete query if no constraints are given. 109 | if (constraint != null) { 110 | // Query the autocomplete API for the (constraint) search string. 111 | mResultList = getAutocomplete(constraint); 112 | if (mResultList != null) { 113 | // The API successfully returned results. 114 | results.values = mResultList; 115 | results.count = mResultList.size(); 116 | } 117 | } 118 | return results; 119 | } 120 | 121 | @Override 122 | protected void publishResults(CharSequence constraint, FilterResults results) { 123 | if (results != null && results.count > 0) { 124 | // The API returned at least one result, update the data. 125 | notifyDataSetChanged(); 126 | } else { 127 | // The API did not return any results, invalidate the data set. 128 | notifyDataSetInvalidated(); 129 | } 130 | } 131 | }; 132 | return filter; 133 | } 134 | 135 | private ArrayList getAutocomplete(CharSequence constraint) { 136 | if (mGoogleApiClient.isConnected()) { 137 | Log.i(TAG, "Starting autocomplete query for: " + constraint); 138 | 139 | // Submit the query to the autocomplete API and retrieve a PendingResult that will 140 | // contain the results when the query completes. 141 | PendingResult results = 142 | Places.GeoDataApi 143 | .getAutocompletePredictions(mGoogleApiClient, constraint.toString(), 144 | mBounds, mPlaceFilter); 145 | 146 | // This method should have been called off the main UI thread. Block and wait for at most 60s 147 | // for a result from the API. 148 | AutocompletePredictionBuffer autocompletePredictions = results 149 | .await(60, TimeUnit.SECONDS); 150 | 151 | // Confirm that the query completed successfully, otherwise return null 152 | final Status status = autocompletePredictions.getStatus(); 153 | if (!status.isSuccess()) { 154 | Toast.makeText(getContext(), "Error contacting API: " + status.toString(), 155 | Toast.LENGTH_SHORT).show(); 156 | Log.e(TAG, "Error getting autocomplete prediction API call: " + status.toString()); 157 | autocompletePredictions.release(); 158 | return null; 159 | } 160 | 161 | Log.i(TAG, "Query completed. Received " + autocompletePredictions.getCount() 162 | + " predictions."); 163 | 164 | // Copy the results into our own data structure, because we can't hold onto the buffer. 165 | // AutocompletePrediction objects encapsulate the API response (place ID and description). 166 | 167 | Iterator iterator = autocompletePredictions.iterator(); 168 | ArrayList resultList = new ArrayList<>(autocompletePredictions.getCount()); 169 | while (iterator.hasNext()) { 170 | AutocompletePrediction prediction = iterator.next(); 171 | // Get the details of this prediction and copy it into a new PlaceAutocomplete object. 172 | resultList.add(new PlaceAutocomplete(prediction.getPlaceId(), 173 | prediction.getDescription())); 174 | } 175 | 176 | // Release the buffer now that all data has been copied. 177 | autocompletePredictions.release(); 178 | 179 | return resultList; 180 | } 181 | Log.e(TAG, "Google API client is not connected for autocomplete query."); 182 | return null; 183 | } 184 | 185 | /** 186 | * Holder for Places Geo Data Autocomplete API results. 187 | */ 188 | class PlaceAutocomplete { 189 | 190 | public CharSequence placeId; 191 | public CharSequence description; 192 | 193 | PlaceAutocomplete(CharSequence placeId, CharSequence description) { 194 | this.placeId = placeId; 195 | this.description = description; 196 | } 197 | 198 | @Override 199 | public String toString() { 200 | return description.toString(); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangqi92/MyGooglePlaces/a29e2569ed8b9c6884194821110f25f422c0461d/app/src/main/res/drawable/ic_location.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 24 | 25 | 31 | 32 | 37 | 38 |