Navigasi dan data sebelumnya

1. Sebelum memulai

Dalam codelab ini, Anda akan menyelesaikan penerapan aplikasi Cupcake lainnya, yang Anda mulai di codelab sebelumnya. Aplikasi Cupcake memiliki beberapa layar dan menampilkan alur pesanan untuk cupcake. Aplikasi yang sudah selesai harus memungkinkan pengguna dapat menjalankannya untuk:

  • Membuat pesanan cupcake
  • Menggunakan tombol Atas atau Kembali untuk membuka langkah urutan pesanan sebelumnya
  • Membatalkan pesanan
  • Mengirim pesanan ke aplikasi lain seperti aplikasi email

Di sepanjang proses, Anda akan mempelajari cara Android menangani tugas dan data sebelumnya untuk aplikasi. Ini akan memungkinkan Anda untuk memanipulasi data sebelumnya dalam skenario seperti membatalkan pesanan, yang membawa pengguna kembali ke layar pertama aplikasi (bukan layar sebelumnya dari alur pesanan).

Prasyarat

  • Dapat membuat dan menggunakan model tampilan bersama di seluruh fragmen dalam suatu aktivitas
  • Telah terbiasa menggunakan komponen Navigasi Jetpack
  • Telah menggunakan data binding dengan LiveData untuk menjaga UI tetap sinkron dengan model tampilan
  • Dapat membuat intent untuk memulai aktivitas baru

Yang akan Anda pelajari

  • Cara navigasi memengaruhi data sebelumnya dari aplikasi
  • Cara menerapkan perilaku data sebelumnya yang khusus

Yang akan Anda build

  • Aplikasi pemesanan cupcake yang memungkinkan pengguna mengirim pesanan ke aplikasi lain dan memungkinkan pembatalan pesanan

Yang Anda perlukan

  • Komputer yang dilengkapi Android Studio.
  • Kode untuk aplikasi Cupcake setelah menyelesaikan codelab sebelumnya

2. Ringkasan aplikasi awal

Codelab ini menggunakan aplikasi Cupcake dari codelab sebelumnya. Anda dapat menggunakan kode dari penyelesaian codelab sebelumnya atau mendownload kode awal dari GitHub.

Mendownload kode awal untuk codelab ini

Jika Anda mendownload kode awal dari GitHub, perhatikan bahwa nama folder project adalah android-basics-kotlin-cupcake-app-viewmodel. Pilih folder ini saat Anda membuka project di Android Studio.

Untuk mendapatkan kode codelab ini dan membukanya di Android Studio, lakukan hal berikut.

Mendapatkan kode

  1. Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
  2. Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.

5b0a76c50478a73f.png

  1. Di dialog, 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 an existing Android Studio project.

36cc44fcf0f89a1d.png

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.

21f3eec988dcfbe9.png

  1. Di dialog Import Project, 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 11c34fc5e516fb1c.png untuk membangun dan menjalankan aplikasi. Pastikan aplikasi dibangun seperti yang diharapkan.
  5. Cari file project di jendela alat Project untuk melihat cara aplikasi disiapkan.

Sekarang, jalankan aplikasi dan seharusnya akan terlihat seperti ini.

45844688c0dc69a2.png

Dalam codelab ini, Anda akan menyelesaikan implementasi tombol Naik di aplikasi terlebih dahulu, sehingga mengetuknya akan membawa pengguna ke langkah sebelumnya dalam alur pesanan.

fbdc1793f9fea6da.png

Selanjutnya, Anda akan menambahkan tombol Cancel sehingga pengguna dapat membatalkan pesanan jika mereka berubah pikiran selama proses pemesanan.

d66fdafeac1b0dcf.gif

Anda kemudian akan memperluas aplikasi sehingga mengetuk Send Order to Another App akan membagikan pesanan ke aplikasi lain. Lalu, pesanan bisa saja dikirim ke toko cupcake melalui email.

170d76b64ce78f56.png

Mari selami dan selesaikan aplikasi Cupcake!

3. Mengimplementasikan perilaku tombol Atas

Di aplikasi Cupcake, panel aplikasi menampilkan panah untuk kembali ke layar sebelumnya. Panah ini dikenal sebagai tombol Naik, dan telah Anda pelajari di codelab sebelumnya. Tombol Naik saat ini tidak melakukan apa pun, jadi perbaiki bug navigasi ini di aplikasi terlebih dahulu.

fbdc1793f9fea6da.png

  1. Di MainActivity, Anda seharusnya sudah memiliki kode untuk menyiapkan panel aplikasi (juga dikenal sebagai panel tindakan) dengan pengontrol nav. Jadikan navController sebagai variabel class sehingga Anda dapat menggunakannya di metode lain.
class MainActivity : AppCompatActivity(R.layout.activity_main) {

    private lateinit var navController: NavController

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

        val navHostFragment = supportFragmentManager
                .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController

        setupActionBarWithNavController(navController)
    }
}
  1. Dalam class yang sama, tambahkan kode untuk mengganti fungsi onSupportNavigateUp(). Kode ini akan meminta navController menangani navigasi naik di aplikasi. Jika tidak, kembali ke implementasi superclass (dalam AppCompatActivity) untuk menangani tombol Naik.
override fun onSupportNavigateUp(): Boolean {
   return navController.navigateUp() || super.onSupportNavigateUp()
}
  1. Jalankan aplikasi. Tombol Naik kini seharusnya berfungsi dari FlavorFragment, PickupFragment, dan SummaryFragment. Saat Anda menavigasi langkah sebelumnya dalam alur pesanan, fragmen akan menampilkan rasa dan tanggal pengambilan yang tepat dari model tampilan.

