Project: Aplikasi Lunch Tray

1. Sebelum memulai

Codelab ini memperkenalkan aplikasi baru bernama Lunch Tray yang akan Anda bangun sendiri. Codelab ini memandu Anda melakukan langkah-langkah untuk menyelesaikan project aplikasi Lunch Tray, seperti penyiapan dan pengujian project dalam Android Studio.

Codelab ini berbeda dari yang lain di kursus ini. Tidak seperti codelab sebelumnya, tujuan codelab ini bukan untuk menyediakan tutorial langkah demi langkah tentang cara membangun aplikasi. Codelab ini dimaksudkan untuk menyiapkan project yang akan Anda selesaikan secara independen, yang memberi Anda petunjuk cara menyelesaikan aplikasi dan memeriksa sendiri aplikasi Anda.

Kami menyediakan paket pengujian sebagai bagian dari aplikasi yang akan Anda download, bukan kode solusi. Anda akan menjalankan pengujian ini di Android Studio (kami akan menunjukkan cara melakukannya nanti dalam codelab ini) dan melihat apakah kode Anda lulus. Mungkin diperlukan beberapa kali percobaan—bahkan developer profesional jarang sekali lulus pengujian pada percobaan pertama. Setelah kode Anda lulus semua pengujian, Anda dapat menganggap project ini selesai.

Kami memahami bahwa Anda mungkin hanya ingin solusi untuk memeriksanya. Kami sengaja tidak menyediakan kode solusi karena ingin Anda mempraktikkan bagaimana rasanya menjadi developer profesional. Hal ini mungkin mengharuskan Anda menggunakan berbagai keterampilan yang belum Anda pelajari, seperti:

  • Menelusuri Google untuk istilah-istilah, pesan error, dan bit kode dalam aplikasi yang tidak Anda kenali;
  • Menguji kode, membaca error, lalu melakukan perubahan pada kode dan mengujinya lagi;
  • Kembali ke konten sebelumnya di Dasar-Dasar Android untuk mengingat kembali apa yang telah Anda pelajari;
  • Membandingkan kode yang Anda ketahui berfungsi (yaitu, kode yang diberikan dalam project, atau kode solusi sebelumnya dari aplikasi lain dalam Unit 3) dengan kode yang Anda tulis.

Pada awalnya mungkin tampak sulit, tetapi kami 100 persen yakin Anda siap untuk project ini jika bisa menyelesaikan Unit 3. Jangan terburu-buru, dan jangan menyerah. Anda pasti bisa melakukannya.

Prasyarat

  • Project ini ditujukan untuk pengguna yang telah menyelesaikan Unit 3 kursus Dasar-dasar Android di Kotlin.

Yang akan Anda bangun

  • Anda akan mengambil aplikasi pemesanan makanan bernama Lunch Tray, menerapkan ViewModel dengan data binding, serta menambahkan navigasi antarfragmen.

Yang akan Anda butuhkan

  • Komputer yang dilengkapi Android Studio.

2. Ringkasan aplikasi yang sudah selesai

Selamat datang di Project: Lunch Tray!

Seperti yang mungkin Anda ketahui, navigasi adalah bagian dasar dari pengembangan Android. Saat menggunakan aplikasi untuk mencari resep, menemukan rute ke restoran favorit, atau yang paling penting, memesan makanan, Anda tentu akan membuka-buka beberapa layar konten. Dalam project ini, Anda akan memanfaatkan keterampilan yang Anda pelajari di Unit 3 untuk membangun aplikasi pemesanan makan siang bernama Lunch Tray, yang menerapkan model tampilan, data binding, dan navigasi antar-layar.

Berikut adalah screenshot aplikasi final. Saat pertama kali meluncurkan aplikasi Lunch Tray, pengguna akan disambut dengan layar berisi satu tombol yang bertuliskan "Start Order".

20fa769d4ba93ef3.png

Setelah mengklik Start Order, pengguna dapat memilih hidangan utama dari pilihan yang tersedia. Pengguna dapat mengubah pilihan mereka, yang memperbarui Subtotal yang ditampilkan di bagian bawah.

438b61180d690b3a.png

