Membangun Aplikasi Android dengan View

1. Sebelum memulai

Pengantar

Sejauh ini, Anda telah mempelajari semua cara mem-build aplikasi Android dengan Compose. Sungguh suatu kemajuan yang baik. Compose adalah alat yang sangat canggih dan dapat menyederhanakan proses pengembangan. Namun, aplikasi Android tidak selalu dibuat dengan UI deklaratif. Compose adalah alat terbaru dalam histori Aplikasi Android. UI Android awalnya dibuat menggunakan View. Dengan demikian, kemungkinan besar Anda akan melihat View saat melanjutkan perjalanan sebagai developer Android. Dalam codelab ini, Anda akan mempelajari dasar-dasar cara membangun aplikasi Android sebelum Compose menggunakan XML, View, View Binding, dan Fragment.

Prasyarat:

  • Selesaikan kursus Dasar-Dasar Android di Compose hingga Unit 7.

Yang akan Anda butuhkan

  • Komputer yang memiliki akses internet dan Android Studio
  • Perangkat atau emulator
  • Kode awal untuk aplikasi Juice Tracker

Yang akan Anda build

Dalam codelab ini, Anda akan menyelesaikan aplikasi Juice Tracker. Aplikasi ini memungkinkan Anda memantau berbagai jus yang sedang tren dengan membuat daftar yang berisi item mendetail. Anda dapat menambahkan dan mengubah dan XML untuk menyelesaikan UI dan kode awal. Khususnya, Anda akan mem-build formulir entri untuk membuat jus baru, seperti UI dan logika atau navigasi terkait. Hasilnya adalah aplikasi dengan daftar kosong yang dapat Anda gunakan untuk menambahkan jus Anda sendiri.

d6dc43171ae62047.png 87b2ca7b49e814cb.png 2d630489477e216e.png

2. Mendapatkan kode awal

  1. Di Android Studio, buka folder basic-android-kotlin-compose-training-juice-tracker.
  2. Buka kode aplikasi Juice Tracker di Android Studio.

3. Membuat Tata Letak

Saat membangun aplikasi dengan Views, Anda harus membuat UI di dalam Tata Letak. Tata letak biasanya dideklarasikan menggunakan XML. File tata letak XML ini berada di direktori resource di bagian res > layout. Tata letak berisi komponen yang membentuk UI; komponen ini dikenal sebagai View. Sintaksis XML terdiri atas tag, elemen, dan atribut. Untuk mengetahui detail selengkapnya tentang sintaksis XML, lihat Membuat tata letak XML untuk codelab Android.

Di bagian ini, Anda akan membangun tata letak XML untuk dialog entri "Type of juice" seperti dalam gambar.

87b2ca7b49e814cb.png

  1. Buat Layout Resource File baru di direktori main > res > layout yang disebut fragment_entry_dialog.

Panel konteks panel project Android Studio terbuka dan berisi opsi untuk membuat file resource tata letak.

6adb279d6e74ab13.png

Tata letak fragment_entry_dialog.xml berisi komponen UI yang ditampilkan aplikasi kepada pengguna.

Perhatikan bahwa Elemen root adalah ConstraintLayout. Jenis tata letak ini adalah ViewGroup yang memungkinkan Anda memosisikan dan mengukur View dengan cara yang fleksibel menggunakan batasan. ViewGroup adalah jenis View yang berisi View lainnya, dan disebut View turunan. Langkah-langkah berikut membahas topik ini secara lebih detail, tetapi Anda dapat mempelajari lebih lanjut tentang ConstraintLayout dalam Membuat UI yang Responsif dengan ConstraintLayout.

  1. Setelah Anda membuat file, tentukan ruang nama aplikasi di ConstraintLayout.

fragment_entry_dialog.xml

<androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
  1. Tambahkan panduan berikut ke ConstraintLayout.

fragment_entry_dialog.xml

<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_left"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_middle"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_top"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="horizontal"
   app:layout_constraintGuide_begin="16dp" />

