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

כדי להתחיל לספק משבצות מהאפליקציה, צריך לכלול את יחסי התלות הבאים בקובץ 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")
}

יצירת משבצת

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

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>

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

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

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

סקירה כללית על מחזור החיים של שירות של קטעי מפות

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

TileService הוא שירות מחויב. ה-TileService מחויב כתוצאה מהבקשה של האפליקציה או אם המערכת צריכה לתקשר איתו. מחזור החיים של שירות מקושר כולל את ארבע שיטות הקריאה החוזרת הבאות: onCreate(),‏ onBind(),‏ onUnbind() ו-onDestroy(). המערכת מפעילה את השיטות האלה בכל פעם שהשירות נכנס לשלב חדש במחזור החיים.

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

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

    onTileAddEvent() נקראת רק כשמגדירים מחדש את קבוצת המשבצות, ולא בכל פעם שהמערכת יוצרת משבצת. לדוגמה, כשמפעילים מחדש את המכשיר או מפעילים אותו, לא מתבצעת קריאה ל-onTileAddEvent() עבור המשבצות שכבר נוספו. במקום זאת, אפשר להשתמש ב-getActiveTilesAsync() כדי לקבל תמונת מצב של המשבצות ששייכות לכם שפעילות.

  • onTileRemoveEvent(): המערכת קוראת לשיטה הזו רק אם המשתמש מסיר את המשבצת שלכם.

  • onTileEnterEvent(): המערכת קוראת לשיטה הזו כשמשבצת שמספקת הספק הזה מופיעה במסך.

  • onTileLeaveEvent(): המערכת קוראת לשיטה הזו כשמשבצת שמספקת הספק הזה יוצאת מהמסך.

  • onTileRequest(): המערכת קוראת לשיטה הזו כשהיא מבקשת ציר זמן חדש מהספק הזה.

  • onTileResourcesRequest(): המערכת קוראת לשיטה הזו כשהיא מבקשת חבילת משאבים מהספק הזה. זה יכול לקרות בפעם הראשונה שמשאב מסוים נטען או בכל פעם שגרסת המשאב משתנה.

שאילתות לגבי המשבצות הפעילות

אריחים פעילים הם אריחים שנוספו כדי להציג אותם בשעון. משתמשים ב-method הסטטי getActiveTilesAsync() של TileService כדי לשלוח שאילתה לגבי המשבצות ששייכות לאפליקציה שפעילות.

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

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

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

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

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

רכיבי חומר

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

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

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

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

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

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

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

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

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

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

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_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()
);
}