ניהול גרסאות של אריחים

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

  • ספריות שקשורות ל-Jetpack Tiles: הספריות האלה (כולל Wear Tiles ו-Wear ProtoLayout) מוטמעות באפליקציה, ואתם, בתור מפתחים, שולטים בגרסאות שלהן. האפליקציה משתמשת בספריות האלה כדי ליצור אובייקט TileBuilder.Tile (מבנה הנתונים שמייצג את המשבצת) בתגובה לקריאה onTileRequest() של המערכת.
  • ProtoLayout Renderer: רכיב המערכת הזה אחראי לעיבוד הגרפי של האובייקט Tile במסך ולטיפול באינטראקציות של המשתמשים. מפתח האפליקציה לא יכול לשלוט בגרסה של המרת הווידאו, והיא יכולה להשתנות בין מכשירים שונים, גם אם יש להם חומרה זהה.

המראה או ההתנהגות של המשבצת יכולים להשתנות בהתאם לגרסאות של ספריית Jetpack Tiles באפליקציה ולגרסה של ProtoLayout Renderer במכשיר של המשתמש. לדוגמה, מכשיר אחד עשוי לתמוך ברוטציה או בהצגת נתוני דופק, ומכשיר אחר לא.

במסמך הזה נסביר איך לוודא שהאפליקציה תואמת לגרסאות שונות של ספריית Tiles ושל ProtoLayout Renderer, ואיך לעבור לגרסאות מתקדמות יותר של ספריות Jetpack.

התאמה

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

זיהוי גרסת המרינר

  • משתמשים בשיטה getRendererSchemaVersion() של האובייקט DeviceParameters שמוענק לשיטה onTileRequest()‎. השיטה הזו מחזירה את מספרי הגרסה הראשית והמשנית של ה-ProtoLayout Renderer במכשיר.
  • לאחר מכן תוכלו להשתמש בלוגיקה מותנית בהטמעה של onTileRequest() כדי להתאים את העיצוב או ההתנהגות של המשבצת על סמך גרסת המרינר שזוהתה.
    • לדוגמה, אם אנימציה מסוימת לא נתמכת, תוכלו להציג תמונה סטטית במקום זאת.

ההערה @RequiresSchemaVersion

  • ההערה @RequiresSchemaVersion בשיטות ProtoLayout מציינת את גרסת הסכימה המינימלית של ה-renderer שנדרשת כדי שהשיטה תתנהג כפי שמתואר (דוגמה).
    • קריאה ל-method שדורשת גרסת עיבוד תמונה גבוהה יותר מזו שזמינה במכשיר לא תגרום לקריסה של האפליקציה, אבל היא עלולה לגרום לכך שהתוכן לא יוצג או שהתכונה תתעלם.

דוגמה

override fun onTileRequest(
    requestParams: TileService.TileRequest
): ListenableFuture<Tile> {
    val rendererVersion =
        requestParams.deviceConfiguration.rendererSchemaVersion
    val tile = Tile.Builder()

    if (
        rendererVersion.major > 1 ||
            (rendererVersion.major == 1 && rendererVersion.minor >= 300)
    ) {
        // Use a feature supported in renderer version 1.300 or later
        tile.setTileTimeline(/* ... */ )
    } else {
        // Provide fallback content for older renderers
        tile.setTileTimeline(/* ... */ )
    }

    return Futures.immediateFuture(tile.build())
}

בדיקה עם גרסאות שונות של המנגן

כדי לבדוק את המשבצות בגרסאות שונות של המרתן, צריך לפרוס אותן בגרסאות שונות של אמולטור Wear OS. (במכשירים פיזיים, עדכוני ה-ProtoLayout Renderer מועברים על ידי חנות Play או עדכוני מערכת. אי אפשר לאלץ התקנה של גרסה ספציפית של המנגן).

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

מעבר ל-Tiles 1.5 או ל-ProtoLayout 1.3 (Material 3 Expressive)

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

ב-Jetpack Tiles 1.5 וב-Jetpack ProtoLayout 1.3 יש כמה שיפורים ושינויים משמעותיים. למשל:

  • ממשק API שדומה ל-Compose לתיאור ממשק המשתמש.
  • רכיבים של Material 3 Expressive, כולל לחצן קצה שמצמיד את המסך למטה ותמיכה ברכיבים חזותיים משופרים: אנימציות Lottie, סוגים נוספים של פס מודגש וסגנונות חדשים של קווים מעוקלים. - הערה: אפשר להשתמש בחלק מהתכונות האלה גם בלי לעבור ל-API החדש.

