Dasar-Dasar Android Kotlin 08.2: Memuat dan menampilkan gambar dari internet

Codelab ini adalah bagian dari kursus Dasar-Dasar Android Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan 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, dan DiffUtil.

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

  • Memodifikasi aplikasi MarsRealEstate untuk mendapatkan URL gambar dari data properti Mars, dan menggunakan Glide untuk memuat dan menampilkan gambar tersebut.
  • Menambahkan 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 akan menggunakan aplikasi bernama MarsRealEstate, yang menampilkan properti yang dijual di Mars. Aplikasi ini terhubung ke server internet untuk mengambil dan menampilkan data properti, termasuk detail seperti harga dan apakah properti tersedia untuk dijual atau disewakan. Gambar yang merepresentasikan setiap properti adalah foto kehidupan nyata dari Mars yang diambil dari penjelajah Mars NASA.

Versi aplikasi yang Anda buat dalam codelab ini akan terisi di halaman ringkasan, yang menampilkan petak gambar. Gambar adalah bagian dari data properti yang didapatkan aplikasi Anda dari layanan web properti 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-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. Mempelajari cara memuat gambar secara efektif dari jaringan bisa menjadi codelab tersendiri.

Untungnya, Anda dapat menggunakan library yang dikembangkan komunitas yang disebut Glide untuk mendownload, melakukan buffering, mendekode, dan meng-cache gambar Anda. Glide membuat pekerjaan Anda jauh lebih mudah daripada jika Anda harus melakukan semua ini dari awal.

Pada dasarnya, Glide memerlukan dua hal:

  • URL gambar yang ingin Anda muat dan tampilkan.
  • Objek ImageView untuk menampilkan gambar tersebut.

Dalam tugas ini, Anda akan mempelajari cara menggunakan Glide untuk menampilkan satu gambar dari layanan web real estate. Anda menampilkan gambar yang mewakili properti Mars pertama dalam daftar properti yang ditampilkan oleh layanan web. Berikut adalah screenshot sebelum dan sesudah:

Langkah 1: Tambahkan dependensi Glide

  1. Buka aplikasi MarsRealEstate dari codelab terakhir. (Anda dapat mendownload MarsRealEstateNetwork di sini jika Anda tidak memiliki aplikasi tersebut.)
  2. Jalankan aplikasi untuk melihat fungsinya. (Menampilkan detail teks properti yang secara hipotetis tersedia di Mars.)
  3. Buka build.gradle (Module: app).
  4. Di bagian dependencies, tambahkan baris ini untuk library Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"


Perhatikan bahwa nomor versi sudah ditentukan secara terpisah dalam file Gradle project.

  1. 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.

  1. Buka overview/OverviewViewModel.kt. Tepat di bawah LiveData untuk _response, tambahkan data live internal (dapat berubah) dan eksternal (tidak dapat berubah) untuk satu objek MarsProperty.

    Impor class MarsProperty (com.example.android.marsrealestate.network.MarsProperty) saat diminta.
private val _property = MutableLiveData<MarsProperty>()

val property: LiveData<MarsProperty>
   get() = _property
  1. Pada metode getMarsRealEstateProperties(), temukan baris di dalam blok try/catch {} yang menyetel _response.value ke jumlah properti. Tambahkan pengujian yang ditunjukkan di bawah. Jika objek MarsProperty tersedia, pengujian ini akan menetapkan nilai LiveData _property ke properti pertama di listResult.
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}"
 }
  1. Buka file res/layout/fragment_overview.xml. Dalam elemen <TextView>, ubah android:text untuk terikat ke komponen imgSrcUrl dari property LiveData:
android:text="@{viewModel.property.imgSrcUrl}"
  1. Jalankan aplikasi. TextView hanya menampilkan URL gambar di properti Mars pertama. Semua yang telah Anda lakukan sejauh ini adalah menyiapkan model tampilan dan data live untuk URL tersebut.

Langkah 3: Buat adaptor pengikatan dan panggil Glide

