Menghitung tip

1. Sebelum memulai

Dalam codelab ini, Anda akan menulis kode untuk kalkulator tip untuk digunakan pada UI yang Anda buat di codelab sebelumnya, Membuat tata letak XML untuk Android.

Prasyarat

Yang akan Anda pelajari

  • Struktur dasar aplikasi Android.
  • Cara memasukkan nilai dari UI ke dalam kode dan memanipulasinya.
  • Cara menggunakan view binding sebagai ganti findViewById() agar lebih mudah menulis kode yang berinteraksi dengan tampilan.
  • Cara menggunakan angka desimal di Kotlin dengan jenis data Double.
  • Cara memformat angka sebagai mata uang.
  • Cara menggunakan parameter string untuk membuat string secara dinamis.
  • Cara menggunakan Logcat di Android Studio untuk menemukan masalah di aplikasi Anda.

Yang akan Anda bangun

  • Aplikasi kalkulator tip dengan tombol Calculate yang berfungsi.

Yang Anda perlukan

  • Komputer yang diinstal Android Studio versi stabil terbaru.
  • Kode awal untuk aplikasi Tip Time yang berisi tata letak untuk kalkulator tip.

2. Ringkasan aplikasi awal

Aplikasi Tip Time dari codelab terakhir memiliki semua UI yang diperlukan untuk kalkulator tip, tetapi tidak memiliki kode untuk menghitung tip. Terdapat tombol Calculate, tetapi belum berfungsi. EditText Cost of Service memungkinkan pengguna memasukkan biaya layanan. Daftar RadioButtons memungkinkan pengguna memilih persentase tip, dan Switch memungkinkan pengguna memilih apakah tip harus dibulatkan atau tidak. Jumlah tip ditampilkan di TextView, dan terakhir Button Calculate memerintahkan aplikasi untuk mengambil data dari kolom lain dan menghitung jumlah tip. Di sinilah codelab ini dimulai.

ebf5c40d4e12d4c7.png

Struktur project aplikasi

Project aplikasi di IDE terdiri dari sejumlah bagian, termasuk kode Kotlin, tata letak XML, dan resource lain seperti string dan gambar. Sebelum membuat perubahan pada aplikasi, sebaiknya Anda mempelajarinya sendiri.

  1. Buka project Tip Time di Android Studio.
  2. Jika jendela Project tidak muncul, pilih tab Project di sebelah kiri Android Studio.
  3. Jika belum dipilih, pilih tampilan Android dari drop-down.

2a83e2b0aee106dd.png

  • Folder java untuk file Kotlin (atau file Java)
  • MainActivity - class tempat semua kode Kotlin untuk logika kalkulator tip akan dimasukkan
  • Folder res untuk resource aplikasi
  • activity_main.xml - file tata letak untuk aplikasi Android Anda
  • strings.xml - berisi resource string untuk aplikasi Android Anda
  • Folder Gradle Scripts

Gradle adalah sistem build otomatis yang digunakan oleh Android Studio. Setiap kali Anda mengubah kode, menambahkan resource, atau membuat perubahan lain ke aplikasi Anda, Gradle mencari tahu apa yang telah berubah dan mengambil langkah yang diperlukan untuk mem-build ulang aplikasi Anda. Gradle juga menginstal aplikasi Anda di emulator atau perangkat fisik dan mengontrol eksekusinya.

Ada beberapa folder dan file lain yang terlibat dalam pembuatan aplikasi Anda, namun ini adalah folder dan file utama yang akan Anda gunakan untuk codelab ini dan setelahnya.

3. View binding

Untuk menghitung tip, kode Anda harus mengakses semua elemen UI untuk membaca input dari pengguna. Anda mungkin ingat dari codelab sebelumnya bahwa kode Anda harus menemukan referensi terhadap View seperti Button atau TextView sebelum kode Anda dapat memanggil metode di View atau mengakses atributnya. Framework Android menyediakan satu metode, findViewById(), yang berfungsi persis seperti yang Anda inginkan, yaitu menyediakan referensi menurut ID View yang diberikan. Pendekatan ini berfungsi baik, namun semakin banyak tampilan yang Anda tambahkan ke aplikasi dan semakin kompleks UI-nya, penggunaan findViewById() dapat menjadi rumit.

Untuk memudahkan, Android juga menyediakan fitur yang disebut view binding. Dengan sedikit bersusah payah di awal, view binding memudahkan dan mempercepat panggilan metode pada tampilan di UI Anda. Anda harus mengaktifkan view binding untuk aplikasi Anda di Gradle, dan membuat beberapa perubahan kode.

Mengaktifkan view binding

  1. Buka file build.gradle aplikasi ( Gradle Scripts > build.gradle (Module: Tip_Time.app) )
  2. Di bagian android, tambahkan baris berikut:
buildFeatures {
    viewBinding = true
}
  1. Perhatikan pesan Gradle files have changed since last project sync.
  2. Tekan Sync Now.

