Pengantar
Pada codelab sebelumnya, Anda telah mempelajari cara mendapatkan data dari layanan web dan mengurai respons menjadi objek Kotlin. Dalam codelab ini, Anda membangun pengetahuan tersebut untuk memuat dan menampilkan foto dari URL web. Anda juga meninjau kembali cara membuat RecyclerView dan menggunakannya untuk menampilkan petak gambar pada halaman ringkasan.
Prasyarat
- Cara membuat dan menggunakan fragmen.
- Cara mengambil JSON dari layanan web REST dan mengurai data tersebut ke dalam objek Kotlin menggunakan library Retrofit dan Moshi.
- Cara membuat tata letak petak dengan
RecyclerView. - Cara kerja
Adapter,ViewHolder, danDiffUtil.
Yang akan Anda pelajari
- Cara menggunakan library Coil untuk memuat dan menampilkan gambar dari URL web.
- Cara menggunakan
RecyclerViewdan adaptor petak untuk menampilkan petak gambar. - Cara menangani potensi error saat gambar didownload dan ditampilkan.
Yang akan Anda buat
- Modifikasi aplikasi MarsPhotos untuk mendapatkan URL gambar dari data Mars, dan gunakan Coil untuk memuat dan menampilkan gambar itu.
- Tambahkan animasi pemuatan dan ikon error ke aplikasi.
- Gunakan
RecyclerViewuntuk menampilkan petak gambar Mars. - Tambahkan penanganan status dan error ke
RecyclerView.
Yang Anda perlukan
- Komputer dengan browser web modern, seperti Chrome versi terbaru.
- Akses internet di komputer.
Dalam codelab ini, Anda akan terus bekerja dengan aplikasi dari codelab sebelumnya yang disebut MarsPhotos. Aplikasi MarsPhotos terhubung ke layanan web untuk mengambil dan menampilkan jumlah objek Kotlin yang diambil menggunakan Retrofit. Objek Kotlin ini berisi URL foto kehidupan nyata dari permukaan Mars yang diambil dari penjelajah Mars NASA.
Versi aplikasi yang akan Anda buat dalam codelab ini akan terisi di halaman ringkasan, yang menampilkan foto Mars dalam petak gambar. Gambar adalah bagian dari data yang diambil aplikasi Anda dari layanan web Mars. Aplikasi Anda akan menggunakan library Coil untuk memuat dan menampilkan gambar, serta RecyclerView untuk membuat tata letak petak untuk gambar. Aplikasi Anda juga akan menangani error jaringan dengan baik.

Menampilkan foto dari URL web mungkin terdengar mudah, tetapi ada sedikit teknik untuk membuatnya berfungsi dengan baik. Gambar harus didownload, disimpan secara internal, dan didekode-kan dari format terkompresi menjadi gambar yang dapat digunakan Android. Gambar harus di-cache ke cache dalam memori, cache berbasis penyimpanan, atau keduanya. Semua ini harus terjadi dalam thread latar belakang prioritas rendah sehingga UI tetap responsif. Selain itu, untuk performa jaringan dan CPU terbaik, Anda dapat mengambil dan mendekode lebih dari satu gambar sekaligus.
Untungnya, Anda dapat menggunakan library yang dikembangkan komunitas yang disebut Coil untuk mendownload, buffer, mendekode, dan meng-cache gambar Anda. Tanpa menggunakan Coil, Anda akan memiliki lebih banyak pekerjaan yang harus dilakukan.
Coil pada dasarnya memerlukan dua hal:
- URL gambar yang ingin Anda muat dan tampilkan.
- Objek
ImageViewuntuk benar-benar menampilkan gambar tersebut.
Dalam tugas ini, Anda akan mempelajari cara menggunakan Coil untuk menampilkan satu gambar dari layanan web Mars. Anda menampilkan gambar foto Mars pertama dalam daftar foto yang ditampilkan oleh layanan web. Berikut adalah screenshot sebelum dan sesudah:
|
|
Menambahkan dependensi Coil
- Buka aplikasi MarsPhotos dari codelab sebelumnya.
- Jalankan aplikasi untuk melihat fungsinya. (Ini menunjukkan jumlah total foto Mars yang diambil).
- Buka build.gradle (Module: app).
- Di bagian
dependencies, tambahkan baris ini untuk library Coil:
// Coil
implementation "io.coil-kt:coil:1.1.1"
Periksa dan update versi terbaru library dari halaman dokumentasi Coil.
- Library Coil dihosting dan tersedia di repositori
mavenCentral(). Di build.gradle (Project: MarsPhotos), tambahkanmavenCentral()di blokrepositoriesatas.
repositories {
google()
jcenter()
mavenCentral()
}
- Klik Sync Now untuk membuat ulang project dengan dependensi baru.
Mengupdate ViewModel
Pada langkah ini, Anda akan menambahkan properti LiveData ke class OverviewViewModel untuk menyimpan objek Kotlin yang diterima, MarsPhoto.
- Buka
overview/OverviewViewModel.kt. Tepat di bawah deklarasi properti_status, tambahkan properti baru yang dapat berubah yang disebut_photos, dari jenisMutableLiveDatayang dapat menyimpan objekMarsPhototunggal.
private val _photos = MutableLiveData<MarsPhoto>()
Impor com.example.android.marsphotos.network.MarsPhoto saat diminta.
- Tepat di bawah deklarasi
_photos, tambahkan kolom dukungan publik yang disebutphotosdari jenisnya,LiveData<MarsPhoto>.
val photos: LiveData<MarsPhoto> = _photos
- Pada metode
getMarsPhotos(), di dalam bloktry{}, temukan baris yang menyetel data yang diambil dari layanan web menjadilistResult.
try {
val listResult = MarsApi.retrofitService.getPhotos()
...
}
- Tetapkan foto Mars pertama yang diambil ke variabel baru
_photos. UbahlistResultmenjadi_photos.value. Tetapkan url foto pertama pada indeks0. Ini akan memunculkan error, Anda akan memperbaikinya nanti.
try {
_photos.value = MarsApi.retrofitService.getPhotos()[0]
...
}
- Di baris berikutnya, update
status.valuemenjadi yang berikut. Gunakan data dari properti baru, bukanlistResult. Tampilkan URL gambar pertama dari Daftar foto.
try {
...
_status.value = " First Mars image URL : ${_photos.value!!.imgSrcUrl}"
}
- Blok lengkap
try{}sekarang terlihat seperti ini:
try {
_photos.value = MarsApi.retrofitService.getPhotos()[0]
_status.value = " First Mars image URL : ${_photos.value!!.imgSrcUrl}"
}
- Jalankan aplikasi.
TextViewsekarang menampilkan URL foto Mars pertama. Semua yang telah Anda lakukan sejauh ini adalah menyiapkanViewModeldanLiveDatauntuk URL tersebut.