Sekarang Anda memiliki URL gambar untuk ditampilkan, dan saatnya mulai menggunakan Glide untuk memuat gambar tersebut. Pada langkah ini, Anda menggunakan adaptor binding untuk mengambil URL dari atribut XML yang terkait dengan ImageView, dan Anda 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.

  1. Buka BindingAdapters.kt. File ini akan menyimpan adaptor binding yang Anda gunakan di seluruh aplikasi.
  2. Buat fungsi bindImage() yang menggunakan parameter ImageView dan String. Anotasikan fungsi dengan @BindingAdapter. Anotasi @BindingAdapter memberi tahu data binding bahwa Anda ingin adaptor binding ini dieksekusi saat item XML memiliki atribut imageUrl.

    Impor androidx.databinding.BindingAdapter dan android.widget.ImageView bila diminta.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {

}
  1. Di dalam fungsi bindImage(), tambahkan blok let {} untuk argumen imgUrl:
imgUrl?.let { 
}
  1. Di dalam blok let {}, tambahkan baris yang ditunjukkan di bawah untuk mengonversi string URL (dari XML) menjadi objek Uri. Impor androidx.core.net.toUri saat diminta.

    Anda ingin objek Uri akhir menggunakan skema HTTPS, karena server tempat Anda menarik gambar memerlukan skema tersebut. Untuk menggunakan skema HTTPS, tambahkan buildUpon.scheme("https") ke builder toUri. Metode toUri() adalah fungsi ekstensi Kotlin dari library inti Android KTX, sehingga terlihat seperti bagian dari class String.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
  1. Masih di dalam let {}, panggil Glide.with() untuk memuat gambar dari objek Uri ke ImageView. Impor com.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 selanjutnya adalah memperbarui tata letak dan fragmen dengan ImageView untuk menampilkan gambar.

  1. Buka res/layout/gridview_item.xml. Ini adalah file resource tata letak yang akan Anda gunakan untuk setiap item di RecyclerView nanti dalam codelab. Anda menggunakannya di sini untuk sementara guna menampilkan satu gambar saja.
  2. Di atas elemen <ImageView>, tambahkan elemen <data> untuk data binding, dan binding ke class OverviewViewModel:
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
  1. Tambahkan atribut app:imageUrl ke elemen ImageView untuk menggunakan adaptor binding pemuatan gambar baru:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
  1. Buka overview/OverviewFragment.kt. Dalam metode onCreateView(), jadikan baris sebagai komentar, yaitu baris yang meng-inflate class FragmentOverviewBinding dan menetapkannya ke variabel binding. Ini hanya sementara; Anda akan kembali ke sana nanti.
//val binding = FragmentOverviewBinding.inflate(inflater)
  1. Tambahkan baris untuk meng-inflate class GridViewItemBinding. Impor com.example.android.marsrealestate. databinding.GridViewItemBinding bila diminta.
val binding = GridViewItemBinding.inflate(inflater)
  1. Jalankan aplikasi. Sekarang Anda akan melihat foto gambar dari MarsProperty pertama dalam daftar hasil.

Langkah 5: Tambahkan gambar pemuatan dan error sederhana

Glide dapat meningkatkan kualitas 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 ke tata letak.

  1. 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 atribut android:tint untuk mewarnai ikon menjadi abu-abu.

  1. Buka res/drawable/loading_animation.xml. Drawable ini adalah animasi yang ditentukan dengan tag <animate-rotate>. Animasi memutar drawable gambar, loading_img.xml, mengitari titik tengah. (Anda tidak melihat animasinya di pratinjau.)

  1. Kembali ke file BindingAdapters.kt. Pada metode bindImage(), update panggilan ke Glide.with() untuk memanggil fungsi apply() di antara load() dan into(). Impor com.bumptech.glide.request.RequestOptions saat diminta.

    Kode ini menetapkan gambar pemuatan placeholder yang akan digunakan saat memuat (drawable loading_animation). Kode ini juga menetapkan gambar yang akan digunakan jika pemuatan gambar gagal (drawable 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()
        Glide.with(imgView.context)
                .load(imgUri)
                .apply(RequestOptions()
                        .placeholder(R.drawable.loading_animation)
                        .error(R.drawable.ic_broken_image))
                .into(imgView)
    }
}
  1. 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 memperbaiki error 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—objek pertama dalam daftar respons dari layanan web. Pada langkah ini, Anda mengubah LiveData tersebut untuk menyimpan seluruh daftar objek MarsProperty.

  1. Buka overview/OverviewViewModel.kt.
  2. Ubah variabel _property pribadi menjadi _properties. Ubah jenisnya menjadi daftar objek MarsProperty.
