Maps API ב-Wear OS

מפה במכשיר לביש

באמצעות ה-SDK של מפות ל-Android, אתם יכולים ליצור אפליקציה לבישה שמבוססת על מפות ופועלת ישירות במכשירי Wear OS by Google. המשתמשים באפליקציה יכולים לראות את המיקום שלהם במפה במבט חטוף על פרק כף היד. לדוגמה, הם יכולים לשרטט את המיקום שלהם במסלול, ואז להגדיל את התצוגה כדי לראות פרטים, או ללחוץ על סמן כדי לראות חלון מידע שהאפליקציה שלכם מספקת.

בדף הזה מתוארת הפונקציונליות של ה-API שזמינה במכשיר Wear, ומוסבר איך להתחיל לפתח את האפליקציה.

איך מתחילים להשתמש ב-Wear OS

פיתוח אפליקציה לבישה באמצעות SDK של מפות ל-Android דומה מאוד לפיתוח אפליקציית מפות Google לכל מכשיר Android אחר. ההבדל הוא בעיצוב של המכשיר הלביש, שקטן יותר, כדי לשפר את השימושיות והביצועים של האפליקציה.

‫Android Studio הוא הכלי המומלץ לפיתוח ב-Wear OS, כי הוא מספק הגדרת פרויקט, הכללת ספרייה ונוחות אריזה.

לקבלת עזרה כללית בנושא עיצוב אפליקציה לבישה, אפשר לעיין בהנחיות לעיצוב ב-Wear OS. כדי לקבל עזרה ביצירת האפליקציה הלבישה הראשונה, אפשר לעיין במדריך בנושא יצירת אפליקציות לבישות.

איך יוצרים את אפליקציית המפות הראשונה ב-Wear OS

המדריך המהיר הזה מניח שאתם מכירים את ה-SDK של מפות ל-Android, שפעלתם לפי המדריכים של Wear OS כדי ליצור מודול למוצר לביש באפליקציה, ושעכשיו אתם רוצים להוסיף מפה למודול הזה.

הוספת יחסי תלות למודול Wear

מוודאים שיחסי התלות הבאים כלולים בקובץ build.gradle.kts של מודול Wear OS של האפליקציה:

dependencies {
    // ...
    // Modern Android projects use version catalogs to manage dependencies.  To include the necessary dependencies,
    // first add the following to your libs.versions.toml file:
    //
    // [versions]
    // playServicesMaps = "20.1.0"
    // wear = "1.4.0"
    // wearable = "2.9.0"
    //
    // [libraries]
    // play-services-maps = { group = "com.google.android.gms", name = "play-services-maps", version.ref = "playServicesMaps" }
    // wear = { group = "androidx.wear", name = "wear", version.ref = "wear" }
    // wearable-compile = { group = "com.google.android.wearable", name = "wearable", version.ref = "wearable" }
    // wearable-support = { group = "com.google.android.support", name = "wearable", version.ref = "wearable" }

    compileOnly(libs.wearable.compile)
    implementation(libs.wearable.support)
    implementation(libs.play.services.maps)

    // This dependency is necessary for ambient mode
    implementation(libs.wear)

    // Android Test Dependencies
    androidTestImplementation(libs.ext.junit)
    androidTestImplementation(libs.espresso.core)
    androidTestImplementation(libs.uiautomator)
    androidTestImplementation(libs.truth)
    androidTestImplementation(libs.junit)
    
    // If your project does not use a version catalog, you can use the following dependencies instead:
    //
    //    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:20.1.0")
    //    implementation("androidx.wear:wear:1.4.0")
    //    androidTestImplementation("androidx.test.ext:junit:1.3.0")
    //    androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
    //    androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
    //    androidTestImplementation("com.google.truth:truth:1.4.5")
    //    androidTestImplementation("junit:junit:4.13.2")
}

מידע נוסף על יחסי התלות זמין במדריך הוספת מודול Wear OS לפרויקט קיים.

הטמעה של תנועת החלקה לסגירה והגדרה של צבע הרקע הראשוני

מומלץ להשתמש ב-SwipeDismissFrameLayout כדי להציג את המפה במכשיר הלביש. באמצעות המחלקה SwipeDismissFrameLayout, אתם יכולים להטמיע את תנועת ההחלקה לסגירה, שמאפשרת למשתמשים לצאת מהאפליקציה בהחלקה מהקצה השמאלי של המסך.

כדי להגדיר צבע רקע ראשוני בהתאמה אישית, משתמשים במאפיין ה-XML‏ map:backgroundColor כדי להגדיר את הצבע שיוצג עד לטעינת משבצות המפה בפועל.

מוסיפים את הרכיבים SwipeDismissFrameLayout ו-backgroundColor להגדרת הפריסה בתור מאגר התגים של 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 בפעילות, מוסיפים קריאה חוזרת (callback) ומגדירים את ההתנהגות של הקריאה החוזרת כך שתבצע את פעולת הסגירה הנדרשת, כמו שמוצג בהמשך:

Kotlin

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


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

        // 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.
        try {
            val controller = AmbientModeSupport.attach(this)
            Log.d(MainActivity::class.java.simpleName, "Is ambient enabled: " + controller.isAmbient)
        } catch (e: Exception) {
            Log.w(MainActivity::class.java.simpleName, "Ambient mode unavailable on this device: ${e.message}")
        }

        // 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.
        try {
            AmbientModeSupport.AmbientController controller = AmbientModeSupport.attach(this);
            Log.d(
                    MainActivity.class.getSimpleName(),
                    "Is ambient enabled: " + controller.isAmbient());
        } catch (Exception e) {
            Log.w(
                    MainActivity.class.getSimpleName(),
                    "Ambient mode unavailable on this device: " + e.getMessage());
        }

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

    // ...
}

      

הוספת מפה

משתמשים בשיטת הקריאה החוזרת (callback) 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));
}


      

הפעלת תאורת אווירה

‫SDK של מפות ל-Android תומך במצב רגיש לסביבה באפליקציות לבישות. אפליקציות שתומכות במצב רגיש לסביבה נקראות לפעמים אפליקציות פועלות תמיד. מצב רגיש לסביבה מופעל כשהמשתמש מפסיק להשתמש באפליקציה באופן פעיל, ומאפשר לאפליקציה להישאר גלויה במכשיר הלביש.

ה-SDK של מפות ל-Android מספק רינדור פשוט של המפה עם מעט צבעים לשימוש במצב רגיש לסביבה, וסגנון המפה משתנה באופן אוטומטי כשהמכשיר עובר ממצב אינטראקטיבי למצב רגיש לסביבה. כל הסמנים, האובייקטים והרכיבים האינטראקטיביים נעלמים במצב רגיש לסביבה. כך צריכת החשמל של האפליקציה תפחת, והעיצוב והסגנון שלה יהיו עקביים עם אפליקציות אחרות שפועלות במצב אווירה, כמו תצוגות השעון.