Menggunakan Adaptor Binding
Adaptor Binding adalah metode beranotasi yang digunakan untuk membuat penyetel khusus untuk properti kustom tampilan Anda.
Biasanya saat Anda menetapkan atribut dalam XML menggunakan kode: android:text="Sample Text". Sistem Android otomatis mencari penyetel dengan nama yang sama dengan atribut text, yang ditetapkan oleh metode setText(String: text). Metode setText(String: text) adalah metode penyetel untuk beberapa tampilan yang disediakan oleh Framework Android. Perilaku serupa dapat disesuaikan menggunakan adaptor binding; Anda dapat memberikan atribut khusus dan logika khusus yang akan dipanggil oleh library Data binding.
Contoh:
Untuk melakukan sesuatu yang lebih kompleks daripada hanya memanggil penyetel pada tampilan Gambar, yang menetapkan gambar drawable. Pertimbangkan untuk memuat gambar dari UI thread (thread utama), dari internet. Pertama, pilih atribut khusus untuk menetapkan gambar ke ImageView. Pada contoh berikut adalah imageUrl.
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:imageUrl="@{product.imageUrl}"/>
Jika Anda tidak menambahkan kode lebih lanjut, sistem akan mencari metode setImageUrl(String) di ImageView dan tidak menemukannya, sehingga menimbulkan error karena ini adalah atribut khusus yang tidak disediakan oleh framework. Anda harus membuat cara untuk menerapkan dan menetapkan atribut app:imageUrl ke ImageView. Anda akan menggunakan adaptor Binding (metode beranotasi) untuk melakukannya.
Contoh Adaptor Binding:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
// Load the image in the background using Coil.
}
}
}
Anotasi @BindingAdapter mengambil nama atribut sebagai parameternya.
Pada metode bindImage, parameter metode pertama adalah jenis Tampilan target dan yang kedua adalah nilai yang ditetapkan ke atribut.
Di dalam metode tersebut, library Coil memuat gambar dari UI thread dan menyetelnya ke ImageView.
Membuat adaptor binding dan menggunakan Coil
- Dalam paket
com.example.android.marsphotos, buat file Kotlin bernamaBindingAdapters. File ini akan menyimpan adaptor binding yang Anda gunakan di seluruh aplikasi.