המלצות

  • העברת כל האריחים בו-זמנית. מומלץ לא לשלב גרסאות של משבצות באפליקציה. רכיבי Material 3 נמצאים בארטיפקט נפרד (androidx.wear.protolayout:protolayout-material3), כך שאפשר להשתמש גם במשבצות M2.5 וגם במשבצות M3 באותה אפליקציה. עם זאת, אנחנו ממליצים מאוד לא להשתמש בגישה הזו אלא אם יש צורך בכך באופן מוחלט (לדוגמה, אם יש באפליקציה מספר גדול של משבצות שאי אפשר להעביר את כולן בבת אחת).
  • יישום ההנחיות בנושא חוויית משתמש של משבצות מכיוון שהקובצי המשבצות הם בעלי מבנה ותבנית קפדניים, מומלץ להשתמש בעיצובים שבדוגמאות הקיימות כנקודות מוצא לעיצובים שלכם.
  • כדאי לבדוק את המודעות במגוון גדלים של מסכים וגופנים. לרוב, משבצות מכילות מידע רב, ולכן הטקסט (במיוחד כשהוא ממוקם על לחצנים) עלול לחרוג מהמסגרת או להיחתוך. כדי לצמצם את הזמן הזה, מומלץ להשתמש ברכיבים מוכנים ולהימנע מהתאמה אישית נרחבת. כדאי לבדוק את התצוגה באמצעות התכונה 'תצוגה מקדימה של משבצות' ב-Android Studio וגם במספר מכשירים אמיתיים.

תהליך ההעברה

עדכון יחסי התלות

קודם מעדכנים את הקובץ build.gradle.kts. מעדכנים את הגרסאות ומשנים את התלות ב-protolayout-material ל-protolayout-material3, כפי שמוצג:

// In build.gradle.kts

//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"

// Use these versions for M3.
val tilesVersion = "1.5.0-rc01"
val protoLayoutVersion = "1.3.0-rc01"

 dependencies {
     // Use to implement support for wear tiles
     implementation("androidx.wear.tiles:tiles:$tilesVersion")

     // Use to utilize standard components and layouts in your tiles
     implementation("androidx.wear.protolayout:protolayout:$protoLayoutVersion")

     // Use to utilize components and layouts with Material Design in your tiles
     // implementation("androidx.wear.protolayout:protolayout-material:$protoLayoutVersion")
     implementation("androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion")

     // Use to include dynamic expressions in your tiles
     implementation("androidx.wear.protolayout:protolayout-expression:$protoLayoutVersion")

     // Use to preview wear tiles in your own app
     debugImplementation("androidx.wear.tiles:tiles-renderer:$tilesVersion")

     // Use to fetch tiles from a tile provider in your tests
     testImplementation("androidx.wear.tiles:tiles-testing:$tilesVersion")
 }

שירות TileService לא השתנה במידה רבה

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

היוצא מן הכלל העיקרי נוגע למעקב אחר פעילות של משבצות: אם האפליקציה שלכם משתמשת ב-onTileEnterEvent() או ב-onTileLeaveEvent(), עליכם לעבור ל-onRecentInteractionEventsAsync(). החל מ-API 36, האירועים האלה יצורפו לקבוצות.

התאמה של הקוד ליצירת הפריסה

ב-ProtoLayout 1.2‏ (M2.5), השיטה onTileRequest() מחזירה TileBuilders.Tile. האובייקט הזה הכיל רכיבים שונים, כולל TimelineBuilders.Timeline, שבתורו הכיל את LayoutElement שמתאר את ממשק המשתמש של המשבצת.

ב-ProtoLayout 1.3‏ (M3), מבנה הנתונים והתהליך הכללי לא השתנו, אבל ה-LayoutElement נוצר עכשיו באמצעות גישה בהשראת Compose, עם פריסה שמבוססת על תאים מוגדרים. התאים האלה הם (למעלה למטה) titleSlot (בדרך כלל לכותרת ראשית או לכותרת), mainSlot (לתוכן הליבה) ו-bottomSlot (לרוב לפעולות כמו לחצן קצה או מידע נוסף כמו טקסט קצר). הפריסה הזו נוצרת על ידי הפונקציה primaryLayout().

הפריסה של אריח שמוצגים בו mainSlot,‏ titleSlot ו-bottomSlot
איור 1.: חריצי כרטיס מידע.
השוואה בין פונקציות של פריסה M2.5 לבין פונקציות של פריסה M3

M2.5

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters
) =
    PrimaryLayout.Builder(deviceConfiguration)
        .setResponsiveContentInsetEnabled(true)
        .setContent(
            Text.Builder(context, "Hello World!")
                .setTypography(Typography.TYPOGRAPHY_BODY1)
                .setColor(argb(0xFFFFFFFF.toInt()))
                .build()
        )
        .build()

