1. Pengantar
Kartu Wear OS menyediakan akses mudah ke informasi dan tindakan yang dibutuhkan pengguna untuk menyelesaikan berbagai hal. Hanya dengan menggeser dari tampilan jam, pengguna dapat mengetahui perkiraan terbaru atau memulai timer.
Kartu berjalan sebagai bagian dari UI sistem, bukan berjalan di container aplikasinya sendiri. Kita menggunakan Service untuk mendeskripsikan tata letak dan konten kartu. UI sistem kemudian akan merender kartu saat diperlukan.
Yang akan Anda lakukan
Anda akan membuat kartu untuk aplikasi pesan, yang menampilkan percakapan terbaru. Dari platform ini, pengguna dapat langsung ke salah satu dari 3 tugas umum:
- Membuka percakapan
- Menelusuri percakapan
- Menulis pesan baru
Yang akan Anda pelajari
Dalam codelab ini, Anda akan mempelajari cara menulis Kartu Wear OS Anda sendiri, termasuk cara:
- Membuat
TileService
- Menguji kartu di perangkat
- Melakukan pratinjau UI untuk kartu di Android Studio
- Mengembangkan UI untuk kartu
- Menambahkan gambar
- Menangani interaksi
Prasyarat
- Pemahaman dasar tentang Kotlin
2. Mempersiapkan
Pada langkah ini, Anda akan menyiapkan lingkungan dan mendownload project awal.
Yang akan Anda butuhkan
- Android Studio Dolphin (2021.3.1) atau yang lebih baru
- Perangkat atau emulator Wear OS
Jika Anda tidak terbiasa menggunakan Wear OS, membaca panduan cepat ini sebelum memulai akan membantu. Panduan ini berisi petunjuk untuk menyiapkan emulator Wear OS dan menjelaskan cara bernavigasi di sekitar sistem.
Mendownload kode
Jika sudah menginstal git, Anda dapat menjalankan perintah di bawah ini untuk meng-clone kode dari repositori ini. Untuk memeriksa apakah git sudah diinstal, ketik versi git di terminal atau command line dan pastikan git dijalankan dengan benar.
git clone https://github.com/googlecodelabs/wear-tiles.git cd wear-tiles
Jika tidak memiliki git, Anda dapat mengklik tombol berikut untuk mendownload semua kode untuk codelab ini:
Membuka project di Android Studio
Pada jendela "Welcome to Android Studio", pilih Open an Existing Project atau File > Open lalu pilih folder[Download Location].
3. Membuat kartu dasar
Titik entri untuk kartu adalah layanan kartu. Pada langkah ini, Anda akan mendaftarkan layanan kartu dan menentukan tata letak kartu.
HelloWorldTileService
Class yang menerapkan TileService
harus menentukan dua fungsi:
onResourcesRequest(requestParams: ResourcesRequest): ListenableFuture<Resources>
onTileRequest(requestParams: TileRequest): ListenableFuture<Tile>
Fungsi pertama memetakan ID string ke resource gambar. Di sinilah kita akan menyediakan resource gambar yang akan digunakan dalam kartu.
Fungsi kedua menampilkan deskripsi kartu seperti tata letaknya. Di sinilah kita menentukan tata letak kartu dan cara data terikat dengannya.
Buka HelloWorldTileService.kt
dari modul start
. Semua perubahan yang akan Anda buat ada dalam modul ini. Ada juga modul finished
jika Anda ingin melihat hasil codelab ini.
HelloWorldTileService
memperluas CoroutinesTileService
, yaitu wrapper yang cocok untuk coroutine Kotlin dari library Kartu Horologist. Horologist adalah sekumpulan library dari Google yang bertujuan melengkapi developer Wear OS dengan fitur yang umumnya diperlukan oleh developer, tetapi belum tersedia di Jetpack.
CoroutinesTileService
menyediakan dua fungsi penangguhan yang merupakan versi coroutine fungsi dari TileService
:
suspend resourcesRequest(requestParams: ResourcesRequest): Resources
suspend tileRequest(requestParams: TileRequest): Tile
Untuk mempelajari coroutine lebih lanjut, lihat dokumentasi untuk coroutine Kotlin di Android.
HelloWorldTileService
belum selesai. Kita harus mendaftarkan layanan dalam manifes dan juga perlu menyediakan implementasi untuk tileLayout
.
Mendaftarkan layanan kartu
Anda perlu mendaftarkan layanan kartu dalam manifes sehingga sistem mengetahuinya. Setelah didaftarkan, layanan akan muncul dalam daftar kartu yang tersedia untuk ditambahkan pengguna.
Tambahkan <service>
di dalam elemen <application>
:
start/src/main/AndroidManifest.xml
<service
android:name="com.example.wear.tiles.hello.HelloWorldTileService"
android:icon="@drawable/ic_waving_hand_24"
android:label="@string/hello_tile_label"
android:description="@string/hello_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>
<!-- The tile preview shown when configuring tiles on your phone -->
<meta-data
android:name="androidx.wear.tiles.PREVIEW"
android:resource="@drawable/tile_hello" />
</service>
Ikon dan label digunakan (sebagai placeholder) saat kartu dimuat untuk pertama kalinya, atau jika terjadi error saat memuat kartu. Meta-data di bagian akhir menentukan gambar pratinjau yang ditampilkan dalam carousel saat pengguna menambahkan kartu.
Menentukan tata letak kartu
HelloWorldTileService
memiliki fungsi bernama tileLayout
dengan TODO()
sebagai isi. Sekarang, mari ganti fungsi tersebut dengan implementasi yang dapat kita gunakan untuk menentukan tata letak kartu, dan mengikat data:
start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt
private fun tileLayout(): LayoutElement {
val text = getString(R.string.hello_tile_body)
return LayoutElementBuilders.Box.Builder()
.setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
.setWidth(DimensionBuilders.expand())
.setHeight(DimensionBuilders.expand())
.addContent(
LayoutElementBuilders.Text.Builder()
.setText(text)
.build()
)
.build()
}
Kita membuat elemen Text
dan menyetelnya di dalam Box
sehingga kita dapat melakukan beberapa penyelarasan dasar.
Dan itu adalah Kartu Wear OS pertama Anda yang dibuat. Mari kita instal kartu ini dan melihat bagaimana tampilannya.
4. Menguji kartu di perangkat
Dengan modul awal yang dipilih di dropdown konfigurasi run, Anda dapat menginstal aplikasi (modul start
) di perangkat atau emulator dan menginstal kartu secara manual, seperti yang dilakukan pengguna.
Sebagai gantinya, mari kita gunakan Direct Surface Launch, fitur yang diperkenalkan di Android Studio Dolphin, untuk membuat konfigurasi run baru guna meluncurkan kartu langsung dari Android Studio. Pilih "Edit Configurations..." dari menu dropdown di panel atas.
Klik tombol "Add new configuration" dan pilih "Wear OS Tile". Tambahkan nama deskriptif, lalu pilih modul Tiles_Code_Lab.start
dan kartu HelloWorldTileService
.
Tekan "Oke" untuk menyelesaikan.
Direct Surface Launch memungkinkan kami menguji kartu dengan cepat di emulator Wear OS atau perangkat fisik. Cobalah dengan menjalankan "HelloTile". Tampilannya akan terlihat seperti screenshot di bawah.
5. Membuat kartu pesan
Kartu pesan yang akan kita build lebih umum dari kartu dunia nyata. Tidak seperti contoh HelloWorld, contoh ini memuat data dari repositori lokal, mengambil gambar untuk ditampilkan dari jaringan dan menangani interaksi untuk membuka aplikasi, langsung dari kartu.
MessagingTileService
MessagingTileService
memperluas class CoroutinesTileService
yang telah kita lihat sebelumnya.
Perbedaan utama antara hal ini dan contoh sebelumnya adalah saat ini kita mengamati data dari repositori, dan juga mengambil data gambar dari jaringan.
Untuk pekerjaan yang berpotensi berjalan lama (misalnya panggilan jaringan), akan lebih tepat untuk menggunakan sesuatu seperti WorkManager, karena fungsi layanan kartu memiliki waktu tunggu yang relatif singkat. Dalam codelab ini, kami tidak akan memperkenalkan WorkManager—untuk mencobanya sendiri, lihat codelab ini.
MessagingTileRenderer
MessagingTileRenderer
memperluas class TileRenderer
(abstraksi lain dari Kartu Horologist). Ini sepenuhnya sinkron - status diteruskan ke fungsi perender, yang mempermudah penggunaan dalam pengujian dan pratinjau Android Studio.
Pada langkah berikutnya, kita akan melihat cara menambahkan pratinjau Android Studio untuk kartu.
6. Menambahkan fungsi pratinjau
Kita dapat melihat pratinjau UI kartu di Android Studio menggunakan TileLayoutPreview
(dan yang serupa) dari Kartu Horologist. Hal ini mempersingkat feedback loop saat mengembangkan UI, sehingga proses iterasi jauh lebih cepat.
Kita akan menggunakan alat dari Jetpack Compose untuk melihat pratinjau ini—itulah sebabnya Anda akan melihat anotasi @Composable
pada fungsi pratinjau di bawah. Anda dapat mempelajari pratinjau composable lebih lanjut tanpa harus menyelesaikan codelab ini.
Tambahkan pratinjau composable untuk MessagingTileRenderer
di akhir file.
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
@WearDevicePreview
@Composable
fun MessagingTileRendererPreview() {
TileLayoutPreview(
state = MessagingTileState(MessagingRepo.knownContacts),
resourceState = emptyMap(),
renderer = MessagingTileRenderer(LocalContext.current)
)
}
Perhatikan bahwa fungsi composable menggunakan TileLayoutPreview
; kita tidak dapat melihat pratinjau tata letak kartu secara langsung.
Gunakan mode editor "Split" (Terpisah) untuk melihat pratinjau kartu:
Kita meneruskan data buatan di MessagingTileState
, dan kita belum memiliki status resource sehingga dapat meneruskan peta kosong.
Pada langkah berikutnya, kita akan menggunakan Tiles Material untuk mengupdate tata letak.
7. Menambahkan Tiles Material
Tiles Material menyediakan Komponen Material dan tata letak bawaan, memungkinkan Anda membuat kartu yang menggunakan desain Material terbaru untuk Wear OS.
Tambahkan dependensi Tiles Material ke file build.gradle
Anda:
start/build.gradle
implementation "androidx.wear.tiles:tiles-material:$tilesVersion"
Bergantung pada kompleksitas desain Anda, sebaiknya tempatkan kode tata letak dengan perender, menggunakan fungsi tingkat atas dalam file yang sama untuk mengenkapsulasi unit logis UI.
Tambahkan kode untuk tombol di bagian bawah file perender, serta pratinjaunya:
start/src/main/java/MessagingTileRenderer.kt
private fun searchLayout(
context: Context,
clickable: ModifiersBuilders.Clickable,
) = Button.Builder(context, clickable)
.setContentDescription(context.getString(R.string.tile_messaging_search))
.setIconContent(MessagingTileRenderer.ID_IC_SEARCH)
.setButtonColors(ButtonColors.secondaryButtonColors(MessagingTileTheme.colors))
.build()
@IconSizePreview
@Composable
private fun SearchButtonPreview() {
LayoutElementPreview(
searchLayout(
context = LocalContext.current,
clickable = emptyClickable
)
) {
addIdToImageMapping(
MessagingTileRenderer.ID_IC_SEARCH,
drawableResToImageResource(R.drawable.ic_search_24)
)
}
}
LayoutElementPreview
mirip dengan TileLayoutPreview
tetapi digunakan untuk komponen individual seperti tombol, chip, atau label. Lambda di bagian akhir memungkinkan kita menentukan pemetaan ID resource (ke resource gambar), jadi di sini kita memetakan ID_IC_SEARCH
ke resource gambar penelusuran.
Dengan menggunakan mode editor "Split", kita dapat melihat pratinjau tombol penelusuran:
Kita juga dapat melakukan sesuatu yang serupa untuk membuat tata letak kontak:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
private fun contactLayout(
context: Context,
contact: Contact,
clickable: ModifiersBuilders.Clickable,
) = Button.Builder(context, clickable)
.setContentDescription(contact.name)
.apply {
if (contact.avatarUrl != null) {
setImageContent(contact.imageResourceId())
} else {
setTextContent(contact.initials)
setButtonColors(ButtonColors.secondaryButtonColors(MessagingTileTheme.colors))
}
}
.build()
Tiles Material tidak hanya menyertakan komponen. Daripada menggunakan serangkaian kolom dan baris bertingkat, kita dapat menggunakan tata letak dari Tiles Material untuk mendapatkan tampilan yang diinginkan dengan cepat.
Di sini, kita dapat menggunakan PrimaryLayout
dan MultiButtonLayout
untuk mengatur 4 kontak dan tombol penelusuran. Update fungsi messagingTileLayout()
di MessagingTileRenderer
dengan tata letak ini:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
private fun messagingTileLayout(
context: Context,
deviceParameters: DeviceParametersBuilders.DeviceParameters,
state: MessagingTileState
) = PrimaryLayout.Builder(deviceParameters)
.setContent(
MultiButtonLayout.Builder()
.apply {
// In a PrimaryLayout with a compact chip at the bottom, we can fit 5 buttons.
// We're only taking the first 4 contacts so that we can fit a Search button too.
state.contacts.take(4).forEach { contact ->
addButtonContent(
contactLayout(
context = context,
contact = contact,
clickable = emptyClickable
)
)
}
}
.addButtonContent(searchLayout(context, emptyClickable))
.build()
)
.build()
MultiButtonLayout
mendukung hingga 7 tombol, dan akan menata letaknya dengan spasi yang sesuai untuk Anda. Mari tambahkan chip "Baru" ke PrimaryLayout
juga, di builder PrimaryLayout di fungsi messagingTileLayout()
:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
.setPrimaryChipContent(
CompactChip.Builder(
/* context = */ context,
/* text = */ context.getString(R.string.tile_messaging_create_new),
/* clickable = */ emptyClickable,
/* deviceParameters = */ deviceParameters
)
.setChipColors(ChipColors.primaryChipColors(MessagingTileTheme.colors))
.build()
)
Pada langkah berikutnya, kita akan memperbaiki gambar yang hilang.
8. Menambahkan gambar
Menampilkan gambar lokal di kartu adalah tugas sederhana: sediakan pemetaan dari ID string (yang Anda gunakan dalam tata letak) ke gambar, menggunakan fungsi praktis dari Kartu Horologist untuk memuat drawable dan mengubahnya menjadi resource gambar. Contoh tersedia di SearchButtonPreview
:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
addIdToImageMapping(
ID_IC_SEARCH,
drawableResToImageResource(R.drawable.ic_search_24)
)
Untuk kartu pesan, kita juga perlu memuat gambar dari jaringan (bukan hanya resource lokal), dan untuk itu, kita menggunakan Coil, loader gambar berbasis coroutine Kotlin.
Kode untuk ini sudah ditulis:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileService.kt
override suspend fun resourcesRequest(requestParams: ResourcesRequest): Resources {
val avatars = imageLoader.fetchAvatarsFromNetwork(
context = this@MessagingTileService,
requestParams = requestParams,
tileState = latestTileState()
)
return renderer.produceRequestedResources(avatars, requestParams)
}
Karena perender kartu sepenuhnya sinkron, layanan kartu akan mengambil bitmap dari jaringan. Seperti sebelumnya, bergantung pada ukuran gambar, akan lebih tepat menggunakan WorkManager untuk mengambil gambar sebelumnya, tetapi untuk codelab ini, kita akan mengambilnya secara langsung.
Kita meneruskan peta avatars
(Contact
ke Bitmap
) ke perender sebagai "status" untuk resource. Kini perender dapat mengubah bitmap ini ke resource gambar untuk kartu.
Kode ini juga sudah ditulis:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
override fun ResourceBuilders.Resources.Builder.produceRequestedResources(
resourceState: Map<Contact, Bitmap>,
deviceParameters: DeviceParametersBuilders.DeviceParameters,
resourceIds: MutableList<String>
) {
addIdToImageMapping(
ID_IC_SEARCH,
drawableResToImageResource(R.drawable.ic_search_24)
)
resourceState.forEach { (contact, bitmap) ->
addIdToImageMapping(
/* id = */ contact.imageResourceId(),
/* image = */ bitmap.toImageResource()
)
}
}
Jadi, jika layanan mengambil bitmap, dan perender mengubah bitmap tersebut ke resource gambar, mengapa kartu tidak menampilkan gambar?
Tentu saja ada. Jika menjalankan kartu pada perangkat (dengan akses internet), Anda akan melihat gambar benar-benar dimuat. Masalah ini hanya dalam pratinjau karena kita masih meneruskan emptyMap()
untuk resourceState
.
Untuk kartu yang sebenarnya, kita mengambil bitmap dari jaringan dan memetakannya ke kontak yang berbeda, tetapi untuk pratinjau dan pengujian, kita tidak perlu mencapai jaringan sama sekali.
Update MessagingTileRendererPreview()
agar kita dapat menyediakan bitmap untuk dua kontak yang memerlukan:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
@WearDevicePreview
@Composable
fun MessagingTileRendererPreview() {
val state = MessagingTileState(MessagingRepo.knownContacts)
val context = LocalContext.current
TileLayoutPreview(
state = state,
resourceState = mapOf(
state.contacts[1] to (context.getDrawable(R.drawable.ali) as BitmapDrawable).bitmap,
state.contacts[2] to (context.getDrawable(R.drawable.taylor) as BitmapDrawable).bitmap,
),
renderer = MessagingTileRenderer(context)
)
}
Sekarang, jika kita memuat ulang pratinjau, gambar akan menampilkan:
Pada langkah berikutnya, kita akan menangani klik pada setiap elemen.
9. Menangani interaksi
Salah satu hal paling berguna yang dapat kita lakukan dengan kartu adalah menyediakan pintasan ke perjalanan penting pengguna. Ini berbeda dengan peluncur aplikasi yang baru saja membuka aplikasi - di sini, kita memiliki ruang untuk menyediakan pintasan kontekstual ke layar tertentu dalam aplikasi Anda.
Sejauh ini, kita telah menggunakan emptyClickable
untuk chip dan setiap tombol. Tindakan ini tidak masalah untuk pratinjau yang tidak interaktif, tetapi mari kita lihat cara menambahkan tindakan untuk elemen tersebut.
Dua builder dari class 'ActionBuilders' menentukan tindakan yang Dapat Diklik: LoadAction
dan LaunchAction
.
LoadAction
LoadAction
dapat digunakan jika Anda ingin menjalankan logika di layanan kartu saat pengguna mengklik elemen, misalnya menambahkan penghitung.
.setClickable(
Clickable.Builder()
.setId(ID_CLICK_INCREMENT_COUNTER)
.setOnClick(ActionBuilders.LoadAction.Builder().build())
.build()
)
)
Saat diklik, onTileRequest
akan dipanggil dalam layanan Anda (tileRequest
di CoroutinesTileService
) sehingga sebaiknya Anda memuat ulang UI kartu:
override suspend fun tileRequest(requestParams: TileRequest): Tile {
if (requestParams.state.lastClickableId == ID_CLICK_INCREMENT_COUNTER) {
// increment counter
}
// return an updated tile
}
LaunchAction
LaunchAction
dapat digunakan untuk meluncurkan aktivitas. Pada MessagingTileRenderer
, mari kita update tombol yang dapat diklik untuk tombol penelusuran.
Tombol penelusuran ditentukan oleh fungsi searchLayout()
di MessagingTileRenderer
. Fungsi ini sudah menggunakan Clickable
sebagai parameter, tetapi sejauh ini, kita telah meneruskan emptyClickable
, implementasi tanpa pengoperasian yang tidak akan berfungsi sama sekali saat tombol diklik.
Mari kita update messagingTileLayout()
agar meneruskan tindakan klik nyata. Tambahkan parameter searchButtonClickable
, lalu teruskan ke searchLayout()
:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
private fun messagingTileLayout(
context: Context,
deviceParameters: DeviceParametersBuilders.DeviceParameters,
state: MessagingTileState,
searchButtonClickable: ModifiersBuilders.Clickable
...
.addButtonContent(searchLayout(context, searchButtonClickable))
Kita juga perlu memperbarui renderTile
yang merupakan tempat kita memanggil messagingTileLayout
karena kita baru saja menambahkan parameter baru (searchButtonClickable
). Kita akan menggunakan fungsi launchActivityClickable()
untuk membuat fungsi baru yang dapat diklik dengan meneruskan openSearch()
ActionBuilder
sebagai tindakan:
start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt
override fun renderTile(
state: MessagingTileState,
deviceParameters: DeviceParametersBuilders.DeviceParameters
): LayoutElementBuilders.LayoutElement {
return messagingTileLayout(
context = context,
deviceParameters = deviceParameters,
state = state,
searchButtonClickable = launchActivityClickable("search_button", openSearch())
)
}
Buka launchActivityClickable
untuk melihat cara kerja fungsi ini (sudah ditentukan):
start/src/main/java/com/example/wear/tiles/messaging/tile/ClickableActions.kt
internal fun launchActivityClickable(
clickableId: String,
androidActivity: ActionBuilders.AndroidActivity
) = ModifiersBuilders.Clickable.Builder()
.setId(clickableId)
.setOnClick(
ActionBuilders.LaunchAction.Builder()
.setAndroidActivity(androidActivity)
.build()
)
.build()
Ini sangat mirip dengan LoadAction
- perbedaan utamanya adalah kita memanggil setAndroidActivity
. Dalam file yang sama, kita memiliki berbagai contoh ActionBuilder.AndroidActivity
.
Untuk openSearch
, yang kita gunakan untuk tombol yang dapat diklik ini, kita memanggil setMessagingActivity
dan meneruskan string tambahan untuk mengidentifikasi tombol mana yang diklik.
start/src/main/java/com/example/wear/tiles/messaging/tile/ClickableActions.kt
internal fun openSearch() = ActionBuilders.AndroidActivity.Builder()
.setMessagingActivity()
.addKeyToExtraMapping(
MainActivity.EXTRA_JOURNEY,
ActionBuilders.stringExtra(MainActivity.EXTRA_JOURNEY_SEARCH)
)
.build()
...
internal fun ActionBuilders.AndroidActivity.Builder.setMessagingActivity(): ActionBuilders.AndroidActivity.Builder {
return setPackageName("com.example.wear.tiles")
.setClassName("com.example.wear.tiles.messaging.MainActivity")
}
Jalankan kartu, lalu klik tombol telusuri. Tindakan ini akan membuka MainActivity
dan menampilkan teks untuk mengonfirmasi bahwa tombol penelusuran diklik.
Langkah untuk menambahkan tindakan pada contoh lainnya serupa. ClickableActions
berisi fungsi yang Anda butuhkan. Jika Anda memerlukan petunjuk, lihat MessagingTileRenderer
dari modul finished
.
10. Selamat
Selamat! Anda telah mempelajari cara membuat kartu untuk Wear OS.
Apa selanjutnya?
Untuk informasi selengkapnya, lihat penerapan Golden Tile di GitHub dan panduan Kartu Wear OS.