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

หากต้องการเริ่มใส่การ์ดจากแอป ให้ใส่ทรัพยากร Dependency ต่อไปนี้ใน 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 และติดตั้งใช้งานเมธอดดังที่แสดงในตัวอย่างโค้ดต่อไปนี้

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 ในไฟล์ Manifest ของแอปแล้ว คุณจะ สามารถตอบสนองต่อการเปลี่ยนแปลงสถานะของบริการการ์ด

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

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

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

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

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

  • onTileEnterEvent(): ระบบจะเรียกใช้เมธอดนี้เมื่อการ์ด จากผู้ให้บริการรายนี้จะแสดงให้เห็นบนหน้าจอ

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

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

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

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

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

สร้าง UI สำหรับไทล์

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

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

รองรับองค์ประกอบภาพต่อไปนี้จากไลบรารี protolayout รวมถึงคอมโพเนนต์วัสดุ

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

ส่วนประกอบของวัสดุ

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

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

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

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

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

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

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

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

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

  • 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: จะแสดงผลรูปภาพในบรรทัดพร้อมข้อความ

เช่น ทำให้ "โลก" เป็นตัวเอียง ในโหมด "สวัสดีโลก" ไทล์และแทรก รูปภาพระหว่างคำ ดังที่ปรากฏในตัวอย่างโค้ดต่อไปนี้

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