Guideline ini berfungsi sebagai padding untuk tampilan lain. Panduan ini membatasi teks header "Type of juice".

  1. Buat elemen TextView. TextView ini merepresentasikan judul fragmen detail.

110cad4ae809e600.png

  1. Tetapkan id header_title untuk TextView.
  2. Setel layout_width ke 0dp. Pada akhirnya, batasan tata letak akan menentukan lebar TextView ini. Oleh karena itu, menentukan lebar hanya akan menambahkan penghitungan yang tidak perlu selama menggambar UI; menentukan lebar 0dp akan menghindari penghitungan tambahan.
  3. Tetapkan atribut TextView text ke @string/juice_type.
  4. Tetapkan textAppearance ke @style/TextAppearance.MaterialComponents.Headline5.

fragment_entry_dialog.xml

<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" />

Terakhir, Anda harus menentukan batasan. Tidak seperti Guideline yang menggunakan dimensi sebagai batasan, panduannya sendiri membatasi TextView ini. Untuk mencapai hasil ini, Anda dapat mereferensikan ID Guideline yang akan Anda gunakan untuk membatasi tampilan.

  1. Batasi bagian atas header ke bagian bawah guideline_top.

fragment_entry_dialog.xml

<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
   app:layout_constraintTop_toBottomOf="@+id/guideline_top" />
  1. Batasi akhir ke awal dari guideline_middle dan awal ke awal dari guideline_left untuk menyelesaikan penempatan TextView. Perlu diingat bahwa cara Anda membatasi tampilan tertentu sepenuhnya bergantung pada tampilan UI yang Anda inginkan.

fragment_entry_dialog.xml

<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
   app:layout_constraintTop_toBottomOf="@+id/guideline_top"
   app:layout_constraintEnd_toStartOf="@+id/guideline_middle"
   app:layout_constraintStart_toStartOf="@+id/guideline_left" />

Coba Anda mem-build UI lainnya berdasarkan screenshot. Anda dapat menemukan file fragment_entry_dialog.xml yang sudah selesai dalam solusi tersebut.

4. Membuat Fragment dengan View

Di Compose, Anda akan mem-build tata letak secara deklaratif menggunakan Kotlin atau Java. Anda dapat mengakses "layar" yang berbeda dengan membuka Composable yang berbeda, biasanya dalam aktivitas yang sama. Saat mem-build aplikasi dengan View, Fragment yang menghosting tata letak XML akan menggantikan konsep "layar" Composable.

Di bagian ini, Anda akan membuat Fragment untuk menghosting tata letak fragment_entry_dialog dan memberikan data ke UI.

  1. Pada paket juicetracker, buat class baru bernama EntryDialogFragment.
  2. Buat EntryDialogFragment memperluas BottomSheetDialogFragment.

EntryDialogFragment.kt

import com.google.android.material.bottomsheet.BottomSheetDialogFragment

class EntryDialogFragment : BottomSheetDialogFragment() {
}

DialogFragment adalah Fragment yang menampilkan dialog mengambang. BottomSheetDialogFragment mewarisi dari class DialogFragment, tetapi menampilkan sheet dengan lebar layar yang disematkan ke bagian bawah layar. Pendekatan ini cocok dengan desain yang digambarkan sebelumnya.

  1. Build ulang project, yang akan menyebabkan file View Binding berdasarkan tata letak fragment_entry_dialog dibuat secara otomatis. View Binding memungkinkan Anda mengakses dan berinteraksi dengan View yang dideklarasikan oleh XML, Anda dapat membacanya lebih lanjut dalam dokumentasi View Binding.
  2. Di class EntryDialogFragment, implementasikan fungsi onCreateView(). Seperti namanya, fungsi ini akan membuat View untuk Fragment ini.

EntryDialogFragment.kt

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
): View? {
   return super.onCreateView(inflater, container, savedInstanceState)
}

Fungsi onCreateView() menampilkan View, tetapi saat ini, fungsi tersebut tidak menampilkan View yang berguna.

  1. Tampilkan View yang dihasilkan dengan meng-inflate FragmentEntryDialogViewBinding, bukan menampilkan super.onCreateView().

