Pengantar status di Compose

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:

Kalkulator tip dengan kolom teks Cost of service: 100

Prasyarat

  • Pemahaman dasar tentang Compose, seperti anotasi @Composable.
  • Pemahaman dasar tentang tata letak Compose, seperti composable tata letak Row dan Column.
  • 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 Simbol ini menampilkan 4 sudut pada kotak yang disorot untuk menunjukkan mode layar penuh. 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

  1. Lihat kalkulator tip online Google. Harap perhatikan bahwa ini hanyalah contoh dan bukan aplikasi Android yang akan Anda buat dalam kursus ini.

46bf4366edc1055f.png 18da3c120daa0759.png

  1. Masukkan nilai yang berbeda di kotak Bill dan Tip %. Nilai tip dan total akan berubah.

c0980ba3e9ebba02.png

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.

Kalkulator tip dengan kolom teks Cost of service

Kalkulator tip dengan kolom teks Cost of service: 100

4. Membuat project

Siapkan project di Android Studio dengan template Empty Compose Activity dan resource string yang diperlukan:

  1. 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.
  2. Di panel Project, klik res > values > strings.xml. Anda harus memiliki satu resource string untuk nama aplikasi.
  3. 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:

  1. Di file MainActivity.kt, hapus fungsi Greeting():
// Delete this.
@Composable
fun Greeting(name: String) {
   //...
}
  1. Di fungsi onCreate() dan DefaultPreview(), hapus panggilan fungsi Greeting():
// Delete this.
Greeting("Android")
  1. Di bawah MainActivity class, tambahkan fungsi composable TipTimeScreen() untuk menampilkan layar aplikasi:
@Composable
fun TipTimeScreen() {
}
  1. Dalam blok Surface() fungsi onCreate(), panggil fungsi TipTimeScreen():
override fun onCreate(savedInstanceState: Bundle?) {
   //...
   setContent {
       TipTimeTheme {
           Surface(
           //...
           ) {
               TipTimeScreen()
           }
       }
   }
}
  1. Dalam blok TipTimeTheme fungsi DefaultPreview(), panggil fungsi TipTimeScreen():
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
   TipTimeTheme {
       TipTimeScreen()
   }
}

Menampilkan judul layar

Implementasikan fungsi TipTimeScreen() untuk menampilkan judul layar:

  1. Dalam fungsi TipTimeScreen(), tambahkan elemen Column. Elemen berada dalam kolom vertikal, itulah sebabnya Anda menggunakan elemen Column.
  2. Di blok Column, teruskan parameter bernama modifier yang disetel ke fungsi Modifier.padding() yang menerima argumen 32.dp:
Column(
   modifier = Modifier.padding(32.dp)
) {}
  1. Impor fungsi dan properti ini:
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.unit.dp
  1. Dalam composable Column(), teruskan argumen bernama verticalArrangement yang disetel ke fungsi Arrangement.spacedBy() yang menerima argumen 8.dp:
Column(
   modifier = Modifier.padding(32.dp),
   verticalArrangement = Arrangement.spacedBy(8.dp)
) {}

Tindakan ini akan menambahkan jarak 8dp tetap di antara elemen turunan.

  1. Impor hal berikut:
import androidx.compose.foundation.layout.Arrangement
  1. Dalam lambda Column, tambahkan elemen Text yang menggunakan parameter bernama text yang ditetapkan ke fungsi stringResource(R.string.calculate_tip), parameter bernama fontSize yang ditetapkan ke nilai 24.sp, dan argumen bernama modifier yang ditetapkan ke fungsi Modifier.align(Alignment.CenterHorizontally):
Text(
   text = stringResource(R.string.calculate_tip),
   fontSize = 24.sp,
   modifier = Modifier.align(Alignment.CenterHorizontally)
)
  1. Lakukan impor pada impor ini:
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import androidx.compose.ui.Alignment
  1. Di panel Design, klik Build & Refresh. Anda akan melihat Calculate Tip muncul sebagai judul layar, yang merupakan elemen teks yang Anda tambahkan.

da56236494529e77.png

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:

58671affa01fb9e1.png

Fungsi composable TextField memungkinkan pengguna memasukkan teks dalam aplikasi. Misalnya, perhatikan kotak teks di layar login aplikasi Gmail pada gambar ini:

30d9c9123b5d26fe.png

Tambahkan composable TextField ke aplikasi:

  1. Di blok Column setelah elemen Text, tambahkan fungsi composable Spacer() dengan tinggi 16dp.
@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.

  1. Impor fungsi ini:
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
  1. Dalam file MainActivity.kt, tambahkan fungsi composable EditNumberField().
  2. Dalam isi fungsi EditNumberField(), tambahkan TextField yang menerima parameter bernama value yang disetel ke string kosong dan parameter bernama onValueChange yang disetel ke ekspresi lambda kosong:
@Composable
fun EditNumberField() {
   TextField(
      value = "",
      onValueChange = {},
   )
}
  1. 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.
  1. Impor fungsi ini:
import androidx.compose.material.TextField
  1. Pada baris setelah fungsi composable Spacer(), panggil fungsi EditNumberField():
@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.

  1. Di panel Design, klik be24da86724b252c.png Build & Refresh. Anda akan melihat judul layar Calculate Tip dan kotak teks kosong dengan jarak 16dp di antara keduanya.

1ff60ec32d3b15c1.png

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:

  1. Di awal fungsi EditNumberField(), gunakan kata kunci val untuk menambahkan variabel amountInput yang ditetapkan ke nilai "0" statis:
val amountInput = "0"

Ini adalah status aplikasi untuk biaya layanan.

  1. Setel parameter bernama value ke nilai amountInput:
TextField(
   value = amountInput,
   onValueChange = {},
)
  1. Build dan jalankan aplikasi. Kotak teks menampilkan nilai yang disetel ke variabel status seperti yang dapat Anda lihat di gambar ini:

ba0f07ef1162855b.png

  1. Coba masukkan nilai lain. Status hardcode tetap tidak berubah karena composable TextField tidak memperbarui sendiri. Composable ini diperbarui saat parameter value berubah, yang disetel ke properti amountInput.

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:

  1. Pada fungsi EditNumberField(), ubah kata kunci val sebelum variabel status amountInput menjadi kata kunci var:
var amountInput = "0"

Ini membuatnya dapat berubah.

  1. Gunakan jenis MutableState<String>, bukan variabel String hardcode, sehingga Compose tahu cara melacak status amountInput, lalu meneruskan string "0" yang merupakan nilai default awal untuk variabel status amountInput:
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.
  1. Dalam fungsi composable TextField, gunakan properti amountInput.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.

  1. Dalam ekspresi lambda parameter bernama onValueChange, setel properti amountInput.value ke variabel it:
@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.

  1. Jalankan aplikasi dan masukkan teks di kotak teks. Kotak teks masih menampilkan nilai 0 seperti yang dapat Anda lihat pada gambar ini:

6cb691703cc7ecbf.gif

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():

  1. Dalam fungsi EditNumberField(), lakukan inisialisasi variabel amountInput dengan delegasi properti Kotlin by remember, dengan mengapit panggilan ke fungsi mutableStateOf() dengan remember.
  2. 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.

  1. Impor fungsi ini:
import androidx.compose.runtime.remember
  1. 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 },
   )
}
  1. Jalankan aplikasi dan masukkan teks di kotak teks. Anda akan melihat teks yang Anda masukkan sekarang.

270943a84f18572d.png

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:

  1. Pada fungsi EditNumberField() di samping parameter bernama onValueChange, tetapkan titik henti sementara baris.
  2. Di menu navigasi, klik Debug 'app'. Aplikasi diluncurkan di emulator atau perangkat. Eksekusi aplikasi Anda dijeda untuk pertama kalinya saat elemen TextField dibuat.

e225f2d67e9f2c40.png

  1. Di panel Debug, klik 2a29a3bad712bec.png Resume Program. Kotak teks telah dibuat.
  2. 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:

1d5e08d32052d02e.png

  1. Di panel Debug, klik 2a29a3bad712bec.png Resume Program. Teks yang dimasukkan di emulator atau perangkat ditampilkan di samping baris dengan titik henti sementara seperti yang terlihat dalam gambar ini:

1f5db6ab5ca5b477.png

Ini adalah status kolom teks.

  1. Klik 2a29a3bad712bec.png 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.

a2afd6c7fc547b06.png

Ubah fungsi EditNumberField() untuk menambahkan label ke kolom teks:

  1. Dalam fungsi composable TextField() dari fungsi EditNumberField(), tambahkan parameter bernama label yang disetel ke ekspresi lambda kosong:
TextField(
//...
   label = { }
)
  1. Dalam ekspresi lambda, panggil fungsi Text() yang menerima stringResource(R.string.cost_of_service):
label = { Text(stringResource(R.string.cost_of_service)) }
  1. Dalam fungsi composable TextField(), tambahkan parameter bernama modifier yang disetel ke Modifier.fillMaxWidth():
TextField(
  // Other parameters
   modifier = Modifier.fillMaxWidth(),
)
  1. Impor hal berikut:
import androidx.compose.foundation.layout.fillMaxWidth
  1. Dalam fungsi composable TextField(), tambahkan parameter bernama singleLine yang disetel ke nilai true:
TextField(
  // Other parameters
   singleLine = true,
)

Ini akan meringkas kotak teks dari beberapa baris menjadi satu baris yang dapat di-scroll secara horizontal.

  1. Tambahkan parameter keyboardOptions yang disetel ke KeyboardOptions():
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.

  1. Setel jenis keyboard ke keyboard angka untuk memasukkan angka. Teruskan fungsi KeyboardOptions dengan parameter bernama keyboardType yang disetel ke KeyboardType.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)

   )
}
  1. Impor hal berikut:
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.foundation.text.KeyboardOptions
  1. Jalankan aplikasi.

Anda dapat melihat perubahan pada gambar ini:

48368bf5df67af37.png

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:

aaf86be8d13431f5.png

Menghitung jumlah tip

Tentukan dan terapkan fungsi yang menerima biaya layanan dan persentase tip, serta menampilkan jumlah tip:

  1. Di file MainActivity.kt setelah fungsi EditNumberField(), tambahkan fungsi private calculateTip().
  2. Tambahkan parameter bernama amount dan tipPercent, keduanya dari jenis Double. Parameter amount meneruskan biaya layanan.
  3. Setel parameter tipPercent ke nilai argumen default 15.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
) {
}
  1. Dalam isi fungsi, gunakan kata kunci val untuk menentukan variabel tip yang membagi parameter tipPercent dengan nilai 100 dan mengalikan hasilnya dengan parameter amount 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.

  1. Pada baris berikutnya dari isi fungsi calculateTip(), panggil fungsi NumberFormat.getCurrencyInstance():
NumberFormat.getCurrencyInstance()

Tindakan ini memberi Anda pemformat angka yang dapat digunakan untuk memformat bilangan sebagai mata uang.

  1. Pada panggilan fungsi NumberFormat.getCurrencyInstance(), hubungkan metode format() dan teruskan variabel tip sebagai parameter:
NumberFormat.getCurrencyInstance().format(tip)
  1. Saat diminta oleh Android Studio, impor class ini.
import java.text.NumberFormat
  1. Langkah terakhir adalah menampilkan string berformat dari fungsi. Ubah tanda tangan fungsi untuk menampilkan jenis String. Tambahkan kata kunci return di depan pernyataan NumberFormat:
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.

  1. Dalam fungsi composable TipTimeScreen(), panggil fungsi toDoubleOrNull pada variabel amountInput untuk mengonversi String menjadi Double:
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.

  1. Di akhir pernyataan, tambahkan operator Elvis ?: yang menampilkan nilai 0.0 jika amountInput adalah null:
val amount = amountInput.toDoubleOrNull() ?: 0.0
  1. Setelah variabel amount, buat variabel val lain bernama tip. Lakukan inisialisasi dengan calculateTip(), dengan meneruskan parameter amount.
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:

97734d91a3844d22.png

  1. Pada fungsi TipTimeScreen() di akhir blok Column(), tambahkan composable Spacer() dan teruskan tinggi 24.dp:
@Composable
fun TipTimeScreen() {
   Column(
       //...
   ) {
       Text(
           //...
       )
       //...
       EditNumberField()
       Spacer(Modifier.height(24.dp))
   }
}

Tindakan ini akan menambahkan jarak setelah kolom teks.

  1. Setelah composable Spacer(), tambahkan composable Text 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.

  1. 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:

5ec86acdbfa1907b.png

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:

e11d5bba4d8abd0d.png

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():

  1. Perbarui definisi fungsi EditNumberField(), untuk mengangkat status dengan menambahkan parameter value dan onValueChange:
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.

  1. Dalam fungsi EditNumberField(), perbarui fungsi composable TextField() untuk menggunakan parameter yang diteruskan:
TextField(
   value = value,
   onValueChange = onValueChange,
   // Rest of the code
)
  1. Mengangkat status, memindahkan status yang diingat dari fungsi EditNumberField() ke fungsi TipTimeScreen():
@Composable
fun TipTimeScreen() {
   var amountInput by remember { mutableStateOf("") }

   val amount = amountInput.toDoubleOrNull() ?: 0.0
   val tip = calculateTip(amount)

   Column(
       //...
   ) {
       //...
   }
}
  1. Anda telah mengangkat status ke TipTimeScreen(), sekarang teruskan ke EditNumberField(). Dalam fungsi TipTimeScreen(), perbarui panggilan fungsi EditNumberField() untuk menggunakan status yang diangkat:
EditNumberField(
    value = amountInput,
    onValueChange = { amountInput = it }
)
  1. Gunakan properti tip untuk menampilkan jumlah tip. Perbarui parameter text composable Text untuk menggunakan variabel tip 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.

  1. 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.

Pelajari lebih lanjut