private val _properties = MutableLiveData<List<MarsProperty>>()
  1. Ganti data live property eksternal dengan properties. Tambahkan daftar ke jenis LiveData di sini juga:
 val properties: LiveData<List<MarsProperty>>
        get() = _properties
  1. Scroll ke bawah, ke metode getMarsRealEstateProperties(). Di dalam blok try {}, ganti seluruh pengujian yang Anda tambahkan pada tugas sebelumnya dengan baris yang ditampilkan di bawah. Karena variabel listResult menyimpan daftar objek MarsProperty, Anda cukup 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.

  1. Buka res/layout/gridview_item.xml. Ubah data binding dari OverviewViewModel menjadi MarsProperty, lalu ganti nama variabel menjadi "property".
<variable
   name="property"
   type="com.example.android.marsrealestate.network.MarsProperty" />
  1. Di <ImageView>, ubah atribut app:imageUrl untuk merujuk ke URL gambar di objek MarsProperty:
app:imageUrl="@{property.imgSrcUrl}"
  1. Buka overview/OverviewFragment.kt. Di onCreateview(), hapus tanda komentar pada baris yang meng-inflate FragmentOverviewBinding. Hapus atau jadikan baris yang meng-inflate GridViewBinding sebagai komentar. Perubahan ini mengurungkan perubahan sementara yang Anda buat di tugas terakhir.
val binding = FragmentOverviewBinding.inflate(inflater)
 // val binding = GridViewItemBinding.inflate(inflater)
  1. Buka res/layout/fragment_overview.xml. Hapus seluruh elemen <TextView>.
  2. Sebagai gantinya, tambahkan elemen <RecyclerView> ini, yang menggunakan GridLayoutManager dan tata letak grid_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 satu ImageView. Pada langkah ini, Anda akan mengikat data ke RecyclerView melalui adaptor RecyclerView.

  1. Buka overview/PhotoGridAdapter.kt.
  2. Buat class PhotoGridAdapter, dengan parameter konstruktor yang ditampilkan di bawah ini. Class PhotoGridAdapter memperluas ListAdapter, yang konstruktornya memerlukan jenis item daftar, holder tampilan, dan implementasi DiffUtil.ItemCallback.

    Impor class androidx.recyclerview.widget.ListAdapter dan com.example.android.marsrealestate.network.MarsProperty bila 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) {

}
  1. Klik di mana saja dalam class PhotoGridAdapter, lalu tekan Control+i untuk menerapkan metode ListAdapter, yaitu onCreateViewHolder() dan onBindViewHolder().
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
   TODO("not implemented") 
}

override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
   TODO("not implemented") 
}
  1. Di akhir definisi class PhotoGridAdapter, setelah metode yang baru saja Anda tambahkan, tambahkan definisi objek pendamping untuk DiffCallback, seperti yang ditunjukkan di bawah ini.

    Impor androidx.recyclerview.widget.DiffUtil bila diminta.

    Objek DiffCallback memperluas DiffUtil.ItemCallback dengan jenis objek yang ingin Anda bandingkan—MarsProperty.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
  1. Tekan Control+i untuk mengimplementasikan metode pembanding untuk objek ini, yaitu areItemsTheSame() dan areContentsTheSame().
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") 
}

override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") }
  1. Untuk metode areItemsTheSame(), hapus TODO. Gunakan operator persamaan referensial Kotlin (===), yang menampilkan true jika referensi objek untuk oldItem dan newItem sama.
override fun areItemsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem === newItem
}
  1. Untuk areContentsTheSame(), gunakan operator kesetaraan standar hanya pada ID oldItem dan newItem.
override fun areContentsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem.id == newItem.id
}
  1. Masih di dalam class PhotoGridAdapter, di bawah objek pendamping, tambahkan definisi class internal untuk MarsPropertyViewHolder, yang memperluas RecyclerView.ViewHolder.

    Impor androidx.recyclerview.widget.RecyclerView dan com.example.android.marsrealestate.databinding.GridViewItemBinding jika diminta.

    Anda memerlukan variabel GridViewItemBinding untuk mengikat MarsProperty ke tata letak, jadi teruskan variabel tersebut ke dalam MarsPropertyViewHolder. Karena class ViewHolder dasar memerlukan tampilan dalam konstruktornya, Anda meneruskannya dengan tampilan root binding.
class MarsPropertyViewHolder(private var binding: 
                   GridViewItemBinding):
       RecyclerView.ViewHolder(binding.root) {

}
  1. Di MarsPropertyViewHolder, buat metode bind() yang menggunakan objek MarsProperty sebagai argumen dan menyetel binding.property ke objek tersebut. Panggil executePendingBindings() setelah menetapkan properti, yang menyebabkan update segera dijalankan.