Layar berikutnya memungkinkan pengguna menambahkan makanan pendamping.

768352680759d3e2.png

Setelah itu, layar akan memungkinkan pengguna memilih makanan tambahan untuk pesanannya.

8ee2bf41e9844614.png

Terakhir, pengguna melihat ringkasan biaya pesanan, yang dibagi menjadi subtotal, pajak penjualan, dan biaya total. Pengguna juga dapat mengirimkan atau membatalkan pesanan.

61c883c34d94b7f7.png

Kedua opsi tersebut mengembalikan pengguna ke layar pertama. Jika pengguna mengirimkan pesanannya, toast akan muncul di bagian bawah layar yang memberi tahu bahwa pesanan telah dikirim.

acb7d7a5d9843bac.png

3. Memulai

Mendownload kode project

Perhatikan bahwa nama folder adalah android-basics-kotlin-lunch-tray-app. Pilih folder ini saat Anda membuka project di Android Studio.

  1. Buka halaman repositori GitHub yang disediakan untuk project.
  2. Pastikan nama cabang cocok dengan nama cabang yang ditentukan dalam codelab. Misalnya, dalam screenshot berikut, nama cabang adalah main (utama).

1e4c0d2c081a8fd2.png

  1. Di halaman GitHub project, klik tombol Code yang akan menampilkan pop-up.

1debcf330fd04c7b.png

  1. Pada pop-up, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
  2. Temukan file di komputer Anda (mungkin di folder Downloads).
  3. Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.

Membuka project di Android Studio

  1. Mulai Android Studio.
  2. Di jendela Welcome to Android Studio, klik Open.

d8e9dbdeafe9038a.png

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > Open.

8d1fda7396afe8e5.png

  1. Di file browser, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
  2. Klik dua kali pada folder project tersebut.
  3. Tunggu Android Studio membuka project.
  4. Klik tombol Run 8de56cba7583251f.png untuk membangun dan menjalankan aplikasi. Pastikan aplikasi dibangun seperti yang diharapkan.

Sebelum mulai menerapkan ViewModel dan navigasi, luangkan waktu untuk memastikan project akan berhasil dibuat dan Anda sudah memahami project tersebut. Saat menjalankan aplikasi untuk pertama kalinya, Anda akan melihat layar kosong. MainActivity tidak menampilkan fragmen apa pun, karena Anda belum menyiapkan grafik navigasi.

Struktur project harus mirip dengan project lain yang telah Anda gunakan. Paket terpisah disediakan untuk data, model, dan UI, serta direktori terpisah untuk resource.

a19fd8a4bc92f2fc.png

Semua opsi makan siang yang dapat dipesan pengguna (hidangan utama, pendamping, dan tambahan) ditampilkan oleh class MenuItem dalam paket model. Objek MenuItem memiliki nama, deskripsi, harga, dan jenis.

data class MenuItem(
    val name: String,
    val description: String,
    val price: Double,
    val type: Int
) {
    fun getFormattedPrice(): String = NumberFormat.getCurrencyInstance().format(price)
}

Jenis ini ditampilkan oleh bilangan bulat yang berasal dari objek ItemType dalam paket konstan.

object ItemType {
    val ENTREE = 1
    val SIDE_DISH = 2
    val ACCOMPANIMENT = 3
}

Setiap objek MenuItem dapat ditemukan di DataSource.kt dalam paket data.

