Kartu

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

Kartu memberikan akses mudah ke informasi dan tindakan yang diperlukan pengguna untuk menyelesaikan berbagai aktivitas. Hanya dengan menggeser dari tampilan jam, pengguna dapat mengetahui prakiraan cuaca terbaru atau memulai timer.

pergeseran kartu dari waktu ke cuaca, lalu ke timer.

Pengguna dapat memilih Kartu yang ingin dilihat. Ada Kartu untuk memeriksa cuaca, menyetel timer, melacak progres kebugaran harian, memulai olahraga dengan cepat, memutar lagu, melihat jadwal rapat yang akan datang, dan mengirimkan pesan ke kontak favorit.

Contoh Kartu yang bersebelahan.

Dengan Tiles API, developer dapat mem-build Kartu kustom yang dapat disertakan pengguna di smartwatch mereka. Penggunaan Tiles API memerlukan penargetan API level 26 atau versi lebih tinggi.

Untuk informasi selengkapnya, baca postingan blog Kartu.

Praktik terbaik

Ada beberapa pertimbangan khusus untuk menggunakan Kartu:

  • Daripada menggunakan komponen dasar, sebaiknya gunakan Komponen material dan Tata letak material untuk memastikan kepatuhan terhadap rekomendasi antarmuka pengguna Desain Material dan konsistensi secara keseluruhan.
  • Meskipun OS menangani rendering UI Kartu, Anda harus menyediakan tata letak, informasi, dan resource menggunakan TileService.
  • Kartu dimaksudkan untuk informasi sekilas yang dapat dibaca pengguna dalam hitungan detik. Hanya tampilkan konten paling penting dengan hierarki informasi yang jelas.
  • Untuk melindungi baterai pengguna, hindari elemen yang perlu sering dirender ulang.
  • Simpan pengalaman yang sangat interaktif untuk aktivitas Anda. Namun, Anda dapat menautkan ke aktivitas tersebut dari Kartu.
  • Hindari teks seperti “x menit yang lalu” atau "dalam x menit" untuk peristiwa sebelumnya atau yang akan datang karena akan memerlukan pembaruan secara berkala. Sebagai gantinya, tampilkan waktu mulai atau waktu berakhir yang sebenarnya atau gunakan pernyataan seperti "dahulu".
  • Hindari tugas asinkron yang berjalan lama saat menyediakan tata letak dan/atau resource Kartu. Kode Kartu Anda akan dijalankan dengan cepat.
  • Sebaiknya izinkan pengguna mengetuk Kartu agar dapat mempelajari lebih lanjut dan mengambil tindakan dalam overlay saat ada dukungan untuk beragam interaktivitas dan pengguna dapat men-scroll untuk mengetahui informasi selengkapnya.
  • Jika Anda memiliki aplikasi besar yang mendukung pengguna dengan beberapa tugas, sebaiknya buat Kartu untuk setiap tugas. Misalnya, aplikasi kebugaran mungkin memiliki Kartu Target dan Kartu Aktivitas Olahraga.

Memulai

Penyiapan

Untuk mulai menyediakan Kartu dari aplikasi, sertakan dependensi berikut dalam file build.gradle aplikasi.

Groovy

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

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

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

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

Kotlin

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

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

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

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

Catatan: API ini bersifat asinkron dan sangat mengandalkan ListenableFuture. Lihat Menggunakan ListenableFuture untuk informasi selengkapnya tentang konsep ini.

Membuat Kartu

Untuk menyediakan Kartu dari aplikasi, buat class yang memperluas TileService dan implementasikan metode tersebut, seperti ditunjukkan dalam contoh kode berikut:

Kotlin

private val RESOURCES_VERSION = "1"
class MyTileService : TileService() {
    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTimeline(Timeline.Builder().addTimelineEntry(
                TimelineEntry.Builder().setLayout(
                    Layout.Builder().setRoot(
                        Text.Builder().setText("Hello world!").setFontStyle(
                            FontStyle.Builder().setColor(argb(0xFF000000)).build()
                        ).build()
                    ).build()
                ).build()
            ).build()
        ).build())

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

