เริ่มต้นใช้งานการ์ด

หากต้องการเริ่มให้บริการการ์ดจากแอป ให้รวมไลบรารีต่อไปนี้ไว้ในไฟล์ 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>

ตัวกรองสิทธิ์และ Intent จะลงทะเบียนบริการนี้ในฐานะผู้ให้บริการไทล์

ไอคอน ป้ายกำกับ และคำอธิบายจะแสดงต่อผู้ใช้เมื่อกำหนดค่าการ์ดในโทรศัพท์หรือนาฬิกา

ใช้แท็กข้อมูลเมตาของตัวอย่างเพื่อแสดงตัวอย่างการ์ดเมื่อกําหนดค่าในโทรศัพท์

ภาพรวมวงจรชีวิตของบริการแผนที่ย่อย

เมื่อสร้างและประกาศ TileService ในไฟล์ Manifest ของแอปแล้ว คุณจะตอบสนองต่อการเปลี่ยนแปลงสถานะของบริการการ์ดได้

TileService เป็นบริการที่มีผลผูกพัน TileService ของคุณจะได้รับการเชื่อมโยงเนื่องจากคำขอแอปหรือในกรณีที่ระบบต้องสื่อสารกับ TileService วงจรชีวิตของบริการที่เชื่อมโยงโดยทั่วไปจะมีเมธอดการเรียกกลับ 4 รายการ ได้แก่ onCreate(), onBind(), onUnbind() และ onDestroy() ระบบจะเรียกใช้เมธอดเหล่านี้ทุกครั้งที่บริการเข้าสู่ระยะวงจรใหม่

นอกจากการเรียกกลับที่ควบคุมวงจรบริการที่เชื่อมโยงแล้ว คุณยังใช้เมธอดอื่นๆ สำหรับวงจร TileService โดยเฉพาะได้ด้วย บริการการ์ดทั้งหมดต้องใช้ onTileRequest() และ onTileResourcesRequest() เพื่อตอบสนองคำขออัปเดตจากระบบ

  • onTileAddEvent(): ระบบจะเรียกใช้เมธอดนี้เฉพาะเมื่อผู้ใช้เพิ่มการ์ดของคุณเป็นครั้งแรก และในกรณีที่ผู้ใช้นำการ์ดออกและเพิ่มการ์ดอีกครั้ง นี่เป็นเวลาที่เหมาะที่สุดในการเริ่มต้นใช้งานแบบครั้งเดียว

    onTileAddEvent() จะเรียกใช้เฉพาะเมื่อมีการกำหนดค่าชุดการ์ดใหม่เท่านั้น ไม่ใช่ทุกครั้งที่ระบบสร้างการ์ด ตัวอย่างเช่น เมื่อรีบูตหรือเปิดอุปกรณ์ ระบบจะไม่เรียกใช้ onTileAddEvent() สำหรับการ์ดที่เพิ่มไว้แล้ว คุณใช้ getActiveTilesAsync()แทนได้เพื่อดูภาพรวมของการ์ดที่เป็นของคุณซึ่งใช้งานอยู่

  • onTileRemoveEvent(): ระบบจะเรียกใช้เมธอดนี้เฉพาะในกรณีที่ผู้ใช้นำการ์ดของคุณออก

  • onTileEnterEvent(): ระบบจะเรียกใช้เมธอดนี้เมื่อการ์ดที่ผู้ให้บริการรายนี้ระบุปรากฏบนหน้าจอ

  • onTileLeaveEvent(): ระบบจะเรียกใช้เมธอดนี้เมื่อการ์ดที่ผู้ให้บริการรายนี้ระบุไม่อยู่ในมุมมองบนหน้าจอ

  • onTileRequest(): ระบบจะเรียกใช้เมธอดนี้เมื่อระบบขอไทม์ไลน์ใหม่จากผู้ให้บริการรายนี้

  • onTileResourcesRequest(): ระบบจะเรียกใช้เมธอดนี้เมื่อระบบขอแพ็กเกจทรัพยากรจากผู้ให้บริการรายนี้ กรณีนี้อาจเกิดขึ้นเมื่อโหลดไทล์เป็นครั้งแรกหรือเมื่อใดก็ตามที่เวอร์ชันทรัพยากรมีการเปลี่ยนแปลง

ค้นหาว่าการ์ดใดที่ใช้งานอยู่

การ์ดที่ใช้งานอยู่คือการ์ดที่เพิ่มไว้เพื่อแสดงบนนาฬิกา ใช้เมธอดแบบคงที่ getActiveTilesAsync() ของ TileService เพื่อค้นหาว่าการ์ดใดของแอปคุณที่ใช้งานอยู่

สร้าง UI สำหรับการ์ด