4. Mempelajari tugas dan data sebelumnya

Sekarang Anda akan memperkenalkan tombol Cancel dalam alur pesanan aplikasi. Membatalkan pesanan kapan saja dalam proses pesanan akan mengembalikan pengguna ke StartFragment. Untuk menangani perilaku ini, Anda akan mempelajari tugas dan data sebelumnya di Android.

Tugas

Aktivitas di Android ada dalam tugas. Saat Anda membuka aplikasi untuk pertama kalinya dari ikon peluncur, Android akan membuat tugas baru dengan aktivitas utama Anda. Tugas adalah koleksi aktivitas yang berinteraksi dengan pengguna saat melakukan pekerjaan tertentu (misalnya memeriksa email, membuat pesanan kue, mengambil foto).

Aktivitas disusun dalam stack, yang dikenal sebagai data sebelumnya, tempat setiap aktivitas baru yang dikunjungi pengguna didorong ke data sebelumnya untuk tugas. Anda dapat menganggapnya sebagai tumpukan panekuk, yang membuat setiap panekuk baru ditambahkan di atas tumpukan. Aktivitas di bagian atas tumpukan adalah aktivitas saat ini yang berinteraksi dengan pengguna. Aktivitas di bawahnya pada tumpukan telah ditempatkan di latar belakang dan telah dihentikan.

517054e483795b46.png

Data sebelumnya berguna saat pengguna ingin menavigasi mundur. Android dapat menghapus aktivitas saat ini dari bagian atas stack, menghancurkannya, dan memulai aktivitas di bawahnya lagi. Ini dikenal sebagai memunculkan aktivitas dari tumpukan, dan membawa aktivitas sebelumnya ke latar depan untuk diajak berinteraksi oleh pengguna. Jika pengguna ingin kembali beberapa kali, Android akan terus memunculkan aktivitas di bagian teratas tumpukan hingga Anda mencapai bagian bawah tumpukan. Jika tidak ada lagi aktivitas dalam data sebelumnya, pengguna akan diarahkan kembali ke layar peluncur perangkat (atau ke aplikasi yang meluncurkannya).

Mari kita lihat versi aplikasi Words yang Anda implementasikan dengan 2 aktivitas: MainActivity dan DetailActivity.

Saat pertama kali Anda meluncurkan aplikasi, MainActivity terbuka dan ditambahkan ke data sebelumnya dari tugas.

4bc8f5aff4d5ee7f.png

Saat Anda mengklik sebuah huruf, DetailActivity akan diluncurkan dan didorong ke data sebelumnya. Ini berarti DetailActivity telah dibuat, dimulai, dan dilanjutkan sehingga pengguna dapat berinteraksi dengannya. MainActivity ditempatkan di latar belakang, dan ditampilkan dengan warna latar belakang abu-abu dalam diagram.

80f7c594ae844b84.png

Jika Anda mengetuk tombol Kembali, DetailActivity dikeluarkan dari data sebelumnya dan instance DetailActivity akan dihancurkan dan selesai.

80f532af817191a4.png

Kemudian, item berikutnya di bagian atas data sebelumnya (MainActivity) dibawa ke latar depan.

85004712d2fbcdc1.png

Dengan cara yang sama seperti data sebelumnya dapat melacak aktivitas yang telah dibuka oleh pengguna, data sebelumnya juga dapat melacak tujuan fragmen yang telah dikunjungi pengguna dengan bantuan komponen Navigasi Jetpack.

fe417ac5cbca4ce7.png

Library Navigasi memungkinkan Anda memunculkan tujuan fragmen dari data sebelumnya setiap kali pengguna menekan tombol Kembali. Perilaku default ini tersedia secara cuma-cuma, tanpa Anda perlu menerapkannya. Anda hanya perlu menulis kode jika memerlukan perilaku data sebelumnya yang khusus, yang akan Anda lakukan untuk aplikasi Cupcake.

Perilaku default aplikasi Cupcake

Mari kita lihat cara kerja data sebelumnya di aplikasi Cupcake. Hanya ada satu aktivitas di aplikasi, tetapi ada beberapa tujuan fragmen yang dinavigasi oleh pengguna. Oleh karena itu, tombol Kembali diinginkan untuk kembali ke tujuan fragmen sebelumnya setiap kali diketuk.

Saat pertama kali membuka aplikasi, tujuan StartFragment akan ditampilkan. Tujuan tersebut akan didorong ke atas tumpukan.

cf0e80b4907d80dd.png

Setelah memilih jumlah cupcake yang akan dipesan, Anda akan diarahkan ke FlavorFragment, yang didorong ke data sebelumnya.

39081dcc3e537e1e.png

Saat memilih rasa dan mengetuk Next, Anda akan diarahkan ke PickupFragment, yang akan didorong ke data sebelumnya.

37dca487200f8f73.png

Dan terakhir, setelah Anda memilih tanggal pengambilan dan mengetuk Next, Anda akan diarahkan ke SummaryFragment, yang akan ditambahkan ke bagian atas data sebelumnya.

d67689affdfae0dd.png

Dari SummaryFragment, misalnya Anda mengetuk tombol Kembali atau Naik. SummaryFragment muncul dari tumpukan dan dihancurkan.

215b93fd65754017.png

PickupFragment kini berada di atas data sebelumnya dan ditampilkan kepada pengguna.

37dca487200f8f73.png

Ketuk tombol Kembali atau Atas lagi. PickupFragment dikeluarkan dari tumpukan, lalu FlavorFragment ditampilkan.