Java

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)
            .setTimeline(new Timeline.Builder()
                .addTimelineEntry(new TimelineEntry.Builder()
                    .setLayout(new Layout.Builder()
                        .setRoot(new Text.Builder()
                            .setText("Hello world!")
                            .setFontStyle(new FontStyle.Builder()
                                .setColor(argb(0xFF000000)).build()
                            ).build()
                        ).build()
                    ).build()
                ).build()
            ).build()
        );
   }

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

Selanjutnya, tambahkan layanan di dalam tag <application> AndroidManifest.xml.

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

Filter izin dan intent akan mendaftarkan layanan ini sebagai penyedia Kartu.

Ikon, label, dan deskripsi ditampilkan kepada pengguna saat mereka mengonfigurasi Kartu di ponsel atau smartwatch.

Gunakan tag metadata pratinjau untuk menampilkan pratinjau Kartu saat mengonfigurasinya di ponsel.

Catatan: Pada umumnya, Anda harus menyediakan resource untuk layar bulat (drawable-round-nodpi) dan persegi (drawable-notround-nodpi).

Mendeklarasikan layanan di manifes aplikasi Anda:

Library wear-tiles-renderer menyediakan cara untuk melihat pratinjau Kartu di aktivitas dalam aplikasi Anda.

Untuk melihat pratinjau Kartu, buat aktivitas yang menggunakan library perender untuk merender Kartu. Tambahkan aktivitas ini di src/debug, bukan src/main, karena Anda akan menggunakan aktivitas ini hanya untuk tujuan proses debug. Lihat contoh kode berikut untuk mengetahui contohnya:

Kotlin

class MainActivity : ComponentActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var tileUiClient: TileUiClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        tileUiClient = TileUiClient(
            context = this,
            component = ComponentName(this, MyTileService::class.java),
            parentView = binding.tileContainer
        )
        tileUiClient.connect()
    }

    override fun onDestroy() {
        super.onDestroy()
        tileUiClient.close()
    }
}

Java

public class MainActivity extends ComponentActivity {
    private ActivityMainBinding binding;
    private TileUiClient mTileUiClient;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        mTileUiClient = new TileUiClient(
                this,
                new ComponentName(this, MyTileService.class),
                binding.tileContainer
        );
        mTileUiClient.connect();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTileUiClient.close();
    }
}

Buat file tata letak di activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/tile_container"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

Menggunakan linimasa

Linimasa terdiri dari satu atau beberapa instance TimelineEntry yang masing-masing berisi tata letak yang ditampilkan selama interval waktu tertentu. Semua Kartu memerlukan linimasa.

Diagram linimasa Kartu

Kartu sekali masuk

Sering kali Kartu dapat dijelaskan dengan satu TimelineEntry. Tata letak bersifat tetap, dan hanya informasi di dalam tata letak yang berubah. Misalnya, Kartu yang menampilkan progres kebugaran hari ini akan selalu menampilkan tata letak progres yang sama, meskipun Anda dapat menyesuaikan tata letak tersebut untuk menampilkan nilai lain. Dalam kasus ini, Anda tidak tahu sebelumnya kapan konten dapat berubah.

Lihat contoh Kartu berikut dengan satu TimelineEntry:

Kotlin

override fun onTileRequest(
    requestParams: TileRequest
): ListenableFuture<Tile> {
    val tile = Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTimeline(Timeline.Builder()
            // We add a single timeline entry when our layout is fixed, and
            // we don't know in advance when its contents might change.
            .addTimelineEntry(TimelineEntry.Builder()
                // .setLayout(...).build()
            ).build()
        ).build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
   Tile Tile = new Tile.Builder()
       .setResourcesVersion(RESOURCES_VERSION)
       .setTimeline(
           new Timeline.Builder()
               // We add a single timeline entry when our layout is fixed, and
               // we don't know in advance when its contents might change.
               .addTimelineEntry(new TimelineEntry.Builder()
                   // .setLayout(...).build()
               ).build()
       ).build();
   return Futures.immediateFuture(tile);
}