เลย์เอาต์ของการ์ดเขียนโดยใช้รูปแบบ Builder เลย์เอาต์ของการ์ดจะสร้างขึ้นเหมือนต้นไม้ที่ประกอบด้วยคอนเทนเนอร์เลย์เอาต์และองค์ประกอบเลย์เอาต์พื้นฐาน องค์ประกอบเลย์เอาต์แต่ละรายการมีพร็อพเพอร์ตี้ ซึ่งคุณตั้งค่าได้ผ่านเมธอด setter ต่างๆ

องค์ประกอบเลย์เอาต์พื้นฐาน

ระบบรองรับองค์ประกอบภาพต่อไปนี้จากคลัง protolayout พร้อมกับคอมโพเนนต์ Material

  • Text: แสดงผลสตริงข้อความโดยตัดขึ้นบรรทัดใหม่หรือไม่ก็ได้
  • Image: แสดงผลรูปภาพ
  • Spacer: ให้ระยะห่างจากขอบระหว่างองค์ประกอบหรือทำหน้าที่เป็นตัวแบ่งเมื่อคุณตั้งค่าสีพื้นหลัง

คอมโพเนนต์ของ Material

นอกจากองค์ประกอบพื้นฐานแล้ว ไลบรารี protolayout-material ยังมีคอมโพเนนต์ที่ช่วยให้การออกแบบการ์ดสอดคล้องกับคําแนะนําของอินเทอร์เฟซผู้ใช้ Material Design

  • Button: คลิกได้ คอมโพเนนต์วงกลมที่ออกแบบมาเพื่อใส่ไอคอน
  • Chip: คอมโพเนนต์รูปทรงสนามกีฬาที่คลิกได้ซึ่งออกแบบมาเพื่อใส่ข้อความได้สูงสุด 2 บรรทัดและมีไอคอน (ไม่บังคับ)

  • CompactChip: คอมโพเนนต์รูปทรงสนามกีฬาที่คลิกได้ซึ่งออกแบบมาเพื่อใส่บรรทัดข้อความ

  • TitleChip: คอมโพเนนต์รูปทรงสนามกีฬาที่คลิกได้ ซึ่งคล้ายกับ Chip แต่มีความสูงมากกว่าเพื่อให้รองรับข้อความชื่อ

  • CircularProgressIndicator: สัญญาณบอกสถานะความคืบหน้าแบบวงกลมที่สามารถวางไว้ใน EdgeContentLayout เพื่อแสดงความคืบหน้ารอบขอบของหน้าจอ

คอนเทนเนอร์เลย์เอาต์

ระบบรองรับคอนเทนเนอร์ต่อไปนี้พร้อมกับเลย์เอาต์ Material

  • Row: จัดเรียงองค์ประกอบย่อยในแนวนอนทีละรายการ
  • Column: จัดวางองค์ประกอบย่อยในแนวตั้งทีละรายการ
  • Box: วางซ้อน องค์ประกอบย่อยทับกัน
  • Arc: จัดวางองค์ประกอบย่อยเป็นวงกลม
  • Spannable: ใช้รูปแบบที่เฉพาะเจาะจง FontStyles กับส่วนของข้อความพร้อมกับการแทรกข้อความและรูปภาพ ดูข้อมูลเพิ่มเติมได้ที่Spannables

คอนเทนเนอร์ทุกรายการมีคอนเทนเนอร์ย่อยได้อย่างน้อย 1 รายการ ซึ่งคอนเทนเนอร์ย่อยเองก็เป็นคอนเทนเนอร์ได้เช่นกัน เช่น Column อาจมีองค์ประกอบ Row หลายรายการเป็นองค์ประกอบย่อย ซึ่งส่งผลให้มีเลย์เอาต์คล้ายตารางกริด

ตัวอย่างเช่น ไทล์ที่มีเลย์เอาต์คอนเทนเนอร์และองค์ประกอบเลย์เอาต์ย่อย 2 รายการอาจมีลักษณะดังนี้

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: จัดตำแหน่งชุดปุ่มที่จัดเรียงตามหลักเกณฑ์ของ Material Design

  • 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 ย่อยมี 2 ประเภทดังนี้

  • SpanText: แสดงผลข้อความที่มีสไตล์ที่เฉพาะเจาะจง
  • SpanImage: แสดงผลรูปภาพในบรรทัดเดียวกับข้อความ

เช่น คุณอาจทำให้ "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() ทำได้ 2 วิธีดังนี้

  • ระบุทรัพยากรที่วาดได้โดยใช้ setAndroidResourceByResId()
  • ระบุรูปภาพแบบไดนามิกเป็น ByteArray โดยใช้ setInlineResource()

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