object DataSource {
    val menuItems = mapOf(
        "cauliflower" to
        MenuItem(
            name = "Cauliflower",
            description = "Whole cauliflower, brined, roasted, and deep fried",
            price = 7.00,
            type = ItemType.ENTREE
        ),
    ...
}

Objek ini hanya berisi peta yang terdiri dari kunci dan MenuItem yang sesuai. Anda akan mengakses DataSource dari ObjectViewModel, yang akan Anda terapkan terlebih dahulu.

Menentukan ViewModel

Seperti yang terlihat pada screenshot di halaman sebelumnya, aplikasi ini meminta tiga hal dari pengguna: hidangan utama, pendamping, dan tambahan. Layar ringkasan pesanan kemudian menampilkan subtotal dan menghitung pajak penjualan berdasarkan item yang dipilih, yang digunakan untuk menghitung total pesanan.

Dalam paket model, buka OrderViewModel.kt lalu Anda akan melihat beberapa variabel sudah ditetapkan. Properti menuItems hanya memungkinkan Anda mengakses DataSource dari ViewModel.

val menuItems = DataSource.menuItems

Pertama, ada juga beberapa variabel untuk previousEntreePrice, previousSidePrice, dan previousAccompanimentPrice. Karena subtotal diupdate saat pengguna membuat pilihannya (bukan ditambahkan di bagian akhir), variabel ini digunakan untuk melacak pilihan pengguna sebelumnya jika mereka mengubah pilihan sebelum beralih ke layar berikutnya. Anda akan menggunakannya untuk memastikan subtotal memperhitungkan perbedaan antara harga item sebelumnya dan yang dipilih saat ini.

private var previousEntreePrice = 0.0
private var previousSidePrice = 0.0
private var previousAccompanimentPrice = 0.0

Ada juga variabel pribadi, _entree, _side, dan _accompaniment, untuk menyimpan pilihan yang saat ini dipilih. Ini adalah jenis MutableLiveData<MenuItem?>. Masing-masing disertai dengan properti pendukung publik, entree, side, dan accompaniment, dari jenis yang tidak dapat diubah LiveData<MenuItem?>. Ini diakses oleh tata letak fragmen untuk menampilkan item yang dipilih di layar. MenuItem yang terdapat dalam objek LiveData juga nullable karena pengguna tidak dapat memilih hidangan utama, pendamping, dan/atau tambahan.

// Entree for the order
private val _entree = MutableLiveData<MenuItem?>()
val entree: LiveData<MenuItem?> = _entree

// Side for the order
private val _side = MutableLiveData<MenuItem?>()
val side: LiveData<MenuItem?> = _side

// Accompaniment for the order.
private val _accompaniment = MutableLiveData<MenuItem?>()
val accompaniment: LiveData<MenuItem?> = _accompaniment

Terdapat pula LiveData variabel untuk subtotal, total, dan pajak, yang menggunakan pemformatan angka sehingga ditampilkan sebagai mata uang.

// Subtotal for the order
private val _subtotal = MutableLiveData(0.0)
val subtotal: LiveData<String> = Transformations.map(_subtotal) {
    NumberFormat.getCurrencyInstance().format(it)
}

// Total cost of the order
private val _total = MutableLiveData(0.0)
val total: LiveData<String> = Transformations.map(_total) {
    NumberFormat.getCurrencyInstance().format(it)
}

// Tax for the order
private val _tax = MutableLiveData(0.0)
val tax: LiveData<String> = Transformations.map(_tax) {
    NumberFormat.getCurrencyInstance().format(it)
}

Terakhir, tarif pajak adalah nilai hardcode sebesar 0,08 (8%).

private val taxRate = 0.08

Ada enam metode di OrderViewModel yang perlu Anda terapkan.

setEntree(), setSide(), dan setAccompaniment()

Semua metode ini masing-masing harus berfungsi dengan cara yang sama untuk hidangan utama, pendamping, dan tambahan. Sebagai contoh, setEntree() harus melakukan hal berikut:

  1. Jika _entree bukan null (yaitu pengguna sudah memilih hidangan utama, tetapi mengubah pilihannya), setel previousEntreePrice ke harga current _entree.
  2. Jika _subtotal bukan null, kurangi subtotal dengan previousEntreePrice.
  3. Perbarui nilai _entree ke hidangan utama yang diteruskan ke dalam fungsi (akses MenuItem menggunakan menuItems).
  4. Panggil updateSubtotal(), yang meneruskan harga hidangan utama yang baru dipilih.

Logika untuk setSide() dan setAccompaniment() identik dengan implementasi untuk setEntree().

updateSubtotal()

updateSubtotal() dipanggil dengan argumen untuk harga baru yang harus ditambahkan ke subtotal. Metode ini harus melakukan tiga hal:

  1. Jika _subtotal bukan null, tambahkan itemPrice ke _subtotal.
  2. Sebaliknya, jika _subtotal adalah null, setel _subtotal ke itemPrice.
  3. Setelah _subtotal ditetapkan (atau diupdate), panggil calculateTaxAndTotal() sehingga nilai tersebut diupdate untuk mencerminkan subtotal baru.

calculateTaxAndTotal()

calculateTaxAndTotal() harus mengupdate variabel untuk pajak dan total berdasarkan subtotal. Implementasikan metode berikut:

  1. Setel _tax sama dengan tarif pajak dikalikan dengan subtotal.
  2. Setel _total sama dengan subtotal ditambah pajak.

resetOrder()

resetOrder() akan dipanggil saat pengguna mengirimkan atau membatalkan pesanan. Anda ingin memastikan aplikasi tidak memiliki sisa data saat pengguna memulai pesanan baru.

Implementasikan resetOrder() dengan menetapkan semua variabel yang Anda ubah di OrderViewModel kembali ke nilai aslinya (baik 0,0 atau null).

Membuat variabel data binding

Implementasikan data binding di file tata letak. Buka file tata letak, dan tambahkan variabel data binding jenis OrderViewModel dan/atau class fragmen yang sesuai.

Anda harus menerapkan semua komentar TODO untuk menetapkan teks dan mengklik pemroses dalam empat file tata letak:

  1. fragment_entree_menu.xml
  2. fragment_side_menu.xml
  3. fragment_accompaniment_menu.xml
  4. fragment_checkout.xml

Setiap tugas tertentu dicantumkan dalam komentar TODO dalam file tata letak, tetapi langkah-langkahnya dirangkum di bawah ini.

  1. Di fragment_entree_menu.xml, di tag <data>, tambahkan variabel binding untuk EntreeMenuFragment. Untuk setiap tombol pilihan, Anda harus menyetel hidangan utama di ViewModel saat dipilih. Teks subtotal pada tampilan teks harus diupdate. Anda juga harus menetapkan atribut onClick untuk cancel_button dan next_button masing-masing untuk membatalkan pesanan atau membuka layar berikutnya.
  2. Lakukan hal yang sama di fragment_side_menu.xml, dengan menambahkan variabel binding untuk SideMenuFragment, kecuali untuk menyetel hidangan pendamping dalam model tampilan saat setiap tombol pilihan dipilih. Teks subtotal juga harus diperbarui dan Anda juga harus menyetel atribut onClick untuk tombol batal dan tombol berikutnya.
  3. Lakukan hal yang sama sekali lagi, tetapi di fragment_accompaniment_menu.xml, kali ini dengan variabel binding untuk AccompanimentMenuFragment, menetapkan hidangan tambahan saat setiap tombol pilihan dipilih. Sekali lagi, Anda juga harus menyetel atribut untuk teks subtotal, tombol batal, dan tombol berikutnya.
  4. Di fragment_checkout.xml, Anda harus menambahkan tag <data> agar dapat menentukan variabel binding. Dalam tag <data>, tambahkan dua variabel binding, satu untuk OrderViewModel, dan satu lagi untuk CheckoutFragment. Pada tampilan teks, Anda harus menyetel nama dan harga hidangan utama, hidangan pendamping, dan hidangan tambahan yang dipilih dari OrderViewModel. Anda juga harus menetapkan subtotal, pajak, dan total dari OrderViewModel. Kemudian, tetapkan onClickAttributes saat pesanan dikirimkan, dan saat pesanan dibatalkan, dengan menggunakan fungsi yang sesuai dari CheckoutFragment.

.

Menginisialisasi variabel data binding di fragmen

Lakukan inisialisasi variabel data binding dalam file fragmen yang terkait di dalam metode, onViewCreated().

  1. EntreeMenuFragment
  2. SideMenuFragment
  3. AccompanimentMenuFragment
  4. CheckoutFragment

Membuat grafik navigasi

Seperti yang Anda pelajari di Unit 3, grafik navigasi dihosting di FragmentContainerView, yang terdapat dalam aktivitas. Buka activity_main.xml dan ganti TODO dengan kode berikut untuk mendeklarasikan FragmentContainerView.

<androidx.fragment.app.FragmentContainerView
   android:id="@+id/nav_host_fragment"
   android:name="androidx.navigation.fragment.NavHostFragment"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:defaultNavHost="true"
   app:navGraph="@navigation/mobile_navigation"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintLeft_toLeftOf="parent"
   app:layout_constraintRight_toRightOf="parent"
   app:layout_constraintTop_toTopOf="parent" />

Pada grafik navigasi, mobile_navigation.xml ditemukan dalam paket res.navigation.

e3381215c35c1726.png

Ini adalah grafik navigasi untuk aplikasi. Namun, file tersebut saat ini kosong. Tugas Anda adalah menambahkan tujuan ke grafik navigasi dan membuat model navigasi berikut di antara layar.

