在 Wear OS 上使用 Maps API

穿戴式设备上的地图

利用 Maps SDK for Android,您可以构建可直接在 Wear OS by Google 谷歌设备上运行且基于地图的穿戴式应用。使用您应用的用户只需瞥一眼手腕,便能在地图上了解自己的位置。举例来说,他们可以将自己的位置标绘在路线上,然后放大查看详图,或者点按标记来查看您的应用提供的信息窗口。

本页面介绍了可在 Wear 设备上使用的 API 功能,有助于您着手构建自己的应用。

开始基于 Wear OS 开发应用

您可以借助 Maps SDK for Android 构建穿戴式应用,方法与为其他 Android 设备构建 Google 地图应用基本相同,区别在于,您的设计要适合穿戴式设备较小的机身,以便提高应用易用性和性能。

推荐您使用 Android Studio 开发 Wear OS 应用,因为该工具便于进行项目设置、纳入库和打包。

如果在设计穿戴式应用方面需要一般性的帮助,请参阅 Wear OS 设计指南。如果您在构建首款穿戴式应用时需要帮助,请参阅构建穿戴式应用的相关指南。

基于 Wear OS 构建您的首款地图应用

本快速指南假定您熟悉 Maps SDK for Android,已经遵照 Wear OS 指南在您的应用内创建了穿戴式设备模块,并且现在想要向穿戴式设备模块添加地图。

为您的穿戴式设备模块添加依赖项

应确保应用内 Wear OS 模块的 build.gradle.kts 文件中包含下列依赖项:

dependencies {
    // ...
    compileOnly("com.google.android.wearable:wearable:2.9.0")
    implementation("com.google.android.support:wearable:2.9.0")
    implementation("com.google.android.gms:play-services-maps:19.0.0")

    // This dependency is necessary for ambient mode
    implementation("androidx.wear:wear:1.3.0")
}

如需详细了解这些依赖项,请参阅在现有项目中添加 Wear OS 模块的相关指南。

实现滑动关闭手势并设置初始背景颜色

建议您使用 SwipeDismissFrameLayout 在穿戴式设备上显示地图。使用 SwipeDismissFrameLayout 类,您可以实现滑动关闭手势,让用户可以通过从屏幕最左边向右滑动来退出应用。

若要设置自定义的初始背景颜色,可以使用 map:backgroundColor XML 属性定义在实际地图图块加载前显示的颜色。

SwipeDismissFrameLayoutbackgroundColor 元素作为 SupportMapFragment 的容器添加到布局定义中:

  <androidx.wear.widget.SwipeDismissFrameLayout
      android:id="@+id/map_container"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        map:backgroundColor="#fff0b2dd" />
  </androidx.wear.widget.SwipeDismissFrameLayout>

当您获取 activity 中的 SwipeDismissFrameLayout 对象时,请添加一个回调函数并将该回调函数的行为设置为执行必要的关闭操作,如下所示:

Kotlin

class MainActivity : AppCompatActivity(), OnMapReadyCallback,
                     AmbientModeSupport.AmbientCallbackProvider {


    public override fun onCreate(savedState: Bundle?) {
        super.onCreate(savedState)

        // Set the layout. It only contains a SupportMapFragment and a DismissOverlay.
        setContentView(R.layout.activity_main)

        // Enable ambient support, so the map remains visible in simplified, low-color display
        // when the user is no longer actively using the app but the app is still visible on the
        // watch face.
        val controller = AmbientModeSupport.attach(this)
        Log.d(MainActivity::class.java.simpleName, "Is ambient enabled: " + controller.isAmbient)

        // Retrieve the containers for the root of the layout and the map. Margins will need to be
        // set on them to account for the system window insets.
        val mapFrameLayout = findViewById<SwipeDismissFrameLayout>(R.id.map_container)
        mapFrameLayout.addCallback(object : SwipeDismissFrameLayout.Callback() {
            override fun onDismissed(layout: SwipeDismissFrameLayout) {
                onBackPressed()
            }
        })

        // Obtain the MapFragment and set the async listener to be notified when the map is ready.
        mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)
    }

    // ...
}

      

Java

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback,
    AmbientModeSupport.AmbientCallbackProvider {


    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);

        // Set the layout. It only contains a SupportMapFragment and a DismissOverlay.
        setContentView(R.layout.activity_main);

        // Enable ambient support, so the map remains visible in simplified, low-color display
        // when the user is no longer actively using the app but the app is still visible on the
        // watch face.
        AmbientModeSupport.AmbientController controller = AmbientModeSupport.attach(this);
        Log.d(MainActivity.class.getSimpleName(), "Is ambient enabled: " + controller.isAmbient());

        // Retrieve the containers for the root of the layout and the map. Margins will need to be
        // set on them to account for the system window insets.
        final SwipeDismissFrameLayout mapFrameLayout = (SwipeDismissFrameLayout) findViewById(
            R.id.map_container);
        mapFrameLayout.addCallback(new SwipeDismissFrameLayout.Callback() {
            @Override
            public void onDismissed(SwipeDismissFrameLayout layout) {
                onBackPressed();
            }
        });

        // Obtain the MapFragment and set the async listener to be notified when the map is ready.
        mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    // ...
}

      