EntryDialogFragment.kt

import com.example.juicetracker.databinding.FragmentEntryDialogBinding

override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
): View? {
   return FragmentEntryDialogBinding.inflate(inflater, container, false).root
}
  1. Di luar fungsi onCreateView(), tetapi di dalam class EntryDialogFragment, buat instance EntryViewModel.
  2. Implementasikan fungsi onViewCreated().

Setelah meng-inflate View binding, Anda dapat mengakses dan mengubah View dalam tata letak. Metode onViewCreated() dipanggil setelah onCreateView() dalam siklus proses. Metode onViewCreated() adalah tempat yang direkomendasikan untuk mengakses dan mengubah View dalam tata letak.

  1. Buat instance view binding dengan memanggil metode bind() di FragmentEntryDialogBinding.

Pada tahap ini, kode Anda akan terlihat seperti contoh berikut:

EntryDialogFragment.kt

import androidx.fragment.app.viewModels
import com.example.juicetracker.ui.AppViewModelProvider
import com.example.juicetracker.ui.EntryViewModel

class EntryDialogFragment : BottomSheetDialogFragment() {

   private val entryViewModel by viewModels<EntryViewModel> { AppViewModelProvider.Factory }

   override fun onCreateView(
       inflater: LayoutInflater,
       container: ViewGroup?,
       savedInstanceState: Bundle?
   ): View {
       return FragmentEntryDialogBinding.inflate(inflater, container, false).root
   }

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
    }
}

Anda dapat mengakses dan menetapkan View melalui binding. Misalnya, Anda dapat menetapkan TextView melalui metode setText().

binding.name.setText("Apple juice")

UI dialog entri berfungsi sebagai tempat bagi pengguna untuk membuat item baru, tetapi Anda juga dapat menggunakannya untuk mengubah item yang ada. Oleh karena itu, Fragment harus mengambil item yang diklik. Komponen Navigasi memfasilitasi navigasi ke EntryDialogFragment dan mengambil item yang diklik.

EntryDialogFragment belum selesai, tetapi jangan khawatir. Untuk saat ini, lanjutkan ke bagian berikutnya untuk mempelajari lebih lanjut cara menggunakan Komponen Navigasi dalam aplikasi dengan View.

5. Mengubah Komponen Navigasi

Di bagian ini, Anda akan menggunakan komponen navigasi untuk meluncurkan dialog entri dan mengambil item, jika berlaku.

Compose memberi peluang untuk merender composable yang berbeda hanya dengan memanggilnya. Namun, Fragment bekerja secara berbeda. Komponen Navigasi mengoordinasikan "tujuan" Fragment sehingga memberikan cara mudah untuk berpindah antara Fragment yang berbeda dan View yang ada di dalamnya.

Menggunakan Komponen Navigasi untuk mengoordinasikan navigasi ke EntryDialogFragment.

  1. Buka file nav_graph.xml dan pastikan tab Design dipilih. 783cb5d7ff0ba127.png
  2. Klik ikon 93401bf098936c15.png untuk menambahkan tujuan baru.

d5410c90e408b973.png

  1. Pilih tujuan EntryDialogFragment. Tindakan ini akan mendeklarasikan entryDialogFragment dalam grafik navigasi sehingga dapat diakses oleh tindakan navigasi.

418feed425072ea4.png

Anda harus meluncurkan EntryDialogFragment dari TrackerFragment. Oleh karena itu, tindakan navigasi diperlukan untuk menyelesaikan tugas ini.

  1. Tarik kursor ke trackerFragment. Memilih titik abu-abu dan menarik garis ke entryDialogFragment. 85decb6fcddec713.png
  2. Tampilan desain nav_graph memungkinkan Anda mendeklarasikan argumen untuk suatu tujuan dengan memilih tujuan lalu mengklik ikon a0d73140a20e4348.png di samping menu dropdown Arguments. Gunakan fitur ini untuk menambahkan argumen itemId dari jenis Long ke entryDialogFragment; nilai defaultnya harus 0L.

555cf791f64f62b8.png

840105bd52f300f7.png