fun bind(marsProperty: MarsProperty) {
   binding.property = marsProperty
   binding.executePendingBindings()
}
  1. Di onCreateViewHolder(), hapus TODO dan tambahkan baris yang ditunjukkan di bawah. Impor android.view.LayoutInflater bila diminta.

    Metode onCreateViewHolder() perlu menampilkan MarsPropertyViewHolder baru, yang dibuat dengan meng-inflate GridViewItemBinding dan menggunakan LayoutInflater dari konteks ViewGroup induk.
   return MarsPropertyViewHolder(GridViewItemBinding.inflate(
      LayoutInflater.from(parent.context)))
  1. Dalam metode onBindViewHolder(), hapus TODO dan tambahkan baris yang ditampilkan di bawah ini. Di sini, panggil getItem() untuk mendapatkan objek MarsProperty yang terkait dengan posisi RecyclerView saat ini, lalu teruskan properti tersebut ke metode bind() di MarsPropertyViewHolder.
val marsProperty = getItem(position)
holder.bind(marsProperty)

Langkah 4: Tambahkan adaptor binding dan sambungkan bagian-bagiannya

Terakhir, gunakan BindingAdapter untuk menginisialisasi 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.

  1. Buka BindingAdapters.kt.
  2. Di akhir file, tambahkan metode bindRecyclerView() yang menggunakan RecyclerView dan daftar objek MarsProperty sebagai argumen. Anotasikan metode tersebut dengan @BindingAdapter.

    Impor androidx.recyclerview.widget.RecyclerView dan com.example.android.marsrealestate.network.MarsProperty bila diminta.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, 
    data: List<MarsProperty>?) {
}
  1. Di dalam fungsi bindRecyclerView(), transmisi recyclerView.adapter ke PhotoGridAdapter, dan panggil adapter.submitList() dengan data. Ini akan memberi tahu RecyclerView saat daftar baru tersedia.

Impor com.example.android.marsrealestate.overview.PhotoGridAdapter bila diminta.

val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
  1. Buka res/layout/fragment_overview.xml. Tambahkan atribut app:listData ke elemen RecyclerView dan tetapkan ke viewmodel.properties menggunakan data binding.
app:listData="@{viewModel.properties}"
  1. Buka overview/OverviewFragment.kt. Di onCreateView(), tepat sebelum panggilan ke setHasOptionsMenu(), inisialisasi adaptor RecyclerView di binding.photosGrid menjadi objek PhotoGridAdapter baru.
binding.photosGrid.adapter = PhotoGridAdapter()
  1. Jalankan aplikasi. Anda akan melihat petak gambar MarsProperty. Saat Anda men-scroll untuk melihat gambar baru, aplikasi akan menampilkan ikon progres 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 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 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. Saat aplikasi mengambil daftar MarsProperty, aplikasi akan menampilkan animasi pemuatan.

Langkah 1: Menambahkan status ke model tampilan

Untuk memulai, Anda membuat LiveData di 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().

  1. Buka overview/OverviewViewModel.kt. Di bagian atas file (setelah impor, sebelum definisi class), tambahkan enum untuk mewakili semua status yang tersedia:
enum class MarsApiStatus { LOADING, ERROR, DONE }
  1. Ganti nama definisi data live _response internal dan eksternal di seluruh class OverviewViewModel menjadi _status. Karena Anda menambahkan dukungan untuk _properties LiveData sebelumnya dalam codelab ini, respons layanan web lengkap tidak digunakan. Anda memerlukan LiveData di sini untuk melacak status saat ini, jadi Anda cukup mengganti nama variabel yang ada.

Selain itu, ubah jenis dari String menjadi MarsApiStatus.

private val _status = MutableLiveData<MarsApiStatus>()

