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
- Kode dari codelab Membuat tata letak XML untuk Android.
- Cara menjalankan aplikasi Android dari Android Studio di emulator atau perangkat.
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.
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.
- Buka project Tip Time di Android Studio.
- Jika jendela Project tidak muncul, pilih tab Project di sebelah kiri Android Studio.
- Jika belum dipilih, pilih tampilan Android dari drop-down.
- 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 Andastrings.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
- Buka file
build.gradle
aplikasi ( Gradle Scripts > build.gradle (Module: Tip_Time.app) ) - Di bagian
android
, tambahkan baris berikut:
buildFeatures { viewBinding = true }
- Perhatikan pesan Gradle files have changed since last project sync.
- Tekan Sync Now.
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.
- Buka
MainActivity.kt
(app > java > com.example.tiptime > MainActivity). - Ganti semua kode yang ada untuk class
MainActivity
dengan kode ini guna menyiapkanMainActivity
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)
}
}
- 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.
- Baris ini melakukan inisialisasi objek
binding
yang akan Anda gunakan untuk mengaksesViews
di tata letakactivity_main.xml
.
binding = ActivityMainBinding.inflate(layoutInflater)
- 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.
- Di
MainActivity.kt
dalamonCreate()
, setelah panggilan kesetContentView()
, setel pemroses klik pada tombol Calculate agar memanggilcalculateTip()
.
binding.calculateButton.setOnClickListener{ calculateTip() }
- Masih di dalam class
MainActivity
, tetapi di luaronCreate()
, tambahkan metode helper yang bernamacalculateTip()
.
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()
.
- Pertama, dapatkan teks untuk biaya layanan. Di metode
calculateTip()
, dapatkan atribut teks Cost of ServiceEditText
, dan tetapkan ke variabel bernamastringInTextField
. Perlu diingat bahwa Anda dapat mengakses elemen UI menggunakan objekbinding
, 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.
- Selanjutnya, konversikan teks menjadi angka desimal. Panggil
toDouble()
distringInTextField
, dan simpan di variabel bernamacost
.
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.
- Panggil
toString()
dibinding.costOfService.text
untuk mengonversinya menjadiString
:
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
.
- Di
calculateTip()
, dapatkan atributcheckedRadioButtonId
daritipOptions
RadioGroup
, dan tetapkan ke variabel bernamaselectedId
.
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
.
- 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.
- Di
calculateTip()
setelah kode lain yang telah Anda tambahkan, kalikantipPercentage
dengancost
, dan tetapkan ke variabel bernamatip
.
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".
- Tetapkan atribut
isChecked
tombol pembulatan ke atas ke variabel bernamaroundUp
.
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()
.
Jika ada beberapa fungsi matematika yang ingin Anda gunakan, akan lebih mudah untuk menambahkan pernyataan import
.
- Tambahkan pernyataan
if
yang menetapkan batas tip untuk variabeltip
jikaroundUp
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.
- Di
calculateTip()
setelah kode Anda yang lain, panggilNumberFormat.getCurrencyInstance()
.
NumberFormat.getCurrencyInstance()
Tindakan ini memberi Anda pemformat angka yang dapat digunakan untuk memformat bilangan sebagai mata uang.
- Menggunakan pemformat angka, rantaikan panggilan ke metode
format()
dengantip
, dan tetapkan hasilnya ke variabel bernamaformattedTip
.
val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
- Perhatikan bahwa
NumberFormat
berwarna merah. Hal ini karena Android Studio tidak dapat otomatis menebak versiNumberFormat
mana yang Anda maksud. - Arahkan kursor ke
NumberFormat
, dan pilih Import di popup yang muncul. - Di daftar impor yang mungkin, pilih NumberFormat (java.text). Android Studio menambahkan pernyataan
import
di bagian atas fileMainActivity
, danNumberFormat
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.
- Buka
strings.xml
(app > res > values > strings.xml) - Ubah string
tip_amount
dariTip Amount
menjadiTip Amount: %s
.
<string name="tip_amount">Tip Amount: %s</string>
%s
akan menjadi lokasi mata uang yang telah diformat.
- Sekarang atur teks
tipResult
. Kembali ke metodecalculateTip()
diMainActivity.kt
, panggilgetString(R.string.tip_amount, formattedTip)
dan tetapkan panggilan tersebut ke atributtext
hasil tipTextView
.
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.
- Buka
activity_main.xml
(app > res > layout > activity_main.xml). - Temukan
tip_result
TextView
. - Hapus baris dengan atribut
android:text
.
android:text="@string/tip_amount"
- Tambahkan baris untuk atribut
tools:text
yang ditetapkan keTip 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.
- Perhatikan bahwa teks alat muncul di Layout Editor.
- Jalankan aplikasi Anda. Masukkan jumlah biaya dan pilih beberapa opsi, lalu tekan tombol Calculate.
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?
- Jalankan aplikasi Anda di emulator melalui Run > Debug ‘app', bukan Run > Run ‘app'.
- 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.
- 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.
- Tekan tombol Logcat di bagian bawah Android Studio, atau pilih View > Tool Windows > Logcat di menu.
- Jendela Logcat akan muncul di bagian bawah Android Studio, berisi teks yang tampak aneh.
Teks ini adalah pelacakan tumpukan, daftar metode mana yang dipanggil saat error terjadi.
- 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)
- 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.
- Terus baca ke bawah, dan Anda akan melihat beberapa panggilan ke
parseDouble()
. - Di bawah panggilan tersebut, temukan baris dengan
calculateTip
. Perhatikan bahwa baris ini juga menyertakan classMainActivity
Anda.
at com.example.tiptime.MainActivity.calculateTip(MainActivity.kt:22)
- 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 mengonversiString
menjadiDouble
dan menetapkan hasilnya ke variabelcost
.
val cost = stringInTextField.toDouble()
- Lihat di dokumentasi Kotlin untuk metode
toDouble()
yang berfungsi diString
. Metode ini disebut sebagaiString.toDouble()
. - 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.
- Di
calculateTip()
, ubah baris yang mendeklarasikan variabelcost
untuk memanggiltoDoubleOrNull()
, bukan memanggiltoDouble()
.
val cost = stringInTextField.toDoubleOrNull()
- Setelah baris tadi, tambahkan pernyataan untuk memeriksa apakah
cost
bernilainull
dan, jika demikian, keluar dari metode tersebut. Instruksireturn
berarti keluar dari metode tersebut tanpa mengeksekusi instruksi lainnya. Jika metode ini diperlukan untuk menampilkan nilai, Anda harus menentukannya dengan instruksireturn
dengan ekspresi.
if (cost == null) {
return
}
- Jalankan aplikasi Anda lagi.
- 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:
- memasukkan jumlah biaya yang valid
- mengetuk Calculate untuk menghitung tip
- menghapus biaya
- 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.
- 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.
- Di dalam
if
yang baru saja ditambahkan, sebelum pernyataanreturn
, tambahkan baris untuk menetapkan atributtext
daritipResult
menjadi string kosong.
if (cost == null) {
binding.tipResult.text = ""
return
}
Tindakan ini akan menghapus jumlah tip sebelum menampilkan hasil calculateTip()
.
- 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.
- Buka
MainActivity.kt
(app > java > com.example.tiptime > MainActivity). - Lihat bagian awal metode
calculateTip()
, dan Anda mungkin melihat bahwa metode tersebut digarisbawahi dengan garis abu-abu bergelombang.
- Arahkan kursor ke
calculateTip()
, Anda akan melihat pesan, Function ‘calculateTip' could be private dengan saran di bawah Make ‘calculateTip' ‘private'.
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.
- Pilih Make ‘calculateTip' ‘private', atau tambahkan kata kunci
private
sebelumfun calculateTip()
. Garis abu-abu di bawahcalculateTip()
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.
- Saat
MainActivity.kt
masih terbuka, pilih Analyze > Inspect Code... di menu. Kotak dialog bernama Specify Inspection Scope akan muncul. - Pilih opsi yang dimulai dengan File dan tekan OK. Ini akan membatasi inspeksi hanya pada
MainActivity.kt
. - Jendela dengan Inspection Results akan muncul di bagian bawah.
- 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.
- 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 variabelbinding
. - Tekan tombol Make ‘binding' ‘private'. Android Studio menghapus masalah dari Inspection Results.
- Pada
binding
dalam kode, Anda akan melihat bahwa Android Studio telah menambahkan kata kunciprivate
sebelum deklarasi tersebut.
private lateinit var binding: ActivityMainBinding
- 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
. - Pada kode, Anda akan melihat bahwa
selectedId
hanya digunakan dua kali, yaitu di baris yang ditandai saat variabel tersebut diberi nilaitipOptions.checkedRadioButtonId
dan di baris berikutnya diwhen
. - Tekan tombol Inline variable. Android Studio mengganti
selectedId
di ekspresiwhen
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.
- Salin ekspresi ini di sebelah kanan
=
dari baris tempatroundUp
diberi nilai.
val roundUp = binding.roundUpSwitch.isChecked
- Ganti
roundUp
di baris berikutnya dengan ekspresi yang baru saja disalin,binding.roundUpSwitch.isChecked
.
if (binding.roundUpSwitch.isChecked) {
tip = kotlin.math.ceil(tip)
}
- 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.
- Identifikasi kode duplikat di
MainActivity.kt
. Baris kode ini dapat digunakan beberapa kali di fungsicalculateTip()
, sekali untuk kasus0.0
, dan sekali untuk kasus umum.
val formattedTip = NumberFormat.getCurrencyInstance().format(0.0)
binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
- 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)
}
- Perbarui fungsi
calculateTip()
Anda untuk menggunakan fungsi helperdisplayTip()
dan periksa0.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.
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
dariRadioGroup
untuk menemukanRadioButton
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
- Jenis data
Double
di Kotlin - Jenis data numerik di Kotlin
- Keamanan Null di Kotlin
- Manifes Aplikasi
View
bindingNumberFormat.getCurrencyInstance()
- parameter string
- pengujian
- Logcat
- Menganalisis pelacakan tumpukan
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.