Entri linimasa dengan batas waktu

Secara opsional, TimelineEntry dapat menentukan periode validitas yang memungkinkan Kartu mengubah tata letaknya pada suatu waktu yang diketahui tanpa mengharuskan aplikasi mendorong Kartu baru.

Contoh kanonis adalah Kartu agenda yang linimasanya berisi daftar acara mendatang. Setiap peristiwa mendatang berisi periode validitas guna menunjukkan waktu untuk menampilkannya.

Tiles API memungkinkan periode validitas yang tumpang-tindih, dan layar yang akan ditampilkan adalah layar dengan sisa periode waktu terpendek. Hanya satu peristiwa yang ditampilkan dalam satu waktu.

Developer dapat memberikan entri penggantian default. Misalnya, Kartu agenda dapat memiliki Kartu dengan periode validitas tak terbatas, dan akan digunakan jika tidak ada entri linimasa yang valid, seperti yang ditunjukkan pada contoh kode berikut:

Kotlin

public override fun onTileRequest(
    requestParams: TileRequest
): ListenableFuture<Tile> {
    val timeline = Timeline.builder()

    // Add fallback "no meetings" entry
    timeline.addTimelineEntry(TimelineEntry.Builder().setLayout(getNoMeetingsLayout()).build())

    // Retrieve a list of scheduled meetings
    val meetings = MeetingsRepo.getMeetings()
    // Add a timeline entry for each meeting
    meetings.forEach { meeting ->
        timeline.addTimelineEntry(TimelineEntry.Builder()
            .setLayout(getMeetingLayout(meeting))
            .setValidity(
                // The Tile should disappear when the meeting begins
                TimeInterval.Builder()
                    .setEndMillis(meeting.dateTimeMillis).build()
            ).build()
        )
    }

    val tile = Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTimeline(timeline.build())
        .build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
   Timeline.Builder timeline = new Timeline.Builder();
   // Add fallback "no meetings" entry
   timeline.addTimelineEntry(new TimelineEntry.Builder().setLayout(getNoMeetingsLayout()).build());
   // Retrieve a list of scheduled meetings
   List<Meeting> meetings = MeetingsRepo.getMeetings();
   // Add a timeline entry for each meeting
   for(Meeting meeting : meetings) {
        timeline.addTimelineEntry(new TimelineEntry.Builder()
            .setLayout(getMeetingLayout(meeting))
            .setValidity(
                // The Tile should disappear when the meeting begins
                new TimeInterval.builder()
                    .setEndMillis(meeting.getDateTimeMillis()).build()
            ).build()
        );
    }

    Tile Tile = new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTimeline(timeline.build())
        .build();
    return Futures.immediateFuture(tile);
}

Me-refresh Kartu

Informasi yang ditampilkan di Kartu mungkin tidak akan berlaku lagi setelah beberapa waktu. Misalnya, Kartu cuaca yang menampilkan suhu yang sama sepanjang hari akan menjadi tidak lagi akurat.

Untuk menangani data yang tidak berlaku lagi, tetapkan interval keaktualan pada saat membuat Kartu untuk menentukan periode validitas Kartu. Dalam contoh Kartu cuaca, kontennya mungkin diperbarui setiap satu jam sekali, seperti yang ditunjukkan pada contoh kode berikut:

Kotlin

override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
    Futures.immediateFuture(Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTimeline(Timeline.Builder()
            .addTimelineEntry(TimelineEntry.Builder()
                .setLayout(getWeatherLayout())
                .build()
            ).build()
        ).build()
    )

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
    return Futures.immediateFuture(new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTimeline(new Timeline.Builder()
            .addTimelineEntry(new TimelineEntry.Builder()
                .setLayout(getWeatherLayout())
                .build()
            ).build()
        ).build());
}