添加地图

照常使用 onMapReady(GoogleMap) 回调方法获取 GoogleMap 对象的句柄。此回调方法将在地图做好使用准备时触发。在此回调方法中,您可以向地图添加标记或多段线、添加监听器或移动镜头。以下示例代码用于在悉尼歌剧院附近添加一个标记:

Kotlin

private val sydney = LatLng(-33.85704, 151.21522)

override fun onMapReady(googleMap: GoogleMap) {
    // Add a marker with a title that is shown in its info window.
    googleMap.addMarker(
        MarkerOptions().position(sydney)
            .title("Sydney Opera House")
    )

    // Move the camera to show the marker.
    googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 10f))
}

      

Java

private static final LatLng SYDNEY = new LatLng(-33.85704, 151.21522);

@Override
public void onMapReady(@NonNull GoogleMap googleMap) {
    // Add a marker with a title that is shown in its info window.
    googleMap.addMarker(new MarkerOptions().position(SYDNEY)
        .title("Sydney Opera House"));

    // Move the camera to show the marker.
    googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 10));
}

      

启用氛围模式

Maps SDK for Android 支持为穿戴式应用启用氛围模式。支持氛围模式的应用有时称作“始终开启”的应用。氛围模式会在用户当前未使用应用时激活,从而让应用在穿戴式设备上保持可见。

Maps SDK for Android 提供了一种简化的低色度地图渲染方式,可在氛围模式下使用;当设备从交互模式切换到氛围模式时,地图样式会自动调整。在氛围模式下,所有标记、对象和界面控件都会消失。这可以降低应用的功耗,并可确保与表盘主题等其他氛围应用保持一致的外观和风格。

如果要确保您的应用使用地图的氛围模式,请按下列步骤操作:

  1. 更新您的 Android SDK,使其包含 Android 6.0 (API 23) 或更高版本的平台;这些平台提供的 API 可让 activity 进入氛围模式。如需了解如何更新 SDK,请参阅有关添加 SDK 软件包的 Android 文档。
  2. 通过在应用清单中将 targetSdkVersion 设置为 23 或更高版本,确保您的项目的目标平台为 Android 6.0 或更高版本。
  3. 向应用的 build.gradle.kts 文件添加穿戴式设备依赖项。请参阅本页面上的示例
  4. 按照保持应用可见的 Android 培训课程中所述,向穿戴式应用清单文件添加穿戴式设备共享库条目。
  5. 按照保持应用可见的 Android 培训课程中所述,向手持设备和穿戴式应用清单文件添加 WAKE_LOCK 权限。
  6. 在您的 activity 的 onCreate() 方法中,调用 AmbientModeSupport.attach() 方法。其作用是指示操作系统让应用始终开启,这样在设备休眠时,应用就会进入氛围模式而不是恢复为表盘主题。
  7. 在您的 activity 中实现 AmbientModeSupport.AmbientCallbackProvider 接口,使其可以接收氛围模式状态更改。
  8. 设置地图,使其支持氛围模式。为此,可以在 activity 的 XML 布局文件中设置 map:ambientEnabled="true" 属性,或者通过设置 GoogleMapOptions.ambientEnabled(true) 以编程方式做到这一点。此设置的作用是告知 API 必须预先加载必要的地图图块,以供在氛围模式下使用。
  9. 当 activity 切换到氛围模式时,系统会在您提供的 AmbientCallback 中调用 onEnterAmbient() 方法。重写 onEnterAmbient() 并调用 SupportMapFragment.onEnterAmbient(ambientDetails)MapView.onEnterAmbient(ambientDetails)。该 API 便会切换到非交互式、低色度地图渲染方式。
  10. 同样,在 onExitAmbient() 中调用 SupportMapFragment.onExitAmbient()MapView.onExitAmbient()。API 便会切换到正常的地图渲染方式。

以下代码示例用于在 activity 中启用氛围模式:

Kotlin

class AmbientActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {

    private lateinit var mapFragment: SupportMapFragment

    public override fun onCreate(savedState: Bundle?) {
        super.onCreate(savedState)

        // Set the layout. It only contains a SupportMapFragment and a DismissOverlay.
        setContentView(R.layout.activity_main)

        // Enable ambient support, so the map remains visible in simplified, low-color display
        // when the user is no longer actively using the app but the app is still visible on the
        // watch face.
        val controller = AmbientModeSupport.attach(this)
        Log.d(AmbientActivity::class.java.simpleName, "Is ambient enabled: " + controller.isAmbient)

        // Obtain the MapFragment and set the async listener to be notified when the map is ready.
        mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment
    }

