1. Sebelum memulai
Dalam codelab ini, Anda akan membuat aplikasi Dice Roller interaktif yang memungkinkan pengguna melempar dadu dengan mengetuk composable Button
. Hasil lemparan ditampilkan dengan composable Image
di layar.
Anda menggunakan Jetpack Compose dengan Kotlin untuk membuat tata letak aplikasi, lalu menulis logika bisnis untuk menangani peristiwa yang terjadi saat composable Button
diketuk.
Prasyarat
- Mampu membuat dan menjalankan aplikasi Compose dasar di Android Studio.
- Memahami cara menggunakan composable
Text
dalam aplikasi. - Memahami cara mengekstrak teks ke resource string untuk memudahkan penerjemahan aplikasi dan menggunakan kembali string tersebut.
- Memahami dasar-dasar pemrograman Kotlin.
Yang akan Anda pelajari
- Cara menambahkan composable
Button
ke aplikasi Android dengan Compose. - Cara menambahkan perilaku ke composable
Button
di aplikasi Android dengan Compose. - Cara membuka dan mengubah kode
Activity
untuk aplikasi Android.
Yang akan Anda bangun
- Aplikasi Android interaktif bernama Dice Roller yang memungkinkan pengguna melempar dadu dan menampilkan hasil lemparannya.
Yang akan Anda butuhkan
- Komputer yang dilengkapi Android Studio.
Tampilan aplikasi akan terlihat seperti berikut saat Anda menyelesaikan codelab ini:
2. Menetapkan dasar pengukuran
Membuat project
- Di Android Studio, klik File > New > New Project.
- Dalam dialog New Project, pilih Empty Activity, lalu klik Next.
- Di kolom Name, masukkan
Dice Roller
. - Di kolom Minimum SDK, pilih level API minimum 24 (Nougat) dari menu, lalu klik Finish.
3. Membuat infrastruktur tata letak
Melihat pratinjau project
Untuk melihat pratinjau project:
- Klik Build & Refresh di panel Split atau Design.
Sekarang Anda akan melihat pratinjau di panel Design. Jangan khawatir jika project terlihat kecil karena akan berubah saat Anda mengubah tata letak.
Mengubah struktur kode contoh
Anda perlu mengubah beberapa kode yang telah dibuat agar lebih mirip dengan tema aplikasi dadu di atas.
Seperti yang Anda lihat di screenshot aplikasi akhir, ada gambar dadu dan tombol untuk melemparnya. Anda akan menyusun fungsi composable untuk mencerminkan arsitektur ini.
Untuk menyusun ulang kode contoh:
- Hapus fungsi
GreetingPreview()
. - Buat fungsi
DiceWithButtonAndImage()
dengan anotasi@Composable
.
Fungsi composable ini mewakili komponen UI tata letak dan juga menampung logika klik tombol dan tampilan gambar.
- Hapus fungsi
Greeting(name: String, modifier: Modifier = Modifier)
. - Buat fungsi
DiceRollerApp()
dengan anotasi@Preview
dan@Composable
.
Aplikasi ini hanya terdiri dari tombol dan gambar. Jadi, anggap fungsi composable ini sebagai aplikasi itu sendiri. Itulah alasannya fungsi ini disebut fungsi DiceRollerApp()
.
MainActivity.kt
@Preview
@Composable
fun DiceRollerApp() {
}
@Composable
fun DiceWithButtonAndImage() {
}
Panggilan ke Greeting("Android")
dalam isi lambda DiceRollerTheme()
disorot dengan warna merah karena Anda menghapus fungsi Greeting()
. Ini karena compiler tidak dapat menemukan referensi ke fungsi tersebut lagi.
- Hapus semua kode di dalam lambda
setContent{}
yang ditemukan dalam metodeonCreate()
. - Di bagian lambda
setContent{}
, panggil lambdaDiceRollerTheme{}
, lalu di dalam lambdaDiceRollerTheme{}
, panggil fungsiDiceRollerApp()
.
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
DiceRollerTheme {
DiceRollerApp()
}
}
}
- Di fungsi
DiceRollerApp()
, panggil fungsiDiceWithButtonAndImage()
.
MainActivity.kt
@Preview
@Composable
fun DiceRollerApp() {
DiceWithButtonAndImage()
}
Menambahkan pengubah
Compose menggunakan objek Modifier
yang merupakan kumpulan elemen yang mendekorasi atau mengubah perilaku elemen UI Compose. Anda menggunakannya untuk menata gaya komponen UI dari komponen aplikasi Dice Roller.
Untuk menambahkan pengubah:
- Ubah fungsi
DiceWithButtonAndImage()
untuk menerima argumenmodifier
dari jenisModifier
dan tetapkan nilai defaultModifier
.
MainActivity.kt
@Composable
fun DiceWithButtonAndImage(modifier: Modifier = Modifier) {
}
Cuplikan kode sebelumnya dapat membingungkan Anda. Jadi, mari kita perinci. Fungsi ini memungkinkan penerusan parameter modifier
. Nilai default parameter modifier
adalah objek Modifier
sehingga bagian = Modifier
dari tanda tangan metode. Nilai default parameter memungkinkan siapa saja yang memanggil metode ini di lain waktu yang akan memutuskan apakah akan meneruskan nilai untuk parameter atau tidak. Jika meneruskan objek Modifier
-nya sendiri, pengguna dapat menyesuaikan perilaku dan dekorasi UI. Jika memilih untuk tidak meneruskan objek Modifier
, objek tersebut akan dianggap sebagai nilai default, yaitu objek Modifier
biasa. Anda dapat menerapkan praktik ini ke parameter apa pun. Untuk informasi selengkapnya argumen default, lihat Argumen default.
- Setelah composable
DiceWithButtonAndImage()
memiliki parameter pengubah, teruskan pengubah saat composable dipanggil. Tanda tangan metode untuk fungsiDiceWithButtonAndImage()
mengalami perubahan sehingga objekModifier
dengan dekorasi yang diinginkan harus diteruskan saat dipanggil. ClassModifier
bertanggung jawab atas dekorasi, atau penambahan perilaku, pada composable di fungsiDiceRollerApp()
. Dalam hal ini, ada beberapa dekorasi penting yang dapat ditambahkan ke objekModifier
yang diteruskan ke fungsiDiceWithButtonAndImage()
.
Anda mungkin bertanya-tanya mengapa Anda harus bersusah payah meneruskan argumen Modifier
ketika terdapat nilai default. Alasannya karena composable dapat menjalani rekomposisi yang pada dasarnya berarti blok kode dalam metode @Composable
akan dijalankan lagi. Jika objek Modifier
dibuat di blok kode, objek tersebut dapat berpotensi dibuat ulang dan tidak efisien. Rekomposisi dibahas nanti di codelab ini.
MainActivity.kt
DiceWithButtonAndImage(modifier = Modifier)
- Buat rantai metode
fillMaxSize()
ke objekModifier
sehingga tata letak mengisi seluruh layar.
Metode ini menetapkan bahwa komponen harus mengisi ruang yang tersedia. Di bagian awal codelab ini, Anda telah melihat screenshot UI akhir aplikasi Dice Roller. Salah satu fitur pentingnya adalah dadu dan tombol berada di tengah layar. Metode wrapContentSize()
menentukan bahwa ruang yang tersedia minimal harus sebesar komponen di dalamnya. Namun, karena metode fillMaxSize()
digunakan, jika komponen di dalam tata letak lebih kecil dari ruang yang tersedia, objek Alignment
dapat diteruskan ke metode wrapContentSize()
yang menentukan cara komponen harus diratakan dalam ruang yang tersedia.
MainActivity.kt
DiceWithButtonAndImage(modifier = Modifier
.fillMaxSize()
)
- Buat rantai metode
wrapContentSize()
ke objekModifier
, lalu teruskanAlignment.Center
sebagai argumen untuk memusatkan komponen.Alignment.Center
menentukan bahwa komponen dipusatkan secara vertikal dan horizontal.
MainActivity.kt
DiceWithButtonAndImage(modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)
)
4. Membuat tata letak vertikal
Di Compose, tata letak vertikal dibuat dengan fungsi Column()
.
Fungsi Column()
adalah tata letak composable yang menempatkan turunannya dalam urutan vertikal. Di desain aplikasi yang diharapkan, Anda dapat melihat bahwa gambar dadu ditampilkan secara vertikal di atas tombol lempar:
Untuk membuat tata letak vertikal:
- Dalam fungsi
DiceWithButtonAndImage()
, tambahkan fungsiColumn()
.
- Teruskan argumen
modifier
dari tanda tangan metodeDiceWithButtonAndImage()
ke argumen pengubahColumn()
.
Argumen modifier
memastikan bahwa composable dalam fungsi Column()
mematuhi batasan yang dipanggil pada instance modifier
.
- Teruskan argumen
horizontalAlignment
ke fungsiColumn()
, lalu tetapkan ke nilaiAlignment.CenterHorizontally
.
Ini memastikan bahwa turunan dalam kolom dipusatkan pada layar perangkat sesuai dengan lebarnya.
MainActivity.kt
fun DiceWithButtonAndImage(modifier: Modifier = Modifier) {
Column (
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {}
}
5. Menambahkan tombol
- Di file
strings.xml
, tambahkan string dan tetapkan ke nilaiRoll
.
res/values/strings.xml
<string name="roll">Roll</string>
- Dalam isi lambda
Column()
, tambahkan fungsiButton()
.
- Di file
MainActivity.kt
, tambahkan fungsiText()
keButton()
dalam isi lambda fungsi. - Teruskan ID resource string dari string
roll
ke fungsistringResource()
dan teruskan hasilnya ke composableText
.
MainActivity.kt
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { /*TODO*/ }) {
Text(stringResource(R.string.roll))
}
}
6. Menambahkan gambar
Komponen penting lain dari aplikasi ini adalah gambar dadu yang menampilkan hasil saat pengguna mengetuk tombol Lempar. Anda menambahkan gambar dengan composable Image
, tetapi resource gambar juga diperlukan. Jadi, Anda harus mendownload beberapa gambar yang disediakan untuk aplikasi ini terlebih dahulu.
Mendownload gambar dadu
- Buka URL ini untuk mendownload file zip gambar dadu ke komputer, lalu tunggu download selesai.
Temukan file di komputer Anda. File tersebut mungkin ada di folder Downloads.
- Ekstrak file zip untuk membuat folder
dice_images
baru yang berisi enam file gambar dadu dengan nilai dadu dari 1 sampai 6.
Menambahkan gambar dadu ke aplikasi Anda
- Di Android Studio, klik View > Tool Windows > Resource Manager.
- Klik + > Import Drawables untuk membuka file browser.
- Temukan dan pilih enam folder gambar dadu, lalu lanjutkan untuk menguploadnya.
Gambar yang diupload akan muncul seperti berikut.
- Klik Next.
Dialog Import Drawables akan muncul dan menampilkan lokasi file resource di struktur file.
- Klik Import untuk mengonfirmasi bahwa Anda ingin mengimpor enam gambar.
Gambar akan muncul di panel Resource Manager.
Bagus! Pada tugas berikutnya, Anda menggunakan gambar ini di aplikasi Anda.
Menambahkan composable Image
Gambar dadu akan muncul di atas tombol Roll. Compose menempatkan komponen UI secara berurutan. Dengan kata lain, composable mana pun yang dideklarasikan pertama kali akan ditampilkan terlebih dahulu. Artinya, deklarasi pertama ditampilkan di atas, atau sebelum, composable yang dideklarasikan setelahnya. Composable di dalam composable Column
akan muncul di atas/di bawah composable lain di perangkat. Dalam aplikasi ini, Anda menggunakan Column
untuk menumpuk Composable secara vertikal sehingga Composable mana pun yang dideklarasikan pertama dalam fungsi Column()
akan ditampilkan sebelum composable dideklarasikan setelah itu dalam fungsi Column()
yang sama.
Untuk menambahkan composable Image
:
- Pada isi fungsi
Column()
, buat fungsiImage()
sebelum fungsiButton()
.
MainActivity.kt
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image()
Button(onClick = { /*TODO*/ }) {
Text(stringResource(R.string.roll))
}
}
- Teruskan argumen
painter
ke fungsiImage()
, lalu tetapkan nilaipainterResource
yang menerima argumen ID resource drawable. Untuk saat ini, teruskan ID resource berikut: argumenR.drawable.dice_1
.
MainActivity.kt
Image(
painter = painterResource(R.drawable.dice_1)
)
- Setiap kali membuat Gambar di aplikasi, Anda harus memberikan apa yang disebut dengan "deskripsi konten". Deskripsi konten adalah bagian penting dari pengembangan Android. Gambar menyediakan deskripsi ke komponen UI masing-masing untuk meningkatkan aksesibilitas. Untuk informasi deskripsi konten selengkapnya, lihat Menjelaskan setiap elemen UI. Anda dapat meneruskan deskripsi konten ke gambar sebagai parameter.
MainActivity.kt
Image(
painter = painterResource(R.drawable.dice_1),
contentDescription = "1"
)
Kini semua komponen UI yang diperlukan sudah ada. Namun, Button
dan Image
sedikit saling bersinggungan.
- Untuk memperbaikinya, tambahkan composable
Spacer
di antara composableImage
danButton
.Spacer
menggunakanModifier
sebagai parameter. Dalam hal ini,Image
berada di atasButton
sehingga harus ada spasi vertikal di antara keduanya. Oleh karena itu, tinggiModifier
dapat disetel agar berlaku untukSpacer
. Coba tetapkan tinggi ke16.dp
. Biasanya, dimensi dp diubah dengan kelipatan4.dp
.
MainActivity.kt
Spacer(modifier = Modifier.height(16.dp))
- Di panel Preview, klik Build & Refresh.
Anda akan melihat sesuatu yang mirip dengan gambar ini:
7. Membuat logika pelemparan dadu
Setelah semua composable yang diperlukan tersedia, Anda akan memodifikasi aplikasi agar tombol melempar dadu jika diketuk.
Membuat tombol menjadi interaktif
- Pada fungsi
DiceWithButtonAndImage()
sebelum fungsiColumn()
, buat variabelresult
dan tetapkan agar sama dengan nilai1
. - Lihat composable
Button
. Anda akan melihat parameteronClick
yang ditetapkan ke sepasang tanda kurung kurawal dengan komentar/*TODO*/
di dalam kurung kurawal. Dalam hal ini kurung kurawal mewakili lambda, area di dalam kurung kurawal yang menjadi badan lambda. Jika diteruskan sebagai argumen, fungsi juga dapat disebut sebagai " callback".
MainActivity.kt
Button(onClick = { /*TODO*/ })
Lambda adalah literal fungsi yang merupakan fungsi seperti lainnya, tetapi bukannya dideklarasikan secara terpisah dengan kata kunci fun
, lambda justru ditulis sebagai inline dan diteruskan sebagai ekspresi. Composable Button
menunggu fungsi diteruskan sebagai parameter onClick
. Ini adalah tempat yang tepat untuk menggunakan lambda, dan Anda akan menulis isi lambda di bagian ini.
- Dalam fungsi
Button()
, hapus komentar/*TODO*/
dari nilai isi lambda parameteronClick
. - Lemparan dadu bersifat acak. Untuk menunjukkan hal itu dalam kode, Anda perlu menggunakan sintaksis yang benar untuk menghasilkan angka acak. Di Kotlin, Anda dapat menggunakan metode
random()
pada rentang nomor. Dalam isi lambdaonClick
, tetapkan variabelresult
ke rentang antara 1 hingga 6, lalu panggil metoderandom()
pada rentang tersebut. Ingat bahwa dalam Kotlin, rentang ditentukan oleh dua titik antara angka pertama dalam rentang dan angka terakhir dalam rentang.
MainActivity.kt
fun DiceWithButtonAndImage(modifier: Modifier = Modifier) {
var result = 1
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.dice_1),
contentDescription = "1"
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { result = (1..6).random() }) {
Text(stringResource(R.string.roll))
}
}
}
Sekarang tombol tersebut dapat diketuk, tetapi ketukan pada tombol belum akan menyebabkan perubahan visual apa pun karena Anda masih harus membangun fungsi tersebut.
Menambahkan kondisional ke aplikasi dice roller
Di bagian sebelumnya, Anda telah membuat variabel result
dan melakukan hard code pada nilai 1
. Pada akhirnya, nilai variabel result
akan direset saat tombol Roll diketuk dan akan menentukan gambar yang ditampilkan.
Composable bersifat stateless secara default, yang berarti bahwa composable tersebut tidak memiliki nilai dan dapat disusun ulang oleh sistem kapan saja sehingga mengakibatkan nilai tersebut direset. Namun, Compose menyediakan cara yang praktis untuk menghindari hal ini. Fungsi composable dapat menyimpan objek dalam memori menggunakan composable remember
.
- Ubah variabel
result
menjadi composableremember
.
Composable remember
memerlukan fungsi yang diteruskan.
- Dalam isi composable
remember
, teruskan fungsimutableStateOf()
, lalu teruskan argumen1
ke fungsi tersebut.
Fungsi mutableStateOf()
akan menampilkan objek yang dapat diamati. Anda akan mempelajari observable lebih lanjut nanti. Namun, untuk saat ini, pada dasarnya ini berarti bahwa saat nilai variabel result
berubah, rekomposisi akan dipicu, nilai hasilnya akan tercermin, dan UI akan di-refresh.
MainActivity.kt
var result by remember { mutableStateOf(1) }
Sekarang, saat tombol diketuk, variabel result
akan diupdate dengan nilai angka acak.
Sekarang, variabel result
dapat digunakan untuk menentukan gambar yang akan ditampilkan.
- Di bawah pembuatan instance variabel
result
, buat variabelimageResource
tetap yang ditetapkan ke ekspresiwhen
yang menerima variabelresult
, lalu tetapkan setiap kemungkinan hasil ke drawable-nya.
MainActivity.kt
val imageResource = when (result) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
- Ubah ID yang diteruskan ke parameter
painterResource
composableImage
dari drawableR.drawable.dice_1
ke variabelimageResource
. - Ubah parameter
contentDescription
composableImage
untuk mencerminkan nilai variabelresult
dengan mengonversi variabelresult
menjadi string dengantoString()
dan meneruskannya sebagaicontentDescription
.
MainActivity.kt
Image(
painter = painterResource(imageResource),
contentDescription = result.toString()
)
- Jalankan aplikasi Anda.
Sekarang seharusnya aplikasi Dice Roller Anda sudah dapat berfungsi sepenuhnya.
8. Mendapatkan kode solusi
Guna mendownload kode untuk codelab yang sudah selesai, Anda dapat menggunakan perintah git ini:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-dice-roller.git
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi, lihat di GitHub.
- Buka halaman repositori GitHub yang disediakan untuk project.
- Pastikan nama cabang cocok dengan nama cabang yang ditentukan dalam codelab. Misalnya, dalam screenshot berikut, nama cabang adalah main (utama).
- Di halaman GitHub project, klik tombol Code yang akan menampilkan pop-up.
- Pada pop-up, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
- Temukan file di komputer Anda (mungkin di folder Downloads).
- Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.
Membuka project di Android Studio
- Mulai Android Studio.
- Di jendela Welcome to Android Studio, klik Open.
Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > Open.
- Di file browser, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
- Klik dua kali pada folder project tersebut.
- Tunggu Android Studio membuka project.
- Klik tombol Run untuk mem-build dan menjalankan aplikasi. Pastikan aplikasi di-build seperti yang diharapkan.
9. Kesimpulan
Anda telah membuat aplikasi Dice Roller interaktif untuk Android dengan Compose.
Ringkasan
- Menentukan fungsi composable
- Membuat tata letak dengan Komposisi.
- Membuat tombol dengan composable
Button
. - Mengimpor resource
drawable
. - Menampilkan gambar dengan composable
Image
. - Membuat UI interaktif dengan composable.
- Menggunakan composable
remember
untuk menyimpan objek dalam Komposisi ke memori. - Memuat ulang UI dengan fungsi
mutableStateOf()
agar dapat diamati.