349d99c67c2f40f1.png

Setelah beberapa saat, Anda seharusnya melihat pesan di bagian bawah jendela Android Studio, Gradle sync finished. Anda dapat menutup file build.gradle jika mau.

Melakukan inisialisasi objek binding

Di codelab sebelumnya, Anda melihat metode onCreate() di class MainActivity. Ini adalah salah satu hal pertama yang dipanggil saat aplikasi Anda dimulai dan MainActivity diinisialisasi. Sebagai ganti memanggil findViewById() untuk setiap View di aplikasi, Anda akan membuat dan melakukan inisialisasi objek binding sekali.

674d243aa6f85b8b.png

  1. Buka MainActivity.kt (app > java > com.example.tiptime > MainActivity).
  2. Ganti semua kode yang ada untuk class MainActivity dengan kode ini guna menyiapkan MainActivity untuk menggunakan view binding:
class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}
  1. Baris ini mendeklarasikan variabel level teratas di class untuk objek binding. Hal ini ditentukan pada tingkat ini karena akan digunakan di beberapa metode di class MainActivity.
lateinit var binding: ActivityMainBinding

Kata kunci lateinit adalah hal yang baru. Ini adalah jaminan bahwa kode Anda akan melakukan inisialisasi variabel sebelum menggunakannya. Jika tidak, aplikasi Anda akan error.

  1. Baris ini melakukan inisialisasi objek binding yang akan Anda gunakan untuk mengakses Views di tata letak activity_main.xml.
binding = ActivityMainBinding.inflate(layoutInflater)
  1. Tetapkan tampilan konten aktivitas ini. Bukannya meneruskan ID resource tata letak, R.layout.activity_main, ini menentukan root hierarki tampilan aplikasi Anda, binding.root.
setContentView(binding.root)

Anda mungkin ingat gagasan tentang tampilan induk dan tampilan turunan. Root ini terhubung ke semua tampilan.

Sekarang saat Anda memerlukan referensi terhadap View di aplikasi, Anda bisa mendapatkannya dari objek binding, bukan memanggil findViewById(). Objek binding secara otomatis menentukan referensi untuk setiap View dalam aplikasi Anda yang memiliki ID. Menggunakan view binding jauh lebih ringkas sehingga sering kali Anda bahkan tidak perlu membuat variabel guna menyimpan referensi untuk View, cukup gunakan langsung dari objek binding.

// Old way with findViewById()
val myButton: Button = findViewById(R.id.my_button)
myButton.text = "A button"

// Better way with view binding
val myButton: Button = binding.myButton
myButton.text = "A button"

// Best way with view binding and no extra variable
binding.myButton.text = "A button"

Keren, bukan?

4. Menghitung tip

Menghitung tip dimulai saat pengguna mengetuk tombol Calculate. Tindakan ini melibatkan pemeriksaan UI untuk melihat berapa biaya layanannya dan persentase tip yang diinginkan pengguna. Dengan menggunakan informasi ini, Anda menghitung jumlah total biaya layanan dan menampilkan jumlah tipnya.

Menambahkan pemroses klik ke tombol

Langkah pertama adalah menambahkan pemroses klik untuk menentukan tindakan yang harus dilakukan tombol Calculate saat pengguna mengetuknya.

  1. Di MainActivity.kt dalam onCreate(), setelah panggilan ke setContentView(), setel pemroses klik pada tombol Calculate agar memanggil calculateTip().
binding.calculateButton.setOnClickListener{ calculateTip() }
  1. Masih di dalam class MainActivity, tetapi di luar onCreate(), tambahkan metode helper yang bernama calculateTip().
fun calculateTip() {

}

Di sinilah Anda akan menambahkan kode untuk memeriksa UI dan menghitung tip.

MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.calculateButton.setOnClickListener{ calculateTip() }
    }

    fun calculateTip() {

    }
}

Mendapatkan biaya layanan

Untuk menghitung tip, hal pertama yang Anda perlukan adalah biaya layanan. Teks disimpan di EditText, tetapi Anda memerlukannya sebagai angka agar Anda dapat menggunakannya dalam perhitungan. Anda mungkin ingat jenis Int dari codelab lain, tetapi Int hanya dapat menyimpan bilangan bulat. Untuk menggunakan angka desimal di aplikasi Anda, gunakan jenis data bernama Double, bukan Int. Anda dapat membaca selengkapnya tentang jenis data numerik di Kotlin di dokumentasi. Kotlin menyediakan metode untuk mengonversi String menjadi Double, bernama toDouble().

  1. Pertama, dapatkan teks untuk biaya layanan. Di metode calculateTip(), dapatkan atribut teks Cost of Service EditText, dan tetapkan ke variabel bernama stringInTextField. Perlu diingat bahwa Anda dapat mengakses elemen UI menggunakan objek binding, dan bahwa Anda dapat merujuk elemen UI berdasarkan nama ID resource-nya dalam camel case.