Ketuk tombol Kembali atau Atas lagi. FlavorFragment muncul dari tumpukan, lalu StartFragment ditampilkan.

Saat Anda menavigasi mundur ke langkah sebelumnya dalam alur pesanan, hanya satu tujuan yang muncul pada satu waktu. Namun di tugas berikutnya, Anda akan menambahkan fitur pembatalan pesanan ke aplikasi. Tindakan ini mungkin mengharuskan Anda memunculkan beberapa tujuan di data sebelumnya sekaligus untuk mengembalikan pengguna ke StartFragment untuk memulai pesanan baru.

e3dae0f492450207.png

Mengubah data sebelumnya di aplikasi Cupcake

Ubah file class dan tata letak FlavorFragment, PickupFragment, dan SummaryFragment untuk menawarkan tombol untuk membatalkan pesanan kepada pengguna.

Menambahkan tindakan navigasi

Pertama-tama, tambahkan tindakan navigasi ke grafik navigasi di aplikasi Anda, sehingga memungkinkan pengguna untuk kembali ke StartFragment dari tujuan berikutnya.

  1. Buka Navigation Editor dengan membuka file res > navigation > nav_graph.xm dan memilih tampilan Design.
  2. Saat ini, ada tindakan dari startFragment ke flavorFragment, tindakan dari flavorFragment ke pickupFragment, dan tindakan dari pickupFragment ke summaryFragment.
  3. Klik dan tarik untuk membuat tindakan navigasi baru dari summaryFragment ke startFragment. Anda dapat melihat petunjuk ini jika ingin mengingat kembali cara menghubungkan tujuan di grafik navigasi.
  4. Dari pickupFragment, klik dan tarik untuk membuat tindakan baru ke startFragment.
  5. Dari flavorFragment, klik dan tarik untuk membuat tindakan baru ke startFragment.
  6. Setelah selesai, grafik navigasi akan terlihat seperti berikut.

dcbd27a08d24cfa0.png

Dengan perubahan ini, pengguna dapat berpindah-pindah dari salah satu fragmen terbaru dalam alur pesanan untuk kembali ke awal alur pesanan. Sekarang Anda memerlukan kode yang benar-benar dapat diakses dengan tindakan tersebut. Tempat yang sesuai adalah saat tombol Cancel diketuk.

Menambahkan tombol Cancel ke tata letak

Pertama-tama, tambahkan tombol Cancel ke file tata letak untuk semua fragmen kecuali StartFragment. Tidak perlu membatalkan pesanan jika Anda sudah berada di layar pertama alur pesanan.

  1. Buka file tata letak fragment_flavor.xml.
  2. Gunakan tampilan Split untuk mengedit XML secara langsung dan melihat pratinjau secara berdampingan.
  3. Tambahkan tombol Cancel di antara tampilan teks subtotal dan tombol Next. Tetapkan ID resource @+id/cancel_button dengan teks untuk ditampilkan sebagai @string/cancel.

Tombol harus diposisikan secara horizontal di samping tombol Next sehingga muncul sebagai baris tombol. Untuk batasan vertikal, batasi bagian atas tombol Cancel di bagian atas tombol Next. Untuk batasan horizontal, batasi awal tombol Cancel ke penampung induk, dan batasi tepinya hingga awal tombol Next.

Selain itu, atur tombol Cancel dengan tinggi wrap_content dan lebar 0dp, sehingga dapat membagi lebar layar dengan tombol lainnya secara sama. Perhatikan bahwa tombol tidak akan terlihat di panel Preview hingga langkah berikutnya.

...

<TextView
    android:id="@+id/subtotal" ... />

<Button
    android:id="@+id/cancel_button"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="@string/cancel"
    app:layout_constraintEnd_toStartOf="@id/next_button"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/next_button" />

<Button
    android:id="@+id/next_button" ... />

...
  1. Pada fragment_flavor.xml, Anda juga perlu mengubah batasan awal tombol Next dari app:layout_constraintStart_toStartOf="parent" menjadi app:layout_constraintStart_toEndOf="@id/cancel_button". Tambahkan juga margin akhir pada tombol Cancel sehingga ada spasi kosong di antara kedua tombol tersebut. Sekarang tombol Cancel akan muncul di panel Preview di Android Studio.
...

<Button
    android:id="@+id/cancel_button"
    android:layout_marginEnd="@dimen/side_margin" ... />

<Button
    android:id="@+id/next_button"
    app:layout_constraintStart_toEndOf="@id/cancel_button"... />

...
  1. Dalam hal gaya visual, terapkan gaya Material Outlined Button (dengan atribut style="?attr/materialButtonOutlinedStyle") sehingga tombol Cancel tidak muncul secara mencolok dibandingkan dengan tombol Next, yang merupakan tindakan utama yang Anda inginkan agar menjadi fokus pengguna.
<Button
    android:id="@+id/cancel_button"
    style="?attr/materialButtonOutlinedStyle" ... />

Tombol dan peletakan posisi sekarang terlihat bagus!

1fb41763cc255c05.png

  1. Dengan cara yang sama, tambahkan tombol Cancel ke file tata letak fragment_pickup.xml.
...

<TextView
    android:id="@+id/subtotal" ... />

<Button
    android:id="@+id/cancel_button"
    style="?attr/materialButtonOutlinedStyle"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="@dimen/side_margin"
    android:text="@string/cancel"
    app:layout_constraintEnd_toStartOf="@id/next_button"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/next_button" />

<Button
    android:id="@+id/next_button" ... />