Saat Anda menetapkan interval keaktualan, sistem akan segera memanggil onTileRequest() setelah interval selesai. Jika Anda tidak menetapkan interval keaktualan, sistem tidak akan memanggil onTileRequest().

Kartu juga dapat tidak berlaku lagi karena peristiwa eksternal. Misalnya, pengguna mungkin menghapus rapat dari kalender, dan jika Kartu tidak di-refresh, Kartu tersebut akan tetap menampilkan rapat yang telah dihapus. Dalam hal ini, minta refresh dari mana saja dalam kode aplikasi Anda, seperti yang ditunjukkan dalam contoh kode berikut:

Kotlin

fun eventDeletedCallback() {
     TileService.getUpdater(context)
             .requestUpdate(MyTileService::class.java)
}

Java

public void eventDeletedCallback() {
   TileService.getUpdater(context)
           .requestUpdate(MyTileService.class);
}

Tata letak desain

Tata letak Kartu ditulis menggunakan pola builder. Tata letak Kartu dibuat seperti hierarki yang terdiri dari penampung tata letak dan elemen tata letak dasar. Setiap elemen tata letak memiliki properti, yang dapat Anda setel melalui berbagai metode penyetel.

Elemen tata letak dasar

Elemen visual berikut didukung:

  • Text: merender string teks, digabungkan secara opsional.
  • Image: merender gambar.
  • Spacer: menyediakan padding di antara elemen atau dapat berfungsi sebagai pemisah saat Anda menetapkan warna latar belakangnya.

Komponen material

Selain elemen dasar, library tiles-material juga menyediakan komponen yang memastikan desain kartu sesuai dengan rekomendasi antarmuka pengguna Desain Material.

  • Button: komponen melingkar yang dapat diklik dan dirancang untuk memuat ikon.
  • Chip: komponen berbentuk stadion yang dapat diklik dan didesain untuk memuat hingga dua baris teks dan ikon opsional.
  • CompactChip: komponen berbentuk stadion yang dapat diklik dan dirancang untuk memuat baris teks.
  • TitleChip: komponen berbentuk stadion yang dapat diklik dan mirip dengan Chip tetapi lebih tinggi untuk menampung teks judul.
  • CircularProgressIndicator: indikator progres melingkar yang dapat ditempatkan di dalam ProgressIndicatorLayout untuk menampilkan progres di sekitar tepi layar.

Penampung tata letak

Penampung berikut didukung:

  • Row: menata letak elemen turunan secara horizontal, satu per satu.
  • Column: menata letak elemen turunan secara vertikal, satu per satu.
  • Box: menempatkan elemen turunan di atas elemen lainnya.
  • Arc: menata letak elemen turunan dalam lingkaran.
  • Spannable: menerapkan FontStyles tertentu pada bagian teks bersama dengan teks dan gambar yang disisipkan. Untuk informasi selengkapnya, lihat Spannable.

Setiap penampung dapat berisi satu atau beberapa turunan yang juga dapat berupa penampung. Misalnya, Column dapat berisi beberapa elemen Row sebagai turunan sehingga menghasilkan tata letak seperti petak.

Sebagai contoh, Kartu dengan tata letak penampung dan dua elemen tata letak turunan dapat terlihat seperti ini:

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

Tata letak material

Selain tata letak dasar, library tiles-material juga menyediakan beberapa tata letak pendapat opini yang dibuat untuk menampung elemen di "slot" tertentu.

  • PrimaryLayout: memosisikan satu tindakan utama CompactChip di bagian bawah dengan konten yang dipusatkan di atasnya.
  • MultiSlotLayout: memosisikan label utama dan sekunder dengan konten opsional di antara keduanya dan CompactChip opsional di bagian bawah.
  • ProgressIndicatorLayout: memosisikan CircularProgressIndicator di sekitar tepi layar dan konten tertentu di dalamnya.