כדי לוודא שהאפליקציה משתמשת במצב הרגיש לסביבה של המפה, צריך לבצע את הפעולות הבאות:

  1. מעדכנים את Android SDK כך שיכלול את פלטפורמת Android 6.0 (API 23) ואילך, שמספקת את ממשקי ה-API שמאפשרים לפעילויות לעבור למצב רגיש לסביבה. במסמכי התיעוד של Android בנושא הוספת חבילות SDK מוסבר איך לעדכן את ה-SDK.
  2. מוודאים שהפרויקט מטרגט ל-Android 6.0 ואילך, על ידי הגדרת targetSdkVersion ל-23 ואילך בקובץ המניפסט של האפליקציה.
  3. מוסיפים את יחסי התלות של המכשיר הלביש לקובץ build.gradle.kts של האפליקציה. לדוגמה
  4. מוסיפים את הרשומה של הספרייה המשותפת של המכשיר הלביש לקובץ מניפסט של אפליקציה למכשיר הלביש, כפי שמתואר בשיעור ההדרכה של Android בנושא שמירה על האפליקציה גלויה.
  5. מוסיפים את ההרשאה WAKE_LOCK לקובצי המניפסט של האפליקציה למכשירים ניידים ולמכשירים לבישים, כמו שמתואר בשיעור ההדרכה של Android בנושא שמירה על האפליקציה גלויה.
  6. ב-method‏ onCreate() של הפעילות, מבצעים קריאה ל-method‏ AmbientModeSupport.attach(). כך מערכת ההפעלה יודעת שהאפליקציה פועלת תמיד, וכשהמכשיר נכבה הוא נכנס למצב רגיש לסביבה במקום לחזור לתצוגת השעון.
  7. מטמיעים את הממשק AmbientModeSupport.AmbientCallbackProvider ב-Activity כדי שיוכל לקבל שינויים במצב הרגיש לסביבה.
  8. מגדירים את המפה כך שתתמוך במצב רגיש לסביבה. כדי לעשות את זה, אפשר להגדיר את המאפיין map:ambientEnabled="true" בקובץ הפריסה של הפעילות ב-XML, או להגדיר את GoogleMapOptions.ambientEnabled(true) באופן פרוגרמטי. ההגדרה הזו מודיעה ל-API שהוא צריך לטעון מראש את משבצות המפה הנדרשות לשימוש במצב רגיש לסביבה.
  9. כשהפעילות עוברת למצב רגיש לסביבה, המערכת קוראת ל-method‏ onEnterAmbient() ב-AmbientCallback שאתם מספקים. עוקפים את 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(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 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.
        try {
            val controller = AmbientModeSupport.attach(this)
            Log.d(AmbientActivity::class.java.simpleName, "Is ambient enabled: " + controller.isAmbient)
        } catch (e: Exception) {
            Log.w(AmbientActivity::class.java.simpleName, "Ambient mode unavailable on this device: ${e.message}")
        }

        // 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 =
        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.
        try {
            AmbientModeSupport.AmbientController controller = AmbientModeSupport.attach(this);
            Log.d(
                    AmbientActivity.class.getSimpleName(),
                    "Is ambient enabled: " + controller.isAmbient());
        } catch (Exception e) {
            Log.w(
                    AmbientActivity.class.getSimpleName(),
                    "Ambient mode unavailable on this device: " + e.getMessage());
        }

        // 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 בנושא שמירה על האפליקציה גלויה.

שימוש ב-Street View ב-Wear OS

יש תמיכה מלאה ב-Street View במכשירים לבישים.

כדי לאפשר למשתמשים לצאת מהאפליקציה כשהם צופים בתמונה פנורמית ב-Street View, משתמשים בממשק StreetViewPanorama.OnStreetViewPanoramaLongClickListener כדי להאזין לתנועת לחיצה ארוכה. כשהמשתמש לוחץ לחיצה ארוכה במקום כלשהו בתמונת Street View, מתקבל אירוע onStreetViewPanoramaLongClick(StreetViewPanoramaOrientation). מפעילים את DismissOverlayView.show() כדי להציג כפתור יציאה.

קוד לדוגמה

ב-GitHub יש אפליקציה לדוגמה שאפשר להשתמש בה כנקודת התחלה לאפליקציה שלכם. הדוגמה מראה איך להגדיר מפת Google בסיסית ב-Wear OS.

הפונקציונליות שנתמכת ב-Maps API ב-Wear OS

בקטע הזה מפורטים ההבדלים בפונקציונליות הנתמכת של מפות במכשירים לבישים בהשוואה למכשירים שניתן להחזיק ביד (טלפונים וטאבלטים). כל התכונות של ה-API שלא מוזכרות בהמשך אמורות לפעול כמו שמתואר במסמכי התיעוד של ה-API המלא.

פונקציונליות
מצב אינטראקטיבי מלא ומצב בסיסי

אתם יכולים להשתמש ב-SDK של מפות ל-Android במצב אינטראקטיבי מלא או במצב בסיסי. כדאי להשתמש במצב בסיסי אם רוצים לשפר את הביצועים במכשיר הלביש, והאפליקציה לא צריכה לתמוך באינטראקציות כמו תנועות, או הזזה ושינוי מרחק התצוגה במפה.

במצב בסיסי, ה-intent להפעלת אפליקציית מפות Google לנייד כשהמשתמש מקיש על המפה מושבת ואי אפשר להפעיל אותו במכשיר לביש.

לרשימה מלאה של ההבדלים בין מצב בסיסי למצב אינטראקטיבי מלא, עיינו במסמכי התיעוד של המצב הבסיסי.

סרגל הכלים של המפה סרגל הכלים של המפה מושבת ואי אפשר להפעיל אותו במכשיר לביש.
רכיבים אינטראקטיביים הרכיבים האינטראקטיביים מושבתים כברירת מחדל במכשירים לבישים. האפשרות הזו כוללת את פקדי הזום, המצפן והמיקום שלי. אפשר להפעיל אותן באמצעות המחלקה UiSettings כרגיל.
תנועות תנועות בנגיעה אחת פועלות כצפוי. לדוגמה, לחיצה וגרירה כדי להזיז את המפה, לחיצה כפולה כדי להגדיל את התצוגה ולחיצה בשתי אצבעות כדי להקטין את התצוגה. התמיכה בתנועות עם מגע במספר נקודות משתנה בהתאם למכשיר של המשתמש. דוגמאות לתנועות עם מגע במספר נקודות: דחיפה בשתי אצבעות כדי להטות את המפה, צביטה כדי לשנות את מרחק התצוגה וסיבוב בשתי אצבעות.
מפות פנים ומבנים מפות פנים מושבתות כברירת מחדל במכשיר לביש. כדי להפעיל אותם, צריך להפעיל את GoogleMap.setIndoorEnabled(true). אם מפות פנים מופעלות, המפה תציג את רמת הקומה שמוגדרת כברירת מחדל. הרכיב בורר הרמות בממשק המשתמש לא נתמך במכשירים לבישים.
שכבות-על של משבצות שכבות-על של משבצות לא נתמכות במכשירים לבישים.

שיטות מומלצות לפיתוח באמצעות Maps API ב-Wear OS

איך לספק את חוויית המשתמש הטובה ביותר באפליקציה:

  • המפה צריכה לתפוס חלק גדול מהמסך. זה נדרש כדי לשפר את נוחות השימוש במפה במכשיר לביש עם גורם צורה קטן.
  • כשאתם מתכננים את חוויית המשתמש באפליקציה, קחו בחשבון שלמכשיר לביש יש סוללה חלשה. אם המסך פעיל והמפה מוצגת, זה משפיע על ביצועי הסוללה.