...
  1. Perbarui juga batasan awal pada tombol Next. Kemudian, tombol Cancel akan muncul di pratinjau.
<Button
    android:id="@+id/next_button"
    app:layout_constraintStart_toEndOf="@id/cancel_button" ... />
  1. Terapkan perubahan yang serupa pada file fragment_summary.xml, meskipun tata letak untuk fragmen ini sedikit berbeda. Anda akan menambahkan tombol Cancel di bawah tombol Send di vertikal induk LinearLayout dengan beberapa margin di antaranya.

741c0f034397795c.png

...

    <Button
        android:id="@+id/send_button" ... />

    <Button
        android:id="@+id/cancel_button"
        style="?attr/materialButtonOutlinedStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin_between_elements"
        android:text="@string/cancel" />

</LinearLayout>
  1. Jalankan dan uji aplikasi. Sekarang Anda akan melihat tombol Cancel muncul di tata letak untuk FlavorFragment, PickupFragment, dan SummaryFragment. Namun, mengetuk tombol tersebut belum menyebabkan apa pun. Siapkan pemroses klik untuk tombol-tombol tersebut di langkah berikutnya.

Menambahkan pemroses klik tombol Cancel

Dalam setiap class fragmen (kecuali StartFragment), tambahkan metode bantuan yang menangani saat tombol Cancel diklik.

  1. Tambahkan metode cancelOrder() ini ke FlavorFragment. Apabila ada opsi rasa, jika pengguna memutuskan untuk membatalkan urutannya, hapus model tampilan dengan memanggil sharedViewModel.resetOrder(). Lalu navigasikan kembali ke StartFragment menggunakan tindakan navigasi dengan ID R.id.action_flavorFragment_to_startFragment.
fun cancelOrder() {
    sharedViewModel.resetOrder()
    findNavController().navigate(R.id.action_flavorFragment_to_startFragment)
}

Jika melihat error yang terkait dengan ID resource tindakan, Anda mungkin perlu kembali ke file nav_graph.xml untuk memverifikasi bahwa tindakan navigasi juga disebut dengan nama yang sama (action_flavorFragment_to_startFragment).

  1. Gunakan binding pemroses untuk menyiapkan pemroses klik pada tombol Cancel di tata letak fragment_flavor.xml. Mengklik tombol ini akan mengaktifkan metode cancelOrder() yang baru saja Anda buat di class FragmentFlavor.
<Button
    android:id="@+id/cancel_button"
    android:onClick="@{() -> flavorFragment.cancelOrder()}" ... />
  1. Ulangi proses yang sama untuk PickupFragment. Menambahkan metode cancelOrder() ke class fragmen, yang mereset pesanan dan menavigasi dari PickupFragment ke StartFragment.
fun cancelOrder() {
    sharedViewModel.resetOrder()
    findNavController().navigate(R.id.action_pickupFragment_to_startFragment)
}
  1. Di fragment_pickup.xml, setel pemroses klik pada tombol Cancel untuk memanggil metode cancelOrder() saat diklik.
<Button
    android:id="@+id/cancel_button"
    android:onClick="@{() -> pickupFragment.cancelOrder()}" ... />
  1. Tambahkan kode serupa untuk tombol Cancel pada SummaryFragment, yang membawa pengguna kembali ke StartFragment. Anda mungkin perlu mengimpor androidx.navigation.fragment.findNavController jika tidak diimpor secara otomatis untuk Anda.
fun cancelOrder() {
    sharedViewModel.resetOrder()
    findNavController().navigate(R.id.action_summaryFragment_to_startFragment)
}
  1. Di fragment_summary.xml, panggil metode cancelOrder() SummaryFragment saat tombol Cancel diklik.
<Button
    android:id="@+id/cancel_button"
    android:onClick="@{() -> summaryFragment.cancelOrder()}" ... />
  1. Jalankan dan uji aplikasi untuk memverifikasi logika yang baru saja Anda tambahkan ke setiap fragmen. Saat membuat pesanan cupcake, ketuk tombol Cancel di FlavorFragment, PickupFragment, atau SummaryFragment akan mengembalikan Anda ke StartFragment. Saat membuat pesanan baru, Anda akan melihat bahwa informasi dari pesanan sebelumnya telah dihapus.

Ini sepertinya sudah berfungsi, tetapi sebenarnya ada bug dengan navigasi mundur, setelah Anda kembali ke StartFragment. Ikuti beberapa langkah berikutnya untuk mereproduksi bug.

  1. Ikuti alur pesanan untuk membuat pesanan cupcake baru sampai Anda mencapai layar ringkasan. Misalnya, Anda dapat memesan 12 cupcake, rasa cokelat, dan memilih tanggal pengambilan berikutnya.
  2. Lalu ketuk Cancel. Anda harus kembali ke StartFragment.
  3. Ini terlihat benar, tetapi jika Anda mengetuk tombol Kembali sistem, Anda akan kembali ke layar ringkasan pesanan dengan ringkasan pesanan untuk 0 cupcake dan tanpa rasa. Hal ini salah dan tidak boleh ditampilkan kepada pengguna.

1a9024cd58a0e643.png

Pengguna mungkin tidak ingin kembali melalui alur pesanan. Selain itu, semua data pesanan dalam model tampilan telah dihapus, sehingga informasi ini tidak berguna. Sebaliknya, mengetuk tombol Kembali dari StartFragment akan menutup aplikasi Cupcake.

Mari kita lihat seperti apa data sebelumnya saat ini, dan cara memperbaiki bug. Saat Anda membuat pesanan melalui layar ringkasan pesanan, setiap tujuan akan didorong ke data sebelumnya.