val stringInTextField = binding.costOfService.text

Perhatikan .text di bagian akhir. Bagian pertama, binding.costOfService, merujuk elemen UI untuk biaya layanan. Menambahkan .text di akhir berarti mengambil hasil tersebut (objek EditText), dan mendapatkan properti text darinya. Ini dikenal sebagai perantaian, dan merupakan pola yang sangat umum di Kotlin.

  1. Selanjutnya, konversikan teks menjadi angka desimal. Panggil toDouble() di stringInTextField, dan simpan di variabel bernama cost.
val cost = stringInTextField.toDouble()

Tetapi, itu tidak berhasil—toDouble() harus dipanggil di String. Ternyata atribut text dari EditText adalah Editable, karena atribut tersebut mewakili teks yang dapat diubah. Untungnya, Anda dapat mengonversi Editable menjadi String dengan menerapkan toString() padanya.

  1. Panggil toString() di binding.costOfService.text untuk mengonversinya menjadi String:
val stringInTextField = binding.costOfService.text.toString()

Sekarang stringInTextField.toDouble() akan berfungsi.

Pada tahap ini, metode calculateTip() akan terlihat seperti ini:

fun calculateTip() {
    val stringInTextField = binding.costOfService.text.toString()
    val cost = stringInTextField.toDouble()
}

Mendapatkan persentase tip

Sejauh ini Anda telah memiliki biaya layanan. Sekarang Anda memerlukan persentase tip, yang dipilih pengguna dari RadioGroup yang berisi RadioButtons.

  1. Di calculateTip(), dapatkan atribut checkedRadioButtonId dari tipOptions RadioGroup, dan tetapkan ke variabel bernama selectedId.
val selectedId = binding.tipOptions.checkedRadioButtonId

Sekarang Anda tahu RadioButton mana yang dipilih, antara R.id.option_twenty_percent, R.id.option_eighteen_percent, atau R.id.fifteen_percent, tetapi Anda memerlukan persentasenya. Anda dapat menuliskan serangkaian pernyataan if/else, tetapi akan jauh lebih mudah untuk menggunakan ekspresi when.

  1. Tambahkan baris berikut untuk mendapatkan persentase tip.
val tipPercentage = when (selectedId) {
    R.id.option_twenty_percent -> 0.20
    R.id.option_eighteen_percent -> 0.18
    else -> 0.15
}

Pada tahap ini, metode calculateTip() akan terlihat seperti ini:

fun calculateTip() {
    val stringInTextField = binding.costOfService.text.toString()
    val cost = stringInTextField.toDouble()
    val selectedId = binding.tipOptions.checkedRadioButtonId
    val tipPercentage = when (selectedId) {
        R.id.option_twenty_percent -> 0.20
        R.id.option_eighteen_percent -> 0.18
        else -> 0.15
    }
}

Menghitung tip dan membulatkannya

Setelah Anda memiliki biaya layanan dan persentase tip, menghitung tip menjadi mudah: tip sama dengan biaya dikali persentase tip, tip = biaya layanan * persentase tip. Jika mau, nilai tersebut dapat dibulatkan.

  1. Di calculateTip() setelah kode lain yang telah Anda tambahkan, kalikan tipPercentage dengan cost, dan tetapkan ke variabel bernama tip.
var tip = tipPercentage * cost

Perhatikan penggunaan var, bukan val. Hal ini karena Anda mungkin perlu membulatkan nilai jika pengguna memilih opsi tersebut, sehingga nilainya mungkin berubah.

Untuk elemen Switch, Anda dapat memeriksa atribut isChecked untuk melihat apakah tombol "aktif".

  1. Tetapkan atribut isChecked tombol pembulatan ke atas ke variabel bernama roundUp.
val roundUp = binding.roundUpSwitch.isChecked

Istilah pembulatan berarti menyesuaikan angka desimal ke atas atau ke bawah yang paling dekat dengan nilai bilangan bulat, tetapi dalam kasus ini, Anda hanya perlu membulatkan ke atas, atau menemukan plafon. Anda dapat menggunakan fungsi ceil() untuk melakukannya. Ada beberapa fungsi dengan nama tersebut, tetapi yang Anda inginkan didefinisikan di kotlin.math. Anda dapat menambahkan pernyataan import, namun dalam kasus ini, akan lebih mudah untuk memberi tahu Android Studio mana yang Anda maksud dengan menggunakan kotlin.math.ceil().

32c29f73a3f20f93.png

Jika ada beberapa fungsi matematika yang ingin Anda gunakan, akan lebih mudah untuk menambahkan pernyataan import.

  1. Tambahkan pernyataan if yang menetapkan batas tip untuk variabel tip jika roundUp bernilai benar (true).
if (roundUp) {
    tip = kotlin.math.ceil(tip)
}

Pada tahap ini, metode calculateTip() akan terlihat seperti ini:

fun calculateTip() {
    val stringInTextField = binding.costOfService.text.toString()
    val cost = stringInTextField.toDouble()
    val selectedId = binding.tipOptions.checkedRadioButtonId
    val tipPercentage = when (selectedId) {
        R.id.option_twenty_percent -> 0.20
        R.id.option_eighteen_percent -> 0.18
        else -> 0.15
    }
    var tip = tipPercentage * cost
    val roundUp = binding.roundUpSwitch.isChecked
    if (roundUp) {
        tip = kotlin.math.ceil(tip)
    }
}

Memformat tip

Aplikasi Anda hampir berfungsi. Setelah menghitung tip, sekarang Anda hanya perlu memformatnya dan menampilkannya.

Seperti yang mungkin Anda pikirkan, Kotlin menyediakan metode untuk memformat berbagai jenis bilangan. Namun jumlah tip agak berbeda karena jumlah ini mewakili nilai mata uang. Negara yang berbeda menggunakan mata uang yang berbeda, dan memiliki aturan yang berbeda untuk memformat bilangan desimal. Misalnya, dalam dolar AS, 1234.56 akan diformat menjadi $1,234.56, tetapi dalam Euro, akan diformat menjadi €1.234,56. Untungnya, framework Android menyediakan metode untuk memformat bilangan sebagai mata uang, sehingga Anda tidak perlu mengetahui semua kemungkinan yang ada. Sistem akan otomatis memformat mata uang berdasarkan bahasa dan setelan lain yang telah dipilih pengguna di ponsel mereka. Baca selengkapnya tentang NumberFormat di dokumentasi developer Android.

  1. Di calculateTip() setelah kode Anda yang lain, panggil NumberFormat.getCurrencyInstance().
NumberFormat.getCurrencyInstance()

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

  1. Menggunakan pemformat angka, rantaikan panggilan ke metode format() dengan tip, dan tetapkan hasilnya ke variabel bernama formattedTip.
val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
  1. Perhatikan bahwa NumberFormat berwarna merah. Hal ini karena Android Studio tidak dapat otomatis menebak versi NumberFormat mana yang Anda maksud.
  2. Arahkan kursor ke NumberFormat, dan pilih Import di popup yang muncul. d9d2f92d5ef01df6.png
  3. Di daftar impor yang mungkin, pilih NumberFormat (java.text). Android Studio menambahkan pernyataan import di bagian atas file MainActivity, dan NumberFormat tidak merah lagi.

Menampilkan tip

Sekarang saatnya Anda menampilkan tip di elemen TextView jumlah tip aplikasi. Anda dapat menetapkan formattedTip ke atribut text, tetapi sebaiknya beri label yang menunjukkan arti jumlah ini. Di AS dalam bahasa Inggris, Anda mungkin memberi label Tip Amount: $12.34, tetapi dalam bahasa lain, angka tersebut mungkin harus ditampilkan di awal atau bahkan di tengah string. Framework Android menyediakan mekanisme untuk hal ini yang bernama parameter string, sehingga orang yang menerjemahkan aplikasi Anda dapat mengubah di mana angka tersebut muncul jika diperlukan.

  1. Buka strings.xml (app > res > values > strings.xml)
  2. Ubah string tip_amount dari Tip Amount menjadi Tip Amount: %s.
<string name="tip_amount">Tip Amount: %s</string>

%s akan menjadi lokasi mata uang yang telah diformat.

  1. Sekarang atur teks tipResult. Kembali ke metode calculateTip() di MainActivity.kt, panggil getString(R.string.tip_amount, formattedTip) dan tetapkan panggilan tersebut ke atribut text hasil tip TextView.
binding.tipResult.text = getString(R.string.tip_amount, formattedTip)

Pada tahap ini, metode calculateTip() akan terlihat seperti ini:

fun calculateTip() {
    val stringInTextField = binding.costOfService.text.toString()
    val cost = stringInTextField.toDouble()
    val selectedId = binding.tipOptions.checkedRadioButtonId
    val tipPercentage = when (selectedId) {
        R.id.option_twenty_percent -> 0.20
        R.id.option_eighteen_percent -> 0.18
        else -> 0.15
    }
    var tip = tipPercentage * cost
    val roundUp = binding.roundUpSwitch.isChecked
    if (roundUp) {
        tip = kotlin.math.ceil(tip)
    }
    val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
    binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
}

Anda hampir selesai. Saat mengembangkan aplikasi (dan menampilkan pratinjau), sebaiknya Anda memiliki placeholder untuk TextView tersebut.

  1. Buka activity_main.xml (app > res > layout > activity_main.xml).
  2. Temukan tip_result TextView.
  3. Hapus baris dengan atribut android:text.
android:text="@string/tip_amount"
  1. Tambahkan baris untuk atribut tools:text yang ditetapkan ke Tip Amount: $10.
