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
- Versi terbaru Android Studio
2. 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 jumlah tagihan, 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.
3. Mendapatkan Kode Awal
Kode awal adalah kode yang telah ditulis sebelumnya yang dapat digunakan sebagai titik awal untuk project baru. Kode ini juga dapat membantu Anda berfokus pada konsep baru yang diajarkan dalam codelab ini.
Mulai menggunakan kode awal dengan mendownloadnya di sini:
Atau, Anda dapat membuat clone repositori GitHub untuk kode tersebut:
$ 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 starter
Anda dapat menjelajahi kode awal di repositori GitHub TipTime
.
Ringkasan aplikasi awal
Untuk memahami kode awal, selesaikan langkah-langkah berikut:
- Buka project dengan kode awal di Android Studio.
- Jalankan aplikasi di perangkat Android atau emulator.
- Anda akan melihat dua komponen teks; satu untuk label dan satunya lagi untuk menampilkan jumlah tip.
Panduan kode awal
Kode awal memiliki composable teks. Dalam pembelajaran ini, Anda akan menambahkan kolom teks untuk mengambil input pengguna. Berikut adalah panduan singkat beberapa file untuk membantu Anda memulai.
res > values > strings.xml
<resources>
<string name="app_name">Tip Time</string>
<string name="calculate_tip">Calculate Tip</string>
<string name="bill_amount">Bill Amount</string>
<string name="tip_amount">Tip Amount: %s</string>
</resources>
File ini adalah file string.xml
dalam resource dengan semua string yang akan Anda gunakan dalam aplikasi ini.
MainActivity
File ini sebagian besar berisi kode yang dihasilkan oleh template dan fungsi berikut.
- Fungsi
TipTimeLayout()
berisi elemenColumn
dengan dua composable teks yang Anda lihat di screenshot. Fungsi ini juga memiliki composablespacer
untuk menambahkan ruang untuk alasan estetika. - Fungsi
calculateTip()
yang menerima jumlah tagihan dan menghitung jumlah tip 15%. ParametertipPercent
disetel ke nilai argumen default15.0
. Untuk saat ini, nilai tip default ditetapkan ke 15%. Di codelab berikutnya, Anda akan mendapatkan jumlah tip dari pengguna.
@Composable
fun TipTimeLayout() {
Column(
modifier = Modifier
.statusBarsPadding()
.padding(horizontal = 40.dp)
.verticalScroll(rememberScrollState())
.safeDrawingPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = stringResource(R.string.calculate_tip),
modifier = Modifier
.padding(bottom = 16.dp, top = 40.dp)
.align(alignment = Alignment.Start)
)
Text(
text = stringResource(R.string.tip_amount, "$0.00"),
style = MaterialTheme.typography.displaySmall
)
Spacer(modifier = Modifier.height(150.dp))
}
}
private fun calculateTip(amount: Double, tipPercent: Double = 15.0): String {
val tip = tipPercent / 100 * amount
return NumberFormat.getCurrencyInstance().format(tip)
}
Dalam blok Surface()
onCreate()
, fungsi TipTimeLayout()
dipanggil. Fungsi ini akan menampilkan tata letak aplikasi di perangkat atau emulator.
override fun onCreate(savedInstanceState: Bundle?) {
//...
setContent {
TipTimeTheme {
Surface(
//...
) {
TipTimeLayout()
}
}
}
}
Blok TipTimeTheme
fungsi TipTimeLayoutPreview()
, fungsi TipTimeLayout()
dipanggil. Fungsi ini akan menampilkan tata letak aplikasi di panel Design dan di panel Split.
@Preview(showBackground = true)
@Composable
fun TipTimeLayoutPreview() {
TipTimeTheme {
TipTimeLayout()
}
}
Mengambil input dari pengguna
Di bagian ini, Anda akan menambahkan elemen UI yang memungkinkan pengguna memasukkan jumlah tagihan di aplikasi. Anda dapat melihat tampilannya dalam gambar ini:
Aplikasi Anda menggunakan gaya dan tema kustom.
Gaya dan tema adalah kumpulan atribut yang menentukan tampilan untuk satu elemen UI. Gaya dapat menentukan atribut seperti warna font, ukuran font, warna latar belakang, dan lainnya yang dapat diterapkan untuk seluruh aplikasi. Codelab berikutnya akan membahas cara menerapkannya di aplikasi Anda. Untuk saat ini, hal ini telah dilakukan agar Anda dapat membuat aplikasi menjadi lebih menarik.
Untuk mendapatkan pemahaman yang lebih baik, berikut adalah perbandingan per aspek dari versi solusi aplikasi dengan dan tanpa tema kustom.
Tanpa tema kustom. | Dengan tema kustom. |
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:
- Dalam file
MainActivity.kt
, tambahkan fungsi composableEditNumberField()
, yang menggunakan parameterModifier
. - Dalam isi fungsi
EditNumberField()
di bawahTipTimeLayout()
, tambahkanTextField
yang menerima parameter bernamavalue
yang disetel ke string kosong dan parameter bernamaonValueChange
yang disetel ke ekspresi lambda kosong:
@Composable
fun EditNumberField(modifier: Modifier = Modifier) {
TextField(
value = "",
onValueChange = {},
modifier = modifier
)
}
- 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.material3.TextField
- Dalam composable
TipTimeLayout()
, di baris setelah fungsi composable teks pertama, panggil fungsiEditNumberField()
, dengan meneruskan pengubah berikut.
import androidx.compose.foundation.layout.fillMaxWidth
@Composable
fun TipTimeLayout() {
Column(
modifier = Modifier
.statusBarsPadding()
.padding(horizontal = 40.dp)
.verticalScroll(rememberScrollState())
.safeDrawingPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
...
)
EditNumberField(modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth())
Text(
...
)
...
}
}
Tindakan ini akan menampilkan kotak teks di layar.
- Di panel Design, Anda akan melihat teks
Calculate Tip
, kotak teks kosong, dan composable teksTip Amount
.
4. Menggunakan status dalam Compose
Status di aplikasi adalah nilai yang dapat berubah dari waktu ke waktu. Di aplikasi ini, status adalah jumlah tagihan.
Tambahkan variabel ke status toko:
- Di awal fungsi
EditNumberField()
, gunakan kata kuncival
untuk menambahkan variabelamountInput
, tetapkan ke nilai"0"
:
val amountInput = "0"
Ini adalah status aplikasi untuk jumlah tagihan.
- Setel parameter bernama
value
ke nilaiamountInput
:
TextField(
value = amountInput,
onValueChange = {},
)
- Periksa pratinjau. Kotak teks menampilkan nilai yang disetel ke variabel status seperti yang dapat Anda lihat di gambar ini:
- Jalankan aplikasi di emulator, 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 jumlah tagihan.
5. Komposisi
Composable dalam aplikasi Anda menjelaskan UI yang menampilkan kolom dengan teks, pengatur jarak, dan kotak teks. Teks menunjukkan teks Calculate Tip
, 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—hal 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 jumlah tagihan.
- 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:
- Dalam fungsi
EditNumberField()
, ubah kata kuncival
sebelum variabel statusamountInput
menjadi kata kuncivar
:
var amountInput = "0"
Hal ini akan membuat amountInput
dapat diubah.
- 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.MutableState
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 = {},
modifier = modifier
)
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(modifier: Modifier = Modifier) {
var amountInput = mutableStateOf("0")
TextField(
value = amountInput.value,
onValueChange = { amountInput.value = it },
modifier = modifier
)
}
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.
6. 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
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(modifier: Modifier = Modifier) {
var amountInput by remember { mutableStateOf("") }
TextField(
value = amountInput,
onValueChange = { amountInput = it },
modifier = modifier
)
}
- Jalankan aplikasi dan masukkan teks di kotak teks. Anda akan melihat teks yang Anda ketik sekarang.
7. 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, callback onValueChange
akan dipanggil. Di dalam lambda, it
memiliki nilai baru yang Anda ketik di keypad.
Setelah nilai "it" ditetapkan ke amountInput
, Compose akan memicu rekomposisi dengan data baru saat nilai yang dapat diamati telah berubah.
- 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 dimasukkan akan ditampilkan di emulator atau perangkat.
8. 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.
bill_amount
)
:
label = { Text(stringResource(R.string.bill_amount)) },
- Dalam fungsi composable
TextField()
, tambahkan parameter bernamasingleLine
yang disetel ke nilaitrue
:
TextField(
// ...
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()
:
import androidx.compose.foundation.text.KeyboardOptions
TextField(
// ...
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
:
import androidx.compose.ui.text.input.KeyboardType
TextField(
// ...
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
)
Fungsi EditNumberField()
yang telah selesai akan terlihat seperti cuplikan kode ini:
@Composable
fun EditNumberField(modifier: Modifier = Modifier) {
var amountInput by remember { mutableStateOf("") }
TextField(
value = amountInput,
onValueChange = { amountInput = it },
singleLine = true,
label = { Text(stringResource(R.string.bill_amount)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = modifier
)
}
- Jalankan aplikasi.
Anda dapat melihat perubahan pada keypad di screenshot ini:
9. Menampilkan jumlah tip
Di bagian ini, Anda akan menerapkan fungsi utama aplikasi, yaitu kemampuan untuk menghitung dan menampilkan jumlah tip.
Dalam file MainActivity.kt
, fungsi private
calculateTip()
diberikan kepada Anda sebagai bagian dari kode awal. Anda akan menggunakan fungsi ini untuk menghitung jumlah tip:
private fun calculateTip(amount: Double, tipPercent: Double = 15.0): String {
val tip = tipPercent / 100 * amount
return NumberFormat.getCurrencyInstance().format(tip)
}
Dalam metode di atas, Anda menggunakan NumberFormat
untuk menampilkan format tip sebagai mata uang.
Sekarang aplikasi Anda dapat menghitung tip, tetapi Anda masih perlu memformat dan menampilkannya dengan class.
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
EditNumberField()
, buat variabel baru bernamaamount
setelah definisiamountInput
. 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(modifier: Modifier = Modifier) {
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.bill_amount)) },
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 menampilkan jumlah tip yang dihitung:
- Dalam fungsi
TipTimeLayout()
di akhir blokColumn()
, perhatikan composable teks yang menampilkan$0.00
. Anda akan memperbarui nilai ini ke jumlah tip yang dihitung.
@Composable
fun TipTimeLayout() {
Column(
modifier = Modifier
.statusBarsPadding()
.padding(horizontal = 40.dp)
.verticalScroll(rememberScrollState())
.safeDrawingPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// ...
Text(
text = stringResource(R.string.tip_amount, "$0.00"),
style = MaterialTheme.typography.displaySmall
)
// ...
}
}
Anda perlu mengakses variabel amountInput
dalam fungsi TipTimeLayout()
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 TipTimeLayout()
. 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 TipTimeLayout()
. 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.
10. 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 untuk memindahkan status ke pemanggil agar 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
:
@Composable
fun EditNumberField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
//...
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 fungsiTipTimeLayout()
:
@Composable
fun TipTimeLayout() {
var amountInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount)
Column(
//...
) {
//...
}
}
- Anda telah mengangkat status ke
TipTimeLayout()
, sekarang teruskan keEditNumberField()
. Dalam fungsiTipTimeLayout()
, perbarui panggilan fungsiEditNumberField
()
untuk menggunakan status yang diangkat:
EditNumberField(
value = amountInput,
onValueChange = { amountInput = it },
modifier = Modifier
.padding(bottom = 32.dp)
.fillMaxWidth()
)
Hal ini membuat EditNumberField
menjadi stateless. Anda mengangkat status UI ke ancestornya, TipTimeLayout()
. TipTimeLayout()
adalah pemilik status (amountInput
) sekarang.
Pemformatan posisi
Pemformatan posisi digunakan untuk 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>
Dalam kode compose, Anda dapat memiliki beberapa argumen placeholder dengan jenis apa pun. Placeholder string
adalah %s
.
Perhatikan composable teks di TipTimeLayout()
, Anda meneruskan tip berformat sebagai argumen ke fungsi stringResource()
.
// No need to copy
Text(
text = stringResource(R.string.tip_amount, "$0.00"),
style = MaterialTheme.typography.displaySmall
)
- Dalam fungsi,
TipTimeLayout()
, gunakan propertitip
untuk menampilkan jumlah tip. Perbarui parametertext
composableText
untuk menggunakan variabeltip
sebagai parameter.
Text(
text = stringResource(R.string.tip_amount, tip),
// ...
Fungsi TipTimeLayout()
dan EditNumberField()
yang selesai akan terlihat seperti cuplikan kode ini:
@Composable
fun TipTimeLayout() {
var amountInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount)
Column(
modifier = Modifier
.statusBarsPadding()
.padding(horizontal = 40.dp)
.verticalScroll(rememberScrollState())
.safeDrawingPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = stringResource(R.string.calculate_tip),
modifier = Modifier
.padding(bottom = 16.dp, top = 40.dp)
.align(alignment = Alignment.Start)
)
EditNumberField(
value = amountInput,
onValueChange = { amountInput = it },
modifier = Modifier
.padding(bottom = 32.dp)
.fillMaxWidth()
)
Text(
text = stringResource(R.string.tip_amount, tip),
style = MaterialTheme.typography.displaySmall
)
Spacer(modifier = Modifier.height(150.dp))
}
}
@Composable
fun EditNumberField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
value = value,
onValueChange = onValueChange,
singleLine = true,
label = { Text(stringResource(R.string.bill_amount)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = modifier
)
}
Singkatnya, Anda mengangkat status amountInput
dari EditNumberField()
ke dalam composable TipTimeLayout()
. 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 TipTimeLayout()
untuk menampilkannya kepada pengguna.
- Jalankan aplikasi di emulator atau perangkat, lalu masukkan nilai di kotak teks jumlah tagihan. Jumlah tip 15 persen dari jumlah tagihan ditampilkan seperti yang dapat Anda lihat pada gambar ini:
11. Mendapatkan kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah git berikut:
$ 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.
12. 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 memindahkan status ke pemanggil agar komponen menjadi stateless.