fc88100cdf1bdd1.png

Dari SummaryFragment, Anda membatalkan pesanan. Saat Anda menavigasi melalui tindakan dari SummaryFragment ke StartFragment, Android menambahkan instance StartFragment lain sebagai tujuan baru di data sebelumnya.

5616cb0028b63602.png

Itulah sebabnya saat Anda mengetuk tombol Kembali dari StartFragment, aplikasi akan menampilkan SummaryFragment lagi (dengan informasi pesanan kosong).

Untuk memperbaiki bug navigasi ini, pelajari tentang cara komponen Navigasi memungkinkan Anda mengeluarkan tujuan tambahan dari data sebelumnya saat bernavigasi menggunakan tindakan.

Memunculkan tujuan tambahan dari data sebelumnya

Dengan menyertakan atribut app:popUpTo pada tindakan navigasi dalam grafik navigasi, lebih dari satu tujuan dapat dimunculkan dari data sebelumnya hingga tujuan yang ditentukan tercapai. Jika Anda menentukan app:popUpTo="@id/startFragment", tujuan di data sebelumnya akan dikeluarkan hingga Anda mencapai StartFragment, yang akan tetap berada dalam tumpukan.

Jika Anda menambahkan perubahan ini ke kode dan menjalankan aplikasi, Anda akan menemukan bahwa saat membatalkan pesanan, Anda akan kembali ke StartFragment. Namun, saat ini, saat mengetuk tombol Kembali dari StartFragment, Anda akan melihat StartFragment lagi (bukan keluar dari aplikasi). Ini juga bukan perilaku yang diinginkan. Seperti yang disebutkan sebelumnya, karena Anda menavigasi ke StartFragment, Android sebenarnya menambahkan StartFragment sebagai tujuan baru di data sebelumnya, jadi sekarang Anda memiliki 2 instance StartFragment di data sebelumnya. Sehingga, Anda perlu mengetuk tombol Kembali dua kali untuk keluar dari aplikasi.

dd0fedc6e231e595.png

Untuk memperbaiki bug baru ini, minta semua tujuan dikeluarkan dari data sebelumnya hingga dan termasuk StartFragment. Lakukan hal ini dengan menentukan app:popUpTo="@id/startFragment"

dan app:popUpToInclusive="true" pada tindakan navigasi yang sesuai. Dengan begitu, Anda hanya akan memiliki satu instance baru StartFragment di data sebelumnya. Lalu, mengetuk tombol Kembali sekali dari StartFragment akan menutup aplikasi. Mari kita buat perubahan ini sekarang.

cf0e80b4907d80dd.png

Mengubah tindakan navigasi

  1. Buka Navigation Editor dengan membuka file res > navigation > nav_graph.xml.
  2. Pilih tindakan dari summaryFragment ke startFragment, sehingga akan disorot dengan warna biru.
  3. Perluas Attributes di sebelah kanan (jika belum dibuka). Cari Pop Behavior dalam daftar atribut yang dapat Anda ubah.

8c87589f9cc4d176.png

  1. Dari opsi dropdown, tetapkan popUpTo menjadi startFragment. Ini berarti semua tujuan di data sebelumnya akan dimunculkan (dimulai dari atas tumpukan dan bergerak ke bawah), hingga startFragment.

a9a17493ed6bc27f.png

  1. Kemudian klik kotak centang popUpToInclusive hingga kotak ini menunjukkan tanda centang dan label true. Hal ini menunjukkan bahwa Anda ingin memunculkan tujuan hingga dan mencakup instance startFragment yang sudah ada di data sebelumnya. Kemudian, Anda tidak akan memiliki dua instance startFragment di data sebelumnya.

4a403838a62ff487.png

  1. Ulangi perubahan ini untuk tindakan yang menghubungkan pickupFragment ke startFragment.

4a403838a62ff487.png

  1. Ulangi untuk tindakan yang menghubungkan flavorFragment ke startFragment.
  2. Setelah selesai, pastikan Anda telah melakukan perubahan yang tepat pada aplikasi Anda dengan melihat tampilan Code file grafik navigasi.
<navigation
    android:id="@+id/nav_graph" ...>
    <fragment
        android:id="@+id/startFragment" ...>
        ...
    </fragment>
    <fragment
        android:id="@+id/flavorFragment" ...>
        ...
        <action
            android:id="@+id/action_flavorFragment_to_startFragment"
            app:destination="@id/startFragment"
            app:popUpTo="@id/startFragment"
            app:popUpToInclusive="true" />
    </fragment>
    <fragment
        android:id="@+id/pickupFragment" ...>
        ...
        <action
            android:id="@+id/action_pickupFragment_to_startFragment"
            app:destination="@id/startFragment"
            app:popUpTo="@id/startFragment"
            app:popUpToInclusive="true" />
    </fragment>
    <fragment
        android:id="@+id/summaryFragment" ...>
        <action
            android:id="@+id/action_summaryFragment_to_startFragment"
            app:destination="@id/startFragment"
            app:popUpTo="@id/startFragment"
            app:popUpToInclusive="true" />
    </fragment>
</navigation>

Perhatikan bahwa untuk masing-masing dari 3 tindakan (action_flavorFragment_to_startFragment, action_pickupFragment_to_startFragment, dan action_summaryFragment_to_startFragment), harus ada atribut yang baru ditambahkan app:popUpTo="@id/startFragment" dan app:popUpToInclusive="true".

  1. Sekarang, jalankan aplikasi. Periksa alur pesanan dan ketuk Cancel. Saat Anda kembali ke StartFragment, ketuk tombol Kembali (hanya sekali!) dan Anda seharusnya keluar dari aplikasi.

