在 Wear OS 上使用 Maps API

穿戴式裝置上的地圖

您可以使用 Maps SDK for Android,建立能直接在 Wear OS by Google 裝置上執行的地圖式穿戴式應用程式。應用程式使用者只要看一下手腕上的裝置,就能在地圖上查看目前的所在位置。舉例來說,使用者可以在路徑上定位,然後放大地圖查看詳細資料,或輕觸標記來查看應用程式提供的資訊視窗。

本頁面說明 Wear 裝置提供的 API 功能,並協助您開始建構應用程式。

開始使用 Wear OS

和建構其他 Android 裝置的 Google 地圖應用程式相比,使用 Maps SDK for Android 建構穿戴式應用程式的做法,基本上沒有太大差異。主要差別在於,為提升應用程式的可用性和效能,設計上需配合穿戴式裝置較小的板型規格。

建議您使用 Android Studio 開發 Wear OS,因為這項工具可以用來設定專案,而且內附程式庫,產生套件 (APK 檔案) 也很便利。

如需設計穿戴式應用程式方面的一般性說明,請參閱「Wear OS 設計指南」。如需建立第一個穿戴式應用程式的相關說明,請參閱建立穿戴式應用程式指南。

在 Wear OS 上建構第一個地圖應用程式

本快速指南假設您已熟悉如何使用 Maps SDK for Android,且已按照 Wear OS 指南說明,在應用程式中建立穿戴式模組,現在則想在穿戴式模組中加入地圖。

為 Wear OS 模組新增依附元件

請確認應用程式 Wear OS 模組的 build.gradle 檔案中包含下列依附元件:

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:18.2.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>

在活動中取得 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 會顯示較不顯色的簡化版地圖以供使用,且當裝置從互動模式切換為微光模式時,地圖樣式也會自動調整。微光模式啟用時,所有標記、物件和 UI 控制項都會消失;這樣可以減少應用程式耗電量,並能與其他使用微光模式的應用程式 (例如錶面) 保持一致的外觀和風格。

請按照下列步驟操作,確保應用程式能使用地圖的微光模式:

  1. 更新 Android SDK 以加入 Android 6.0 (API 23 級) 以上版本平台,就能取得可讓活動進入微光模式的 API。如需 SDK 更新方式的相關資訊,請參閱 Android 說明文件的新增 SDK 套件
  2. 應用程式資訊清單中將 targetSdkVersion 設為 23 以上,確保專案指定 Android 6.0 以上的版本。
  3. 在應用程式的 build.gradle 檔案中加入穿戴式裝置依附元件。請參閱本頁面的範例
  4. 按照 Android 訓練課程「讓應用程式持續顯示」所述,在穿戴式應用程式資訊清單中,加入穿戴式裝置共用程式庫項目。
  5. 按照 Android 訓練課程「讓應用程式持續顯示」所述,在手持裝置和穿戴式應用程式資訊清單中加入 WAKE_LOCK 權限。
  6. 在活動的 onCreate() 方法中呼叫 AmbientModeSupport.attach() 方法,告知作業系統應用程式將持續顯示,這樣裝置關機時才會進入微光模式,而非返回錶面。
  7. 在「活動」中導入 AmbientModeSupport.AmbientCallbackProvider 介面,這樣才能收到微光模式的狀態變更。
  8. 將地圖設為支援微光模式,方法是在活動的 XML 版面配置檔案中設定 map:ambientEnabled="true" 屬性,或是設定 GoogleMapOptions.ambientEnabled(true) 以程式輔助方式進行。這項設定會通知 API 預先載入在微光模式中需要用到的地圖圖塊。
  9. 當活動切換至微光模式時,系統會在您提供的 AmbientCallback 中呼叫 onEnterAmbient() 方法,覆寫 onEnterAmbient() 並呼叫 SupportMapFragment.onEnterAmbient(ambientDetails)MapView.onEnterAmbient(ambientDetails)。API 會切換為較不顯色的非互動式地圖版本。
  10. 同樣地,在 onExitAmbient() 中呼叫 SupportMapFragment.onExitAmbient()MapView.onExitAmbient(),API 就會切換為正常的地圖版本。

下列程式碼範例會在活動中啟用微光模式:

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 地圖行動應用程式,在穿戴式裝置上也是如此。

如需精簡模式和完整互動模式的完整比較清單,請參閱精簡模式說明文件。

地圖工具列 穿戴式裝置上的地圖工具列停用,且無法啟用。
UI 控制項 穿戴式裝置上的 UI 控制項預設為停用,包括縮放、指南針和「我的位置」控制項。您可以照常使用 UiSettings 類別來啟用這些控制項。
手勢 單點觸控手勢會正常運作,例如輕觸並拖曳可平移地圖、輕觸兩下可放大地圖,雙指輕觸則可縮小地圖。 多點觸控手勢支援,會視使用者的裝置而有所不同。 多點觸控手勢範例包括:雙指推動傾斜地圖、雙指撥動縮放,以及雙指旋轉。
室內地圖和建築物 穿戴式裝置上的室內地圖功能預設為停用,您可以呼叫 GoogleMap.setIndoorEnabled(true) 來加以啟用。如果啟用了室內地圖,地圖會顯示預設樓層。 穿戴式裝置不支援層面挑選器 UI 元素。
圖塊疊加層 穿戴式裝置「不支援」圖塊疊加層

在 Wear OS 上使用 Maps API 開發應用程式的最佳做法

如何在應用程式中提供最佳使用者體驗:

  • 地圖應占據大部分螢幕空間。這是因為穿戴式裝置的板型規格較小,有必要提高螢幕空間比例配置,增加地圖的可用性。
  • 規劃應用程式的使用體驗時,請務必考量穿戴式裝置較有限的電量。如果讓螢幕持續待機並顯示地圖,電池效能勢必受到影響。