经过验证的快速注册实现指南

概览

Web iOS API

Google Maps Platform 适用于 Web(JS、TS)、Android 和 iOS,还提供用于获取地点、路线和距离相关信息的网络服务 API。本指南中的示例适用于单个平台,如需在其他平台实现经过验证的快速注册,请参阅提供的文档链接。

立即构建!

借助 Google Cloud 控制台中的快速构建器,您可以使用能够为您生成 JavaScript 代码的交互式界面来构建地址表单自动补全功能。

用户已经习惯在数字世界中生活和处理事务,而方便、快捷和安全是用户对数字世界的基本期望。在办理信用卡、银行账户或贷款申请等业务时,用户希望过程既快速又轻松。

用户反复输入重复数据的次数越多,您将其留住并转化为客户的几率就越小。打造快速、轻松且经过验证的注册体验,不仅可改善用户体验,同时还有助于吸引用户停留在您的网站上。

手动输入地址可能会导致转化次数减少、CRM 数据出现错误,以及发生代价高昂的配送错误。借助经过验证的快速注册,用户可以更快速地完成注册,只需动动拇指,点按几下,系统便会立即推荐附近的地址,同时还会显示输入的地址以供用户直观地进行确认,这样用户就可以确信自己输入的地址正确无误。使用用户当前的位置信息验证其地址还有助于防范欺诈和增强用户对您产品和服务的信心。此外,验证也能让您更安心地即时提供虚拟银行和信用卡服务。

本主题提供了实现指南,可帮助您了解如何使用 Google Maps Platform 打造经过验证的快速注册体验。由于用户最有可能在移动设备上进行注册,因此本主题中的大部分实现示例会侧重于 Android 平台(如需查看完整的示例源代码,请点击此处)。您也可以使用 iOS SDK 完成相同的操作。

下图显示了构建该解决方案所需的核心 API(点击可放大)。

启用 API

若要实现这些建议的做法,您必须在 Google Cloud 控制台中启用以下 API:

如需详细了解设置方法,请参阅 Google Maps Platform 使用入门

最佳实践部分

以下是本主题涵盖的实践和自定义设置。

  • 对勾标记图标表示核心最佳实践。
  • 星形图标表示可选但建议采用的自定义设置,采用后可以强化解决方案。
为输入字段添加自动补全功能 自动填充地址表单。添加“提前输入”(type-as-you-go) 功能,以改进所有平台上的用户体验,在尽可能减少按键操作的同时,提高地址准确性。
直观呈现地址的确认信息 在地图上显示用户的地址,让用户直观地确认自己输入的地址正确无误。
将用户输入的地址与设备位置进行比较 将用户选择或输入的地址与其设备的当前位置进行比较,从而确定用户是否位于指定的地址。(用户应在家中完成注册,此功能才能生效。)
关于进一步提升经过验证的快速注册体验的提示 您可以使用一些额外功能(例如,自定义自动补全 widget 的外观和风格,或者允许用户选择商家或地标的名称作为地址),进一步提升地址输入体验。

为输入字段添加自动补全功能

此示例使用的是Places SDK for Android 同样适用于iOS | JavaScript

地点自动补全功能可简化您应用中的地址输入操作,从而提高转化率并为客户提供顺畅的注册体验。自动补全功能提供了一个带有“提前输入”地址预测功能的快速输入字段,该预测功能可用于自动填充注册地址表单。将地点自动补全功能添加到您的注册流程后,您可以实现以下目的:

  • 减少地址输入错误。
  • 减少注册流程中的步骤。
  • 简化移动设备或穿戴式设备上的地址输入体验。
  • 大大减少客户注册所需的键盘操作和总时间。

用户选择自动补全输入框并开始输入时,系统会显示一个地址预测列表。

当用户从预测列表中选择地址时,您可以使用响应来验证地址并获取位置信息。然后,您的应用就可以填充地址输入表单中的相应字段,如下图所示。

视频:通过地点自动补全功能提升地址表单体验

地址表单

Android

iOS

Web

