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.
2. Mendapatkan kode awal
- Di Android Studio, buka folder
basic-android-kotlin-compose-training-juice-tracker
. - 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.
- Buat Layout Resource File baru di direktori main > res > layout yang disebut
fragment_entry_dialog
.
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.
- 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>
- 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".
- Buat elemen
TextView
.TextView
ini merepresentasikan judul fragmen detail.
- Tetapkan
id
header_title
untukTextView
. - Setel
layout_width
ke0dp
. Pada akhirnya, batasan tata letak akan menentukan lebarTextView
ini. Oleh karena itu, menentukan lebar hanya akan menambahkan penghitungan yang tidak perlu selama menggambar UI; menentukan lebar0dp
akan menghindari penghitungan tambahan. - Tetapkan atribut
TextView text
ke@string/juice_type
. - 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.
- 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" />
- Batasi akhir ke awal dari
guideline_middle
dan awal ke awal dariguideline_left
untuk menyelesaikan penempatanTextView
. 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.
- Pada paket
juicetracker
, buat class baru bernamaEntryDialogFragment
. - Buat
EntryDialogFragment
memperluasBottomSheetDialogFragment
.
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.
- 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 denganView
yang dideklarasikan oleh XML, Anda dapat membacanya lebih lanjut dalam dokumentasi View Binding. - Di class
EntryDialogFragment
, implementasikan fungsionCreateView()
. Seperti namanya, fungsi ini akan membuatView
untukFragment
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.
- Tampilkan
View
yang dihasilkan dengan meng-inflateFragmentEntryDialogViewBinding
, bukan menampilkansuper.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
}
- Di luar fungsi
onCreateView()
, tetapi di dalam classEntryDialogFragment
, buat instanceEntryViewModel
. - 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.
- Buat instance view binding dengan memanggil metode
bind()
diFragmentEntryDialogBinding
.
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
.
- Buka file
nav_graph.xml
dan pastikan tab Design dipilih. - Klik ikon untuk menambahkan tujuan baru.
- Pilih tujuan
EntryDialogFragment
. Tindakan ini akan mendeklarasikanentryDialogFragment
dalam grafik navigasi sehingga dapat diakses oleh tindakan navigasi.
Anda harus meluncurkan EntryDialogFragment
dari TrackerFragment
. Oleh karena itu, tindakan navigasi diperlukan untuk menyelesaikan tugas ini.
- Tarik kursor ke
trackerFragment
. Memilih titik abu-abu dan menarik garis keentryDialogFragment
. - Tampilan desain nav_graph memungkinkan Anda mendeklarasikan argumen untuk suatu tujuan dengan memilih tujuan lalu mengklik ikon di samping menu dropdown Arguments. Gunakan fitur ini untuk menambahkan argumen
itemId
dari jenisLong
keentryDialogFragment
; nilai defaultnya harus0L
.
Perlu diketahui bahwa TrackerFragment
memiliki daftar item Juice
—jika Anda mengklik salah satu item ini, EntryDialogFragment
akan diluncurkan.
- Membangun ulang project. Sekarang, argumen
itemId
dapat diakses diEntryDialogFragment
.
6. Menyelesaikan Fragment
Berbekal data dari argumen navigasi, selesaikan dialog entri.
- Ambil
navArgs()
dalam metodeonViewCreated()
dariEntryDialogFragment
. - Ambil
itemId
darinavArgs()
. - Terapkan
saveButton
untuk menyimpan jus baru/yang telah diubah menggunakanViewModel
.
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()
)
}
}
}
- 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.
- Di
onClickListener()
untuk FAB, panggilnavigate()
pada pengontrol navigasi.
TrackerFragment.kt
import androidx.navigation.findNavController
//...
binding.fab.setOnClickListener { fabView ->
fabView.findNavController().navigate(
)
}
//...
- Pada fungsi navigasi, teruskan tindakan untuk keluar dari pelacak ke dialog entri.
TrackerFragment.kt
//...
binding.fab.setOnClickListener { fabView ->
fabView.findNavController().navigate(
TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment()
)
}
//...
- Ulangi tindakan ini dalam isi lambda untuk metode
onEdit()
diJuiceListAdapter
, tetapi untuk kali ini, teruskanid
dariJuice
.
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.