Sebagai ringkasan dari apa yang terjadi, saat Anda membatalkan pesanan dan menuju kembali ke layar pertama aplikasi, semua tujuan fragmen dalam data sebelumnya muncul dari tumpukan, termasuk instance pertama StartFragment. Setelah menyelesaikan tindakan navigasi, StartFragment ditambahkan sebagai tujuan baru di data sebelumnya. Mengetuk Kembali dari sana akan memunculkan StartFragment dari tumpukan, tanpa meninggalkan fragmen lagi di data sebelumnya. Karena itu, Android menyelesaikan aktivitas dan pengguna keluar dari aplikasi.

Berikut adalah tampilan aplikasi: 2e0599d9b55401f1.png

5. Mengirim pesanan

Aplikasi ini tampak mengagumkan sejauh ini! Tapi ada satu bagian yang hilang. Saat Anda mengetuk tombol kirim pesanan di SummaryFragment, pesan Toast masih akan muncul.

90ed727c7b812fd6.png

Pengguna akan merasakan pengalaman yang lebih berguna jika pesanan dapat dikirim dari aplikasi. Manfaatkan hal-hal yang telah Anda pelajari di codelab sebelumnya tentang penggunaan intent implisit untuk berbagi informasi dari aplikasi Anda ke aplikasi lain. Dengan begitu, pengguna dapat berbagi informasi pesanan cupcake dengan aplikasi email di perangkat, sehingga pesanan dapat dikirim ke toko cupcake melalui email.

170d76b64ce78f56.png

Untuk menerapkan fitur ini, lihat bagaimana subjek email dan isi email disusun dalam screenshot di atas.

Anda akan menggunakan string ini yang sudah ada dalam file strings.xml.

<string name="new_cupcake_order">New Cupcake Order</string>
<string name="order_details">Quantity: %1$s cupcakes \n Flavor: %2$s \nPickup date: %3$s \n Total: %4$s \n\n Thank you!</string>

order_details adalah resource string dengan 4 argumen format berbeda di dalamnya, yang merupakan placeholder untuk jumlah cupcake yang sebenarnya, rasa yang diinginkan, tanggal pengambilan yang diinginkan, dan harga total. Argumen diberi nomor dari 1 sampai 4 dengan sintaksis %1 sampai %4. Jenis argumen juga ditentukan ($s berarti string yang diharapkan ada di sini).

Dalam kode Kotlin, Anda dapat memanggil getString() di R.string.order_details diikuti dengan 4 argumen (urutan sifatnya penting!). Sebagai contoh, memanggil getString(R.string.order_details, "12", "Chocolate", "Sat Dec 12", "$24.00") akan membuat string berikut, yang persis seperti isi email yang Anda inginkan.

Quantity: 12 cupcakes
Flavor: Chocolate
Pickup date: Sat Dec 12
Total: $24.00

Thank you!
  1. Dalam SummaryFragment.kt, ubah metode sendOrder(). Hapus pesan Toast yang ada.
fun sendOrder() {

}
  1. Dalam metode sendOrder(), buat teks ringkasan pesanan. Buat string order_details berformat dengan mendapatkan kuantitas, rasa, tanggal, dan harga pesanan dari model tampilan bersama.
val orderSummary = getString(
    R.string.order_details,
    sharedViewModel.quantity.value.toString(),
    sharedViewModel.flavor.value.toString(),
    sharedViewModel.date.value.toString(),
    sharedViewModel.price.value.toString()
)
  1. Masih dalam metode sendOrder(), buat intent implisit untuk membagikan pesanan ke aplikasi lain. Lihat dokumentasi untuk mengetahui cara membuat intent email. Tentukan Intent.ACTION_SEND untuk tindakan intent, tetapkan jenis ke "text/plain" dan sertakan tambahan intent untuk subjek email (Intent.EXTRA_SUBJECT) dan isi email (Intent.EXTRA_TEXT). Impor android.content.Intent jika diperlukan.
val intent = Intent(Intent.ACTION_SEND)
    .setType("text/plain")
    .putExtra(Intent.EXTRA_SUBJECT, getString(R.string.new_cupcake_order))
    .putExtra(Intent.EXTRA_TEXT, orderSummary)

Sebagai tips bonus, jika Anda menyesuaikan aplikasi ini dengan kasus penggunaan Anda sendiri, Anda dapat mengisi penerima email terlebih dahulu sebagai alamat email toko cupcake. Dalam intent, Anda akan menentukan penerima email dengan intent tambahan Intent.EXTRA_EMAIL.

  1. Karena ini adalah intent implisit, Anda tidak perlu mengetahui terlebih dahulu komponen atau aplikasi spesifik yang akan menangani intent ini. Pengguna akan memutuskan aplikasi mana yang ingin mereka gunakan untuk memenuhi intent tersebut. Namun, sebelum meluncurkan aktivitas dengan intent ini, periksa untuk melihat apakah ada aplikasi yang bahkan dapat menanganinya. Pemeriksaan ini akan mencegah aplikasi Cupcake menjadi error jika tidak ada aplikasi yang menangani intent tersebut, sehingga kode Anda menjadi lebih aman.
if (activity?.packageManager?.resolveActivity(intent, 0) != null) {
    startActivity(intent)
}