Perlu diketahui bahwa TrackerFragment memiliki daftar item Juice—jika Anda mengklik salah satu item ini, EntryDialogFragment akan diluncurkan.

  1. Membangun ulang project. Sekarang, argumen itemId dapat diakses di EntryDialogFragment.

6. Menyelesaikan Fragment

Berbekal data dari argumen navigasi, selesaikan dialog entri.

  1. Ambil navArgs() dalam metode onViewCreated() dari EntryDialogFragment.
  2. Ambil itemId dari navArgs().
  3. Terapkan saveButton untuk menyimpan jus baru/yang telah diubah menggunakan ViewModel.

Ingat kembali dari UI dialog entri bahwa nilai warna default adalah merah. Untuk saat ini, teruskan objek ini sebagai holder tempat.

Teruskan ID item dari argumen saat memanggil saveJuice().

EntryDialogFragment.kt

import androidx.navigation.fragment.navArgs
import com.example.juicetracker.data.JuiceColor

class EntryDialogFragment : BottomSheetDialogFragment() {

   //...
   var selectedColor: JuiceColor = JuiceColor.Red

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
        val args: EntryDialogFragmentArgs by navArgs()
        val juiceId = args.itemId

        binding.saveButton.setOnClickListener {
           entryViewModel.saveJuice(
               juiceId,
               binding.name.text.toString(),
               binding.description.text.toString(),
               selectedColor.name,
               binding.ratingBar.rating.toInt()
           )
        }
    }
}
  1. Setelah data disimpan, tutup dialog dengan metode dismiss().

EntryDialogFragment.kt

class EntryDialogFragment : BottomSheetDialogFragment() {

    //...
    var selectedColor: JuiceColor = JuiceColor.Red
    //...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
        val args: EntryDialogFragmentArgs by navArgs()
        binding.saveButton.setOnClickListener {
           entryViewModel.saveJuice(
               juiceId,
               binding.name.text.toString(),
               binding.description.text.toString(),
               selectedColor.name,
               binding.ratingBar.rating.toInt()
           )
           dismiss()
        }
    }
}

Perlu diingat bahwa kode di atas tidak menyelesaikan EntryDialogFragment. Anda tetap harus menerapkan sejumlah hal, seperti populasi kolom dengan data Juice yang ada (jika ada), pemilihan warna dari colorSpinner, implementasi cancelButton, dan masih banyak lagi. Namun, kode ini tidak unik untuk Fragment, dan Anda dapat menerapkan kode ini sendiri. Cobalah untuk menerapkan fungsi lainnya. Sebagai upaya terakhir, Anda dapat melihat kode solusi untuk codelab ini.

7. Meluncurkan dialog entri

Tugas terakhir adalah meluncurkan dialog entri menggunakan Komponen Navigasi. Dialog entri harus diluncurkan saat pengguna mengklik tombol tindakan mengambang (FAB). ID ini juga harus meluncurkan dan meneruskan ID yang sesuai saat pengguna mengklik item.

  1. Di onClickListener() untuk FAB, panggil navigate() pada pengontrol navigasi.

TrackerFragment.kt

import androidx.navigation.findNavController

//...

binding.fab.setOnClickListener { fabView ->
   fabView.findNavController().navigate(
   )
}

//...
  1. Pada fungsi navigasi, teruskan tindakan untuk keluar dari pelacak ke dialog entri.

TrackerFragment.kt

//...

binding.fab.setOnClickListener { fabView ->
   fabView.findNavController().navigate(
TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment()
   )
}

//...
  1. Ulangi tindakan ini dalam isi lambda untuk metode onEdit() di JuiceListAdapter, tetapi untuk kali ini, teruskan id dari Juice.

TrackerFragment.kt

//...

onEdit = { drink ->
   findNavController().navigate(
       TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment(drink.id)
   )
},

//...

8. Mendapatkan kode solusi

Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah git berikut:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git
$ cd basic-android-kotlin-compose-training-juice-tracker
$ git checkout views

Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.

Jika Anda ingin melihat kode solusi, lihat di GitHub.