Google Maps Platform 提供了适用于移动平台和网站的地点自动补全 widget。如上图所示,该 widget 提供了一个内置自动补全功能的搜索对话框,甚至可让您针对位置范围的搜索进行优化。

本部分介绍了如何为经过验证的快速注册实现地点自动补全功能。

添加地点自动补全 widget

在 Android 中,您可以使用自动补全 intent 来添加自动补全 widget,该 intent 可从地址行 1 的输入字段(用户将在此开始输入地址)启动地点自动补全功能。用户开始输入时,将可以从自动补全预测结果列表中选择地址。

首先,使用 ActivityResultLauncher 准备一个 activity 启动器,用于监听已启动 activity 的结果。结果回调将包含一个与用户从自动补全预测结果中选择的地址对应的 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");
                }
            });

接下来,定义地点自动补全 intent 的字段、位置和类型属性,并使用 Autocomplete.IntentBuilder 进行构建。最后,使用上一个代码示例中定义的 ActivityResultLauncher 启动 intent。

    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);
    }

处理地点自动补全功能返回的地址

之前定义 ActivityResultLauncher 时还定义了回调中返回 activity 结果时应执行的操作。如果用户选择了某个预测的地址,系统将在结果对象中包含的 intent 中提供该地址。由于 intent 是通过 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(),并将各个地址组成部分与其在地址表单中的对应输入字段进行匹配,从而在字段中填充用户所选地点的值。

通过从预测结果(而不是手动输入的地址)中捕获地址数据,您不仅可以确保地址准确无误,还可确保相应地址是可配送到的已知地址,同时还可减少用户的按键操作。

实现地点自动补全功能时的注意事项

如果您还想使用除 widget 以外的其他方式,地点自动补全功能提供的多种选项可以帮助您灵活实现。您可以结合使用多种服务,获取正确匹配位置所需的准确信息。

  • 对于地址表单,请将 types 参数设为 address,以将匹配地址限制为完整的街道地址。您可以详细了解地点自动补全请求支持的类型

  • 如果您不需要在世界范围内进行搜索,请设定适当的限制和偏向。您可以使用多个参数来设定偏向和限制,从而将所有匹配结果限制在特定区域。

    • 使用 RectangularBounds 设置矩形边界,通过这些边界来限定一个区域,并使用 setLocationRestriction() 确保系统仅返回这些区域中的地址。

    • 使用 setCountries() 将响应限制为一组特定的国家/地区。

  • 使字段保持可修改状态,以防匹配结果中缺失某些字段,并可让客户根据需要更新地址。由于地点自动补全功能返回的大多数地址都不包含 subpremise 编号(例如公寓、套房或单元编号),因此您可以重点关注地址行 2,鼓励用户根据需要填写相关信息。

直观呈现地址的确认信息

此示例使用的是Maps SDK for Android 同样适用于iOS | JavaScript

在用户输入地址时,请在地图上向其直观呈现地址的确认信息。这能再次保证用户地址信息填写正确。

在下图中,输入的地址下方显示了一个地图,且地图上输入的地址所在位置放置了一枚图钉。

下例遵循了在 Android 中添加地图的基本步骤。如需了解详情,请参阅相关文档。

添加 SupportMapFragment

首先,将 SupportMapFragment fragment 添加到布局 XML 文件中。

    <fragment
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:id="@+id/confirmation_map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

然后,以编程方式添加该 fragment(如果不存在的话)。

    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);
        }
    }

获取 fragment 的句柄并注册回调函数

  1. 若要获取相应 fragment 的句柄,请调用 FragmentManager.findFragmentById 方法,并向其传递布局文件中的 fragment 的资源 ID。如果您是以动态方式添加该 fragment 的,请跳过此步骤,因为您已经检索了句柄。

  2. 调用 getMapAsync 方法,在 fragment 上设置回调。

例如(假如您是以静态方式添加 fragment 的):

Java


SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
    .findFragmentById(R.id.map);
mapFragment.getMapAsync(this);

      

Kotlin


val mapFragment = supportFragmentManager
    .findFragmentById(R.id.map) as SupportMapFragment
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 上,另一种方法是启用精简模式来提供一定程度的互动。