    override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback {
        return object : AmbientModeSupport.AmbientCallback() {
            /**
             * Starts ambient mode on the map.
             * The API swaps to a non-interactive and low-color rendering of the map when the user is no
             * longer actively using the app.
             */
            override fun onEnterAmbient(ambientDetails: Bundle) {
                super.onEnterAmbient(ambientDetails)
                mapFragment.onEnterAmbient(ambientDetails)
            }

            /**
             * Exits ambient mode on the map.
             * The API swaps to the normal rendering of the map when the user starts actively using the app.
             */
            override fun onExitAmbient() {
                super.onExitAmbient()
                mapFragment.onExitAmbient()
            }
        }
    }
}

      

Java

public class AmbientActivity extends AppCompatActivity implements
    AmbientModeSupport.AmbientCallbackProvider {

    private SupportMapFragment mapFragment;

    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);

        // Set the layout. It only contains a SupportMapFragment and a DismissOverlay.
        setContentView(R.layout.activity_main);

        // Enable ambient support, so the map remains visible in simplified, low-color display
        // when the user is no longer actively using the app but the app is still visible on the
        // watch face.
        AmbientModeSupport.AmbientController controller = AmbientModeSupport.attach(this);
        Log.d(AmbientActivity.class.getSimpleName(), "Is ambient enabled: " + controller.isAmbient());

        // Obtain the MapFragment and set the async listener to be notified when the map is ready.
        mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.map);
    }

    @Override
    public AmbientCallback getAmbientCallback() {
        return new AmbientCallback() {
            /**
             * Starts ambient mode on the map.
             * The API swaps to a non-interactive and low-color rendering of the map when the user is no
             * longer actively using the app.
             */
            @Override
            public void onEnterAmbient(Bundle ambientDetails) {
                super.onEnterAmbient(ambientDetails);
                mapFragment.onEnterAmbient(ambientDetails);
            }

            /**
             * Exits ambient mode on the map.
             * The API swaps to the normal rendering of the map when the user starts actively using the app.
             */
            @Override
            public void onExitAmbient() {
                super.onExitAmbient();
                mapFragment.onExitAmbient();
            }
        };
    }
}

      

您可以在应用处于氛围模式时更新屏幕。如需详细了解如何更新内容以及大致了解氛围模式,请参阅保持应用可见的 Android 培训课程。

在 Wear OS 上使用街景

穿戴式设备全面支持街景

如需允许用户在查看街景全景图片时退出应用,请使用 StreetViewPanorama.OnStreetViewPanoramaLongClickListener 接口来监听长按手势。用户长按街景图片上的某处时,您会收到一个 onStreetViewPanoramaLongClick(StreetViewPanoramaOrientation) 事件。调用 DismissOverlayView.show() 可显示退出按钮。

示例代码

您可以用 GitHub 上提供的一个示例应用作为基础来开发自己的应用。该示例向您展示了如何在 Wear OS 上设置基本 Google 地图。

Wear OS 上支持的 Maps API 功能

此部分概要介绍了穿戴式设备与手持设备(手机和平板电脑)在支持的地图功能上存在的差异。所有下文未提及的 API 功能都应按照完整 API 文档中记载的方式工作。

功能
完全互动模式和精简模式

您可以在完全互动模式或精简模式下使用 Maps SDK for Android。如果您想提升应用在穿戴式设备上的性能,并且您的应用不需要支持手势或平移和缩放地图等交互,可考虑使用精简模式。

在精简模式下,用于在用户点按地图时启动 Google 地图移动应用的 intent 处于停用状态,无法在穿戴式设备上启用。

如需查看精简模式与完全互动模式之间差异的完整列表,请参阅精简模式文档。

地图工具栏 地图工具栏在穿戴式设备上处于停用状态,并且无法启用。
界面控件 默认情况下,界面控件在穿戴式设备上处于停用状态。这包括缩放、罗盘和“我的位置”控件。您可以照常利用 UiSettings 类启用这些控件。
手势 单点触摸手势会按预期运行。举例来说,轻触并拖动可平移地图,点按两次可放大,双指点按可缩小。对多点触摸手势的支持因用户设备而异。举例来说,多点触控手势包括双指上推使地图倾斜,双指张合进行缩放,以及双指旋转。
室内地图和建筑物 默认情况下,室内地图在穿戴式设备上处于停用状态。您可以通过调用 GoogleMap.setIndoorEnabled(true) 来启用它们。如果启用了室内地图,地图将显示默认楼层。穿戴式设备不支持楼层选择器界面元素。
图块叠加层 穿戴式设备不支持图块叠加层

使用 Maps API 在 Wear OS 上开发应用的最佳做法

如何在您的应用中提供最佳用户体验:

  • 地图应占据大部分屏幕。这是为提高地图在穿戴式设备这种小机身上的易用性所必须采取的措施。
  • 在设计应用的用户体验时,请将穿戴式设备电池电量较低的因素考虑在内。保持屏幕的活动状态和地图的可见状态会影响电池性能。