Codelab ini adalah bagian dari kursus Dasar-Dasar Kotlin Android. Anda akan mendapatkan manfaat maksimal dari kursus ini jika Anda mengerjakan codelab secara berurutan. Semua codelab kursus tercantum di halaman landing codelab Dasar-Dasar Android Kotlin.
Pengantar
Pada codelab sebelumnya, Anda telah mempelajari cara mendapatkan data dari layanan web dan mengurai respons menjadi objek data. Dalam codelab ini, Anda menggunakan 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.
Yang harus sudah Anda ketahui
- Cara membuat dan menggunakan fragmen.
- Cara menggunakan komponen arsitektur termasuk model tampilan, factory model tampilan, transformasi, dan
LiveData
. - 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 Glide untuk memuat dan menampilkan gambar dari URL web.
- Cara menggunakan
RecyclerView
dan adaptor petak untuk menampilkan petak gambar. - Cara menangani potensi error saat gambar didownload dan ditampilkan.
Yang akan Anda lakukan
- Modifikasi aplikasi MarsRealEstate untuk mendapatkan URL gambar dari data properti Mars, dan gunakan Glide untuk memuat dan menampilkan gambar tersebut.
- Tambahkan animasi pemuatan dan ikon error ke aplikasi.
- Gunakan
RecyclerView
untuk menampilkan petak gambar properti Mars. - Tambahkan penanganan status dan error ke
RecyclerView
.
Dalam codelab ini (dan codelab terkait), Anda bekerja dengan aplikasi bernama MarsRealEstate, yang menunjukkan properti yang dijual di Mars. Aplikasi terhubung ke server internet untuk mengambil dan menampilkan data properti, termasuk detail seperti harga dan apakah properti tersedia untuk dijual atau disewa. Gambar yang mewakili setiap properti adalah foto kehidupan nyata dari Mars yang diambil dari penjelajah Mars NASA.
Versi aplikasi yang Anda buat di codelab ini akan terisi di halaman ringkasan, yang menampilkan petak gambar. Gambar adalah bagian dari data properti yang diperoleh aplikasi Anda dari layanan web real estate Mars. Aplikasi Anda akan menggunakan library Glide 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, di-buffer, dan didekode dari format terkompresi-nya ke 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. Mempelajari cara memuat gambar secara efektif dari jaringan dapat menjadi codelab itu sendiri.
Untungnya, Anda dapat menggunakan library yang dikembangkan komunitas yang disebut Glide untuk mendownload, melakukan buffering, mendekode, dan meng-cache gambar Anda. Glide menyisakan lebih sedikit pekerjaan dibandingkan jika Anda harus melakukan semua ini dari awal.
Glide pada dasarnya memerlukan dua hal:
- URL gambar yang ingin Anda muat dan tampilkan.
- Objek
ImageView
untuk menampilkan gambar tersebut.
Dalam tugas ini, Anda mempelajari cara menggunakan Glide untuk menampilkan satu gambar dari layanan web real estat. Anda menampilkan gambar yang mewakili properti Mars pertama dalam daftar properti yang ditampilkan layanan web. Berikut adalah screenshot sebelum dan sesudah:
Langkah 1: Tambahkan dependensi Glide
- Buka aplikasi MarsRealEstate dari codelab terakhir. (Anda dapat mendownload MarsRealEstateNetwork di sini jika belum memilikinya.)
- Jalankan aplikasi untuk melihat fungsinya. (Properti ini menampilkan detail teks dari properti yang secara hipotetis tersedia di Mars.)
- Buka build.gradle (Module: app).
- Di bagian
dependencies
, tambahkan baris ini untuk library Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"
Perhatikan bahwa nomor versi telah ditetapkan secara terpisah di file Gradle project.
- Klik Sync Now untuk membuat ulang project dengan dependensi baru.
Langkah 2: Perbarui model tampilan
Selanjutnya, Anda memperbarui class OverviewViewModel
untuk menyertakan data live untuk satu properti Mars.
- Buka
overview/OverviewViewModel.kt
. Tepat di bawahLiveData
untuk_response
, tambahkan data live internal (yang dapat diubah) dan eksternal (yang tidak dapat diubah) untuk satu objekMarsProperty
.
Impor classMarsProperty
(com.example.android.marsrealestate.network.MarsProperty
) saat diminta.
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property
- Pada metode
getMarsRealEstateProperties()
, temukan baris di dalam bloktry/catch {}
yang menetapkan_response.value
ke jumlah properti. Tambahkan pengujian yang ditampilkan di bawah. Jika objekMarsProperty
tersedia, pengujian ini akan menetapkan nilai_property
LiveData
ke properti pertama dilistResult
.
if (listResult.size > 0) {
_property.value = listResult[0]
}
Blok lengkap try/catch {}
sekarang terlihat seperti ini:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
if (listResult.size > 0) {
_property.value = listResult[0]
}
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
- Buka file
res/layout/fragment_overview.xml
. Di elemen<TextView>
, ubahandroid:text
untuk mengikat ke komponenimgSrcUrl
dariproperty
LiveData
:
android:text="@{viewModel.property.imgSrcUrl}"
- Jalankan aplikasi.
TextView
hanya menampilkan URL gambar di properti Mars pertama. Semua yang telah Anda lakukan sejauh ini adalah menyiapkan model tampilan dan data langsung untuk URL tersebut.
Langkah 3: Buat adaptor binding dan panggil Glide
Sekarang Anda memiliki URL gambar yang akan ditampilkan, dan kini saatnya untuk mulai bekerja dengan Glide untuk memuat gambar tersebut. Pada langkah ini, Anda menggunakan adaptor binding untuk mengambil URL dari atribut XML yang terkait dengan ImageView
, dan menggunakan Glide untuk memuat gambar. Adaptor binding adalah metode ekstensi yang berada di antara tampilan dan data terikat untuk memberikan perilaku kustom saat data berubah. Dalam hal ini, perilaku kustomnya adalah memanggil Glide untuk memuat gambar dari URL ke dalam ImageView
.
- Buka
BindingAdapters.kt
. File ini akan menyimpan adaptor binding yang Anda gunakan di seluruh aplikasi. - Buat fungsi
bindImage()
yang menggunakan parameterImageView
danString
. Anotasi fungsi dengan@BindingAdapter
. Anotasi@BindingAdapter
memberi tahu data binding bahwa Anda ingin adaptor binding ini dijalankan saat item XML memiliki atributimageUrl
.
Imporandroidx.databinding.BindingAdapter
danandroid.widget.ImageView
saat diminta.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
- Di dalam fungsi
bindImage()
, tambahkan bloklet {}
untuk argumenimgUrl
:
imgUrl?.let {
}
- Di dalam blok
let {}
, tambahkan baris yang ditampilkan di bawah untuk mengonversi string URL (dari XML) menjadi objekUri
. Imporandroidx.core.net.toUri
saat diminta.
Anda ingin objekUri
akhir menggunakan skema HTTPS, karena server yang Anda pilih gambarnya memerlukan skema itu. Untuk menggunakan skema HTTPS, tambahkanbuildUpon.scheme("https")
ke buildertoUri
. MetodetoUri()
adalah fungsi ekstensi Kotlin dari library inti Android KTX, sehingga terlihat seperti bagian dari classString
.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
- Masih di dalam
let {}
, panggilGlide.with()
untuk memuat gambar dari objekUri
keImageView
. Imporcom.bumptech.glide.Glide
bila diminta.
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)
Langkah 4: Perbarui tata letak dan fragmen
Meskipun Glide telah memuat gambar, belum ada yang dapat dilihat. Langkah berikutnya adalah memperbarui tata letak dan fragmen dengan ImageView
untuk menampilkan gambar.
- Buka
res/layout/gridview_item.xml
. Ini adalah file resource tata letak yang akan Anda gunakan untuk setiap item dalamRecyclerView
nanti dalam codelab. Anda menggunakannya sementara di sini untuk menampilkan hanya satu gambar. - Di atas elemen
<ImageView>
, tambahkan elemen<data>
untuk data binding, dan binding ke classOverviewViewModel
:
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
- Tambahkan atribut
app:imageUrl
ke elemenImageView
untuk menggunakan adaptor binding pemuatan gambar baru:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
- Buka
overview/OverviewFragment.kt
. Dalam metodeonCreateView()
, jadikan baris sebagai komentar, yaitu baris yang meng-inflate classFragmentOverviewBinding
dan menetapkannya ke variabel binding. Ini hanya sementara; Anda akan kembali lagi nanti.
//val binding = FragmentOverviewBinding.inflate(inflater)
- Tambahkan baris untuk meng-inflate class
GridViewItemBinding
. Imporcom.example.android.marsrealestate. databinding.GridViewItemBinding
bila diminta.
val binding = GridViewItemBinding.inflate(inflater)
- Jalankan aplikasi. Anda sekarang akan melihat foto gambar dari
MarsProperty
pertama dalam daftar hasil.
Langkah 5: Tambahkan gambar pemuatan dan error yang sederhana
Glide 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 dan tata letak.
- Buka
res/drawable/ic_broken_image.xml
, lalu klik tab Preview di sebelah kanan. Untuk gambar error, Anda menggunakan ikon gambar rusak yang tersedia di library ikon bawaan. Vektor drawable ini menggunakan atributandroid:tint
untuk mewarnai ikon menjadi abu-abu.
- Buka
res/drawable/loading_animation.xml
. Drawable ini adalah animasi yang ditentukan dengan tag<animate-rotate>
. Animasi memutar drawable gambar,loading_img.xml
, di sekitar titik tengah. (Anda tidak melihat animasi di pratinjau.)
- Kembali ke file
BindingAdapters.kt
. Pada metodebindImage()
, perbarui panggilan keGlide.with()
untuk memanggil fungsiapply()
antaraload()
daninto()
. Imporcom.bumptech.glide.request.RequestOptions
saat diminta.
Kode ini menetapkan gambar pemuatan placeholder yang akan digunakan saat memuat (drawableloading_animation
). Kode ini juga menyetel gambar untuk digunakan jika pemuatan gambar gagal (drawablebroken_image
). MetodebindImage()
lengkap kini terlihat seperti ini:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri =
imgUrl.toUri().buildUpon().scheme("https").build()
Glide.with(imgView.context)
.load(imgUri)
.apply(RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
}
}
- Jalankan aplikasi. Bergantung pada kecepatan koneksi jaringan, Anda mungkin melihat gambar pemuatan secara cepat saat Glide mendownload dan menampilkan gambar properti. Namun, Anda tidak akan melihat ikon gambar yang rusak, meskipun jaringan dinonaktifkan—Anda akan memperbaikinya di bagian terakhir codelab.
Aplikasi Anda sekarang memuat informasi properti dari internet. Dengan menggunakan data dari item daftar MarsProperty
pertama, Anda telah membuat properti LiveData
di model tampilan, dan telah menggunakan URL gambar dari data properti tersebut untuk mengisi ImageView
. Namun, tujuannya adalah agar aplikasi Anda menampilkan petak gambar, sehingga Anda ingin menggunakan RecyclerView
dengan GridLayoutManager
.
Langkah 1: Perbarui model tampilan
Saat ini model tampilan memiliki _property
LiveData
yang menyimpan satu objek MarsProperty
—yang pertama dalam daftar respons dari layanan web. Pada langkah ini, Anda akan mengubah LiveData
tersebut untuk menyimpan seluruh daftar objek MarsProperty
.
- Buka
overview/OverviewViewModel.kt
. - Ubah variabel
_property
pribadi menjadi_properties
. Ubah jenis menjadi daftar objekMarsProperty
.
private val _properties = MutableLiveData<List<MarsProperty>>()
- Ganti data langsung
property
eksternal denganproperties
. Tambahkan juga daftar ke jenisLiveData
di sini:
val properties: LiveData<List<MarsProperty>>
get() = _properties
- Scroll ke bawah, ke metode
getMarsRealEstateProperties()
. Di dalam bloktry {}
, ganti seluruh pengujian yang Anda tambahkan di tugas sebelumnya dengan baris yang ditampilkan di bawah. Karena variabellistResult
menyimpan daftar objekMarsProperty
, Anda dapat menetapkannya ke_properties.value
, bukan menguji respons yang berhasil.
_properties.value = listResult
Seluruh blok try/catch
sekarang terlihat seperti ini:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
_properties.value = listResult
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
Langkah 2: Perbarui tata letak dan fragmen
Langkah berikutnya adalah mengubah tata letak dan fragmen aplikasi untuk menggunakan tampilan recycler dan tata letak petak, bukan satu tampilan gambar.
- Buka
res/layout/gridview_item.xml
. Ubah data binding dariOverviewViewModel
menjadiMarsProperty
, dan ganti nama variabel menjadi"property"
.
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
- Di
<ImageView>
, ubah atributapp:imageUrl
untuk merujuk ke URL gambar di objekMarsProperty
:
app:imageUrl="@{property.imgSrcUrl}"
- Buka
overview/OverviewFragment.kt
. PadaonCreateview()
, hapus tanda komentar pada baris yang meng-inflateFragmentOverviewBinding
. Hapus atau jadikan baris yang meng-inflateGridViewBinding
sebagai komentar. Perubahan ini mengurungkan perubahan sementara yang Anda buat di tugas sebelumnya.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)
- Buka
res/layout/fragment_overview.xml
. Hapus seluruh elemen<TextView>
. - Sebagai gantinya, tambahkan elemen
<RecyclerView>
ini, yang menggunakanGridLayoutManager
dan tata letakgrid_view_item
untuk satu item:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
android:clipToPadding="false"
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" />
Langkah 3: Tambahkan adaptor petak foto
Sekarang tata letak fragment_overview
memiliki RecyclerView
sedangkan tata letak grid_view_item
memiliki ImageView
tunggal. Pada langkah ini, Anda mengikat data ke RecyclerView
melalui adaptor RecyclerView
.
- Buka
overview/PhotoGridAdapter.kt
. - Buat class
PhotoGridAdapter
, dengan parameter konstruktor yang ditampilkan di bawah ini. ClassPhotoGridAdapter
memperluasListAdapter
, yang konstruktornya memerlukan jenis item daftar, holder tampilan, dan implementasiDiffUtil.ItemCallback
.
Impor classandroidx.recyclerview.widget.ListAdapter
dancom.example.android.marsrealestate.network.MarsProperty
saat diminta. Pada langkah-langkah berikut, Anda akan menerapkan bagian lain yang hilang dari konstruktor ini, yang menghasilkan error.
class PhotoGridAdapter : ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
}
- Klik di mana saja pada class
PhotoGridAdapter
lalu tekanControl+i
untuk mengimplementasikan metodeListAdapter
, yaituonCreateViewHolder()
danonBindViewHolder()
.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
TODO("not implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
TODO("not implemented")
}
- Di akhir definisi class
PhotoGridAdapter
, setelah metode yang baru saja Anda tambahkan, tambahkan definisi objek pendamping untukDiffCallback
, seperti yang ditunjukkan di bawah.
Imporandroidx.recyclerview.widget.DiffUtil
saat diminta.
ObjekDiffCallback
memperluasDiffUtil.ItemCallback
dengan jenis objek yang ingin Anda bandingkan—MarsProperty
.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
- Tekan
Control+i
untuk menerapkan metode pembanding untuk objek ini, yaituareItemsTheSame()
danareContentsTheSame()
.
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented")
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented") }
- Untuk metode
areItemsTheSame()
, hapus TODO. Gunakan operator persamaan referensi Kotlin (===
), yang menampilkantrue
jika referensi objek untukoldItem
dannewItem
adalah sama.
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}
- Untuk
areContentsTheSame()
, gunakan operator persamaan standar hanya dengan IDoldItem
dannewItem
.
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}
- Masih di dalam class
PhotoGridAdapter
, di bawah objek pendamping, tambahkan definisi class internal untukMarsPropertyViewHolder
, yang memperluasRecyclerView.ViewHolder
.
Imporandroidx.recyclerview.widget.RecyclerView
dancom.example.android.marsrealestate.databinding.GridViewItemBinding
saat diminta.
Anda memerlukan variabelGridViewItemBinding
untuk mengikatMarsProperty
ke tata letak, jadi teruskan variabel tersebut ke dalamMarsPropertyViewHolder
. Karena classViewHolder
dasar memerlukan tampilan dalam konstruktornya, Anda meneruskannya dengan tampilan root binding.
class MarsPropertyViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}
- Di
MarsPropertyViewHolder
, buat metodebind()
yang menggunakan objekMarsProperty
sebagai argumen dan menyetelbinding.property
ke objek tersebut. PanggilexecutePendingBindings()
setelah menetapkan properti, yang menyebabkan update segera dijalankan.
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}
- Di
onCreateViewHolder()
, hapus TODO dan tambahkan baris yang ditampilkan di bawah. Imporandroid.view.LayoutInflater
bila diminta.
MetodeonCreateViewHolder()
perlu menampilkanMarsPropertyViewHolder
baru, yang dibuat dengan meng-inflateGridViewItemBinding
dan menggunakanLayoutInflater
dari konteksViewGroup
induk.
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))
- Dalam metode
onBindViewHolder()
, hapus TODO dan tambahkan baris yang ditampilkan di bawah ini. Di sini, panggilgetItem()
untuk mendapatkan objekMarsProperty
yang terkait dengan posisiRecyclerView
saat ini, lalu teruskan properti tersebut ke metodebind()
diMarsPropertyViewHolder
.
val marsProperty = getItem(position)
holder.bind(marsProperty)
Langkah 4: Tambahkan adaptor binding dan sambungkan bagian-bagiannya
Terakhir, gunakan BindingAdapter
untuk melakukan inisialisasi PhotoGridAdapter
dengan daftar objek MarsProperty
. Menggunakan BindingAdapter
untuk menyetel data RecyclerView
menyebabkan data binding secara otomatis mengamati LiveData
untuk daftar objek MarsProperty
. Kemudian, adaptor binding dipanggil secara otomatis saat daftar MarsProperty
berubah.
- Buka
BindingAdapters.kt
. - Di akhir file, tambahkan metode
bindRecyclerView()
yang menggunakanRecyclerView
dan daftar objekMarsProperty
sebagai argumen. Anotasikan metode tersebut dengan@BindingAdapter
.
Imporandroidx.recyclerview.widget.RecyclerView
dancom.example.android.marsrealestate.network.MarsProperty
saat diminta.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
}
- Di dalam fungsi
bindRecyclerView()
, transmisirecyclerView.adapter
kePhotoGridAdapter
, dan panggiladapter.submitList()
dengan data. Ini akan memberi tahuRecyclerView
saat daftar baru tersedia.
Impor com.example.android.marsrealestate.overview.PhotoGridAdapter
bila diminta.
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
- Buka
res/layout/fragment_overview.xml
. Tambahkan atributapp:listData
ke elemenRecyclerView
dan tetapkan keviewmodel.properties
menggunakan data binding.
app:listData="@{viewModel.properties}"
- Buka
overview/OverviewFragment.kt
. DionCreateView()
, tepat sebelum panggilan kesetHasOptionsMenu()
, inisialisasi adaptorRecyclerView
dibinding.photosGrid
menjadi objekPhotoGridAdapter
baru.
binding.photosGrid.adapter = PhotoGridAdapter()
- Jalankan aplikasi. Anda akan melihat petak gambar
MarsProperty
. Saat Anda men-scroll untuk melihat gambar baru, aplikasi akan menampilkan ikon kemajuan pemuatan sebelum menampilkan gambar itu sendiri. Jika Anda mengaktifkan mode pesawat, gambar yang belum dimuat akan muncul sebagai ikon gambar yang rusak.
Aplikasi MarsRealEstate menampilkan ikon gambar yang rusak saat gambar tidak dapat diambil. Namun saat tidak ada jaringan, aplikasi akan menampilkan layar kosong.
Ini bukan pengalaman pengguna yang baik. Dalam tugas ini, Anda 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. Saat aplikasi mengambil daftar MarsProperty
, aplikasi akan menampilkan animasi pemuatan.
Langkah 1: Tambahkan status ke model tampilan
Untuk memulai, buat LiveData
dalam model tampilan untuk mewakili status permintaan web. Ada tiga status untuk dipertimbangkan—memuat, berhasil, dan kegagalan. Status pemuatan terjadi saat Anda menunggu data dalam panggilan ke await()
.
- Buka
overview/OverviewViewModel.kt
. Di bagian atas file (setelah impor, sebelum definisi class), tambahkanenum
untuk mewakili semua status yang tersedia:
enum class MarsApiStatus { LOADING, ERROR, DONE }
- Ganti nama definisi data live
_response
internal dan eksternal di seluruh classOverviewViewModel
menjadi_status
. Karena Anda menambahkan dukungan untuk_properties
LiveData
sebelumnya dalam codelab ini, respons layanan web lengkap tidak digunakan. Anda perluLiveData
di sini untuk melacak status saat ini, sehingga Anda bisa mengganti nama variabel yang ada.
Selain itu, ubah jenis dari String
menjadi MarsApiStatus.
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status
- Scroll ke bawah ke metode
getMarsRealEstateProperties()
dan perbarui_response
menjadi_status
di sini juga. Ubah string"Success"
ke statusMarsApiStatus.DONE
, dan string"Failure"
keMarsApiStatus.ERROR
. - Tambahkan status
MarsApiStatus.LOADING
ke bagian atas bloktry {}
, sebelum panggilan keawait()
. Ini adalah status awal saat coroutine berjalan dan Anda sedang menunggu data. Blok lengkaptry/catch {}
sekarang terlihat seperti ini:
try {
_status.value = MarsApiStatus.LOADING
var listResult = getPropertiesDeferred.await()
_status.value = MarsApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}
- Setelah status error di blok
catch {}
, setel_properties
LiveData
ke daftar kosong. Tindakan ini akan menghapusRecyclerView
.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_properties.value = ArrayList()
}
Langkah 2: Tambahkan adaptor binding untuk status ImageView
Sekarang Anda memiliki status di model tampilan, tetapi hanya kumpulan status. Bagaimana cara menampilkannya di aplikasi itu sendiri? Pada langkah ini, Anda menggunakan ImageView
, yang terhubung ke data binding, guna menampilkan ikon untuk status pemuatan dan error. Saat aplikasi berada dalam status pemuatan atau status error, ImageView
akan terlihat. Setelah aplikasi selesai dimuat, ImageView
seharusnya tidak terlihat.
- Buka
BindingAdapters.kt
. Tambahkan adaptor binding baru bernamabindStatus()
yang menggunakan nilaiImageView
danMarsApiStatus
sebagai argumen. Imporcom.example.android.marsrealestate.overview.MarsApiStatus
bila diminta.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}
- Tambahkan
when {}
di dalam metodebindStatus()
untuk beralih antar-status.
when (status) {
}
- Di dalam
when {}
, tambahkan kasus untuk status pemuatan (MarsApiStatus.LOADING
). Untuk status ini, setelImageView
menjadi terlihat, dan tetapkan animasi pemuatan. Ini adalah drawable animasi yang sama dengan yang digunakan untuk Glide di tugas sebelumnya. Imporandroid.view.View
bila diminta.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}
- Tambahkan kasus untuk status error, yaitu
MarsApiStatus.ERROR
. Demikian pula dengan apa yang Anda lakukan untuk statusLOADING
, setel statusImageView
menjadi terlihat dan gunakan kembali 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 nonaktifkan keterlihatan statusImageView
untuk menyembunyikannya.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}
Langkah 3: Tambahkan status ImageView ke tata letak
- Buka
res/layout/fragment_overview.xml
. Di bawah elemenRecyclerView
, di dalamConstraintLayout
, tambahkanImageView
yang ditunjukkan di bawah.ImageView
ini memiliki batasan yang sama sepertiRecyclerView
. Namun, lebar dan tinggi menggunakanwrap_content
untuk menengahkan gambar, bukan melebarkan gambar untuk memenuhi tampilan. Perhatikan juga atributapp:marsApiStatus
, yang memiliki tampilan memanggilBindingAdapter
Anda saat properti status di model tampilan berubah.
<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}" />
- Aktifkan mode pesawat di emulator atau perangkat Anda untuk menyimulasikan koneksi jaringan yang hilang. 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.
Project Android Studio: MarsRealEstateGrid
- Untuk menyederhanakan proses pengelolaan gambar, gunakan library Glide untuk mendownload, melakukan buffering, mendekode, dan meng-cache gambar di aplikasi Anda.
- Glide memerlukan dua hal untuk memuat gambar dari internet: URL gambar, dan objek
ImageView
untuk menempatkan gambar. Untuk menentukan opsi ini, gunakan metodeload()
daninto()
dengan Glide. - Adaptor Binding adalah metode ekstensi yang berada di antara tampilan dan data terikat tampilan. Adaptor binding menyediakan perilaku kustom saat data berubah, misalnya, untuk memanggil Glide untuk memuat gambar dari URL ke dalam
ImageView
. - Adaptor binding adalah metode ekstensi yang dianotasi dengan anotasi
@BindingAdapter
. - Untuk menambahkan opsi ke permintaan Glide, gunakan metode
apply()
. Misalnya, gunakanapply()
denganplaceholder()
untuk menentukan drawable yang dimuat, dan gunakanapply()
denganerror()
untuk menentukan drawable drawable. - Untuk menghasilkan petak gambar, gunakan
RecyclerView
denganGridLayoutManager
. - Untuk memperbarui daftar properti saat berubah, gunakan adaptor binding antara
RecyclerView
dan tata letak.
Kursus Udacity:
Dokumentasi developer Android:
Lainnya:
Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Terserah instruktur untuk melakukan hal berikut:
- Tugaskan pekerjaan rumah jika diperlukan.
- Berkomunikasi dengan siswa cara mengirimkan tugas pekerjaan rumah.
- Beri nilai tugas pekerjaan rumah.
Instruktur dapat menggunakan saran ini sesedikit atau sebanyak yang mereka inginkan, dan harus bebas memberikan pekerjaan rumah lain yang dirasa sesuai.
Jika Anda mengerjakan codelab ini sendiri, silakan gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.
Jawab pertanyaan berikut
Pertanyaan 1
Metode Glide mana yang Anda gunakan untuk menunjukkan ImageView
yang akan berisi gambar yang dimuat?
▢ into()
▢ with()
▢ imageview()
▢ apply()
Pertanyaan 2
Bagaimana cara Anda menentukan gambar placeholder untuk ditampilkan saat Glide memuat?
▢ Menggunakan metode into()
dengan drawable.
▢ Menggunakan RequestOptions()
dan memanggil metode placeholder()
dengan drawable.
▢ Tetapkan properti Glide.placeholder
ke drawable.
▢ Menggunakan RequestOptions()
dan memanggil metode loadingImage()
dengan drawable.
Pertanyaan 3
Bagaimana cara Anda menunjukkan bahwa suatu metode merupakan adaptor binding?
▢ Panggil metode setBindingAdapter()
di LiveData
.
▢ Tempatkan metode ke dalam file Kotlin bernama BindingAdapters.kt
.
▢ Menggunakan atribut android:adapter
dalam tata letak XML.
▢ Anotasikan metode dengan @BindingAdapter
.
Mulai tutorial berikutnya:
Untuk link ke codelab lainnya dalam kursus ini, lihat halaman landing codelab Dasar-Dasar Kotlin Android.