Lakukan pemeriksaan ini dengan mengakses PackageManager, yang berisi informasi tentang paket aplikasi yang diinstal di perangkat. PackageManager dapat diakses melalui activity fragmen, selama activity dan packageManager bukan null. Panggil metode resolveActivity() PackageManager dengan intent yang Anda buat. Jika hasilnya bukan null, maka aman untuk memanggil startActivity() dengan intent Anda.

  1. Jalankan aplikasi Anda untuk menguji kode. Buat pesanan cupcake dan ketuk Send Order to Another App. Saat dialog berbagi muncul, Anda dapat memilih aplikasi Gmail, namun jangan ragu untuk memilih aplikasi lain jika mau. Jika memilih aplikasi Gmail, Anda mungkin perlu menyiapkan akun di perangkat jika belum melakukannya (misalnya, jika menggunakan emulator). Jika Anda tidak melihat pesanan cupcake terbaru muncul di isi email, Anda mungkin harus membuang draf email saat ini terlebih dahulu.

170d76b64ce78f56.png

Dalam menguji skenario yang berbeda, Anda mungkin menemukan bug jika Anda hanya memiliki 1 cupcake. Ringkasan pesanan menyebutkan 1 cupcakes, namun ini salah secara tata bahasa dalam bahasa Inggris.

ef046a100381bb07.png

Sebagai gantinya, seharusnya tertulis 1 cupcake (bukan jamak). Jika Anda ingin memilih apakah kata cupcake atau cupcakes digunakan berdasarkan nilai kuantitas, Anda dapat menggunakan sesuatu yang disebut string kuantitas di Android. Dengan mendeklarasikan resource plurals, Anda dapat menentukan resource string yang berbeda untuk digunakan berdasarkan berapa kuantitasnya, misalnya dalam kasus tunggal atau jamak.

  1. Tambahkan resource jamak cupcakes dalam file strings.xml Anda.
<plurals name="cupcakes">
    <item quantity="one">%d cupcake</item>
    <item quantity="other">%d cupcakes</item>
</plurals>

Dalam kasus tunggal (quantity="one"), string tunggal akan digunakan. Dalam semua kasus lainnya (quantity="other"), string jamak akan digunakan. Perhatikan bahwa sebagai ganti %s yang mengharapkan argumen string, %d mengharapkan argumen bilangan bulat, yang akan Anda teruskan saat memformat string.

Di kode Kotlin Anda, memanggil:

getQuantityString(R.plurals.cupcakes, 1, 1) menampilkan string 1 cupcake

getQuantityString(R.plurals.cupcakes, 6, 6) menampilkan string 6 cupcakes

getQuantityString(R.plurals.cupcakes, 0, 0) menampilkan string 0 cupcakes

  1. Sebelum membuka kode Kotlin, perbarui resource string order_details di strings.xml sehingga versi jamak dari cupcakes tidak lagi di-hardcode ke string tersebut.
<string name="order_details">Quantity: %1$s \n Flavor: %2$s \nPickup date: %3$s \n
        Total: %4$s \n\n Thank you!</string>
  1. Di class SummaryFragment, perbarui metode sendOrder() untuk menggunakan string kuantitas baru. Cara paling mudah adalah terlebih dahulu memahami kuantitas dari model tampilan dan menyimpannya dalam variabel. Karena quantity pada model tampilan adalah jenis LiveData<Int>, bisa saja sharedViewModel.quantity.value adalah null. Jika null, gunakan 0 sebagai nilai default untuk numberOfCupcakes.

Tambahkan ini sebagai baris kode pertama dalam metode sendOrder() Anda.

val numberOfCupcakes = sharedViewModel.quantity.value ?: 0

Operator elvis (?:) berarti bahwa jika ekspresi di sebelah kiri bukan null, maka gunakanlah. Jika ekspresi di sebelah kiri adalah null, gunakan ekspresi di sebelah kanan operator elvis (yang dalam kasus ini adalah 0).

  1. Lalu format string order_details seperti yang Anda lakukan sebelumnya. Alih-alih meneruskan numberOfCupcakes sebagai argumen kuantitas secara langsung, buat string cupcake berformat dengan resources.getQuantityString(R.plurals.cupcakes, numberOfCupcakes, numberOfCupcakes).

Metode sendOrder() lengkap akan terlihat seperti berikut:

fun sendOrder() {
    val numberOfCupcakes = sharedViewModel.quantity.value ?: 0
    val orderSummary = getString(
        R.string.order_details,
        resources.getQuantityString(R.plurals.cupcakes, numberOfCupcakes, numberOfCupcakes),
        sharedViewModel.flavor.value.toString(),
        sharedViewModel.date.value.toString(),
        sharedViewModel.price.value.toString()
    )

    val intent = Intent(Intent.ACTION_SEND)
        .setType("text/plain")
        .putExtra(Intent.EXTRA_SUBJECT, getString(R.string.new_cupcake_order))
        .putExtra(Intent.EXTRA_TEXT, orderSummary)

    if (activity?.packageManager?.resolveActivity(intent, 0) != null) {
        startActivity(intent)
    }
}
  1. Jalankan dan uji kode Anda. Pastikan ringkasan pesanan di isi email menampilkan 1 cupcake vs. 6 cupcake atau 12 cupcake.

Dengan itu, Anda telah menyelesaikan semua fungsi aplikasi Cupcake! Selamat! Ini memang aplikasi yang menantang, dan Anda telah membuat kemajuan luar biasa dalam perjalanan Anda untuk menjadi developer Android! Anda telah berhasil menggabungkan semua konsep yang telah Anda pelajari sejauh ini, sambil mengambil beberapa tips mengatasi masalah baru selama proses ini.