- Di
BindingAdapters.kt. Buat fungsibindImage()yang menggunakan parameterImageViewdanString.
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
Impor android.widget.ImageView bila diminta.
- Anotasi fungsi dengan
@BindingAdapter. Anotasi@BindingAdaptermemberi tahu data binding untuk mengeksekusi adaptor binding ini jika item Tampilan memiliki atributimageUrl.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
Impor androidx.databinding.BindingAdapter bila diminta.
biarkan fungsi cakupan
let adalah salah satu fungsi Cakupan Kotlin yang memungkinkan Anda mengeksekusi blok kode dalam konteks objek. Ada lima fungsi Cakupan di Kotlin, lihat dokumentasi untuk mempelajari lebih lanjut.
Penggunaan:
let digunakan untuk memanggil satu atau beberapa fungsi pada hasil rantai panggilan.
Fungsi let bersama operator panggilan aman( ?.) digunakan untuk melakukan pengoperasian aman null pada objek. Dalam hal ini, blok kode let hanya akan dijalankan jika objek tidak bernilai null.
- Di dalam fungsi
bindImage(), tambahkan bloklet{}ke argumenimageURL, menggunakan operator panggilan aman.
imgUrl?.let {
}
- Di dalam blok
let{}, tambahkan baris berikut untuk mengonversi string URL menjadi objekUrimenggunakan metodetoUri(). Untuk menggunakan skema HTTPS, tambahkanbuildUpon.scheme("https")ke buildertoUri. Panggilbuild()untuk membuat objek.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
Impor androidx.core.net.toUri bila diminta.
- Di dalam blok
let{}, setelah deklarasiimgUri, gunakanload(){}dari Coil, untuk memuat gambar dari objekimgUrikeimgView.
imgView.load(imgUri) {
}
Impor coil.load bila diminta.
- Metode lengkap Anda akan terlihat seperti di bawah ini:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
imgView.load(imgUri)
}
}
Memperbarui tata letak dan fragmen
Di bagian sebelumnya, Anda menggunakan library gambar Coil untuk memuat gambar. Untuk melihat gambar di layar, langkah selanjutnya adalah memperbarui ImageView dengan atribut baru untuk menampilkan satu gambar.
Nanti di codelab, Anda akan menggunakan res/layout/grid_view_item.xml sebagai file resource tata letak setiap item petak di RecyclerView. Dalam tugas ini, Anda akan menggunakan file ini untuk sementara guna menampilkan gambar menggunakan URL gambar yang diambil pada tugas sebelumnya. Untuk sementara, Anda akan menggunakan file tata letak ini untuk menggantikan fragment_overview.xml.
- Buka
res/layout/grid_view_item.xml - Di atas elemen
<ImageView>, tambahkan elemen<data>untuk data binding, dan binding ke classOverviewViewModel:
<data>
<variable
name="viewModel"
type="com.example.android.marsphotos.overview.OverviewViewModel" />
</data>
- Tambahkan atribut
app:imageUrlke elemenImageViewuntuk menggunakan adaptor binding pemuatan gambar baru. Ingatlah bahwaphotosberisi daftarMarsPhotosyang diambil dari server. Tetapkan URL entri pertama ke atributimageUrl.
<ImageView
android:id="@+id/mars_image"
...
app:imageUrl="@{viewModel.photos.imgSrcUrl}"
... />
- Buka
overview/OverviewFragment.kt. Dalam metodeonCreateView(), komentari baris yang meng-inflate classFragmentOverviewBindingdan menetapkannya ke variabel binding. Anda akan melihat error karena penghapusan baris ini. Ini hanya sementara; Anda akan memperbaikinya nanti.
//val binding = FragmentOverviewBinding.inflate(inflater)
- Gunakan
grid_view_item.xml, bukanfragment_overview.xml.Tambahkan baris berikut untuk meng-inflate classGridViewItemBinding.
val binding = GridViewItemBinding.inflate(inflater)
Impor com.example.android.marsphotos. databinding.GridViewItemBinding bila diminta.
- Jalankan aplikasi. Anda sekarang akan melihat satu gambar Mars.

Menambahkan gambar pemuatan dan error
Penggunaan Coil Anda dapat meningkatkan pengalaman pengguna dengan menampilkan gambar placeholder saat memuat gambar dan gambar error jika pemuatan gagal, misalnya jika gambar hilang atau rusak. Pada langkah ini, Anda akan menambahkan fungsi tersebut ke adaptor binding.
- Buka
res/drawable/ic_broken_image.xml, lalu klik tab Design di sebelah kanan. Untuk gambar error, Anda menggunakan ikon gambar rusak yang tersedia di library ikon bawaan. Vektor drawable ini menggunakan atributandroid:tintuntuk mewarnai ikon menjadi abu-abu.

- Buka
res/drawable/loading_animation.xmlDrawable ini adalah animasi yang memutar drawable gambar,loading_img.xml, di sekitar titik tengah. (Anda tidak melihat animasi di pratinjau.) d

- Kembali ke file
BindingAdapters.kt. Dalam metodebindImage(), perbarui panggilan keimgView.load(imgUri)untuk menambahkan lambda trailing sebagai berikut: Kode ini menetapkan placeholder memuat gambar untuk digunakan saat memuat (drawableloading_animation). Kode ini juga menyetel gambar untuk digunakan jika pemuatan gambar gagal (drawablebroken_image).
imgView.load(imgUri) {
placeholder(R.drawable.loading_animation)
error(R.drawable.ic_broken_image)
}
- Metode
bindImage()lengkap kini terlihat seperti ini:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
imgView.load(imgUri) {
placeholder(R.drawable.loading_animation)
error(R.drawable.ic_broken_image)
}
}
}
- Jalankan aplikasi. Bergantung pada kecepatan koneksi jaringan, Anda mungkin melihat gambar pemuatan secara cepat sebagai download Glide dan menampilkan gambar properti. Namun, Anda tidak akan melihat ikon gambar yang rusak, meskipun jaringan dinonaktifkan—Anda akan memperbaikinya di tugas terakhir codelab.