tools:text="Tip Amount: $10"

Karena ini hanya placeholder, Anda tidak perlu mengekstrak string ke dalam resource. Placeholder tidak akan muncul saat Anda menjalankan aplikasi.

  1. Perhatikan bahwa teks alat muncul di Layout Editor.
  2. Jalankan aplikasi Anda. Masukkan jumlah biaya dan pilih beberapa opsi, lalu tekan tombol Calculate.

42fd6cd5e24ca433.png

Selamat—aplikasinya berfungsi! Jika Anda tidak mendapatkan jumlah tip yang benar, kembali ke langkah 1 dari bagian ini dan pastikan Anda telah menerapkan semua perubahan kode yang diperlukan.

5. Menguji dan memproses debug

Anda telah menjalankan aplikasi di berbagai langkah untuk memastikan aplikasi berfungsi sesuai keinginan, tetapi sekarang saatnya melakukan pengujian tambahan.

Sekarang, pikirkan bagaimana informasi ini berpindah-pindah di sepanjang aplikasi Anda di metode calculateTip(), dan kesalahan apa yang dapat terjadi di setiap langkah.

Misalnya, hal yang dapat terjadi di baris ini:

val cost = stringInTextField.toDouble()

jika stringInTextField tidak mewakili angka? Apa yang akan terjadi jika pengguna tidak memasukkan teks dan stringInTextField kosong?

  1. Jalankan aplikasi Anda di emulator melalui Run > Debug ‘app', bukan Run > Run ‘app'.
  2. Coba beberapa kombinasi biaya, jumlah tip, dan membulatkan tip atau tidak, dan pastikan bahwa Anda mendapatkan hasil yang diharapkan untuk setiap kasus saat Anda mengetuk Calculate.
  3. Sekarang coba hapus semua teks di kolom Cost of Service dan ketuk Calculate. Ups, program Anda error.

Men-debug error

Langkah pertama dalam menangani bug adalah mencari tahu apa yang terjadi. Android Studio menyimpan log apa yang terjadi dalam sistem yang dapat Anda gunakan untuk mencari tahu apa yang salah.

  1. Tekan tombol Logcat di bagian bawah Android Studio, atau pilih View > Tool Windows > Logcat di menu.

1b68ee5190018c8a.png

  1. Jendela Logcat akan muncul di bagian bawah Android Studio, berisi teks yang tampak aneh. 22139575476ae9d.png

Teks ini adalah pelacakan tumpukan, daftar metode mana yang dipanggil saat error terjadi.

  1. Scroll ke atas di teks Logcat sampai Anda menemukan baris yang berisi teks FATAL EXCEPTION.
2020-06-24 10:09:41.564 24423-24423/com.example.tiptime E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.tiptime, PID: 24423
    java.lang.NumberFormatException: empty String
        at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
        at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
        at java.lang.Double.parseDouble(Double.java:538)
        at com.example.tiptime.MainActivity.calculateTip(MainActivity.kt:22)
        at com.example.tiptime.MainActivity$onCreate$1.onClick(MainActivity.kt:17)
  1. Baca ke bawah sampai Anda menemukan baris dengan NumberFormatException.
java.lang.NumberFormatException: empty String

Di sebelah kanan tertulis empty String. Jenis pengecualian ini memberi tahu Anda error tersebut berkaitan dengan format angka, dan sisanya merupakan dasar masalah: String yang kosong ditemukan saat variabel tersebut seharusnya berupa String dengan sebuah nilai.

  1. Terus baca ke bawah, dan Anda akan melihat beberapa panggilan ke parseDouble().
  2. Di bawah panggilan tersebut, temukan baris dengan calculateTip. Perhatikan bahwa baris ini juga menyertakan class MainActivity Anda.
at com.example.tiptime.MainActivity.calculateTip(MainActivity.kt:22)
  1. Perhatikan baik-baik baris tersebut, dan Anda dapat melihat lokasi tepat di dalam kode saat panggilan tersebut dilakukan, baris 22 di MainActivity.kt. (Jika Anda mengetik kode dengan cara berbeda, mungkin nomornya akan berbeda). Baris tersebut mengonversi String menjadi Double dan menetapkan hasilnya ke variabel cost.
val cost = stringInTextField.toDouble()
  1. Lihat di dokumentasi Kotlin untuk metode toDouble() yang berfungsi di String. Metode ini disebut sebagai String.toDouble().
  2. Halaman tersebut bertuliskan "Pengecualian: NumberFormatException - jika string bukan berupa angka yang valid."

Pengecualian adalah cara sistem untuk menyatakan bahwa ada masalah. Dalam kasus ini, masalahnya adalah toDouble() tidak dapat mengonversi String kosong menjadi Double. Meskipun EditText memiliki inputType=numberDecimal, jenis nilai lain tetap mungkin dimasukkan sehingga toDouble() tidak dapat menanganinya, seperti string kosong.

Mempelajari tentang null