Arc

Turunan penampung Arc berikut didukung:

  • ArcLine: merender garis melengkung di sekitar Arc.
  • ArcText: merender teks melengkung di dalam Arc.
  • ArcAdapter: merender elemen tata letak dasar di arc, yang digambar bersinggungan dengan arc.

Catatan: Saat ArcText menggambar teks melengkung di sekitar Arc, penggunaan Text dalam ArcAdapter akan menghasilkan gambar teks linear yang bersinggungan dengan arc.

Untuk informasi lebih lanjut, lihat dokumentasi referensi untuk setiap jenis elemen.

Pengubah

Setiap elemen tata letak yang tersedia secara opsional dapat memiliki pengubah yang diterapkan padanya. Gunakan pengubah ini untuk tujuan berikut:

  • Mengubah tampilan visual tata letak. Misalnya, tambahkan latar belakang, batas, atau padding ke elemen tata letak.
  • Menambahkan metadata tentang tata letak. Misalnya, tambahkan pengubah semantik ke elemen tata letak untuk digunakan dengan pembaca layar.
  • Menambahkan fungsi. Misalnya, tambahkan pengubah yang dapat diklik ke elemen tata letak agar Kartu Anda interaktif. Untuk informasi selengkapnya, lihat Berinteraksi dengan Kartu.

Misalnya, kita dapat menyesuaikan tampilan default dan metadata Image, seperti yang ditunjukkan dalam contoh kode berikut:

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 adalah jenis penampung khusus yang meletakkan elemen yang mirip dengan teks. Jenis penampung ini berguna jika Anda ingin menerapkan gaya yang berbeda ke hanya satu substring dalam blok teks yang lebih besar, dan tidak mungkin dilakukan dengan elemen Text.

Penampung Spannable diisi dengan turunan Span. Turunan lain, atau instance Spannable bertingkat, tidak diizinkan.

Ada dua jenis turunan Span:

  • SpanText: merender teks dengan gaya tertentu.
  • SpanImage: merender gambar yang inline dengan teks.

Misalnya, Anda dapat memiringkan huruf “dunia” dalam Kartu “Halo dunia” dan menyisipkan gambar di antara kata, seperti yang ditunjukkan dalam contoh kode berikut:

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

Menggunakan resource

Kartu tidak memiliki akses ke resource mana pun di aplikasi Anda. Ini berarti Anda tidak boleh meneruskan ID gambar Android ke elemen tata letak Image dan mengharapkannya untuk diselesaikan. Sebagai gantinya, ganti metode onResourcesRequest() dan sediakan resource apa pun secara manual.

Ada dua cara untuk menyediakan gambar dalam metode onResourcesRequest():

Kotlin

override fun onResourcesRequest(
    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> onResourcesRequest(
       @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()
);
}

Berinteraksi dengan Kartu

Dengan menambahkan pengubah Clickable ke elemen tata letak, Anda dapat bereaksi terhadap pengguna yang mengetuk elemen tata letak tersebut. Sebagai reaksi terhadap peristiwa klik, Anda dapat melakukan dua tindakan:

  • LaunchAction: meluncurkan aktivitas yang dideklarasikan secara eksplisit sebagai android:exported="true" dalam AndroidManifest.xml.
  • LoadAction: memaksa refresh Kartu, memanggil onTileRequest().

Untuk menyiapkan LaunchAction, teruskan nama class dan nama paket aktivitas yang ingin Anda luncurkan saat pengguna mengetuk elemen, seperti yang ditunjukkan pada contoh kode berikut:

Kotlin