- Tampilkan perubahan sementara yang Anda buat di
overview/OverviewFragment.kt. Pada metodeonCreateview(), hapus tanda komentar pada baris yang meng-inflateFragmentOverviewBinding. Menghapus atau mengomentari baris yang meng-inflateGridViewIteMBinding.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)
Aplikasi Anda sekarang memuat foto Mars dari internet. Dengan menggunakan data dari item daftar MarsPhoto pertama, Anda telah membuat properti LiveData di ViewModel, dan telah menggunakan URL gambar dari data foto Mars tersebut untuk mengisi ImageView. Namun, tujuannya adalah agar aplikasi Anda menampilkan petak gambar, sehingga dalam tugas ini Anda akan menggunakan RecyclerView dengan pengelola tata letak Petak untuk menampilkan petak gambar.
Mengupdate ViewModel
Pada tugas sebelumnya, di OverviewViewModel, Anda telah menambahkan objek LiveData yang disebut _photos yang menyimpan satu objek MarsPhoto—objek pertama dalam daftar respons dari layanan web. Pada langkah ini, Anda akan mengubah LiveData ini untuk menyimpan seluruh daftar objek MarsPhoto.
- Buka
overview/OverviewViewModel.kt. - Ubah jenis
_photosmenjadi daftar objekMarsPhoto.
private val _photos = MutableLiveData<List<MarsPhoto>>()
- Ganti juga jenis properti pendukung
photoske jenisList<MarsPhoto>:
val photos: LiveData<List<MarsPhoto>> = _photos
- Scroll ke bawah ke blok
try {}di dalam metodegetMarsPhotos().MarsApi.retrofitService.getPhotos()
menampilkan daftar objek MarsPhoto, Anda cukup menetapkannya ke _photos.value.
_photos.value = MarsApi.retrofitService.getPhotos()
_status.value = "Success: Mars properties retrieved"
- Seluruh blok
try/catchsekarang terlihat seperti ini:
try {
_photos.value = MarsApi.retrofitService.getPhotos()
_status.value = "Success: Mars properties retrieved"
} catch (e: Exception) {
_status.value = "Failure: ${e.message}"
}
Tata Letak Petak
GridLayoutManager untuk RecyclerView menata letak data sebagai petak yang dapat di-scroll, seperti yang ditampilkan di bawah ini.

Dari perspektif desain, Tata Letak Petak paling baik untuk daftar yang dapat ditampilkan sebagai ikon atau gambar, seperti daftar dalam aplikasi penjelajahan foto Mars Anda.
Cara tata letak Petak menata letak item
Tata letak petak mengatur item dalam petak baris dan kolom. Dengan asumsi scrolling vertikal, secara default, setiap item dalam baris memerlukan satu "span". Sebuah item dapat menempati beberapa span. Dalam kasus di bawah, satu span sama dengan lebar satu kolom yaitu 3.
Dalam dua contoh yang ditampilkan di bawah, setiap baris terdiri dari tiga span. Secara default, GridLayoutManager menata letak setiap item dalam satu span hingga jumlah span, yang Anda tentukan. Saat mencapai jumlah span, span ini akan dibulatkan ke baris berikutnya.
|
|
Menambahkan Recyclerview
Pada langkah ini Anda akan mengubah tata letak aplikasi untuk menggunakan tampilan recycler dengan tata letak petak, bukan satu tampilan gambar.
- Buka
layout/gridview_item.xml. Hapus variabel dataviewModel. - Di dalam tag
<data>, tambahkan variabelphotodari jenisMarsPhotoberikut.
<data>
<variable
name="photo"
type="com.example.android.marsphotos.network.MarsPhoto" />
</data>
- Di
<ImageView>, ubah atributapp:imageUrluntuk merujuk ke URL gambar di objekMarsPhoto. Perubahan ini mengurungkan perubahan sementara yang Anda buat di tugas sebelumnya.
app:imageUrl="@{photo.imgSrcUrl}"
- Buka
layout/fragment_overview.xmlHapus seluruh elemen<TextView>. - Sebagai gantinya, tambahkan elemen
<RecyclerView>berikut. Setel ID ke atributphotos_grid,width, danheightke0dp, sehingga mengisiConstraintLayoutinduk. Anda akan menggunakan tata letak Petak, jadi setel atributlayoutManagerkeandroidx.recyclerview.widget.GridLayoutManager. SetelspanCountke2sehingga Anda akan memiliki dua kolom.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
app:layoutManager=
"androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2" />
- Untuk melihat pratinjau tampilan kode di atas dalam tampilan Design, gunakan
tools:itemCountuntuk menetapkan jumlah item yang ditampilkan dalam tata letak ke16. AtributitemCountmenentukan jumlah item yang harus dirender editor tata letak di jendela Pratinjau. Setel tata letak item daftar kegrid_view_itemmenggunakantools:listitem.
<androidx.recyclerview.widget.RecyclerView
...
tools:itemCount="16"
tools:listitem="@layout/grid_view_item" />
- Beralih ke tampilan Design, Anda akan melihat pratinjau seperti screenshot berikut. Ini tidak terlihat seperti foto Mars, tapi ini akan menunjukkan tampilan tata letak petak recyclerview Anda. Pratinjau menggunakan padding dan tata letak
grid_view_itemuntuk setiap item petak tunggalrecyclerview.

- Menurut Panduan desain material, Anda harus memiliki
8dpruang di bagian atas, bawah, dan sisi daftar, serta4dpruang di antara item. Anda dapat melakukannya dengan kombinasi padding di tata letakfragment_overview.xmldan di tata letakgridview_item.xml.