val status: LiveData<MarsApiStatus>
   get() = _status
  1. Scroll ke bawah, ke metode getMarsRealEstateProperties(), lalu perbarui _response menjadi _status di sini juga. Ubah string "Success" menjadi status MarsApiStatus.DONE, dan string "Failure" menjadi MarsApiStatus.ERROR.
  2. Tambahkan status MarsApiStatus.LOADING ke bagian atas blok try {}, sebelum panggilan ke await(). Ini adalah status awal saat coroutine berjalan dan Anda sedang menunggu data. Blok try/catch {} lengkap 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
}
  1. Setelah status error di blok catch {}, setel _properties LiveData ke daftar kosong. Tindakan ini akan menghapus RecyclerView.
} 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 berupa kumpulan status. Bagaimana cara menampilkannya di aplikasi itu sendiri? Pada langkah ini, Anda menggunakan ImageView, yang terhubung ke data binding, untuk 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.

  1. Buka BindingAdapters.kt. Tambahkan adaptor binding baru bernama bindStatus() yang menggunakan nilai ImageView dan MarsApiStatus sebagai argumen. Impor com.example.android.marsrealestate.overview.MarsApiStatus bila diminta.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView, 
          status: MarsApiStatus?) {
}
  1. Tambahkan when {} di dalam metode bindStatus() untuk beralih antar-status.
when (status) {

}
  1. Di dalam when {}, tambahkan kasus untuk status pemuatan (MarsApiStatus.LOADING). Untuk status ini, setel ImageView menjadi terlihat, dan tetapkan animasi pemuatan. Ini adalah drawable animasi yang sama dengan yang digunakan untuk Glide dalam tugas sebelumnya. Impor android.view.View bila diminta.
when (status) {
   MarsApiStatus.LOADING -> {
      statusImageView.visibility = View.VISIBLE
      statusImageView.setImageResource(R.drawable.loading_animation)
   }
}
  1. Tambahkan kasus untuk status error, yaitu MarsApiStatus.ERROR. Demikian pula dengan apa yang Anda lakukan untuk status LOADING, setel status ImageView menjadi terlihat dan gunakan kembali drawable koneksi-error.
MarsApiStatus.ERROR -> {
   statusImageView.visibility = View.VISIBLE
   statusImageView.setImageResource(R.drawable.ic_connection_error)
}
  1. Tambahkan kasus untuk status selesai, yaitu MarsApiStatus.DONE. Di sini Anda memiliki respons yang berhasil, jadi nonaktifkan visibilitas status ImageView untuk menyembunyikannya.
MarsApiStatus.DONE -> {
   statusImageView.visibility = View.GONE
}

Langkah 3: Tambahkan status ImageView ke tata letak

  1. Buka res/layout/fragment_overview.xml. Di bawah elemen RecyclerView, di dalam ConstraintLayout, tambahkan ImageView yang ditunjukkan di bawah.

    ImageView ini 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 membuat tampilan memanggil BindingAdapter 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}" />
  1. Aktifkan mode pesawat di emulator atau perangkat Anda untuk menyimulasikan koneksi jaringan yang tidak ada. Kompilasi dan jalankan aplikasi, dan perhatikan bahwa gambar error muncul:

  1. 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 metode load() dan into() 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 guna 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, gunakan apply() dengan placeholder() untuk menentukan drawable pemuatan, dan gunakan apply() dengan error() untuk menentukan drawable error.
  • Untuk menghasilkan petak gambar, gunakan RecyclerView dengan GridLayoutManager.
  • 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. Instruktur menentukan hal berikut:

  • Memberikan pekerjaan rumah jika diperlukan.
  • Memberi tahu siswa cara mengirimkan tugas pekerjaan rumah.
  • Memberi nilai tugas pekerjaan rumah.

Instruktur bisa menggunakan saran ini sesuai kebutuhan, dan bebas menugaskan pekerjaan rumah lain yang dirasa cocok.

Jika Anda menyelesaikan codelab ini sendiri, gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Jawab pertanyaan-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?

▢ Gunakan metode into() dengan drawable.

▢ Gunakan RequestOptions() dan panggil metode placeholder() dengan drawable.

▢ Tetapkan properti Glide.placeholder ke drawable.

▢ Gunakan RequestOptions() dan panggil metode loadingImage() dengan drawable.

Pertanyaan 3

Bagaimana cara Anda menunjukkan bahwa suatu metode merupakan adaptor binding?

▢ Panggil metode setBindingAdapter() pada LiveData.

▢ Masukkan metode ke dalam file Kotlin bernama BindingAdapters.kt.

▢ Gunakan atribut android:adapter dalam tata letak XML.

▢ Anotasikan metode dengan @BindingAdapter.

Mulai pelajaran berikutnya: 8.3 Memfilter dan tampilan detail dengan data internet

Untuk link ke codelab lain dalam kursus ini, lihat halaman landing codelab Dasar-Dasar Android Kotlin.