  1. Navigasi dari StartOrderFragment ke EntreeMenuFragment
  2. Navigasi dari EntreeMenuFragment ke SideMenuFragment
  3. Navigasi dari SideMenuFragment ke AccompanimentMenuFragment
  4. Navigasi dari AccompanimentMenuFragment ke CheckoutFragment
  5. Navigasi dari CheckoutFragment ke StartOrderFragment
  6. Navigasi dari EntreeMenuFragment ke StartOrderFragment
  7. Navigasi dari SideMenuFragment ke StartOrderFragment
  8. Navigasi dari AccompanimentMenuFragment ke StartOrderFragment
  9. Tujuan Awal harus StartOrderFragment

Setelah menyetel grafik navigasi, Anda perlu melakukan navigasi di class fragmen. Implementasikan komentar TODO yang tersisa di fragmen, serta MainActivity.kt.

  1. Untuk metode goToNextScreen() di EntreeMenuFragment, SideMenuFragment, dan AccompanimentMenuFragment, buka layar berikutnya di aplikasi.
  2. Untuk metode cancelOrder() di EntreeMenuFragment, SideMenuFragment, AccompanimentMenuFragment, dan CheckoutFragment, terlebih dahulu panggil resetOrder() pada sharedViewModel, lalu buka StartOrderFragment.
  3. Di StartOrderFragment, implementasikan setOnClickListener() agar dapat menavigasi ke EntreeMenuFragment.
  4. Di CheckoutFragment, implementasikan metode submitOrder(). Panggil resetOrder() di sharedViewModel, lalu buka StartOrderFragment.
  5. Terakhir, di MainActivity.kt, setel navController ke navController dari NavHostFragment.

4. Menguji aplikasi

Project Lunch Tray berisi target "androidTest" dengan beberapa kasus pengujian: MenuContentTests, NavigationTests, dan OrderFunctionalityTests.

Menjalankan pengujian

Untuk menjalankan pengujian, Anda dapat melakukan salah satu dari hal berikut:

Untuk satu kasus pengujian, buka class kasus pengujian dan klik panah hijau di sebelah kiri deklarasi class. Kemudian Anda dapat memilih opsi Run dari menu. Tindakan ini akan menjalankan semua pengujian dalam kasus pengujian.

8ddcbafb8ec14f9b.png

Sering kali Anda hanya ingin menjalankan satu pengujian, misalnya, jika hanya ada satu pengujian yang gagal dan yang lainnya lulus pengujian. Anda dapat menjalankan satu pengujian seperti halnya seluruh kasus pengujian. Gunakan panah hijau dan pilih opsi Run.

335664b7fc8b4fb5.png

Jika memiliki beberapa kasus pengujian, Anda juga dapat menjalankan seluruh rangkaian pengujian. Sama seperti menjalankan aplikasi, Anda dapat menemukan opsi ini pada menu Run.

80312efedf6e4dd3.png

Perhatikan bahwa Android Studio akan menetapkan secara default ke target terakhir yang Anda jalankan (aplikasi, target pengujian, dll.), jadi jika menu masih menampilkan Run > Run ‘app', Anda dapat menjalankan target pengujian, dengan memilih Run > Run.

95aacc8f749dee8e.png

Kemudian, pilih target pengujian dari menu pop-up.

8b702efbd4d21d3d.png

5. Opsional: Berikan masukan Anda kepada kami

Kami ingin mendengar pendapat Anda tentang project ini. Ikuti survei singkat ini untuk memberikan masukan kepada kami - masukan Anda akan membantu memandu project mendatang dalam kursus ini.