Pemanggilan toDouble() tidak akan berhasil jika string kosong atau tidak berupa angka desimal yang valid. Untungnya Kotlin juga menyediakan metode bernama toDoubleOrNull() yang menangani masalah ini. Metode ini menampilkan angka desimal jika bisa, atau menampilkan null jika ada masalah.

Null adalah nilai khusus yang berarti "tidak ada nilai". Null berbeda dengan Double yang memiliki nilai 0.0 atau String kosong yang tanpa karakter, "". Null berarti tidak ada nilai, tidak ada Double, atau tidak ada String. Banyak metode memerlukan nilai dan mungkin tidak tahu cara menangani null dan lalu akan berhenti, yang berarti aplikasi akan mengalami error, sehingga Kotlin mencoba membatasi tempat penggunaan null. Anda akan mempelajari selengkapnya tentang hal ini dalam pelajaran berikutnya.

Aplikasi Anda dapat memeriksa apakah null ditampilkan dari toDoubleOrNull() dan menerapkan cara lain dalam kasus tersebut agar aplikasi tidak error.

  1. Di calculateTip(), ubah baris yang mendeklarasikan variabel cost untuk memanggil toDoubleOrNull(), bukan memanggil toDouble().
val cost = stringInTextField.toDoubleOrNull()
  1. Setelah baris tadi, tambahkan pernyataan untuk memeriksa apakah cost bernilai null dan, jika demikian, keluar dari metode tersebut. Instruksi return berarti keluar dari metode tersebut tanpa mengeksekusi instruksi lainnya. Jika metode ini diperlukan untuk menampilkan nilai, Anda harus menentukannya dengan instruksi return dengan ekspresi.
if (cost == null) {
    return
}
  1. Jalankan aplikasi Anda lagi.
  2. Tanpa teks di kolom Cost of Service, ketuk Calculate. Kali ini aplikasi Anda tidak error! Bagus—Anda telah menemukan dan memperbaiki bug!

Menangani kasus lain

Tidak semua bug akan menyebabkan aplikasi Anda error—terkadang hasilnya mungkin dapat membingungkan bagi pengguna.

Berikut ini kasus lain yang perlu dipertimbangkan. Apa yang akan terjadi jika pengguna:

  1. memasukkan jumlah biaya yang valid
  2. mengetuk Calculate untuk menghitung tip
  3. menghapus biaya
  4. mengetuk Calculate lagi?

Pertama-tama tip akan dihitung dan ditampilkan sebagaimana mestinya. Kali kedua, metode calculateTip() akan ditampilkan lebih awal karena pemeriksaan yang baru saja Anda tambahkan, tetapi aplikasi akan tetap menampilkan jumlah tip sebelumnya. Hal ini mungkin membingungkan bagi pengguna, jadi tambahkan beberapa kode untuk menghapus jumlah tip jika ada masalah.

  1. Pastikan masalah inilah yang akan terjadi jika memasukkan biaya yang valid dan mengetuk Calculate, lalu menghapus teks, dan mengetuk Calculate lagi. Nilai tip pertama harus tetap ditampilkan.
  2. Di dalam if yang baru saja ditambahkan, sebelum pernyataan return, tambahkan baris untuk menetapkan atribut text dari tipResult menjadi string kosong.
if (cost == null) {
    binding.tipResult.text = ""
    return
}

Tindakan ini akan menghapus jumlah tip sebelum menampilkan hasil calculateTip().

  1. Jalankan aplikasi Anda lagi, lalu coba kasus di atas. Nilai tip pertama seharusnya hilang saat Anda mengetuk Calculate kali kedua.

Selamat! Anda telah membuat aplikasi kalkulator tip yang berfungsi untuk Android dan menangani beberapa kasus ekstrem!

6. Menerapkan praktik coding yang baik

Kalkulator tip Anda berfungsi sekarang, tetapi Anda dapat membuat kode yang lebih baik lagi dan membuatnya lebih mudah digunakan di masa mendatang dengan menerapkan praktik coding yang baik.

  1. Buka MainActivity.kt (app > java > com.example.tiptime > MainActivity).
  2. Lihat bagian awal metode calculateTip(), dan Anda mungkin melihat bahwa metode tersebut digarisbawahi dengan garis abu-abu bergelombang.

3737ebab72be9a5b.png

  1. Arahkan kursor ke calculateTip(), Anda akan melihat pesan, Function ‘calculateTip' could be private dengan saran di bawah Make ‘calculateTip' ‘private'. 6205e927b4c14cf3.png

Ingat kembali dari codelab sebelumnya bahwa private berarti metode atau variabel ini hanya terlihat oleh kode dalam class tersebut, dalam hal ini class MainActivity. Tidak ada alasan bagi kode di luar MainActivity untuk memanggil calculateTip(), sehingga Anda dapat membuatnya menjadi private dengan aman.

  1. Pilih Make ‘calculateTip' ‘private', atau tambahkan kata kunci private sebelum fun calculateTip(). Garis abu-abu di bawah calculateTip() akan hilang.