- Buka
layout/gridview_item.xml. Perhatikan atributpadding, Anda sudah memiliki2dppadding antara bagian luar item dan konten. Ini akan memberi kami4dpruang di antara konten item, dan2dpdi sepanjang tepi luar, yang berarti kami membutuhkan6dppadding tambahan di tepi luar agar cocok dengan panduan desain. - Kembali ke
layout/fragment_overview.xmlTambahkan6dppadding untukRecyclerView, sehingga Anda akan memiliki8dpdi bagian luar dan4dpdi bagian dalam sebagai panduan.
<androidx.recyclerview.widget.RecyclerView
...
android:padding="6dp"
... />
- Elemen
<RecyclerView>lengkap akan terlihat seperti berikut.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
app:layoutManager=
"androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2"
tools:itemCount="16"
tools:listitem="@layout/grid_view_item" />
Menambahkan adaptor petak foto
Sekarang tata letak fragment_overview memiliki RecyclerView dengan tata letak petak. Pada langkah ini, Anda akan mem-binding data yang diambil dari server web ke RecyclerView melalui adaptor RecyclerView.
ListAdapter (Refresher)
ListAdapter adalah subclass dari class RecyclerView.Adapter untuk menyajikan data Daftar dalam RecyclerView, termasuk perbedaan komputasi antara Daftar pada thread latar belakang.
Dalam aplikasi ini, Anda akan menggunakan implementasi DiffUtil di ListAdapter. Keuntungan menggunakan DiffUtil adalah setiap kali beberapa item di RecyclerView ditambahkan, dihapus, atau diubah, seluruh daftar tidak disegarkan. Hanya item yang telah diubah yang disegarkan.
Menambahkan ListAdapter ke aplikasi Anda.
- Pada paket
overview, buat class Kotlin baru bernamaPhotoGridAdapter.kt. - Perluas class
PhotoGridAdapterdariListAdapterdengan parameter konstruktor yang ditampilkan di bawah ini. ClassPhotoGridAdaptermemperluasListAdapter, yang konstruktornya memerlukan jenis item daftar, holder tampilan, dan implementasiDiffUtil.ItemCallback.
class PhotoGridAdapter : ListAdapter<MarsPhoto,
PhotoGridAdapter.MarsPhotoViewHolder>(DiffCallback) {
}
Impor class androidx.recyclerview.widget.ListAdapter dan com.example.android.marsphoto.network.MarsPhoto bila diminta. Pada langkah-langkah berikut, Anda akan menerapkan implementasi lain yang hilang dari konstruktor ini, yang menghasilkan error.
- Untuk mengatasi error di atas, Anda akan menambahkan metode yang diperlukan pada langkah ini dan menerapkannya nanti dalam tugas ini. Klik class
PhotoGridAdapter, klik bulb merah, dari menu drop-down, pilih Terapkan anggota. Pada pop-up yang ditampilkan, pilih metodeListAdapter,onCreateViewHolder()danonBindViewHolder(). Android Studio akan tetap menampilkan error yang akan Anda perbaiki di akhir tugas ini.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPhotoViewHolder {
TODO("Not yet implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPhotoViewHolder, position: Int) {
TODO("Not yet implemented")
}
Untuk mengimplementasikan metode onCreateViewHolder dan onBindViewHolder, Anda memerlukan MarsPhotoViewHolder, yang akan ditambahkan pada langkah berikutnya.
- Di dalam
PhotoGridAdapter, tambahkan definisi class internal untukMarsPhotoViewHolder, yang memperluasRecyclerView.ViewHolder. Anda memerlukan variabelGridViewItemBindinguntuk mem-bindingMarsPhotoke tata letak, jadi teruskan variabel ke dalamMarsPhotoViewHolder. ClassViewHolderdasar memerlukan tampilan dalam konstruktornya, Anda meneruskannya dengan tampilan root binding.
class MarsPhotoViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}
Impor androidx.recyclerview.widget.RecyclerView dan com.example.android.marsrealestate.databinding.GridViewItemBinding bila diminta.
- Di
MarsPhotoViewHolder, buat metodebind()yang menggunakan objekMarsPhotosebagai argumen dan menyetelbinding.propertyke objek tersebut. PanggilexecutePendingBindings()setelah menetapkan properti, yang menyebabkan update segera dijalankan.
fun bind(MarsPhoto: MarsPhoto) {
binding.photo = MarsPhoto
binding.executePendingBindings()
}
- Masih di dalam class
PhotoGridAdapterdionCreateViewHolder(), hapus TODO dan tambahkan baris yang ditunjukkan di bawah. MetodeonCreateViewHolder()perlu menampilkanMarsPhotoViewHolderbaru, yang dibuat dengan meng-inflateGridViewItemBindingdan menggunakanLayoutInflaterdari konteks indukViewGroup.
return MarsPhotoViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))
Impor android.view.LayoutInflater bila diminta.
- Dalam metode
onBindViewHolder(), hapus TODO dan tambahkan baris yang ditampilkan di bawah ini. Di sini, panggilgetItem()untuk mendapatkan objekMarsPhotoyang terkait dengan posisiRecyclerViewsaat ini, lalu teruskan properti tersebut ke metodebind()diMarsPhotoViewHolder.
val marsPhoto = getItem(position)
holder.bind(marsPhoto)
- Di dalam
PhotoGridAdapter, tambahkan definisi objek pendamping untukDiffCallback, seperti yang ditunjukkan di bawah ini.
ObjekDiffCallbackmemperluasDiffUtil.ItemCallbackdengan jenis objek umum yang ingin Anda bandingkan—MarsPhoto. Anda akan membandingkan dua objek foto Mars dalam penerapan ini.
companion object DiffCallback : DiffUtil.ItemCallback<MarsPhoto>() {
}
Impor androidx.recyclerview.widget.DiffUtil bila diminta.
- Tekan bulb merah untuk mengimplementasikan metode pembanding untuk objek
DiffCallback, yaituareItemsTheSame()danareContentsTheSame().
override fun areItemsTheSame(oldItem: MarsPhoto, newItem: MarsPhoto): Boolean {
TODO("Not yet implemented")
}
override fun areContentsTheSame(oldItem: MarsPhoto, newItem: MarsPhoto): Boolean {
TODO("Not yet implemented") }
- Dalam metode
areItemsTheSame(), hapusTODO. Metode ini dipanggil olehDiffUtiluntuk menentukan apakah dua objek mewakili Item yang sama.DiffUtilmenggunakan metode ini untuk mencari tahu apakah objekMarsPhotoyang baru sama dengan objekMarsPhotolama. ID setiap item(MarsPhotoobjek) bersifat unik. Bandingkan IDoldItemdannewItem, lalu tampilkan hasilnya.
override fun areItemsTheSame(oldItem: MarsPhoto, newItem: MarsPhoto): Boolean {
return oldItem.id == newItem.id
}
- Di
areContentsTheSame(), hapusTODO. Metode ini dipanggil olehDiffUtilsaat ingin memeriksa apakah dua item memiliki data yang sama. Data penting di MarsPhoto adalah URL gambar. Bandingkan URLoldItemdannewItem, lalu tampilkan hasilnya.
override fun areContentsTheSame(oldItem: MarsPhoto, newItem: MarsPhoto): Boolean {
return oldItem.imgSrcUrl == newItem.imgSrcUrl
}
Pastikan Anda dapat mengompilasi dan menjalankan aplikasi tanpa error, tetapi emulator menampilkan layar kosong. Anda telah menyiapkan recyclerview, namun tidak ada data yang diteruskan ke sana, yang akan Anda terapkan pada langkah berikutnya.
Tambahkan adaptor binding dan sambungkan bagian-bagiannya
Pada langkah ini, Anda akan menggunakan BindingAdapter untuk menginisialisasi PhotoGridAdapter dengan daftar objek MarsPhoto. Menggunakan BindingAdapter untuk menyetel data RecyclerView menyebabkan data binding secara otomatis mengamati LiveData untuk daftar objek MarsPhoto. Kemudian, adaptor binding dipanggil secara otomatis saat daftar MarsPhoto berubah.
- Buka
BindingAdapters.kt. - Di akhir file, tambahkan metode
bindRecyclerView()yang menggunakanRecyclerViewdan daftar objekMarsPhotosebagai argumen. Anotasi bahwa metode dengan@BindingAdapterdengan atributlistData.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsPhoto>?) {
}
Impor androidx.recyclerview.widget.RecyclerView dan com.example.android.marsphotos.network.MarsPhoto bila diminta.
- Di dalam fungsi
bindRecyclerView(), transmisirecyclerView.adapterkePhotoGridAdapterdan tetapkan ke propertivalbaruadapter.
val adapter = recyclerView.adapter as PhotoGridAdapter
- Di akhir fungsi
bindRecyclerView(), panggiladapter.submitList()dengan data daftar foto Mars. Ini akan memberi tahuRecyclerViewsaat daftar baru tersedia.
adapter.submitList(data)
Impor com.example.android.marsrealestate.overview.PhotoGridAdapter bila diminta.
- Adaptor binding
bindRecyclerViewlengkap akan terlihat seperti ini:
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsPhoto>?) {
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
}
- Untuk menyambungkan semuanya, buka
res/layout/fragment_overview.xml. Tambahkan atributapp:listDatake elemenRecyclerViewdan tetapkan keviewmodel.photosmenggunakan data binding. Ini mirip dengan apa yang telah Anda lakukan untukImageViewdi tugas sebelumnya.
app:listData="@{viewModel.photos}"
- Buka
overview/OverviewFragment.kt. DionCreateView(), tepat sebelum pernyataanreturn, inisialisasi adaptorRecyclerViewdibinding.photosGridmenjadi objekPhotoGridAdapterbaru.
binding.photosGrid.adapter = PhotoGridAdapter()
- Jalankan aplikasi. Anda akan melihat petak scrolling gambar Mars. Saat Anda scroll untuk melihat gambar baru namun terlihat sedikit aneh. Padding akan tetap berada di bagian atas dan bawah
RecyclerViewsaat Anda men-scroll, sehingga tidak akan pernah tampak seperti daftar di-scroll di bawah Panel Tindakan.

