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

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

מגניב

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.4.0"

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

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

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

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

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

Kotlin

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

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

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

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

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

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

יצירת כרטיס מידע

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

Kotlin

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline
import androidx.wear.protolayout.material.Text
import androidx.wear.tiles.TileBuilders.Tile

private val RESOURCES_VERSION = "1"
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_DISPLAY1)
                        .setColor(argb(0xFF000000.toInt()))
                        .build()))
            .build())

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

Java

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline;
import androidx.wear.protolayout.material.Text;
import androidx.wear.tiles.TileBuilders.Tile;

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    new Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(ColorBuilders.argb(0xFF000000))
                        .build()))
            .build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

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

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/tile_icon_round"
   android:roundIcon="@drawable/tile_icon_round"
   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>

מסנן ההרשאות ומסנן ה-Intent רושמים את השירות הזה כספק של כרטיסי מידע.

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

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

סקירה כללית של מחזור החיים של שירות משבצת

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

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

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

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

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

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

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

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

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

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

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

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

יצירת ממשק משתמש לכרטיסי מידע

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

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

יש תמיכה ברכיבים החזותיים הבאים מהספרייה של protolayout, וגם רכיבי חומר:

  • 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(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();
}

פריסות של חומר לימוד

בנוסף לפריסות בסיסיות, ספריית protolayout-material כוללת עוד כמה תכונות פריסות מקובעות שנועדו להחזיק אלמנטים ב"משבצות" ספציפיות.

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

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

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

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

קשתות

הצאצאים הבאים של מאגרי Arc נתמכים:

  • ArcLine: יוצרת קו מעוקל מסביב לקשת.
  • ArcText: יוצרת טקסט מעוקל בקשת.
  • 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 הוא סוג מיוחד של קונטיינר שפורס אלמנטים באופן דומה טקסט. האפשרות הזו שימושית אם רוצים להחיל סגנון אחר רק על סגנון אחד. מחרוזת משנה בתוך בלוק גדול של טקסט, משהו שאי אפשר עם הפונקציה רכיב Text.

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

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

  • SpanText: ממירה טקסט עם סגנון ספציפי.
  • SpanImage: יוצר תמונה שנמצאת בשורה עם טקסט.

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

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() method:

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