private fun tappableElement(): LayoutElement =
    Text.Builder()
        .setText("Tap me!")
        .setModifiers(Modifiers.Builder()
            .setClickable(Clickable.Builder()
                .setId("foo")
                .setOnClick(LaunchAction.Builder()
                    .setAndroidActivity(AndroidActivity.Builder()
                        .setClassName(MyActivity::class.java.getName())
                        .setPackageName(this.packageName)
                        .build()
                    ).build()
                ).build()
            ).build()
        ).build()

Java

private LayoutElement tappableElement() {
    return new Text.Builder()
        .setText("Tap me!")
        .setModifiers(new Modifiers.Builder()
            .setClickable(new Clickable.Builder()
                .setId("foo")
                .setOnClick(new LaunchAction.Builder()
                    .setAndroidActivity(new AndroidActivity.Builder()
                        .setClassName(MyActivity.class.getName())
                        .setPackageName(this.getPackageName())
                        .build()
                    ).build()
                ).build()
            ).build()
        ).build();
}

Di dalam aktivitas yang diluncurkan, Anda dapat mengambil ID yang digunakan untuk Kartu:

Kotlin

class MyActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val clickableId =
            intent.getStringExtra(TileService.EXTRA_CLICKABLE_ID)
        // clickableId will be "foo" when launched from the Tile
    }
}

Java

public class MyActivity extends FragmentActivity {
   @Override
   public void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       String clickableId =
           getIntent().getStringExtra(TileService.EXTRA_CLICKABLE_ID);
       // clickableId will be "foo" when launched from the Tile
   }
}

Catatan: Anda dapat memulai Aktivitas dari aplikasi Anda sendiri atau dari aplikasi lain, selama android:exported="true" dideklarasikan dalam manifes aplikasi Anda.

Atau, gunakan LoadAction untuk me-refresh Kartu Anda saat pengguna mengetuk elemen tata letak, seperti yang ditunjukkan dalam contoh kode berikut:

Kotlin

private fun tappableElement(): LayoutElement =
    Text.Builder()
        .setText("Tap me!")
        .setModifiers(Modifiers.Builder()
            .setClickable(Clickable.Builder()
                .setId("foo")
                .setOnClick(LoadAction.Builder().build())
                .build()
            ).build()
        ).build()

Java

private LayoutElement tappableElement() {
    return new Text.Builder()
        .setText("Tap me!")
        .setModifiers(new Modifiers.Builder()
            .setClickable(new Clickable.Builder()
                .setId("foo")
                .setOnClick(new LoadAction.Builder().build())
                .build()
            ).build()
        ).build()
}

Dalam hal ini, ID yang dapat diklik dan ditetapkan di setId() akan diteruskan ke panggilan onTileRequest() sehingga Anda dapat merender tata letak yang berbeda berdasarkan ID ini, seperti yang ditunjukkan pada contoh kode berikut:

Kotlin

override fun onTileRequest(requestParams: TileRequest) = Futures.immediateFuture(
    Tile.Builder()
        .setResourcesVersion("1")
        .setTimeline(Timeline.Builder()
            .addTimelineEntry(TimelineEntry.Builder()
                .setLayout(Layout.Builder()
                    .setRoot(
                        when(requestParams.state.lastClickableId) {
                            "foo" -> myFooLayout()
                            else -> myOtherLayout()
                        }
                    ).build()
                ).build()
            ).build()
        ).build()
)

Java

@NonNull
@Override
protected ListenableFuture<Tile> onTileRequest(
   @NonNull TileRequest requestParams
) {
    LayoutElement root;
    if(requestParams.getState().getLastClickableId().equals("foo")) {
        root = myFooLayout();
    } else {
        root = myOtherLayout();
    }
    return Futures.immediateFuture(new Tile.Builder()
        .setResourcesVersion("1")
        .setTimeline(new Timeline.Builder()
            .addTimelineEntry(TimelineEntry.Builder()
                .setLayout(Layout.Builder()
                    .setRoot(root)
                    .build()
                ).build()
            ).build()
        ).build());
}