- Untuk memperbaikinya, Anda perlu memberi tahu
RecyclerViewagar tidak memangkas konten bagian dalam ke padding menggunakan atribut android:clipToPadding. Tindakan ini akan membuat tampilan scroll di area padding. Kembali kelayout/fragment_overview.xml. Tambahkan atributandroid:clipToPaddinguntukRecyclerView, tetapkan kefalse.
<androidx.recyclerview.widget.RecyclerView
...
android:clipToPadding="false"
... />
- Jalankan aplikasi Anda. Perhatikan bahwa aplikasi juga menampilkan ikon kemajuan pemuatan sebelum menampilkan gambar itu sendiri, seperti yang diharapkan. Ini adalah gambar pemuatan placeholder yang Anda teruskan ke library gambar Coil.

- Saat aplikasi berjalan, aktifkan mode pesawat. Scroll gambar di emulator. Gambar yang belum dimuat akan muncul sebagai ikon gambar yang rusak. Ini adalah drawable gambar yang Anda teruskan ke library gambar Coil untuk ditampilkan jika terjadi error jaringan atau gambar tidak bisa diambil.

Selamat, Anda hampir selesai. Pada tugas terakhir berikutnya, Anda akan meningkatkan pengalaman pengguna dengan menambahkan lebih banyak penanganan error untuk aplikasi.
Aplikasi MarsPhotos menampilkan ikon gambar rusak saat gambar tidak dapat diambil. Namun saat tidak ada jaringan, aplikasi akan menampilkan layar kosong. Anda akan memverifikasi layar kosong di langkah berikutnya.
- Aktifkan mode pesawat di perangkat atau emulator. Jalankan aplikasi dari Android Studio. Perhatikan layar kosong.

