1. Sebelum memulai
Codelab ini mengajarkan status, serta cara Jetpack Compose menggunakan dan memanipulasinya.
Pada intinya, status di aplikasi adalah nilai yang dapat berubah dari waktu ke waktu. Definisi ini sangat luas dan mencakup semuanya, mulai dari database hingga variabel dalam aplikasi Anda. Anda dapat mempelajari lebih lanjut tentang database di unit selanjutnya, tetapi untuk saat ini yang perlu Anda ketahui adalah bahwa database adalah kumpulan informasi terstruktur yang tertata, seperti file di komputer Anda.
Semua aplikasi Android menampilkan status kepada pengguna. Beberapa contoh status di aplikasi Android termasuk:
- Pesan yang muncul saat koneksi jaringan tidak dapat dibuat.
- Formulir, seperti formulir pendaftaran. Status dapat diisi dan dikirim.
- Kontrol yang dapat diketuk, seperti tombol. Status dapat tidak diketuk, diketuk (animasi tampilan), atau diketuk (tindakan
onClick
).
Dalam codelab ini, Anda akan mempelajari cara menggunakan dan mempertimbangkan status saat menggunakan Compose. Untuk melakukannya, Anda membuat aplikasi kalkulator tip yang disebut Tip Time dengan elemen UI Compose bawaan ini:
- Composable
TextField
untuk memasukkan dan mengedit teks. - Composable
Text
untuk menampilkan teks. - Composable
Spacer
untuk menampilkan ruang kosong di antara elemen UI.
Di akhir codelab ini, Anda akan membuat kalkulator tip interaktif yang menghitung jumlah tip secara otomatis saat Anda memasukkan jumlah layanan. Gambar ini menunjukkan tampilan aplikasi final:
Prasyarat
- Pemahaman dasar tentang Compose, seperti anotasi
@Composable
. - Pemahaman dasar tentang tata letak Compose, seperti composable tata letak
Row
danColumn
. - Pemahaman dasar tentang pengubah, seperti fungsi
Modifier.padding()
. - Pemahaman tentang composable
Text
.
Yang akan Anda pelajari
- Cara mempertimbangkan status di UI.
- Cara Compose menggunakan status untuk menampilkan data.
- Cara menambahkan kotak teks ke aplikasi Anda.
- Cara mengangkat status.
Yang akan Anda build
- Aplikasi kalkulator tip yang disebut Tip Time yang menghitung jumlah tip berdasarkan jumlah layanan.
Yang akan Anda butuhkan
- Komputer dengan akses internet dan browser web
- Pengetahuan tentang Kotlin
- Android Studio
2. Menonton video tutorial coding langsung (Opsional)
Jika Anda ingin menonton salah satu instruktur kursus saat tengah menyelesaikan codelab, putar video di bawah.
Sebaiknya luaskan video ke layar penuh (dengan ikon ini di pojok kanan bawah video) agar Anda dapat melihat Android Studio dan kodenya dengan lebih jelas.
Langkah ini opsional. Anda juga dapat melewati video dan langsung memulai petunjuk codelab.
3. Memulai
- Lihat kalkulator tip online Google. Harap perhatikan bahwa ini hanyalah contoh dan bukan aplikasi Android yang akan Anda buat dalam kursus ini.
- Masukkan nilai yang berbeda di kotak Bill dan Tip %. Nilai tip dan total akan berubah.
Perhatikan bahwa saat Anda memasukkan nilai, Tip dan Total akan diperbarui. Di akhir codelab berikut, Anda akan mengembangkan aplikasi kalkulator tip serupa di Android.
Dalam kursus ini, Anda akan mem-build aplikasi Android kalkulator tip sederhana.
Developer sering kali akan menggunakan cara ini—menyiapkan aplikasi versi sederhana dan berfungsi (meskipun terlihat tidak bagus), kemudian menambahkan lebih banyak fitur dan membuatnya lebih menarik secara visual nanti.
Di akhir codelab ini, aplikasi kalkulator tip Anda akan terlihat seperti screenshot ini. Saat pengguna memasukkan Biaya Layanan, aplikasi Anda akan menampilkan jumlah tip yang disarankan. Untuk saat ini, persentase tip di-hardcode menjadi 15%. Pada codelab berikutnya, Anda akan terus mengerjakan aplikasi dan menambahkan lebih banyak fitur seperti menetapkan persentase tip kustom.
4. Membuat project
Siapkan project di Android Studio dengan template Empty Compose Activity dan resource string yang diperlukan:
- Di Android Studio, buat project dengan template Empty Compose Activity, masukkan
Tip Time
sebagai namanya, lalu pilih API 21: Android 5.0 (Lollipop) atau yang lebih baru sebagai SDK minimum. File project akan dimuat. - Di panel Project, klik res > values > strings.xml. Anda harus memiliki satu resource string untuk nama aplikasi.
- Di antara tag
<resources>
, masukkan resource string berikut:
<string name="calculate_tip">Calculate Tip</string>
<string name="cost_of_service">Cost of Service</string>
<string name="tip_amount">Tip amount: %s</string>
File strings.xml
akan terlihat seperti cuplikan kode ini:
strings.xml
<resources>
<string name="app_name">Tip Time</string>
<string name="calculate_tip">Calculate Tip</string>
<string name="cost_of_service">Cost of Service</string>
<string name="tip_amount">Tip amount: %s</string>
</resources>
5. Menambahkan judul layar
Di bagian ini, Anda akan menambahkan judul layar ke aplikasi dengan fungsi composable Text
.
Hapus fungsi Greeting()
dan tambahkan fungsi TipTimeScreen()
untuk menambahkan elemen UI yang diperlukan aplikasi:
- Di file
MainActivity.kt
, hapus fungsiGreeting()
:
// Delete this.
@Composable
fun Greeting(name: String) {
//...
}
- Di fungsi
onCreate()
danDefaultPreview()
, hapus panggilan fungsiGreeting()
:
// Delete this.
Greeting("Android")
- Di bawah
MainActivity class
, tambahkan fungsi composableTipTimeScreen()
untuk menampilkan layar aplikasi:
@Composable
fun TipTimeScreen() {
}
- Dalam blok
Surface()
fungsionCreate()
, panggil fungsiTipTimeScreen()
:
override fun onCreate(savedInstanceState: Bundle?) {
//...
setContent {
TipTimeTheme {
Surface(
//...
) {
TipTimeScreen()
}
}
}
}
- Dalam blok
TipTimeTheme
fungsiDefaultPreview()
, panggil fungsiTipTimeScreen()
:
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
TipTimeTheme {
TipTimeScreen()
}
}
Menampilkan judul layar
Implementasikan fungsi TipTimeScreen()
untuk menampilkan judul layar:
- Dalam fungsi
TipTimeScreen()
, tambahkan elemenColumn
. Elemen berada dalam kolom vertikal, itulah sebabnya Anda menggunakan elemenColumn
. - Di blok
Column
, teruskan parameter bernamamodifier
yang disetel ke fungsiModifier.padding()
yang menerima argumen32.dp
:
Column(
modifier = Modifier.padding(32.dp)
) {}
- Impor fungsi dan properti ini:
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.unit.dp
- Dalam composable
Column()
, teruskan argumen bernamaverticalArrangement
yang disetel ke fungsiArrangement.spacedBy()
yang menerima argumen8.dp
:
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {}
Tindakan ini akan menambahkan jarak 8dp
tetap di antara elemen turunan.
- Impor hal berikut:
import androidx.compose.foundation.layout.Arrangement
- Dalam lambda
Column
, tambahkan elemenText
yang menggunakan parameter bernamatext
yang ditetapkan ke fungsistringResource(R.string.
calculate_tip
)
, parameter bernamafontSize
yang ditetapkan ke nilai24.sp
, dan argumen bernamamodifier
yang ditetapkan ke fungsiModifier.align(Alignment.CenterHorizontally)
:
Text(
text = stringResource(R.string.calculate_tip),
fontSize = 24.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
- Lakukan impor pada impor ini:
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import androidx.compose.ui.Alignment
- Di panel Design, klik Build & Refresh. Anda akan melihat
Calculate Tip
muncul sebagai judul layar, yang merupakan elemen teks yang Anda tambahkan.
Menambahkan composable TextField
Di bagian ini, Anda akan menambahkan elemen UI yang memungkinkan pengguna memasukkan biaya layanan di aplikasi. Anda dapat melihat tampilannya dalam gambar ini:
Fungsi composable TextField
memungkinkan pengguna memasukkan teks dalam aplikasi. Misalnya, perhatikan kotak teks di layar login aplikasi Gmail pada gambar ini:
Tambahkan composable TextField
ke aplikasi:
- Di blok
Column
setelah elemenText
, tambahkan fungsi composableSpacer()
dengan tinggi16dp
.
@Composable
fun TipTimeScreen() {
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
...
)
Spacer(Modifier.height(16.dp))
}
}
Tindakan ini akan menampilkan jarak 16dp
kosong setelah judul layar.
- Impor fungsi ini:
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
- Dalam file
MainActivity.kt
, tambahkan fungsi composableEditNumberField()
. - Dalam isi fungsi
EditNumberField()
, tambahkanTextField
yang menerima parameter bernamavalue
yang disetel ke string kosong dan parameter bernamaonValueChange
yang disetel ke ekspresi lambda kosong:
@Composable
fun EditNumberField() {
TextField(
value = "",
onValueChange = {},
)
}
- Perhatikan parameter yang Anda teruskan:
- Parameter
value
adalah kotak teks yang menampilkan nilai string yang Anda teruskan di sini. - Parameter
onValueChange
adalah callback lambda yang dipicu saat pengguna memasukkan teks di kotak teks.
- Impor fungsi ini:
import androidx.compose.material.TextField
- Pada baris setelah fungsi composable
Spacer()
, panggil fungsiEditNumberField()
:
@Composable
fun TipTimeScreen() {
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
...
)
Spacer(Modifier.height(16.dp))
EditNumberField()
}
}
Tindakan ini akan menampilkan kotak teks di layar.
- Di panel Design, klik
Build & Refresh. Anda akan melihat judul layar
Calculate Tip
dan kotak teks kosong dengan jarak16dp
di antara keduanya.
6. Menggunakan status dalam Compose
Status di aplikasi adalah nilai yang dapat berubah dari waktu ke waktu. Di aplikasi ini, status adalah biaya layanan.
Tambahkan variabel ke status toko:
- Di awal fungsi
EditNumberField()
, gunakan kata kuncival
untuk menambahkan variabelamountInput
yang ditetapkan ke nilai"0"
statis:
val amountInput = "0"
Ini adalah status aplikasi untuk biaya layanan.
- Setel parameter bernama
value
ke nilaiamountInput
:
TextField(
value = amountInput,
onValueChange = {},
)
- Build dan jalankan aplikasi. Kotak teks menampilkan nilai yang disetel ke variabel status seperti yang dapat Anda lihat di gambar ini:
- Coba masukkan nilai lain. Status hardcode tetap tidak berubah karena composable
TextField
tidak memperbarui sendiri. Composable ini diperbarui saat parametervalue
berubah, yang disetel ke propertiamountInput
.
Variabel amountInput
mewakili status kotak teks. Memiliki status hardcode tidak berguna karena tidak dapat dimodifikasi dan tidak menampilkan input pengguna. Anda harus mengubah status aplikasi saat pengguna memperbarui biaya layanan.
7. Komposisi
Composable dalam aplikasi Anda menjelaskan UI yang menampilkan kolom dengan teks, pengatur jarak, dan kotak teks. Teks menunjukkan judul Calculate Tip
, Spacer
dengan tinggi 16dp
, dan kotak teks menampilkan nilai 0
atau apa pun nilai defaultnya.
Compose adalah framework UI deklaratif, yang berarti Anda mendeklarasikan tampilan UI dalam kode Anda. Jika ingin kotak teks menampilkan nilai 100
di awal, Anda harus menetapkan nilai awal dalam kode untuk composable ke nilai 100
.
Apa yang terjadi jika Anda ingin UI berubah saat aplikasi sedang berjalan atau saat pengguna berinteraksi dengan aplikasi? Misalnya, bagaimana jika Anda ingin memperbarui variabel amountInput
dengan nilai yang dimasukkan oleh pengguna dan menampilkannya di kotak teks? Saat itulah Anda mengandalkan proses yang disebut rekomposisi untuk memperbarui Komposisi aplikasi.
Komposisi adalah deskripsi UI yang di-build oleh Compose saat mengeksekusi composable. Aplikasi Compose memanggil fungsi composable untuk mengubah data menjadi UI. Jika terjadi perubahan status, Compose akan mengeksekusi kembali fungsi composable yang terpengaruh dengan status baru yang membuat UI yang diupdate—ini disebut rekomposisi. Compose menjadwalkan rekomposisi untuk Anda.
Saat Compose menjalankan composable Anda untuk pertama kalinya, selama komposisi awal, fitur ini terus melacak composable yang Anda panggil untuk mendeskripsikan UI Anda di Komposisi. Rekomposisi adalah saat Compose mengeksekusi ulang composable yang mungkin telah berubah sebagai respons terhadap perubahan data, lalu memperbarui Komposisi untuk menampilkan setiap perubahan.
Komposisi hanya dapat dihasilkan oleh komposisi awal dan diperbarui dengan rekomposisi. Satu-satunya cara untuk mengubah Komposisi adalah melalui rekomposisi. Untuk melakukannya, Compose perlu mengetahui status yang harus dilacak sehingga dapat menjadwalkan rekomposisi saat menerima update. Dalam kasus Anda, status ini adalah variabel amountInput
, sehingga setiap kali nilainya berubah, Compose menjadwalkan rekomposisi.
Anda menggunakan jenis State
dan MutableState
di Compose agar status di aplikasi Anda dapat diamati, atau dilacak, oleh Compose. Jenis State
tidak dapat diubah, sehingga Anda hanya dapat membaca nilai di dalamnya, sedangkan jenis MutableState
dapat berubah. Anda dapat menggunakan fungsi mutableStateOf()
untuk membuat MutableState
yang dapat diamati. Fungsi ini menerima nilai awal sebagai parameter yang digabungkan dalam objek State
, yang kemudian membuat value
-nya dapat diamati.
Nilai yang ditampilkan oleh fungsi mutableStateOf()
:
- Mempertahankan status, yang merupakan biaya layanan.
- Dapat diubah, sehingga nilai dapat diubah.
- Dapat diamati, jadi Compose mengamati setiap perubahan pada nilai dan memicu rekomposisi untuk mengupdate UI.
Tambahkan status biaya layanan:
- Pada fungsi
EditNumberField()
, ubah kata kuncival
sebelum variabel statusamountInput
menjadi kata kuncivar
:
var amountInput = "0"
Ini membuatnya dapat berubah.
- Gunakan jenis
MutableState<String>
, bukan variabelString
hardcode, sehingga Compose tahu cara melacak statusamountInput
, lalu meneruskan string"0"
yang merupakan nilai default awal untuk variabel statusamountInput
:
import androidx.compose.runtime.mutableStateOf
var amountInput: MutableState<String> = mutableStateOf("0")
Inisialisasi amountInput
juga dapat ditulis seperti ini dengan inferensi jenis:
var amountInput = mutableStateOf("0")
Fungsi mutableStateOf()
menerima nilai "0"
awal sebagai argumen, yang kemudian membuat amountInput
dapat diamati. Ini menyebabkan peringatan kompilasi ini di Android Studio, tetapi Anda akan segera memperbaikinya:
Creating a state object during composition without using remember.
- Dalam fungsi composable
TextField
, gunakan propertiamountInput.value
:
TextField(
value = amountInput.value,
onValueChange = {},
)
Compose melacak setiap composable yang membaca properti value
status dan memicu rekomposisi saat value
berubah.
Callback onValueChange
dipicu saat input kotak teks berubah. Dalam ekspresi lambda, variabel it
berisi nilai baru.
- Dalam ekspresi lambda parameter bernama
onValueChange
, setel propertiamountInput.value
ke variabelit
:
@Composable
fun EditNumberField() {
var amountInput = mutableStateOf("0")
TextField(
value = amountInput.value,
onValueChange = { amountInput.value = it },
)
}
Anda memperbarui status TextField
(yaitu variabel amountInput
) saat TextField
memberi tahu Anda bahwa ada perubahan dalam teks melalui fungsi callback onValueChange
.
- Jalankan aplikasi dan masukkan teks di kotak teks. Kotak teks masih menampilkan nilai
0
seperti yang dapat Anda lihat pada gambar ini:
Saat pengguna memasukkan teks di kotak teks, callback onValueChange
akan dipanggil dan variabel amountInput
diperbarui dengan nilai baru. Status amountInput
dilacak oleh Compose, sehingga saat nilainya berubah, rekomposisi dijadwalkan dan fungsi composable EditNumberField()
dieksekusi lagi. Dalam fungsi composable tersebut, variabel amountInput
direset ke nilai 0
awal. Dengan demikian, kotak teks menampilkan nilai 0
.
Dengan kode yang Anda tambahkan, perubahan status menyebabkan rekomposisi dijadwalkan.
Namun, Anda memerlukan cara untuk mempertahankan nilai variabel amountInput
di seluruh rekomposisi sehingga tidak direset ke nilai 0
setiap kali fungsi EditNumberField()
merekomposisi. Anda akan mengatasi masalah ini di bagian berikutnya.
8. Menggunakan fungsi remember untuk menyimpan status
Metode composable bisa dipanggil berkali-kali karena rekomposisi. Composable dapat mereset statusnya selama rekomposisi jika tidak disimpan.
Fungsi composable dapat menyimpan objek di seluruh rekomposisi dengan remember
. Nilai yang dihitung oleh fungsi remember
disimpan dalam Komposisi selama komposisi awal dan nilai yang disimpan ditampilkan selama rekomposisi. Biasanya fungsi remember
dan mutableStateOf
digunakan bersama dalam fungsi composable agar status dan pembaruannya ditampilkan dengan benar di UI.
Gunakan fungsi remember
dalam fungsi EditNumberField()
:
- Dalam fungsi
EditNumberField()
, lakukan inisialisasi variabelamountInput
dengan delegasi properti Kotlinby
remember
, dengan mengapit panggilan ke fungsimutableStateOf
()
denganremember
. - Dalam fungsi
mutableStateOf
()
, teruskan string kosong, bukan string"0"
statis:
var amountInput by remember { mutableStateOf("") }
Sekarang, string kosong adalah nilai default awal untuk variabel amountInput
. by
adalah delegasi properti Kotlin. Fungsi pengambil dan penyetel default untuk properti amountInput
didelegasikan ke fungsi pengambil dan penyetel class remember
.
- Impor fungsi ini:
import androidx.compose.runtime.remember
- Impor fungsi ini secara manual:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Menambahkan impor pengambil dan penyetel memungkinkan Anda membaca dan menyetel amountInput
tanpa merujuk pada properti value
dari MutableState
.
Fungsi EditNumberField()
yang diubah akan terlihat seperti ini:
@Composable
fun EditNumberField() {
var amountInput by remember { mutableStateOf("") }
TextField(
value = amountInput,
onValueChange = { amountInput = it },
)
}
- Jalankan aplikasi dan masukkan teks di kotak teks. Anda akan melihat teks yang Anda masukkan sekarang.
9. Status dan cara kerja rekomposisi
Di bagian ini, Anda menetapkan titik henti sementara dan men-debug fungsi composable EditNumberField()
untuk melihat cara kerja komposisi awal dan rekomposisi.
Tetapkan titik henti sementara dan men-debug aplikasi di emulator atau perangkat:
- Pada fungsi
EditNumberField()
di samping parameter bernamaonValueChange
, tetapkan titik henti sementara baris. - Di menu navigasi, klik Debug 'app'. Aplikasi diluncurkan di emulator atau perangkat. Eksekusi aplikasi Anda dijeda untuk pertama kalinya saat elemen
TextField
dibuat.
- Di panel Debug, klik
Resume Program. Kotak teks telah dibuat.
- Di emulator atau perangkat, masukkan huruf di kotak teks. Eksekusi aplikasi Anda akan dijeda lagi saat mencapai titik henti sementara yang Anda tetapkan.
Saat Anda memasukkan teks, Compose akan memicu rekomposisi dan callback onValueChange
di fungsi EditNumberField()
dipanggil dengan data baru seperti yang dapat Anda lihat dalam gambar ini:
- Di panel Debug, klik
Resume Program. Teks yang dimasukkan di emulator atau perangkat ditampilkan di samping baris dengan titik henti sementara seperti yang terlihat dalam gambar ini:
Ini adalah status kolom teks.
- Klik
Resume Program. Nilai yang Anda masukkan akan ditampilkan di emulator atau perangkat.
10. Mengubah tampilan
Di bagian sebelumnya, Anda mendapatkan kolom teks untuk dikerjakan. Di bagian ini, Anda akan meningkatkan UI.
Menambahkan label ke kotak teks
Setiap kotak teks harus memiliki label yang memungkinkan pengguna mengetahui informasi yang dapat dimasukkan. Di bagian pertama gambar contoh berikut, teks label berada di tengah-tengah kolom teks dan diratakan dengan baris input. Di bagian kedua gambar contoh berikut, label dipindahkan lebih tinggi di kotak teks saat pengguna mengklik di kotak teks untuk memasukkan teks. Untuk mempelajari anatomi kolom teks lebih lanjut, lihat Anatomi.
Ubah fungsi EditNumberField()
untuk menambahkan label ke kolom teks:
- Dalam fungsi composable
TextField()
dari fungsiEditNumberField()
, tambahkan parameter bernamalabel
yang disetel ke ekspresi lambda kosong:
TextField(
//...
label = { }
)
- Dalam ekspresi lambda, panggil fungsi
Text()
yang menerimastringResource
(R.string.
cost_of_service
)
:
label = { Text(stringResource(R.string.cost_of_service)) }
- Dalam fungsi composable
TextField()
, tambahkan parameter bernamamodifier
yang disetel keModifier.
fillMaxWidth
()
:
TextField(
// Other parameters
modifier = Modifier.fillMaxWidth(),
)
- Impor hal berikut:
import androidx.compose.foundation.layout.fillMaxWidth
- Dalam fungsi composable
TextField()
, tambahkan parameter bernamasingleLine
yang disetel ke nilaitrue
:
TextField(
// Other parameters
singleLine = true,
)
Ini akan meringkas kotak teks dari beberapa baris menjadi satu baris yang dapat di-scroll secara horizontal.
- Tambahkan parameter
keyboardOptions
yang disetel keKeyboardOptions()
:
TextField(
// Other parameters
keyboardOptions = KeyboardOptions()
)
Android menyediakan opsi untuk mengonfigurasi keyboard yang ditampilkan di layar guna memasukkan angka, alamat email, URL, dan sandi, serta beberapa opsi lainnya. Untuk mempelajari jenis keyboard lainnya lebih lanjut, lihat KeyboardType.
- Setel jenis keyboard ke keyboard angka untuk memasukkan angka. Teruskan fungsi
KeyboardOptions
dengan parameter bernamakeyboardType
yang disetel keKeyboardType.Number
:
TextField(
// Other parameters
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
Fungsi EditNumberField()
yang telah selesai akan terlihat seperti cuplikan kode ini:
@Composable
fun EditNumberField() {
var amountInput by remember { mutableStateOf("") }
TextField(
value = amountInput,
onValueChange = { amountInput = it },
label = { Text(stringResource(R.string.cost_of_service)) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
- Impor hal berikut:
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.foundation.text.KeyboardOptions
- Jalankan aplikasi.
Anda dapat melihat perubahan pada gambar ini:
11. Menampilkan jumlah tip
Di bagian ini, Anda akan menerapkan fungsi utama aplikasi, yaitu kemampuan untuk menghitung dan menampilkan jumlah tip.
Di akhir tugas ini, aplikasi Anda akan terlihat seperti ini:
Menghitung jumlah tip
Tentukan dan terapkan fungsi yang menerima biaya layanan dan persentase tip, serta menampilkan jumlah tip:
- Di file
MainActivity.kt
setelah fungsiEditNumberField()
, tambahkan fungsiprivate
calculateTip()
. - Tambahkan parameter bernama
amount
dantipPercent
, keduanya dari jenisDouble
. Parameteramount
meneruskan biaya layanan. - Setel parameter
tipPercent
ke nilai argumen default15.0
. Untuk saat ini, nilai tip default ditetapkan ke 15%. Di codelab berikutnya, Anda akan mendapatkan jumlah tip dari pengguna:
private fun calculateTip(
amount: Double,
tipPercent: Double = 15.0
) {
}
- Dalam isi fungsi, gunakan kata kunci
val
untuk menentukan variabeltip
yang membagi parametertipPercent
dengan nilai100
dan mengalikan hasilnya dengan parameteramount
untuk menghitung tip:
private fun calculateTip(
amount: Double,
tipPercent: Double = 15.0
) {
val tip = tipPercent / 100 * amount
}
Sekarang aplikasi Anda dapat menghitung tip, tetapi Anda masih perlu memformat dan menampilkannya dengan class NumberFormat
.
- Pada baris berikutnya dari isi fungsi
calculateTip()
, panggil fungsiNumberFormat.getCurrencyInstance()
:
NumberFormat.getCurrencyInstance()
Tindakan ini memberi Anda pemformat angka yang dapat digunakan untuk memformat bilangan sebagai mata uang.
- Pada panggilan fungsi
NumberFormat.getCurrencyInstance()
, hubungkan metodeformat()
dan teruskan variabeltip
sebagai parameter:
NumberFormat.getCurrencyInstance().format(tip)
- Saat diminta oleh Android Studio, impor class ini.
import java.text.NumberFormat
- Langkah terakhir adalah menampilkan string berformat dari fungsi. Ubah tanda tangan fungsi untuk menampilkan jenis
String
. Tambahkan kata kuncireturn
di depan pernyataanNumberFormat
:
private fun calculateTip(
amount: Double,
tipPercent: Double = 15.0
): String {
val tip = tipPercent / 100 * amount
return NumberFormat.getCurrencyInstance().format(tip)
}
Sekarang fungsi menampilkan string berformat.
Menggunakan fungsi calculateTip()
Teks yang dimasukkan oleh pengguna dalam composable kolom teks ditampilkan ke fungsi callback onValueChange
sebagai String
meskipun pengguna memasukkan angka. Untuk memperbaikinya, Anda harus mengonversi nilai amountInput
yang berisi jumlah yang dimasukkan oleh pengguna.
- Dalam fungsi composable
TipTimeScreen()
, panggil fungsitoDoubleOrNull
pada variabelamountInput
untuk mengonversiString
menjadiDouble
:
val amount = amountInput.toDoubleOrNull()
Fungsi toDoubleOrNull()
adalah fungsi Kotlin yang telah ditentukan dan mengurai string sebagai angka Double
serta menampilkan hasil atau null
jika string bukan representasi angka yang valid.
- Di akhir pernyataan, tambahkan operator Elvis
?:
yang menampilkan nilai0.0
jikaamountInput
adalah null:
val amount = amountInput.toDoubleOrNull() ?: 0.0
- Setelah variabel
amount
, buat variabelval
lain bernamatip
. Lakukan inisialisasi dengancalculateTip()
, dengan meneruskan parameteramount
.
val tip = calculateTip(amount)
Fungsi EditNumberField()
akan terlihat seperti cuplikan kode ini:
@Composable
fun EditNumberField() {
var amountInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount)
TextField(
value = amountInput,
onValueChange = { amountInput = it },
label = { Text(stringResource(R.string.cost_of_service)) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
Menampilkan jumlah tip yang dihitung
Anda telah menulis fungsi untuk menghitung jumlah tip, langkah selanjutnya adalah menambahkan composable Text
untuk menampilkan jumlah tip yang dihitung:
- Pada fungsi
TipTimeScreen()
di akhir blokColumn()
, tambahkan composableSpacer()
dan teruskan tinggi24.dp
:
@Composable
fun TipTimeScreen() {
Column(
//...
) {
Text(
//...
)
//...
EditNumberField()
Spacer(Modifier.height(24.dp))
}
}
Tindakan ini akan menambahkan jarak setelah kolom teks.
- Setelah composable
Spacer()
, tambahkan composableText
berikut:
Text(
text = stringResource(R.string.tip_amount, ""),
modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Kode ini menggunakan resource string tip_amount
untuk menyetel teks, tetapi jumlah tip tidak ditampilkan; Anda akan segera memperbaiki masalah ini. Kode ini akan menempatkan teks di tengah layar dalam ukuran 20.sp
dengan ketebalan font disetel ke tebal.
- Lakukan impor pada impor berikut:
import androidx.compose.ui.text.font.FontWeight
Anda perlu mengakses variabel amountInput
dalam fungsi TipTimeScreen()
untuk menghitung dan menampilkan jumlah tip, tetapi variabel amountInput
adalah status kolom teks yang ditentukan dalam fungsi composable EditNumberField()
sehingga Anda belum dapat memanggilnya dari fungsi TipTimeScreen()
. Gambar ini mengilustrasikan struktur kode:
Struktur ini tidak akan memungkinkan Anda menampilkan jumlah tip di composable Text
baru karena composable Text
perlu mengakses variabel amount
yang dihitung dari variabel amountInput
. Anda perlu mengekspos variabel amount
ke fungsi TipTimeScreen()
. Gambar ini mengilustrasikan struktur kode yang diinginkan yang membuat composable EditNumberField()
menjadi stateless:
Pola ini disebut pengangkatan status. Di bagian berikutnya, Anda akan mengangkat atau meningkatkan status dari composable untuk membuatnya stateless.
12. Pengangkatan status
Di bagian ini, Anda akan mempelajari cara menentukan tempat untuk menentukan status dengan cara yang dapat digunakan kembali dan membagikan composable Anda.
Dalam fungsi composable, Anda dapat menentukan variabel yang menyimpan status untuk ditampilkan di UI. Misalnya, Anda menentukan variabel amountInput
sebagai status dalam composable EditNumberField()
.
Saat aplikasi menjadi lebih kompleks dan composable lainnya memerlukan akses ke status dalam composable EditNumberField()
, Anda perlu mempertimbangkan untuk mengangkat, atau mengekstrak, status dari fungsi composable EditNumberField()
.
Memahami composable stateful versus stateless
Anda harus mengangkat status saat Anda perlu:
- Membagikan status dengan beberapa fungsi composable.
- Membuat composable stateless yang dapat digunakan kembali di aplikasi Anda.
Saat Anda mengekstrak status dari fungsi composable, hasil fungsi composable ini akan disebut stateless. Artinya, fungsi composable dapat dibuat stateless dengan mengekstrak status darinya.
Composable stateless adalah composable yang tidak memiliki status, yang berarti composable tersebut tidak memiliki, menentukan, atau memodifikasi status baru. Di sisi lain, composable stateful adalah composable yang memiliki bagian status yang dapat berubah dari waktu ke waktu.
Pengangkatan status adalah pola pemindahan status ke fungsi yang berbeda untuk membuat komponen menjadi stateless.
Jika diterapkan pada composable, hal ini terkadang berarti memasukkan dua parameter ke composable:
- Parameter
value: T
, yang merupakan nilai saat ini untuk ditampilkan. onValueChange: (T) -> Unit
– lambda callback, yang dipicu saat nilai berubah sehingga status dapat diperbarui di tempat lain, seperti saat pengguna memasukkan teks di kotak teks.
Mengangkat status di fungsi EditNumberField()
:
- Perbarui definisi fungsi
EditNumberField()
, untuk mengangkat status dengan menambahkan parametervalue
danonValueChange
:
fun EditNumberField(
value: String,
onValueChange: (String) -> Unit
)
Parameter value
berjenis String
, dan parameter onValueChange
berjenis (String) -> Unit
, sehingga merupakan fungsi yang menggunakan nilai String
sebagai input dan tidak memiliki nilai yang ditampilkan. Parameter onValueChange
digunakan sebagai callback onValueChange
yang diteruskan ke composable TextField
.
- Dalam fungsi
EditNumberField()
, perbarui fungsi composableTextField()
untuk menggunakan parameter yang diteruskan:
TextField(
value = value,
onValueChange = onValueChange,
// Rest of the code
)
- Mengangkat status, memindahkan status yang diingat dari fungsi
EditNumberField()
ke fungsiTipTimeScreen()
:
@Composable
fun TipTimeScreen() {
var amountInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount)
Column(
//...
) {
//...
}
}
- Anda telah mengangkat status ke
TipTimeScreen()
, sekarang teruskan keEditNumberField()
. Dalam fungsiTipTimeScreen()
, perbarui panggilan fungsiEditNumberField
()
untuk menggunakan status yang diangkat:
EditNumberField(
value = amountInput,
onValueChange = { amountInput = it }
)
- Gunakan properti
tip
untuk menampilkan jumlah tip. Perbarui parametertext
composableText
untuk menggunakan variabeltip
sebagai parameter. Ini disebut pemformatan posisi.
Text(
text = stringResource(R.string.tip_amount, tip),
// Rest of the code
)
Dengan pemformatan posisi, Anda dapat menampilkan konten dinamis dalam string. Misalnya, Anda menginginkan kotak teks Jumlah tip menampilkan nilai xx.xx
dalam jumlah berapa pun yang dihitung dan diformat dalam fungsi Anda. Agar dapat melakukannya dalam file strings.xml
, Anda perlu menentukan resource string dengan argumen placeholder, seperti cuplikan kode ini:
// No need to copy.
// In the res/values/strings.xml file
<string name="tip_amount">Tip Amount: %s</string>
// In your Compose code
Text(
text = stringResource(R.string.tip_amount, tip)
)
Anda dapat memiliki beberapa argumen placeholder dengan jenis apa pun. Placeholder string
adalah %s
. Di Compose, Anda meneruskan tip berformat sebagai argumen ke fungsi stringResource()
.
Fungsi TipTimeScreen()
dan EditNumberField()
yang selesai akan terlihat seperti cuplikan kode ini:
@Composable
fun TipTimeScreen() {
var amountInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount)
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = stringResource(R.string.calculate_tip),
fontSize = 24.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(Modifier.height(16.dp))
EditNumberField(
value = amountInput,
onValueChange = { amountInput = it }
)
Spacer(Modifier.height(24.dp))
Text(
text = stringResource(R.string.tip_amount, tip),
modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
}
@Composable
fun EditNumberField(
value: String,
onValueChange: (String) -> Unit
) {
TextField(
value = value,
onValueChange = onValueChange,
label = { Text(stringResource(R.string.cost_of_service)) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
Singkatnya, Anda mengangkat status amountInput
dari EditNumberField()
ke dalam composable TipTimeScreen()
. Agar kotak teks berfungsi seperti sebelumnya, Anda harus meneruskan dua argumen ke fungsi composable EditNumberField()
: nilai amountInput
dan callback lambda yang memperbarui nilai amountInput
dari input pengguna. Perubahan ini memungkinkan Anda menghitung tip dari properti amountInput
di TipTimeScreen()
untuk menampilkannya kepada pengguna.
- Jalankan aplikasi di emulator atau perangkat, lalu masukkan nilai di kotak teks Cost of Service. Jumlah tip 15% ditampilkan seperti yang dapat Anda lihat pada gambar ini:
13. 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-tip-calculator.git $ cd basic-android-kotlin-compose-training-tip-calculator $ git checkout state
Atau, Anda dapat mendownload repositori sebagai file zip, mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi, lihat di GitHub.
14. Kesimpulan
Selamat! Anda telah menyelesaikan codelab ini dan mempelajari cara menggunakan status dalam aplikasi Compose!
Ringkasan
- Status di aplikasi adalah nilai yang dapat berubah dari waktu ke waktu.
- Komposisi adalah deskripsi UI yang di-build oleh Compose saat mengeksekusi composable. Aplikasi Compose memanggil fungsi composable untuk mengubah data menjadi UI.
- Komposisi awal adalah pembuatan UI oleh Compose saat pertama kali mengeksekusi fungsi composable.
- Rekomposisi adalah proses menjalankan kembali composable yang sama untuk memperbarui hierarki saat datanya berubah.
- Pengangkatan status adalah pola untuk menaikkan status agar komponen menjadi stateless.