Langkah terakhir

Sekarang luangkan waktu untuk membersihkan kode Anda, yang merupakan praktik coding yang baik yang telah Anda pelajari dari codelab sebelumnya.

  • Mengoptimalkan impor
  • Memformat ulang file
  • Menghapus kode yang tidak digunakan atau dijadikan sebagai komentar
  • Menambahkan komentar dalam kode jika diperlukan

Untuk membuat aplikasi lebih mudah diakses, uji aplikasi dengan Talkback yang diaktifkan untuk memastikan kelancaran pengalaman pengguna. Respons lisan harus membantu menyampaikan tujuan setiap elemen di layar, jika sesuai. Selain itu, pastikan bahwa semua elemen aplikasi dapat dinavigasi menggunakan gestur geser.

Periksa kembali bahwa semua kasus penggunaan yang diimplementasikan berfungsi seperti yang diharapkan dalam aplikasi final. Contoh:

  • Data akan tetap dipertahankan meskipun terjadi rotasi perangkat (berkat model tampilan).
  • Jika Anda mengetuk tombol Atas atau Kembali, informasi pesanan seharusnya tetap muncul dengan benar di FlavorFragment dan PickupFragment.
  • Mengirim pesanan ke aplikasi lain harus membagikan detail pesanan yang benar.
  • Membatalkan pesanan akan menghapus semua informasi yang ada dalam pesanan.

Jika Anda menemukan bug, lanjutkan dan perbaiki bug tersebut.

Memeriksa ulang tugas Anda adalah perilaku yang cermat!

6. Kode solusi

Kode solusi untuk codelab ini ada dalam project yang ditampilkan di bawah.

Untuk mendapatkan kode codelab ini dan membukanya di Android Studio, lakukan hal berikut.

Mendapatkan kode

  1. Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
  2. Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.

5b0a76c50478a73f.png

  1. Di dialog, 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 an existing Android Studio project.

36cc44fcf0f89a1d.png

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.

21f3eec988dcfbe9.png

  1. Di dialog Import Project, 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 11c34fc5e516fb1c.png untuk membangun dan menjalankan aplikasi. Pastikan aplikasi dibangun seperti yang diharapkan.
  5. Cari file project di jendela alat Project untuk melihat cara aplikasi disiapkan.

7. Ringkasan

  • Android menyimpan data sebelumnya dari semua tujuan yang Anda kunjungi, dengan setiap tujuan baru dimasukkan ke tumpukan.
  • Dengan mengetuk tombol Atas atau Kembali, Anda dapat mengeluarkan tujuan dari data sebelumnya.
  • Menggunakan komponen Navigasi Jetpack membantu Anda mendorong dan memunculkan tujuan fragmen di data sebelumnya, sehingga perilaku tombol Kembali default tersedia secara cuma-cuma.
  • Tentukan atribut app:popUpTo pada tindakan di grafik navigasi, untuk memunculkan tujuan di data sebelumnya hingga yang ditentukan dalam nilai atribut.
  • Tentukan app:popUpToInclusive="true" pada tindakan jika tujuan yang ditentukan dalam app:popUpTo juga akan dikeluarkan dari data sebelumnya.
  • Anda dapat membuat intent implisit untuk membagikan konten ke aplikasi email, menggunakan Intent.ACTION_SEND dan mengisi tambahan intent seperti Intent.EXTRA_EMAIL, Intent.EXTRA_SUBJECT, dan Intent.EXTRA_TEXT.
  • Gunakan resource plurals jika Anda ingin menggunakan resource string yang berbeda berdasarkan kuantitas, seperti kasus tunggal atau jamak.

8. Mempelajari lebih lanjut

9. Berlatih sendiri

Perluas aplikasi Cupcake dengan variasi Anda sendiri pada alur pesanan cupcake. Contoh:

  • Tawarkan rasa spesial yang disertai beberapa kondisi tertentu, seperti tidak tersedia untuk pengambilan pada hari yang sama.
  • Minta nama pengguna untuk pesanan cupcake.
  • Izinkan pengguna memilih beberapa rasa cupcake untuk pesanan mereka jika jumlahnya lebih dari 1 cupcake.

Area aplikasi mana yang perlu Anda perbarui untuk mengakomodasi fungsi baru ini?

Periksa hasil kerja Anda:

Aplikasi yang sudah selesai akan berjalan tanpa error.

10. Tugas Tantangan

Gunakan apa yang telah Anda pelajari dari mem-build aplikasi Cupcake guna mem-build aplikasi untuk kasus penggunaan Anda sendiri. Aplikasi ini dapat menjadi aplikasi untuk memesan pizza, roti isi, atau apa pun yang Anda inginkan! Sebaiknya Anda membuat sketsa berbagai tujuan aplikasi sebelum mulai menerapkannya.

Untuk mendapatkan inspirasi dari ide desain lain, Anda juga dapat melihat aplikasi Shrine yang merupakan studi Material yang menunjukkan cara menggunakan tema dan komponen Material untuk merek Anda sendiri. Aplikasi Shrine jauh lebih kompleks daripada aplikasi Cupcake yang telah Anda bangun. Sebaiknya pikirkan tentang fitur kecil yang dapat Anda tangani terlebih dahulu daripada bertujuan untuk membangun aplikasi yang sangat menantang di awal. Bangun kepercayaan diri Anda dari waktu ke waktu dengan keberhasilan yang bertahap.

Setelah selesai membuat aplikasi sendiri, bagikan hasil karya Anda ke media sosial. Gunakan hashtag #LearningKotlin agar kita dapat melihatnya!