将用户输入的地址与设备位置进行比较

获取地址证明(用于确认用户位于其输入的地址)可能会因诸多因素而变得复杂,例如:用户位置偏远、用户要搬迁到新地址,或者数字业务(例如数字银行)没有可供用户到访的实体营业地点,导致用户无法在线下提供公共事业缴费单或其他文件作为地址证明。通过为用户提供数字化的地址验证方式,您可以实现更快更流畅的注册体验。

安全性在检查地址时至关重要,尤其是在数字注册流程中。本部分提供了一些指导和示例,可帮助您检查用户注册时所在位置是否与其自行输入的地址相匹配。

将用户输入的地址与设备位置进行比较这一流程涉及以下步骤:

  1. 将用户输入的地址转换为地理坐标
  2. 提示用户授予获取其设备位置信息的权限
  3. 计算用户输入的地址与设备位置之间的距离。您需要设定一个最远距离,超过此距离即认为用户输入的地址与设备位置不匹配。

下图举例说明了如何提示用户将他们输入的地址与当前所在位置进行比较。

将用户输入的地址转换为地理坐标

此示例使用的是Places SDK for Android 同样适用于iOS | JavaScript | Geocoding API

在用户同意进行地址验证后(通过轻触上图中的“Verify I'm there now”[验证当前位置]),将用户输入的地址转换为地理坐标,这是将用户输入的地址与当前所在位置进行比较的第一步。

如果用户是通过地点自动补全功能选择的地址,请务必在地点自动补全字段列表中请求 Place.Field.LAT_LNG(如添加地点自动补全 widget 代码段中所示),并调用 Place.getLatLng() 方法来获取所选地址的地理坐标。

coordinates = place.getLatLng();

如果用户是在地点自动补全功能填充字段后手动输入了地址或进行了修改,请使用 Android Geocoder 服务Geocoding API 查找与该地址对应的坐标。

示例

https://maps.googleapis.com/maps/api/geocode/json?address=1600%20Amphitheatre%2BParkway%2C%20Mountain%20View%2C%20CA%2094043&key=YOUR_API_KEY

请务必对 Geocoding API 的调用进行网址编码

网址编码快速参考:%20 = space、%2B = + (plus)、%2C = , (comma)

提示用户授予获取其设备位置信息的权限

如需获取用户的设备位置信息,您需要请求用户权限以启用位置信息服务。请按照 Android 文档中有关如何构建位置感知应用的指导来实现以下流程:

  • 请求一次性授予访问精确位置信息的权限 (ACCESS_FINE_LOCATION)。

  • 如果用户授予位置信息访问权限,请获取用户位置信息。

  • 如果用户拒绝授予位置信息访问权限,请妥善应对拒绝。例如,您可以显示以下类型的消息(假设您目前没有存储用户当前的位置信息):

    “如果您不允许应用知道您的确切位置,则需要通过邮件进行验证,以便启用您的账号。[确定]”

下图是提示用户授予获取设备位置信息权限的示例。

如需检查是否具有位置权限,请使用 ActivityResultLauncher 准备一个 activity 启动器,用于监听已启动的 activity 的结果。结果回调将包含一个字符串,用于指明用户是授予还是拒绝了所请求的权限。

    // 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 权限。如果没有,请使用上一步中定义的启动器启动权限请求 activity,请求用户授予该权限。

    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 实用程序库提供了一些便捷方法,可用于计算地球上两点间的球面距离

首先,安装 Maps SDK for Android 实用程序库,只需将以下依赖项添加到您应用的 build.gradle 文件中即可:

dependencies {
    implementation 'com.google.maps.android:android-maps-utils:3.4.0'

}

然后,在获取设备最近一次的已知位置后返回到 activity 文件,定义一个可将两个位置视为“匹配”的半径(以米为单位)。该半径应当足够大,以便将 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 属性。

自定义地点自动补全框的外观和风格,使其与您的网站样式相符。如果您更喜欢在应用中控制地点自动补全功能的外观和风格,而非使用 Google 的 widget,则可以编程方式使用地点自动补全功能,为使用地点自动补全服务构建的界面提供支持。