Memeriksa kode

Garis abu-abu tersebut sangat tipis dan mudah terabaikan. Anda dapat menelusuri seluruh file untuk menemukan garis abu-abu lainnya, tetapi ada cara yang lebih praktis untuk memastikan Anda menemukan semua sarannya.

  1. Saat MainActivity.kt masih terbuka, pilih Analyze > Inspect Code... di menu. Kotak dialog bernama Specify Inspection Scope akan muncul. 1d2c6f8415e96231.png
  2. Pilih opsi yang dimulai dengan File dan tekan OK. Ini akan membatasi inspeksi hanya pada MainActivity.kt.
  3. Jendela dengan Inspection Results akan muncul di bagian bawah.
  4. Klik segitiga abu-abu di samping Kotlin, lalu di samping Style issues sampai Anda melihat dua pesan. Pesan pertama berbunyi Class member can have ‘private' visibility. e40a6876f939c0d9.png
  5. Klik segitiga abu-abu sampai Anda melihat pesan Property ‘binding' could be private dan klik pesan tersebut. Android Studio menampilkan beberapa kode di MainActivity dan menandai variabel binding. 8d9d7b5fc7ac5332.png
  6. Tekan tombol Make ‘binding' ‘private'. Android Studio menghapus masalah dari Inspection Results.
  7. Pada binding dalam kode, Anda akan melihat bahwa Android Studio telah menambahkan kata kunci private sebelum deklarasi tersebut.
private lateinit var binding: ActivityMainBinding
  1. Klik segitiga abu-abu di hasil sampai Anda melihat pesan Variable declaration could be inlined. Android Studio menampilkan beberapa kode lagi, namun kali ini menandai variabel selectedId. 781017cbcada1194.png
  2. Pada kode, Anda akan melihat bahwa selectedId hanya digunakan dua kali, yaitu di baris yang ditandai saat variabel tersebut diberi nilai tipOptions.checkedRadioButtonId dan di baris berikutnya di when.
  3. Tekan tombol Inline variable. Android Studio mengganti selectedId di ekspresi when dengan nilai yang ditetapkan di baris sebelumnya. Kemudian kode tersebut menghapus seluruh baris sebelumnya, karena tidak lagi diperlukan!
val tipPercentage = when (binding.tipOptions.checkedRadioButtonId) {
    R.id.option_twenty_percent -> 0.20
    R.id.option_eighteen_percent -> 0.18
    else -> 0.15
}

Bagus! Kode Anda kini lebih ringkas satu baris, dan berkurang satu variabel.

Menghapus variabel yang tidak perlu

Android Studio tidak memiliki hasil lain dari inspeksi. Namun, jika cermat, Anda akan melihat pola yang mirip dengan pola yang baru saja Anda ubah, yaitu variabel roundUp diberi nilai pada satu baris, digunakan pada baris berikutnya, tetapi tidak digunakan di baris lain.

  1. Salin ekspresi ini di sebelah kanan = dari baris tempat roundUp diberi nilai.
val roundUp = binding.roundUpSwitch.isChecked
  1. Ganti roundUp di baris berikutnya dengan ekspresi yang baru saja disalin, binding.roundUpSwitch.isChecked.
if (binding.roundUpSwitch.isChecked) {
    tip = kotlin.math.ceil(tip)
}
  1. Hapus baris dengan roundUp, karena tidak diperlukan lagi.

Anda baru saja melakukan tindakan yang sama seperti yang dilakukan Android Studio pada variabel selectedId. Sekali lagi, kode Anda kini lebih ringkas satu baris, dan berkurang satu variabel. Ini adalah perubahan kecil, tetapi membantu membuat kode Anda lebih ringkas dan lebih mudah dibaca.

(Opsional) Menghilangkan kode berulang

Setelah aplikasi Anda berjalan dengan benar, Anda dapat mencari peluang lain untuk merapikan kode dan membuatnya lebih ringkas. Misalnya, saat Anda tidak memasukkan nilai di biaya layanan, aplikasi akan mengubah tipResult menjadi string kosong "". Jika berisi nilai, gunakan NumberFormat untuk memformatnya. Fungsi ini dapat diterapkan di baris lain dalam aplikasi, misalnya, untuk menampilkan tip 0.0 daripada string kosong.

Untuk mengurangi duplikasi kode yang sangat mirip, Anda dapat mengekstrak dua baris kode ini ke fungsinya sendiri. Fungsi helper ini dapat mengambil jumlah tip sebagai input berupa Double, memformatnya, dan memperbarui tipResult TextView di layar.

  1. Identifikasi kode duplikat di MainActivity.kt. Baris kode ini dapat digunakan beberapa kali di fungsi calculateTip(), sekali untuk kasus 0.0, dan sekali untuk kasus umum.