M3

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
) =
    materialScope(context, deviceConfiguration) {
        primaryLayout(mainSlot = { text("Hello, World!".layoutString) })
    }

כדי להדגיש את ההבדלים העיקריים:

  1. הסרת 'בנאים'. דפוס ה-builder המסורתי לרכיבי ממשק המשתמש של Material3 הוחלף בתחביר דקלרטיבי יותר בהשראת Compose. (גם לרכיבים שאינם ממשק משתמש, כמו String/‏Color/‏Modifiers, יש עכשיו חבילות Kotlin חדשות).
  2. פונקציות סטנדרטיות של פריסת נתונים והפעלה. פריסות M3 מסתמכות על פונקציות סטנדרטיות של מבנה ואתחלה: materialScope() ו-primaryLayout(). הפונקציות החובה האלה מאתחלות את הסביבה של M3 (עיצוב, היקף הרכיבים באמצעות materialScope) ומגדירות את הפריסה הראשית שמבוססת על מודעות (באמצעות primaryLayout). צריך להפעיל את שתי הפונקציות בדיוק פעם אחת לכל פריסה.

קביעת עיצוב

צבע

אחת מהתכונות הבולטות של Material 3 Expressive היא 'עיצוב דינמי': משבצות שמפעילות את התכונה הזו (מופעלת כברירת מחדל) יוצגו בעיצוב שסופק על ידי המערכת (הזמינות תלויה במכשיר ובהגדרות של המשתמש).

שינוי נוסף ב-M3 הוא הרחבת מספר האסימונים של הצבעים, שעלה מ-4 ל-29. אסימוני הצבע החדשים נמצאים בכיתה ColorScheme.

טיפוגרפיה

בדומה ל-M2.5, גם ב-M3 יש שימוש רב בקבועים מוגדרים מראש של גודל גופן – לא מומלץ לציין גודל גופן ישירות. הקבועים האלה נמצאים בכיתה Typography ומציעים מגוון מורחב מעט של אפשרויות ביטוי.

פרטים מלאים זמינים במסמכי העזרה בנושא טיפוגרפיה.

צורה

רוב הרכיבים מסוג M3 יכולים להשתנות במאפיין הצורה וגם בצבע.

textButton (ב-mainSlot) בצורה full:

משבצת בצורה &#39;מלאה&#39; (עם פינות מעוגלות יותר)
איור 2.: אריח בפורמט 'מלא'

אותו textButton עם הצורה small:

משבצת בצורה &#39;קטנה&#39; (פחות פינות מעוגלות)
איור 3.: אריח בצורה 'קטנה'

רכיבים

רכיבי M3 גמישים יותר וניתן להגדיר אותם בקלות רבה יותר בהשוואה לרכיבי M2.5. ב-M2.5 נדרשו לעתים קרובות רכיבים נפרדים לטיפולים חזותיים שונים, בעוד שב-M3 נעשה שימוש לעיתים קרובות ברכיב 'בסיס' כללי שניתן להתאמה אישית במידה רבה, עם הגדרות ברירת מחדל טובות.

העיקרון הזה חל על הפריסה ברמה הבסיסית (root). בגרסה M2.5, הערך הזה היה PrimaryLayout או EdgeContentLayout. ב-M3, אחרי שמוגדר MaterialScope יחיד ברמה העליונה, מתבצעת קריאה לפונקציה primaryLayout(). הפונקציה הזו מחזירה את הפריסה ברמה הבסיסית ישירות (אין צורך ב-builders), והיא מקבלת את הערך LayoutElements לכמה 'מקומות', כמו titleSlot,‏ mainSlot ו-bottomSlot. אפשר לאכלס את החריצים האלה ברכיבי ממשק משתמש ספציפיים, כמו אלה שמוחזרים על ידי text()‎,‏ button()‎ או card()‎, או במבנים של פריסה, כמו Row או Column מ-LayoutElementBuilders.

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

M2.5 M3
רכיבים אינטראקטיביים
Button או Chip
טקסט
Text text()
אינדיקטורים של התקדמות
CircularProgressIndicator circularProgressIndicator() או segmentedCircularProgressIndicator()
פריסה
PrimaryLayout או EdgeContentLayout primaryLayout()
buttonGroup()
תמונות
icon(), ‏ avatarImage() או backgroundImage()

גורמי שינוי

ב-M3, Modifiers, שמשמש לקישוט או להוספה של רכיב, דומה יותר ל-Compose. השינוי הזה יכול לצמצם את הקוד הסטנדרטי על ידי יצירה אוטומטית של הסוגים הפנימיים המתאימים. (השינוי הזה לא משפיע על השימוש ברכיבי ממשק המשתמש של M3. במקרה הצורך, אפשר להשתמש במודיפיקרים בסגנון בונה מ-ProtoLayout 1.2 עם רכיבי ממשק המשתמש של M3, ולהפך).

M2.5

// A Builder-style modifier to set the opacity of an element to 0.5
fun myModifier(): ModifiersBuilders.Modifiers =
    ModifiersBuilders.Modifiers.Builder()
        .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build())
        .build()

M3

// The equivalent Compose-like modifier is much simpler
fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)

אפשר ליצור משתני אופן באמצעות סגנון API כלשהו, ואפשר גם להשתמש בפונקציית התוסף toProtoLayoutModifiers() כדי להמיר LayoutModifier ל-ModifiersBuilders.Modifier.

פונקציות עזר

ב-ProtoLayout 1.3 אפשר להשתמש ברכיבים רבים של ממשק המשתמש באמצעות API בהשראת Compose, אבל רכיבי פריסה בסיסיים כמו שורות ועמודות מ-LayoutElementBuilders ממשיכים להשתמש בתבנית ה-builder. כדי לגשר על הפער בסגנון ולשמור על עקביות עם ממשקי ה-API החדשים של רכיבי M3, כדאי להשתמש בפונקציות עזר.

ללא עוזרים

primaryLayout(
    mainSlot = {
        LayoutElementBuilders.Column.Builder()
            .setWidth(expand())
            .setHeight(expand())
            .addContent(text("A".layoutString))
            .addContent(text("B".layoutString))
            .addContent(text("C".layoutString))
            .build()
    }
)

עם עוזרים

// Function literal with receiver helper function
fun column(builder: Column.Builder.() -> Unit) =
    Column.Builder().apply(builder).build()

primaryLayout(
    mainSlot = {
        column {
            setWidth(expand())
            setHeight(expand())
            addContent(text("A".layoutString))
            addContent(text("B".layoutString))
            addContent(text("C".layoutString))
        }
    }
)

מעבר לגרסה 1.2 של Tiles או לגרסה 1.0 של ProtoLayout

החל מגרסה 1.2, רוב ממשקי ה-API של פריסת המשבצות נמצאים במרחב השמות androidx.wear.protolayout. כדי להשתמש בממשקי ה-API העדכניים ביותר, צריך לבצע את שלבי ההעברה הבאים בקוד.

עדכון יחסי התלות

מבצעים את השינויים הבאים בקובץ ה-build של מודול האפליקציה:

Groovy

  // Remove
  implementation 'androidx.wear.tiles:tiles-material:version'

  // Include additional dependencies
  implementation "androidx.wear.protolayout:protolayout:1.2.1"
  implementation "androidx.wear.protolayout:protolayout-material:1.2.1"
  implementation "androidx.wear.protolayout:protolayout-expression:1.2.1"

  // Update
  implementation "androidx.wear.tiles:tiles:1.4.1"

Kotlin

  // Remove
  implementation("androidx.wear.tiles:tiles-material:version")

  // Include additional dependencies
  implementation("androidx.wear.protolayout:protolayout:1.2.1")
  implementation("androidx.wear.protolayout:protolayout-material:1.2.1")
  implementation("androidx.wear.protolayout:protolayout-expression:1.2.1")

  // Update
  implementation("androidx.wear.tiles:tiles:1.4.1")

עדכון מרחבי שמות

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

  1. מחליפים את כל הייבוא של androidx.wear.tiles.material.* ב-androidx.wear.protolayout.material.*. צריך להשלים את השלב הזה גם בספרייה androidx.wear.tiles.material.layouts.
  2. מחליפים את רוב ייבוא ה-androidx.wear.tiles.* ב-androidx.wear.protolayout.*.

    הייבוא של androidx.wear.tiles.EventBuilders,‏ androidx.wear.tiles.RequestBuilders,‏ androidx.wear.tiles.TileBuilders ו-androidx.wear.tiles.TileService לא אמור להשתנות.

  3. שינוי השם של כמה שיטות שהוצאו משימוש מהכיתות TileService ו-TileBuilder:

    1. TileBuilders: מ-getTimeline() עד getTileTimeline() ומ-setTimeline() עד setTileTimeline()
    2. TileService: onResourcesRequest() עד onTileResourcesRequest()
    3. RequestBuilders.TileRequest: מ-getDeviceParameters() עד getDeviceConfiguration(), מ-setDeviceParameters() עד setDeviceConfiguration(), מ-getState() עד getCurrentState() ומ-setState() עד setCurrentState()