總覽
Google 地圖平台支援網頁 (JavaScript、TypeScript)、Android 和 iOS,也提供網頁服務 API,可擷取地點、路線和距離等資訊。本指南中的範例是以單一平台為主,如要在其他平台上實作,請參閱所提供的說明文件連結。
您可以使用 Google Cloud 控制台中的「快速建構工具」,在互動式使用者介面內取得系統產生的 JavaScript 程式碼,建構地址表單自動完成功能。
使用者已習慣在數位世界中生活及處理事務,方便、快速和安全是他們的基本要求。申辦信用卡、銀行帳戶或貸款等業務時,他們會希望過程快速又簡單。
必須輸入或重複輸入的資料越多,就越難留住客戶。提供快速、簡單且經過驗證的註冊服務,不但能改善使用者體驗,還有助於留住網站使用者。
手動輸入地址可能會導致轉換次數減少、客戶關係管理資料出現錯誤,以及發生代價高昂的送貨失誤。「經過驗證的快速註冊」可以加快註冊速度,只需點幾下,系統就會立即建議附近的地址,並顯示輸入過的地址,供使用者目視確認自己輸入的地址正確無誤。透過使用者目前的所在位置來驗證地址,也有助於防範詐欺行為,提升使用者對產品和服務的信心。採用驗證程序,您也能更放心地即時提供虛擬銀行和信用卡服務。
本主題提供實作指引,協助您瞭解如何使用 Google 地圖平台建立「經過驗證的快速註冊」服務。使用者很有可能是在行動裝置上註冊,因此本主題中大部分的實作範例都以 Android 為主 (請參閱這裡的完整原始碼範例)。您也可以使用 iOS SDK 完成相同操作。
下圖說明建立解決方案所需的核心 API (點按即可放大)。
啟用 API
如要採行這些建議,您必須在 Google Cloud 控制台中啟用下列 API:
- Maps SDK for Android (或您所選平台的 API)
- Places API
- Geocoding API
如要進一步瞭解設定,請參閱「開始使用 Google 地圖平台」一文。
最佳做法章節
以下為本主題將說明的做法和自訂項目。
- 勾號圖示代表核心最佳做法。
- 星號圖示為選用但建議使用的自訂項目,可強化解決方案。
在輸入欄位中加入自動完成功能 | 自動填入地址表單。加入預先輸入功能,改善所有平台上的使用者體驗,減少所需的按鍵動作數並提高地址的正確性。 | |
讓使用者能目視確認地址 | 讓使用者能在地圖上看到地址,目視確認自己輸入的地址是否正確。 | |
將使用者輸入的地址與裝置的所在位置進行比對 | 將使用者選取或輸入的地址與裝置的目前位置進行比對,判斷使用者是否位於指定地址 (註冊時使用者必須在家,這項功能方可正常運作)。 | |
進一步提升「經過驗證的快速註冊」體驗的提示 | 您還可使用額外的功能,例如自訂「自動完成」小工具的外觀和風格,或是讓使用者選取商家或地標的名稱做為地址,進一步提升地址輸入體驗。 |
在輸入欄位中加入自動完成功能
此範例使用:Places SDK for Android | 同時支援:iOS | JavaScript |
Place Autocomplete 能夠簡化在應用程式中輸入地址的過程,為客戶提供流暢體驗並提升轉換率。自動完成功能提供一個可「預先輸入」地址預測字串的快速輸入欄位,能用來自動填入註冊地址表單。在註冊流程中納入 Place Autocomplete 功能,您就可以:
- 減少地址輸入錯誤。
- 減少註冊流程包含的步驟數。
- 簡化行動裝置或穿戴式裝置上的地址輸入體驗。
- 大幅減少註冊時所需的按鍵動作數和花費時間。
使用者選取自動完成輸入方塊並開始輸入時,系統便會顯示地址預測清單:
使用者從預測清單中選取所需地址時,您可以利用此回應來驗證地址並取得位置資訊。接著應用程式便會將資訊填入地址輸入表單中的正確欄位 (如下圖所示)。
Google 地圖平台提供行動平台和網站專用的 Place Autocomplete 小工具。如上圖所示,這個小工具提供搜尋對話方塊,並內建自動完成功能,甚至可讓您針對位置範圍的搜尋進行最佳化。
本節說明如何導入「經過驗證的快速註冊」所需的 Place Autocomplete 功能。
新增 Place Autocomplete 小工具
在 Android 中,您可以使用「自動完成意圖」新增自動完成小工具,該意圖會從地址行 1 輸入欄位 (使用者會在其中開始輸入地址) 啟動 Place Autocomplete。使用者開始輸入時,可以從自動預測清單中選取需要的地址。
首先,使用 ActivityResultLauncher
製作活動啟動器,從已啟動的活動監聽結果。結果回呼將包含 Place 物件 (此物件會對應使用者從自動預測清單中選取的地址)。
private final ActivityResultLauncher<Intent> startAutocomplete = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent intent = result.getData();
if (intent != null) {
Place place = Autocomplete.getPlaceFromIntent(intent);
// Write a method to read the address components from the Place
// and populate the form with the address components
Log.d(TAG, "Place: " + place.getAddressComponents());
fillInAddress(place);
}
} else if (result.getResultCode() == Activity.RESULT_CANCELED) {
// The user canceled the operation.
Log.i(TAG, "User canceled autocomplete");
}
});
接著,定義 Place Autocomplete 意圖的欄位、位置和類型屬性,並使用 Autocomplete.IntentBuilder
建立。最後,請使用上一個程式碼範例中定義的 ActivityResultLauncher
啟動意圖。
private void startAutocompleteIntent() {
// Set the fields to specify which types of place data to
// return after the user has made a selection.
List<Place.Field> fields = Arrays.asList(Place.Field.ADDRESS_COMPONENTS,
Place.Field.LAT_LNG, Place.Field.VIEWPORT);
// Build the autocomplete intent with field, country, and type filters applied
Intent intent = new Autocomplete.IntentBuilder(AutocompleteActivityMode.OVERLAY, fields)
.setCountries(Arrays.asList("US"))
.setTypesFilter(new ArrayList<String>() {{
add(TypeFilter.ADDRESS.toString().toLowerCase());
}})
.build(this);
startAutocomplete.launch(intent);
}
處理 Place Autocomplete 傳回的地址
先前定義 ActivityResultLauncher
,也等於定義了回呼中傳回活動結果時應完成的操作。如果使用者選取某項預測結果,這項結果會透過結果物件中的意圖提供。這個意圖是由 Autocomplete.IntentBuilder
所建立,因此 Autocomplete.getPlaceFromIntent()
方法可以從中擷取 Place 物件。
private final ActivityResultLauncher<Intent> startAutocomplete = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent intent = result.getData();
if (intent != null) {
Place place = Autocomplete.getPlaceFromIntent(intent);
// Write a method to read the address components from the Place
// and populate the form with the address components
Log.d(TAG, "Place: " + place.getAddressComponents());
fillInAddress(place);
}
} else if (result.getResultCode() == Activity.RESULT_CANCELED) {
// The user canceled the operation.
Log.i(TAG, "User canceled autocomplete");
}
});
接著,呼叫 Place.getAddressComponents()
並比對各地址元件與地址表單中的相應輸入欄位,在欄位內填入使用者所選地點的值。
從預測結果 (而非手動輸入的地址) 擷取地址資料,有助於提高地址的正確性,不但能確保該地址是已知的送貨地址,還能減少所需的使用者按鍵動作數。
導入 Place Autocomplete 的注意事項
如果不只想使用小工具,Place Autocomplete 功能亦提供多種選項,讓您可靈活導入此功能。您可以搭配使用多項服務,取得正確比對位置所需的資訊。
以地址表單來說,您可以將 types 參數設為
address
,限制系統只比對出完整的街道地址。進一步瞭解 Place Autocomplete 要求支援的類型。如果不需搜尋全世界的地址,可以設定適當的限制和偏好。您可利用多個參數來限定或限制只針對特定區域進行比對。
使用
RectangularBounds
即可設定限制區域的矩形邊界,使用setLocationRestriction()
即可確保系統只會傳回這些區域中的地址。使用
setCountries()
即可將回應限制於特定的一組國家/地區。
建議將欄位設為可編輯,萬一比對結果遺漏部分欄位,客戶可以視情況更新地址。Place Autocomplete 傳回的地址大多都不包含次要場所號碼 (例如公寓、套房或住宅號碼),因此您可以將重點移至第 2 行地址,鼓勵使用者視需要填入資訊。
讓使用者能目視確認地址
此範例使用:Maps SDK for Android | 同時支援:iOS | JavaScript |
輸入地址時,讓使用者能在地圖上目視確認地址,如此就能再次確保輸入的地址正確無誤。
下圖在地址下方顯示地圖,且在輸入的地址上放了一個圖釘。
以下範例遵循在 Android 中新增地圖的基本步驟。詳情請參閱說明文件。
- 加入
SupportMapFragment
(在本例中為動態新增片段) - 取得片段的處理常式並註冊回呼
- 在地圖中設定樣式及加入標記
- 停用地圖控制項
加入 SupportMapFragment
首先,在版面配置 XML 檔案中加入 SupportMapFragment
片段。
<fragment
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/confirmation_map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
接著,透過程式輔助的方式加入該片段 (如果不存在)。
private void showMap(Place place) {
coordinates = place.getLatLng();
// It isn't possible to set a fragment's id programmatically so we set a tag instead and
// search for it using that.
mapFragment = (SupportMapFragment)
getSupportFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG);
// We only create a fragment if it doesn't already exist.
if (mapFragment == null) {
mapPanel = ((ViewStub) findViewById(R.id.stub_map)).inflate();
GoogleMapOptions mapOptions = new GoogleMapOptions();
mapOptions.mapToolbarEnabled(false);
// To programmatically add the map, we first create a SupportMapFragment.
mapFragment = SupportMapFragment.newInstance(mapOptions);
// Then we add it using a FragmentTransaction.
getSupportFragmentManager()
.beginTransaction()
.add(R.id.confirmation_map, mapFragment, MAP_FRAGMENT_TAG)
.commit();
mapFragment.getMapAsync(this);
} else {
updateMap(coordinates);
}
}
取得片段的處理常式並註冊回呼
如要取得片段的處理常式,請呼叫
FragmentManager.findFragmentById
方法,並傳遞版面配置檔案中的片段資源 ID。如果您動態新增片段,請跳過此步驟,因為已擷取到處理常式。呼叫
getMapAsync
方法來設定片段的回呼。
舉例來說,如果您靜態新增片段:
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
在地圖中設定樣式及加入標記
地圖準備就緒後,請設定樣式,將相機置中,然後在所輸入地址的座標加入標記。以下程式碼使用 JSON 物件中定義的樣式,您也可以載入使用雲端式地圖樣式設定定義的地圖 ID。
@Override
public void onMapReady(@NonNull GoogleMap googleMap) {
map = googleMap;
try {
// Customise the styling of the base map using a JSON object defined
// in a string resource.
boolean success = map.setMapStyle(
MapStyleOptions.loadRawResourceStyle(this, R.raw.style_json));
if (!success) {
Log.e(TAG, "Style parsing failed.");
}
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Can't find style. Error: ", e);
}
map.moveCamera(CameraUpdateFactory.newLatLngZoom(coordinates, 15f));
marker = map.addMarker(new MarkerOptions().position(coordinates));
}
停用地圖控制項
如要顯示位置而不顯示其他地圖控制項 (例如指南針、工具列或其他內建功能),讓地圖更加簡潔,不妨停用您認為不必要的控制項。在 Android 上,另一種方法是啟用精簡模式來提供有限的互動功能。
將使用者輸入的地址與裝置的所在位置進行比對
有時要取得地址證明 (確認使用者位於輸入的地址) 可能不是那麼容易,原因包括使用者位置偏遠、使用者搬遷至新地址,或是數位業務 (例如數位銀行) 沒有可供造訪的實體據點,使用者無法現場提供公用事業費帳單或其他文件等地址證明。讓使用者能以數位方式驗證地址,您就能提供更快速、更流暢的註冊體驗。
安全性是檢查地址時的首要考量,在數位註冊流程中更是如此。本節提供指引和範例,協助您確認註冊期間使用者的所在位置,是否與他們自行輸入的位置相符。
比對輸入的地址與裝置位置時,需要完成下列步驟:
- 將使用者輸入的地址轉換成地理座標。
- 提示使用者授予裝置位置資訊的存取權。
- 計算輸入的地址與裝置位置之間的距離。您必須設定能夠成功比對前述兩者的最遠距離。
下圖舉例說明如何提示使用者比對他們輸入的地址與目前的所在位置。
將使用者輸入的地址轉換成地理座標
此範例使用:Places SDK for Android | 同時支援:iOS | JavaScript | Geocoding API |
在使用者同意驗證地址後 (輕觸上圖中的「Verify I'm there now」(驗證目前位置)),比對地址與目前所在位置的第一個步驟,就是將輸入的地址轉換成地理座標。
如果使用者透過 Place Autocomplete 選取地址,請務必在 Place Autocomplete 欄位清單中要求 Place.Field.LAT_LNG
(如「新增 Place Autocomplete 小工具」程式碼片段所示),然後呼叫 Place.getLatLng()
方法取得所選地址的地理座標。
coordinates = place.getLatLng();
如果 Place Autocomplete 在欄位中填入資訊後,使用者又手動輸入地址或進行編輯,請使用 Android 地理編碼器服務或 Geocoding API 查詢與該地址對應的座標。
範例
https://maps.googleapis.com/maps/api/geocode/json?address=1600%20Amphitheatre%2BParkway%2C%20Mountain%20View%2C%20CA%2094043&key=
請務必對 Geocoding API 呼叫進行網址編碼。
網址編碼快速參照:%20
= 空格,%2B
= + (加號),%2C
= , (半形逗號)
提示使用者授予裝置位置資訊的存取權
如要取得使用者裝置的位置資訊,您必須要求使用者權限才能啟用定位服務。請查看與建構位置辨識應用程式相關的 Android 說明文件,然後按照其中的指引執行下列流程:
要求一次性授予精確位置資訊的存取權 (
ACCESS_FINE_LOCATION
)。如果使用者授予位置資訊存取權,請取得相關位置資訊。
如果使用者拒絕提供位置資訊存取權,請妥善回應。舉例來說,您可以顯示下列類型的訊息 (假設您沒有儲存使用者目前的位置資訊):
「如果不允許應用程式知道您的精確位置,則必須透過郵件進行驗證,才能啟用帳戶。[確定]」
下圖是提示使用者授予裝置位置資訊存取權的範例。
如要檢查是否已有位置存取權,請使用 ActivityResultLauncher
製作活動啟動器,從已啟動的活動監聽結果。結果回呼將包含字串,指出使用者授予或拒絕要求的權限。
// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher, as an instance variable.
private final ActivityResultLauncher<String> requestPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
if (isGranted) {
// Since ACCESS_FINE_LOCATION is the only permission in this sample,
// run the location comparison task once permission is granted.
// Otherwise, check which permission is granted.
getAndCompareLocations();
} else {
// Fallback behavior if user denies permission
Log.d(TAG, "User denied permission");
}
});
接著,確認應用程式是否已有 ACCESS_FINE_LOCATION
權限。如果沒有,請使用前一個步驟中定義的啟動器啟動權限要求活動,向使用者要求這項權限。
private void checkLocationPermissions() {
if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
getAndCompareLocations();
} else {
requestPermissionLauncher.launch(
ACCESS_FINE_LOCATION);
}
}
獲得 ACCESS_FINE_LOCATION
權限後,請使用整合式位置預測提供工具,取得裝置的最後已知位置,並據此建立 LatLng
物件。
FusedLocationProviderClient fusedLocationClient =
LocationServices.getFusedLocationProviderClient(this);
fusedLocationClient.getLastLocation()
.addOnSuccessListener(this, location -> {
// Got last known location. In some rare situations this can be null.
if (location == null) {
return;
}
deviceLocation = new LatLng(location.getLatitude(), location.getLongitude());
// ...
});
}
計算輸入的地址與裝置位置之間的距離
使用數學計算兩個經緯度座標 (輸入的地址和裝置位置) 之間的距離。開放原始碼的 Maps SDK for Android 公用程式庫提供幾個實用方法,可計算地球上兩點之間的大圓距離。
首先,將以下依附元件加入應用程式的 build.gradle.kts
檔案,安裝 Maps SDK for Android 公用程式庫:
dependencies {
// Utility Library for Maps SDK for Android
// You do not need to add a separate dependency for the Maps SDK for Android
// since this library builds in the compatible version of the Maps SDK.
implementation("com.google.maps.android:android-maps-utils:3.8.2")
}
接著,在取得最後已知裝置位置後返回活動檔案,定義可將這兩個位置視為「相符」的半徑 (以公尺為單位)。半徑必須夠長,才能一併考量 GPS 精確度變數及使用者輸入地址的地點規模。舉例來說:
private static final double acceptableProximity = 150;
接著,使用公用程式庫方法 computeDistanceBetween()
,計算裝置位置和使用者輸入的地址位置之間的距離。只要距離落在前述定義的半徑範圍內,就算是位置相符。
// Use the computeDistanceBetween function in the Maps SDK for Android Utility Library
// to use spherical geometry to compute the distance between two Lat/Lng points.
double distanceInMeters = computeDistanceBetween(deviceLocation, enteredLocation);
if (distanceInMeters <= acceptedProximity) {
Log.d(TAG, "location matched");
// TODO: Display UI based on the locations matching
} else {
Log.d(TAG, "location not matched");
// TODO: Display UI based on the locations not matching
}
如果地址和位置相符,請在應用程式中顯示確認通知,如下圖所示。
進一步提升「經過驗證的快速註冊」體驗的提示
允許使用者根據商家或搜尋點名稱輸入地址。「預先輸入」預測服務除了可用於地址,您也可以允許使用者輸入商家或地標名稱。為了讓使用者能輸入地址和地點名稱,請從「自動完成」定義中移除 types
屬性。
自訂 Place Autocomplete 方塊的外觀和風格,搭配您的網站設計。如果想在應用程式中控制 Place Autocomplete 的外觀和風格,而不使用 Google 的小工具,您可以使用 Place Autocomplete 程式輔助功能,支援透過 Place Autocomplete 服務建立的使用者介面。