val formattedTip = NumberFormat.getCurrencyInstance().format(0.0)
binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
  1. Pindahkan kode duplikat ke fungsinya sendiri. Satu perubahan pada kode adalah untuk mengambil tip parameter sehingga kode berfungsi di beberapa tempat.
private fun displayTip(tip : Double) {
   val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
   binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
}
  1. Perbarui fungsi calculateTip() Anda untuk menggunakan fungsi helper displayTip() dan periksa 0.0 juga.

MainActivity.kt

private fun calculateTip() {
    ...

        // If the cost is null or 0, then display 0 tip and exit this function early.
        if (cost == null || cost == 0.0) {
            displayTip(0.0)
            return
        }

    ...
    if (binding.roundUpSwitch.isChecked) {
        tip = kotlin.math.ceil(tip)
    }

    // Display the formatted tip value on screen
    displayTip(tip)
}

Catatan

Meskipun sekarang sudah berfungsi, aplikasi belum siap untuk tahap produksi. Anda harus melakukan lebih banyak pengujian. Anda juga harus memoles tampilan visualnya dan mengikuti panduan Desain Material. Anda juga akan belajar mengubah tema aplikasi dan ikon aplikasi di codelab berikut.

7. Kode solusi

Kode solusi untuk codelab ini adalah sebagai berikut.

966018df4a149822.png

MainActivity.kt

(catatan untuk baris pertama: ganti nama paket jika milik Anda bukan com.example.tiptime)

package com.example.tiptime

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.tiptime.databinding.ActivityMainBinding
import java.text.NumberFormat

class MainActivity : AppCompatActivity() {

   private lateinit var binding: ActivityMainBinding

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       binding = ActivityMainBinding.inflate(layoutInflater)
       setContentView(binding.root)

       binding.calculateButton.setOnClickListener { calculateTip() }
   }

   private fun calculateTip() {
       val stringInTextField = binding.costOfService.text.toString()
       val cost = stringInTextField.toDoubleOrNull()
       if (cost == null) {
           binding.tipResult.text = ""
           return
       }

       val tipPercentage = when (binding.tipOptions.checkedRadioButtonId) {
           R.id.option_twenty_percent -> 0.20
           R.id.option_eighteen_percent -> 0.18
           else -> 0.15
       }

       var tip = tipPercentage * cost
       if (binding.roundUpSwitch.isChecked) {
           tip = kotlin.math.ceil(tip)
       }

       val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
       binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
   }
}

Memodifikasi strings.xml

<string name="tip_amount">Tip Amount: %s</string>

Memodifikasi activity_main.xml

...

<TextView
   android:id="@+id/tip_result"
   ...
   tools:text="Tip Amount: $10" />

...

Memodifikasi build.gradle modul aplikasi

android {
    ...

    buildFeatures {
        viewBinding = true
    }
    ...
}

8. Ringkasan

  • View binding memungkinkan Anda lebih mudah menulis kode yang berinteraksi dengan elemen UI di aplikasi Anda.
  • Jenis data Double di Kotlin dapat menyimpan angka desimal.
  • Gunakan atribut checkedRadioButtonId dari RadioGroup untuk menemukan RadioButton mana yang dipilih.
  • Gunakan NumberFormat.getCurrencyInstance() agar pemformat digunakan untuk memformat angka sebagai mata uang.
  • Anda dapat menggunakan parameter string seperti %s untuk membuat string dinamis yang tetap dapat mudah diterjemahkan ke dalam bahasa lain.
  • Pengujian itu penting!
  • Anda dapat menggunakan Logcat di Android Studio untuk memecahkan masalah seperti aplikasi error.
  • Pelacakan tumpukan menampilkan daftar metode yang dipanggil. Fungsi ini dapat berguna jika kode menghasilkan pengecualian.
  • Pengecualian menunjukkan masalah yang tidak diharapkan kode.
  • Null berarti "tidak ada nilai".
  • Tidak semua kode dapat menangani nilai null, jadi hati-hati saat menggunakannya.
  • Gunakan Analyze > Inspect Code untuk mendapatkan saran guna memperbaiki kode Anda.

9. Codelab lainnya untuk meningkatkan UI Anda

Anda berhasil membuat kalkulator tip berfungsi! Anda akan melihat bahwa ada cara lain untuk meningkatkan UI untuk membuat aplikasi tampak lebih bagus. Jika Anda tertarik, lihat codelab tambahan ini untuk mempelajari lebih lanjut tentang cara mengubah tema dan ikon aplikasi, serta cara mengikuti praktik terbaik dari panduan Desain Material untuk aplikasi Tip Time!

10. Pelajari lebih lanjut

11. Berlatih sendiri

  • Dengan aplikasi pengonversi unit untuk memasak di praktik sebelumnya, tambahkan kode logika dan perhitungan untuk mengonversi unit seperti mililiter ke atau dari ons cairan.