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

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, 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

  • 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

  1. Buka aplikasi MarsRealEstate dari codelab terakhir. (Anda dapat mendownload MarsRealEstateNetwork di sini jika belum memilikinya.)
  2. Jalankan aplikasi untuk melihat fungsinya. (Properti ini menampilkan detail teks dari 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 telah ditetapkan secara terpisah di 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 (yang dapat diubah) dan eksternal (yang tidak dapat diubah) 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 menetapkan _response.value ke jumlah properti. Tambahkan pengujian yang ditampilkan di bawah. Jika objek MarsProperty tersedia, pengujian ini akan menetapkan nilai _property LiveData 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. Di elemen <TextView>, ubah android:text untuk mengikat 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 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.

  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. Anotasi fungsi dengan @BindingAdapter. Anotasi @BindingAdapter memberi tahu data binding bahwa Anda ingin adaptor binding ini dijalankan saat item XML memiliki atribut imageUrl.

    Impor androidx.databinding.BindingAdapter dan android.widget.ImageView saat 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 ditampilkan 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 yang Anda pilih gambarnya memerlukan skema itu. 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 berikutnya 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 dalam RecyclerView nanti dalam codelab. Anda menggunakannya sementara di sini untuk menampilkan hanya satu gambar.
  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 lagi 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. 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.

  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, di sekitar titik tengah. (Anda tidak melihat animasi di pratinjau.)

  1. Kembali ke file BindingAdapters.kt. Pada metode bindImage(), perbarui panggilan ke Glide.with() untuk memanggil fungsi apply() 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 menyetel gambar untuk 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 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.

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

  1. Buka res/layout/gridview_item.xml. Ubah data binding dari OverviewViewModel menjadi MarsProperty, dan 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. Pada 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 sebelumnya.
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 ImageView tunggal. Pada langkah ini, Anda 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 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) {

}
  1. Klik di mana saja pada class PhotoGridAdapter lalu tekan Control+i untuk mengimplementasikan 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.

    Impor androidx.recyclerview.widget.DiffUtil saat 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 menerapkan 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 referensi Kotlin (===), yang menampilkan true jika referensi objek untuk oldItem dan newItem adalah sama.
override fun areItemsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem === newItem
}
  1. Untuk areContentsTheSame(), gunakan operator persamaan standar hanya dengan 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 saat 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 ditampilkan 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 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.

  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 saat 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 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().

  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 perlu LiveData 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
  1. Scroll ke bawah ke metode getMarsRealEstateProperties() dan perbarui _response menjadi _status di sini juga. Ubah string "Success" ke status MarsApiStatus.DONE, dan string "Failure" ke 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 lengkap try/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
}
  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 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.

  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 di 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 keterlihatan 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 seperti RecyclerView. Namun, lebar dan tinggi menggunakan wrap_content untuk menengahkan gambar, bukan melebarkan gambar untuk memenuhi tampilan. Perhatikan juga atribut app:marsApiStatus, yang memiliki 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 hilang. 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 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, gunakan apply() dengan placeholder() untuk menentukan drawable yang dimuat, dan gunakan apply() dengan error() untuk menentukan drawable drawable.
  • 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. 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: 8.3 Memfilter dan tampilan detail dengan data internet

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