Ini bukan pengalaman pengguna yang baik. Dalam tugas ini, Anda akan menambahkan penanganan error dasar, untuk memberikan ide yang lebih baik kepada pengguna tentang apa yang terjadi. Jika internet tidak tersedia, aplikasi akan menampilkan ikon error koneksi dan saat aplikasi mengambil daftar MarsPhoto, aplikasi akan menampilkan animasi pemuatan.
Menambahkan status ke ViewModel
Dalam tugas ini, Anda akan membuat properti di OverviewViewModel untuk mewakili status permintaan web. Ada tiga status untuk dipertimbangkan—memuat, berhasil, dan kegagalan. Status pemuatan terjadi saat Anda menunggu data. Status keberhasilan adalah saat kami berhasil mengambil data dari webservice. Status kegagalan menunjukkan adanya error jaringan atau koneksi.
Class Enum di Kotlin
Untuk mewakili ketiga status ini dalam aplikasi, Anda akan menggunakan enum. enum adalah kependekan dari enumerasi, yang berarti daftar yang diurutkan dari semua item dalam koleksi. Setiap konstanta enum adalah objek dari class enum.
Di Kotlin, enum adalah jenis data yang dapat menyimpan kumpulan konstanta. Semua itu ditentukan dengan menambahkan kata kunci enum di depan definisi class seperti yang ditunjukkan di bawah ini. Konstanta Enum dipisahkan dengan koma.
Definisi:
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
Penggunaan:
var direction = Direction.NORTH;
Seperti yang ditunjukkan di atas, objek enum dapat direferensikan menggunakan nama class yang diikuti dengan operator titik (.) dan nama konstanta.
Tambahkan definisi class enum dengan nilai status di Viewmodel.
- Buka
overview/OverviewViewModel.kt. Di bagian atas file (setelah impor, sebelum definisi class), tambahkanenumuntuk mewakili semua status yang tersedia:
enum class MarsApiStatus { LOADING, ERROR, DONE }
- Scroll ke definisi properti
_statusdanstatus, mengubah jenis dariStringmenjadiMarsApiStatus. MarsApiStatusadalah class enum yang Anda tentukan di langkah sebelumnya.
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus> = _status
- Pada metode
getMarsPhotos(), ubah string"Success: ..."menjadi statusMarsApiStatus.DONE, dan string"Failure..."menjadiMarsApiStatus.ERROR.
try {
_photos.value = MarsApi.retrofitService.getPhotos()
_status.value = MarsApiStatus.DONE
} catch (e: Exception)
_status.value = MarsApiStatus.ERROR
}
- Setel status ke
MarsApiStatus.LOADINGdi atas bloktry {}. Ini adalah status awal saat coroutine berjalan dan Anda sedang menunggu data. BlokviewModelScope.launch{}lengkap sekarang akan terlihat seperti ini:
viewModelScope.launch {
_status.value = MarsApiStatus.LOADING
try {
_photos.value = MarsApi.retrofitService.getPhotos()
_status.value = MarsApiStatus.DONE
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}
}
- Setelah status error di blok
catch {}, setel_photoske daftar kosong. Ini akan menghapus tampilan Recycler.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_photos.value = listOf()
}
- Metode
getMarsPhotos()yang lengkap akan terlihat seperti ini:
private fun getMarsPhotos() {
viewModelScope.launch {
_status.value = MarsApiStatus.LOADING
try {
_photos.value = MarsApi.retrofitService.getPhotos()
_status.value = MarsApiStatus.DONE
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_photos.value = listOf()
}
}
}
Anda telah menentukan status enum untuk status dan menyetel status pemuatan di awal coroutine, menyetel selesai saat aplikasi selesai mengambil data dari server web, dan menetapkan error saat ada pengecualian. Pada tugas berikutnya, Anda akan menggunakan adaptor binding untuk menampilkan ikon yang sesuai.
Menambahkan adaptor binding untuk status ImageView
Anda telah menyiapkan MarsApiStatus di OverviewViewModel, menggunakan kumpulan enum status. Pada langkah ini, Anda akan menampilkannya di aplikasi. Anda menggunakan adaptor Binding untuk ImageView, guna menampilkan ikon untuk status pemuatan dan error. Saat aplikasi berada dalam status pemuatan atau status error, ImageView akan terlihat. Saat aplikasi selesai dimuat, ImageView seharusnya tidak terlihat.
- Buka
BindingAdapters.kt, scroll ke akhir file untuk menambahkan adaptor lain. Tambahkan adaptor binding baru bernamabindStatus()yang menggunakan nilaiImageViewdanMarsApiStatussebagai argumen. Anotasi metode dengan@BindingAdaptermeneruskan atribut khususmarsApiStatussebagai parameter.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}
Impor com.example.android.marsrealestate.overview.MarsApiStatus bila diminta.
- Tambahkan blok
when {}di dalam metodebindStatus()untuk beralih antar-status.
when (status) {
}
- Di dalam
when {}, tambahkan kasus untuk status pemuatan (MarsApiStatus.LOADING). Untuk status ini, setelImageViewmenjadi terlihat, dan tetapkan animasi pemuatan. Ini adalah drawable animasi yang sama dengan yang digunakan untuk Coil dalam tugas sebelumnya.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}
Impor android.view.View bila diminta.
- Tambahkan kasus untuk status error, yaitu
MarsApiStatus.ERROR. Demikian pula dengan apa yang Anda lakukan untuk statusLOADING, setel statusImageViewmenjadi terlihat dan gunakan drawable koneksi-error.
MarsApiStatus.ERROR -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.ic_connection_error)
}
- Tambahkan kasus untuk status selesai, yaitu
MarsApiStatus.DONE. Di sini Anda memiliki respons yang berhasil, jadi setel keterlihatan statusImageViewmenjadiView.GONEuntuk menyembunyikannya.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}
Anda telah menyiapkan adaptor binding untuk tampilan Gambar status, pada langkah berikutnya Anda akan menambahkan tampilan Gambar yang menggunakan adaptor binding baru.
Menambahkan status ImageView
Pada langkah ini, Anda akan menambahkan tampilan Gambar di fragment_overview.xml yang akan menampilkan status yang Anda tentukan sebelumnya.
- Buka
res/layout/fragment_overview.xmlDi dalamConstraintLayout, di bawah elemenRecyclerView, tambahkanImageViewyang ditunjukkan di bawah.
<ImageView
android:id="@+id/status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:marsApiStatus="@{viewModel.status}" />
ImageView di atas memiliki batasan yang sama dengan RecyclerView. Namun, lebar dan tinggi menggunakan wrap_content untuk menengahkan gambar, bukan melebarkan gambar untuk memenuhi tampilan. Perhatikan juga atribut app:marsApiStatus yang disetel ke viewModel.status, yang memanggil BindingAdapter Anda saat properti status di ViewModel berubah.
- Untuk menguji kode di atas, simulasi error koneksi jaringan dengan mengaktifkan mode pesawat di emulator atau perangkat Anda. Kompilasi dan jalankan aplikasi, dan perhatikan bahwa gambar error muncul:

- Ketuk tombol Kembali untuk menutup aplikasi dan nonaktifkan mode pesawat. Gunakan layar terbaru untuk menampilkan aplikasi. Bergantung pada kecepatan koneksi jaringan, Anda mungkin melihat indikator lingkaran berputar yang sangat singkat saat aplikasi menanyakan layanan web sebelum gambar mulai dimuat.
Selamat karena telah menyelesaikan codelab ini dan membuat aplikasi MarsPhotos! Kini saatnya menampilkan aplikasi Anda dengan gambar Mars nyata bersama keluarga dan teman Anda.
Kode solusi untuk codelab ini ada dalam project yang ditampilkan di bawah. Gunakan cabang utama untuk mengambil atau mendownload kode.
Guna mendapatkan kode untuk codelab ini dan membukanya di Android Studio, lakukan hal berikut.
Mendapatkan kode
- Klik URL yang diberikan. Tindakan ini akan membuka halaman GitHub project di browser.
- Di halaman GitHub project, klik tombol Code yang akan menampilkan dialog.
- Di dialog, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
- Temukan file di komputer Anda (mungkin di folder Downloads).
- Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.
Membuka project di Android Studio
- Mulai Android Studio.
- Di jendela Welcome to Android Studio, klik Open an existing Android Studio project.
Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > New > Import Project.
- Di dialog Import Project, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
- Klik dua kali pada folder project tersebut.
- Tunggu Android Studio membuka project.
- Klik tombol Run
untuk membuat dan menjalankan aplikasi. Pastikan aplikasi berfungsi seperti yang diharapkan.
- Cari file project di jendela fitur Project untuk melihat cara aplikasi diterapkan.
- Library Coil menyederhanakan proses pengelolaan gambar, seperti download, buffer, decode, dan cache gambar di aplikasi Anda.
- Adaptor Binding adalah metode ekstensi yang berada di antara tampilan dan data yang mem-binding tampilan. Adaptor binding menyediakan perilaku kustom saat data berubah, misalnya, untuk memanggil Coil untuk memuat gambar dari URL ke dalam
ImageView. - Adaptor binding adalah metode ekstensi yang dianotasi dengan anotasi
@BindingAdapter. - Untuk menampilkan petak gambar, gunakan
RecyclerViewdenganGridLayoutManager. - Untuk memperbarui daftar properti saat berubah, gunakan adaptor binding antara
RecyclerViewdan tata letak.
Dokumentasi developer Android:
Lainnya:



