תחילת העבודה עם כרטיסי מידע

כדי להתחיל לספק משבצות מהאפליקציה, צריך לכלול את יחסי התלות הבאים בקובץ build.gradle של האפליקציה.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.5.0-alpha04"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.3.0-alpha04"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.3.0-alpha04"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.3.0-alpha04"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.5.0-alpha04"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.5.0-alpha04"
}

Kotlin

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.5.0-alpha04")

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

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

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

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

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

מושגים מרכזיים

המשבצות לא נוצרות באותו אופן שבו נוצרות אפליקציות ל-Android, והן מבוססות על מושגים שונים:

  • תבניות פריסה: הגדרת הסדר הכללי של הרכיבים החזותיים במסך. משבצת משתמשת בתבנית פריסה של EdgeContentLayout, שכוללת מדד התקדמות מסביב לקצה המסך, או ב-PrimaryLayout, שלא מציג את המדד הזה.
  • רכיבי פריסה: מייצגים אלמנט גרפי בודד, כמו Button או Chip, או כמה אלמנטים כאלה שמקובצים יחד באמצעות Column, ‏ MultiButtonLayout, ‏ MultiSlotLayout או רכיב דומה. הם מוטמעים בתבנית פריסה.
  • משאבים: אובייקטים מסוג ResourceBuilders.Resources מורכבים ממפה של צמדי מפתח/ערך של משאבי Android (תמונות) שנדרשים לעיבוד (רנדור) של פריסה, וגרסה.
  • ציר זמן: אובייקט TimelineBuilders.Timeline הוא רשימה של מופע אחד או יותר של אובייקט פריסה. אפשר לספק מנגנונים וביטויים שונים כדי לציין מתי ה-Renderer צריך לעבור מאובייקט פריסה אחד לאחר, למשל כדי להפסיק להציג פריסה בשעה מסוימת.
  • מצב: מבנה נתונים מסוג StateBuilders.State שמועברים בין המשבצת לאפליקציה, כדי לאפשר לשני הרכיבים לתקשר ביניהם. לדוגמה, אם מקישים על לחצן באריח, המצב מכיל את המזהה של הלחצן. אפשר גם להמיר בין סוגי נתונים באמצעות מפה.
  • משבצת: אובייקט TileBuilders.Tile שמייצג משבצת, ומורכב מציר זמן, מזהה גרסה של משאבים, מרווח זמן עדכניות ומצב.
  • Protolayout: המונח הזה מופיע בשם של כיתות שונות שקשורות לאריחים, והוא מתייחס לספריית Protolayout של Wear OS, ספריית גרפיקה שמשמשת בממשקים שונים של Wear OS.

יצירת משבצת

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

class MyTileService : TileService() {

    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(
            Tile.Builder()
                .setResourcesVersion(RESOURCES_VERSION)
                .setTileTimeline(
                    Timeline.fromLayoutElement(
                        Text.Builder(this, "Hello World!")
                            .setTypography(Typography.TYPOGRAPHY_BODY1)
                            .setColor(argb(0xFFFFFFFF.toInt()))
                            .build()
                    )
                )
                .build()
        )

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(
            Resources.Builder()
                .setVersion(RESOURCES_VERSION)
                .build()
        )
}

בשלב הבא, מוסיפים שירות בתוך התג <application> בקובץ AndroidManifest.xml.

<service
    android:name=".snippets.tile.MyTileService"
    android:label="@string/tile_label"
    android:description="@string/tile_description"
    android:icon="@mipmap/ic_launcher"
    android:exported="true"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
    <intent-filter>
        <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
    </intent-filter>

    <meta-data android:name="androidx.wear.tiles.PREVIEW"
        android:resource="@drawable/tile_preview" />
</service>

מסנן ההרשאות והכוונות רושם את השירות הזה כספק משבצות.

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

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

איור 1. משבצת 'Hello World'.

לדוגמה מלאה, אפשר לעיין בקוד לדוגמה ב-GitHub או בcodelab.

יצירת ממשק משתמש לאריחים

פריסת המשבצת נכתבת באמצעות תבנית build. הפריסה של המשבצת מורכבת מעץ שמכיל קונטיינרים של פריסה ורכיבי פריסה בסיסיים. לכל רכיב פריסה יש מאפיינים שאפשר להגדיר באמצעות שיטות שונות להגדרת מאפיינים.

רכיבי פריסה בסיסיים

יש תמיכה ברכיבים החזותיים הבאים מהספרייה protolayout, יחד עם רכיבי Material:

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

רכיבי חומר

בנוסף לרכיבים הבסיסיים, הספרייה protolayout-material מספקת רכיבים שמאפשרים ליצור עיצוב של משבצות בהתאם להמלצות לממשק המשתמש של Material Design.

  • Button: רכיב עגול שניתן ללחוץ עליו, שנועד להכיל סמל.
  • Chip: רכיב בצורת אצטדיון שניתן ללחוץ עליו, שנועד להכיל עד שני שורות טקסט וסמל אופציונלי.

  • CompactChip: רכיב בצורת אצטדיון שניתן ללחוץ עליו, שנועד להכיל שורה של טקסט.

  • TitleChip: רכיב בצורת אצטדיון שניתן ללחוץ עליו, בדומה לרכיב Chip, אבל עם גובה גדול יותר כדי להכיל טקסט של שם.

  • CircularProgressIndicator: אינדיקטור של התקדמות מעגלית שאפשר למקם בתוך EdgeContentLayout כדי להציג את ההתקדמות בקצוות המסך.

קונטיינרים של פריסה

אפשר להשתמש בקונטיינרים הבאים, יחד עם פריסות Material:

  • Row: הצגת רכיבי הצאצאים באופן אופק, אחד אחרי השני.
  • Column: הרכיבים הצאצאים מוצגים אנכית, אחד אחרי השני.
  • Box: שכבות-על של רכיבי הצאצאים זה על גבי זה.
  • Arc: הצבת רכיבי הצאצאים במעגל.
  • Spannable: החלת FontStyles ספציפי על קטעי טקסט, יחד עם החלפת טקסט ותמונות. מידע נוסף זמין במאמר Spannables.

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

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

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VERTICAL_ALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

Java

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

פריסות של Material

בנוסף לפריסות בסיסיות, בספרייה protolayout-material יש כמה פריסות מוגדרות מראש שנועדו להכיל רכיבים ב'משבצות' ספציפיות.

  • PrimaryLayout: מיקום של פעולה ראשית אחת CompactChip בתחתית המסך, עם התוכן במרכז מעליו.

  • MultiSlotLayout: ממקם תוויות ראשיות ומשניות עם תוכן אופציונלי ביניהן, וCompactChip אופציונלי בתחתית המסך.

  • MultiButtonLayout: ממקם קבוצת לחצנים שמסודרים בהתאם להנחיות של Material.

  • EdgeContentLayout: ממקם תוכן סביב קצה המסך, למשל CircularProgressIndicator. כשמשתמשים בפריסה הזו, התוכן שבתוכה מקבל באופן אוטומטי את השוליים והרווחים המתאימים.

קשתות

יש תמיכה בצאצאים הבאים של קונטיינר Arc:

  • ArcLine: יוצר קו מעוקל סביב ה-Arc.
  • ArcText: עיבוד טקסט מעוקל ב-Arc.
  • ArcAdapter: המערכת תיצור עיבוד של אלמנט פריסה בסיסי בתוך קשת, שתוחם את הקשת.

מידע נוסף זמין במאמרי העזרה של כל אחד מסוגי הרכיבים.

גורמי שינוי

אפשר להחיל משתני אופן פעולה על כל אלמנט פריסה זמין. אפשר להשתמש במקשי השינוי האלה למטרות הבאות:

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

לדוגמה, אפשר להתאים אישית את המראה והמטא-נתונים שמוגדרים כברירת מחדל של Image, כפי שמתואר בקטע הקוד הבא:

Kotlin

private fun myImage(): LayoutElement =
    Image.Builder()
        .setWidth(dp(24f))
        .setHeight(dp(24f))
        .setResourceId("image_id")
        .setModifiers(Modifiers.Builder()
            .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build())
            .setPadding(Padding.Builder().setStart(dp(12f)).build())
            .setSemantics(Semantics.builder()
                .setContentDescription("Image description")
                .build()
            ).build()
        ).build()

Java

private LayoutElement myImage() {
   return new Image.Builder()
           .setWidth(dp(24f))
           .setHeight(dp(24f))
           .setResourceId("image_id")
           .setModifiers(new Modifiers.Builder()
                   .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build())
                   .setPadding(new Padding.Builder().setStart(dp(12f)).build())
                   .setSemantics(new Semantics.Builder()
                           .setContentDescription("Image description")
                           .build()
                   ).build()
           ).build();
}

מודעות Spannable

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

קונטיינר Spannable מלא בצאצאים מסוג Span. אסור להשתמש בילדים אחרים או במופעי Spannable בתצוגת עץ.

יש שני סוגים של צאצאים של Span:

  • SpanText: עיבוד טקסט בסגנון ספציפי.
  • SpanImage: הפיכת תמונה ל-inline עם טקסט.

לדוגמה, אפשר להדגיש את המילה 'world' בכותרת 'Hello world' ולהוסיף תמונה בין המילים, כמו בקטע הקוד הבא:

Kotlin

private fun mySpannable(): LayoutElement =
    Spannable.Builder()
        .addSpan(SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(SpanText.Builder()
            .setText("world")
            .setFontStyle(FontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build()

Java

private LayoutElement mySpannable() {
   return new Spannable.Builder()
        .addSpan(new SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(new SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(new SpanText.Builder()
            .setText("world")
            .setFontStyle(newFontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build();
}

עבודה עם משאבים

לכרטיסי המידע אין גישה למשאבים של האפליקציה. המשמעות היא שאי אפשר להעביר מזהה תמונה של Android לרכיב פריסה מסוג Image ולצפות שהוא יפתור את הבעיה. במקום זאת, צריך לשנות את השיטה onTileResourcesRequest() ולספק את המשאבים באופן ידני.

יש שתי דרכים לספק תמונות בתוך השיטה onTileResourcesRequest():

Kotlin

override fun onTileResourcesRequest(
    requestParams: ResourcesRequest
) = Futures.immediateFuture(
Resources.Builder()
    .setVersion("1")
    .addIdToImageMapping("image_from_resource", ImageResource.Builder()
        .setAndroidResourceByResId(AndroidImageResourceByResId.Builder()
            .setResourceId(R.drawable.image_id)
            .build()
        ).build()
    )
    .addIdToImageMapping("image_inline", ImageResource.Builder()
        .setInlineResource(InlineImageResource.Builder()
            .setData(imageAsByteArray)
            .setWidthPx(48)
            .setHeightPx(48)
            .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
            .build()
        ).build()
    ).build()
)

Java

@Override
protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
) {
return Futures.immediateFuture(
    new Resources.Builder()
        .setVersion("1")
        .addIdToImageMapping("image_from_resource", new ImageResource.Builder()
            .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.image_id)
                .build()
            ).build()
        )
        .addIdToImageMapping("image_inline", new ImageResource.Builder()
            .setInlineResource(new InlineImageResource.Builder()
                .setData(imageAsByteArray)
                .setWidthPx(48)
                .setHeightPx(48)
                .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
                .build()
            ).build()
        ).build()
);
}