Android Kotlin 基礎知識 08.2:載入並顯示網際網路上的圖片

這個程式碼研究室是 Android Kotlin 基礎知識課程的一部分。如果您按部就班完成程式碼研究室,就能充分體驗到本課程的價值。所有課程程式碼研究室都列在 Android Kotlin 基礎知識程式碼研究室到達網頁

簡介

在先前的程式碼研究室中,您已瞭解如何從網路服務獲取資料,並將回應剖析為資料物件。在本程式碼研究室中,您可以在該知識的基礎上,瞭解如何從網址載入並顯示相片。您也可以回顧如何建構 RecyclerView,並在總覽頁面用以顯示圖片格線。

須知事項

  • 如何建立及使用片段。
  • 如何使用架構元件,包括檢視畫面模型、檢視畫面模型工廠、轉換和 LiveData
  • 如何從 REST 網路服務擷取 JSON,並使用 RetrofitMoshi 程式庫將資料剖析為 Kotlin 物件。
  • 如何使用 RecyclerView 建構格線版面配置。
  • AdapterViewHolderDiffUtil 的運作方式。

課程內容

  • 如何使用 Glide 程式庫從網址載入及顯示圖片。
  • 如何使用 RecyclerView 和格線轉接器顯示圖片格線。
  • 如何處理圖片下載及顯示時出現的潛在錯誤。

執行步驟

  • 修改 MarsRealEstate 應用程式,從火星房地產資料取得圖片網址,並使用 Glide 載入及顯示該圖片。
  • 在應用程式中新增載入動畫和錯誤圖示。
  • 使用 RecyclerView 顯示火星房地產圖片格線。
  • RecyclerView 加入狀態和錯誤處理機制。

在本程式碼研究室 (和相關程式碼研究室) 中,您將使用名為 MarsRealEstate 的應用程式,顯示火星上的待售地產。應用程式會連線至網際網路伺服器,擷取並顯示房地產資料,包括價格和房地產是否待售或待租等詳細資料。代表各個屬性的圖片是自 NASA 火星漫遊者擷取的火星實景相片。

您在本程式碼研究室中建構的應用程式版本會填入總覽頁面,此頁面會顯示圖片格線。這些圖片來自您的應用程式從 Mars 房地產網路服務取得的房地產資料。您的應用程式會使用 Glide 程式庫來載入並顯示圖片,也會使用 RecyclerView 來建立圖片的格線版面配置。此外,應用程式還會妥善處理網路錯誤。

想要顯示某個網址的相片,聽起來可能很簡單,但其實需要執行一些軟體工程才能達成。圖片必須經過下載、緩衝處理、對壓縮格式進行解碼,才能供 Android 使用。圖片應快取至記憶體快取或儲存空間快取,或同時存放在這兩個地方。系統只會在低優先順序的背景執行緒中採取這些動作,讓 UI 保持回應。此外,為獲得最佳網路和 CPU 效能,建議您一次擷取多張圖片並解碼。如何有效載入網路圖片本身就是一個程式碼研究室。

好消息是,您可以使用社群開發的程式庫 Glide 下載、緩衝處理、解碼及快取圖片。與從頭開始執行所有作業相比,Glide 可大幅減少您的工作量。

Glide 基本上需要下列兩項:

  • 要載入並顯示的圖片網址。
  • 用來顯示該圖片的 ImageView 物件。

在這項工作中,您會瞭解如何使用 Glide 顯示來自房地產 Web 服務的單張圖片。Web 服務會傳回地產清單,您將顯示清單上的第一個火星地產圖片。以下是前後對照的螢幕截圖:

步驟 1:新增 Glide 依附元件

  1. 開啟上一個程式碼研究室中的 MarsRealEstate 應用程式。(如果沒有這個應用程式,可以MarsRealEstateNetwork下載)。
  2. 執行應用程式以查看其用途。(系統會顯示假設在火星上可用的房地產文字詳細資料)。
  3. 開啟「build.gradle (Module: app)」
  4. dependencies 區段,為 Glide 程式庫新增此行內容:
implementation "com.github.bumptech.glide:glide:$version_glide"


請注意,版本號碼已在專案 Gradle 檔案中另外定義。

  1. 按一下「Sync Now」,使用新依附元件重新建構專案。

步驟 2:更新檢視畫面模型

接著,您會更新 OverviewViewModel 類別,加入單一火星屬性的即時資料。

  1. 開啟 overview/OverviewViewModel.kt。在 _responseLiveData 下方,為單一 MarsProperty 物件新增內部 (可變動) 和外部 (不可變動) 的即時資料。

    視需要匯入 MarsProperty 類別 (com.example.android.marsrealestate.network.MarsProperty)。
private val _property = MutableLiveData<MarsProperty>()

val property: LiveData<MarsProperty>
   get() = _property
  1. getMarsRealEstateProperties() 方法的 try/catch {} 區塊中,找出將 _response.value 設為屬性數量的行。新增下列測試。如果 MarsProperty 物件可用,這項測試會將 _property LiveData 的值設為 listResult 中的第一個屬性。
if (listResult.size > 0) {   
    _property.value = listResult[0]
}

現在,完整的 try/catch {} 區塊如下所示:

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. 開啟 res/layout/fragment_overview.xml 檔案。在 <TextView> 元素中,將 android:text 變更為繫結至 property LiveDataimgSrcUrl 元件:
android:text="@{viewModel.property.imgSrcUrl}"
  1. 執行應用程式。TextView 只會顯示第一個火星屬性中的圖片網址。目前,您已設定好該網址的檢視畫面模型和即時資料。

步驟 3:建立繫結介面卡並呼叫 Glide

現在您已取得要顯示的圖片網址,接下來請開始使用 Glide 載入該圖片。在這個步驟中,您會使用繫結轉接器,從與 ImageView 相關聯的 XML 屬性取得網址,並使用 Glide 載入圖片。繫結轉接器是介於檢視畫面和繫結資料之間的擴充方法,可在資料變更時提供自訂行為。在本例中,自訂行為是呼叫 Glide,將網址中的圖片載入 ImageView

  1. 開啟 BindingAdapters.kt。這個檔案會保留您在應用程式中使用的繫結轉接器。
  2. 建立可接受 ImageViewString 做為參數的 bindImage() 函式。使用 @BindingAdapter 為函式加上註解。@BindingAdapter 註解會通知資料繫結,在 XML 項目擁有 imageUrl 屬性時執行此繫結轉接器。

    視需要匯入 androidx.databinding.BindingAdapterandroid.widget.ImageView
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {

}
  1. bindImage() 函式中,為 imgUrl 引數新增 let {} 區塊:
imgUrl?.let { 
}
  1. let {} 區塊內,新增下方顯示的程式碼行,將網址字串 (來自 XML) 轉換為 Uri 物件。視需要匯入 androidx.core.net.toUri

    您希望最終的 Uri 物件使用 HTTPS 通訊協定,因為您從中提取圖片的伺服器需要該通訊協定。如要使用 HTTPS 配置,請將 buildUpon.scheme("https") 附加至 toUri 建構工具。toUri() 方法是 Android KTX 核心程式庫的 Kotlin 擴充功能函式,因此看起來就像是 String 類別的一部分。
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
  1. 同樣在 let {} 中,呼叫 Glide.with(),將 Uri 物件的圖片載入 ImageView。依要求匯入 com.bumptech.glide.Glide
Glide.with(imgView.context)
       .load(imgUri)
       .into(imgView)

步驟 4:更新版面配置和片段

雖然 Glide 已載入圖片,但目前沒有任何內容可供查看。下一步是更新版面配置和片段,並使用 ImageView 顯示圖片。

  1. 開啟 res/layout/gridview_item.xml。這是您稍後在程式碼研究室中,將用於 RecyclerView 中每個項目的版面配置資源檔案。您在這裡暫時使用它,只顯示單一圖片。
  2. <ImageView> 元素上方,為資料繫結新增 <data> 元素,並繫結至 OverviewViewModel 類別:
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
  1. ImageView 元素中新增 app:imageUrl 屬性,即可使用新的圖片載入繫結轉接器:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
  1. 開啟 overview/OverviewFragment.kt。在 onCreateView() 方法中,對於加載 FragmentOverviewBinding 類別並將其指派給繫結變數的行,排除其註解。請放心,這是暫時現象,稍後就會恢復。
//val binding = FragmentOverviewBinding.inflate(inflater)
  1. 請改為新增一行內容,以加載 GridViewItemBinding 類別。依要求匯入 com.example.android.marsrealestate. databinding.GridViewItemBinding
val binding = GridViewItemBinding.inflate(inflater)
  1. 執行應用程式。現在螢幕上會顯示結果清單中第一個 MarsProperty 的圖片。

步驟 5:新增簡單的載入和錯誤圖片

Glide 不僅可以顯示預留位置圖片,還能載入圖片,如果因圖片缺失或損毀等原因造成載入失敗,也可以載入錯誤圖片,進而改善使用者體驗。在這個步驟中,您需要為繫結轉接器和版面配置新增該功能。

  1. 開啟 res/drawable/ic_broken_image.xml,然後按一下右側的「預覽」分頁標籤。如果出現錯誤圖片,表示您使用的是內建圖示庫中的無效圖片圖示。此向量可繪項目使用 android:tint 屬性,將圖示變為灰色。

  1. 開啟 res/drawable/loading_animation.xml。這個可繪項目是使用 <animate-rotate> 標記定義的動畫。動畫會圍繞中心點旋轉圖片可繪項目 loading_img.xml。(預覽畫面不會顯示動畫)。

  1. 返回 BindingAdapters.kt 檔案。在 bindImage() 方法中,更新 Glide.with() 的呼叫,以便在 load()into() 之間呼叫 apply() 函式。在系統要求時匯入 com.bumptech.glide.request.RequestOptions

    此程式碼會設定載入時要使用的預留位置載入圖片 (loading_animation 可繪項目),此程式碼也會設定圖片載入失敗時要使用的圖片 (broken_image 可繪項目)。現在,完整的 bindImage() 方法大致如下:
@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. 執行應用程式。系統可能會在 Glide 下載並顯示屬性圖片時,暫時顯示載入圖片,具體情況視網路連線速度而定。但即使您關閉網路,此刻仍不會顯示毀損圖片圖示。此問題會在程式碼研究室的最後一部分修正。

現在,您的應用程式會從網際網路載入房地產資訊。透過使用第一個 MarsProperty 清單項目的資料,您已在檢視畫面模型中建立 LiveData 屬性,並使用該屬性資料中的圖片網址填入 ImageView。但我們的目標是讓應用程式顯示圖片格線,因此您要使用 RecyclerView 搭配 GridLayoutManager

步驟 1:更新檢視區塊模型

目前檢視模型有一個 _property LiveData,其中包含一個 MarsProperty 物件,也就是網路服務回應清單中的第一個物件。在此步驟中,您必須變更該 LiveData,使其存放 MarsProperty 物件的完整清單。

  1. 開啟 overview/OverviewViewModel.kt
  2. 將私有 _property 變數變更為 _properties。將類型變更為 MarsProperty 物件的清單。
private val _properties = MutableLiveData<List<MarsProperty>>()
  1. 將外部 property 即時資料替換為 properties。在這裡將清單加入 LiveData 型別:
 val properties: LiveData<List<MarsProperty>>
        get() = _properties
  1. 向下捲動至 getMarsRealEstateProperties() 方法。在 try {} 區塊中,將您在上一個工作新增的整個測試,替換為以下顯示的行。由於 listResult 變數會保留 MarsProperty 物件的清單,因此您可以將其指派給 _properties.value,而不必測試是否成功回應。
_properties.value = listResult

現在整個 try/catch 區塊如下所示:

try {
   var listResult = getPropertiesDeferred.await()
   _response.value = "Success: ${listResult.size} Mars properties retrieved"
   _properties.value = listResult
} catch (e: Exception) {
   _response.value = "Failure: ${e.message}"
}

步驟 2:更新版面配置和片段

下一個步驟是變更應用程式的版面配置和片段,改用回收器檢視畫面和格線版面配置,而非單張圖片檢視畫面。

  1. 開啟 res/layout/gridview_item.xml。將資料繫結從 OverviewViewModel 變更為 MarsProperty,並將變數重新命名為 "property"
<variable
   name="property"
   type="com.example.android.marsrealestate.network.MarsProperty" />
  1. <ImageView> 中,將 app:imageUrl 屬性變更為參照 MarsProperty 物件中的圖片網址:
app:imageUrl="@{property.imgSrcUrl}"
  1. 開啟 overview/OverviewFragment.kt。在 onCreateview() 中,取消註解加載 FragmentOverviewBinding 的行。刪除加載 GridViewBinding 的行或排除其註解。這些變更會復原您在上一個工作中做出的臨時變更。
val binding = FragmentOverviewBinding.inflate(inflater)
 // val binding = GridViewItemBinding.inflate(inflater)
  1. 開啟 res/layout/fragment_overview.xml。刪除整個 <TextView> 元素。
  2. 請改為新增這個 <RecyclerView> 元素,該元素會使用 GridLayoutManager 和單一項目的 grid_view_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" />

步驟 3:新增相片格線轉接器

現在,fragment_overview 版面配置有 RecyclerView,而 grid_view_item 版面配置則有單一 ImageView。在這個步驟中,您會透過 RecyclerView 轉接器將資料繫結至 RecyclerView

  1. 開啟 overview/PhotoGridAdapter.kt
  2. 使用如下所示的建構函式參數,建立 PhotoGridAdapter 類別。PhotoGridAdapter 類別會擴充 ListAdapter,其建構函式需要清單項目類型、檢視畫面持有者和 DiffUtil.ItemCallback 實作。

    視需要匯入 androidx.recyclerview.widget.ListAdaptercom.example.android.marsrealestate.network.MarsProperty 類別。在後續步驟中,您將實作此建構函式缺少的其他部分,這些部分會產生錯誤。
class PhotoGridAdapter : ListAdapter<MarsProperty,
        PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {

}
  1. 按一下 PhotoGridAdapter 類別中的任意位置,然後按下 Control+i 實作 ListAdapter 方法,也就是 onCreateViewHolder()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. PhotoGridAdapter 類別定義結尾,於您剛新增的方法後,為 DiffCallback 新增隨附物件定義,如下所示。

    依要求匯入 androidx.recyclerview.widget.DiffUtil

    DiffCallback 物件會將 DiffUtil.ItemCallback 擴充為要比較的物件類型:MarsProperty
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
  1. 按下 Control+i,為這個物件實作比較器方法,也就是 areItemsTheSame()areContentsTheSame()
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") 
}

override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") }
  1. areItemsTheSame() 方法中,移除 TODO。使用 Kotlin 的參照等式運算子 (===),如果 oldItemnewItem 的物件參照相同,則會傳回 true
override fun areItemsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem === newItem
}
  1. 對於 areContentsTheSame(),請只對 oldItemnewItem 的 ID 使用標準等號運算子。
override fun areContentsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem.id == newItem.id
}
  1. 仍在 PhotoGridAdapter 類別中,在隨附物件下方新增 MarsPropertyViewHolder 的內部類別定義,該定義可擴充 RecyclerView.ViewHolder

    系統要求時,請匯入 androidx.recyclerview.widget.RecyclerViewcom.example.android.marsrealestate.databinding.GridViewItemBinding

    您需要使用 GridViewItemBinding 變數將 MarsProperty 繫結至版面配置,因此請將該變數傳送至 MarsPropertyViewHolder。由於基本 ViewHolder 類別需要在其建構函式中設定檢視畫面,因此您要將其傳送至繫結的根檢視畫面。
class MarsPropertyViewHolder(private var binding: 
                   GridViewItemBinding):
       RecyclerView.ViewHolder(binding.root) {

}
  1. MarsPropertyViewHolder 中建立 bind() 方法,將 MarsProperty 物件做為引數,並將 binding.property 設為該物件。設定屬性後,請呼叫 executePendingBindings(),以立即執行更新作業。
fun bind(marsProperty: MarsProperty) {
   binding.property = marsProperty
   binding.executePendingBindings()
}
  1. onCreateViewHolder() 中,移除 TODO 並新增下列一行內容。依要求匯入 android.view.LayoutInflater

    onCreateViewHolder() 方法需要傳回新的 MarsPropertyViewHolder,加載 GridViewItemBinding 並使用父項 ViewGroup 背景資訊中的 LayoutInflater 即可建立。
   return MarsPropertyViewHolder(GridViewItemBinding.inflate(
      LayoutInflater.from(parent.context)))
  1. onBindViewHolder() 方法中,移除 TODO 並新增下列幾行內容。您可以在這裡呼叫 getItem(),取得與目前 RecyclerView 位置相關的 MarsProperty 物件,然後將該屬性傳遞至 MarsPropertyViewHolder 中的 bind() 方法。
val marsProperty = getItem(position)
holder.bind(marsProperty)

步驟 4:新增繫結轉接器並連結各個部分

最後,使用 BindingAdapter 初始化包含 MarsProperty 物件清單的 PhotoGridAdapter。若使用 BindingAdapter 設定 RecyclerView 資料,資料繫結將自動監控 LiveDataMarsProperty 物件清單。當 MarsProperty 清單有所變更時,系統會自動呼叫繫結轉接器。

  1. 開啟 BindingAdapters.kt
  2. 在檔案結尾,新增 bindRecyclerView() 方法,以 RecyclerViewMarsProperty 物件清單做為引數。請使用 @BindingAdapter 為該方法加上註解。

    視需要匯入 androidx.recyclerview.widget.RecyclerViewcom.example.android.marsrealestate.network.MarsProperty
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, 
    data: List<MarsProperty>?) {
}
  1. bindRecyclerView() 函式中,將 recyclerView.adapter 投放至 PhotoGridAdapter,然後使用資料呼叫 adapter.submitList()。出現新的清單時,這個屬性會通知 RecyclerView

依要求匯入 com.example.android.marsrealestate.overview.PhotoGridAdapter

val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
  1. 開啟 res/layout/fragment_overview.xml。接著將 app:listData 屬性新增至 RecyclerView 元素,然後使用資料繫結將屬性設為 viewmodel.properties
app:listData="@{viewModel.properties}"
  1. 開啟 overview/OverviewFragment.kt。在 onCreateView() 中,於呼叫 setHasOptionsMenu() 之前,將 binding.photosGrid 中的 RecyclerView 轉接器初始化為新的 PhotoGridAdapter 物件。
binding.photosGrid.adapter = PhotoGridAdapter()
  1. 執行應用程式。畫面上應該會顯示 MarsProperty 圖片格線。捲動畫面即可查看新圖片,但應用程式會在正常顯示圖片前,先顯示載入進度圖示。開啟飛航模式後,尚未載入的圖片會顯示為無效圖片圖示。

MarsRealEstate 應用程式會將無法擷取的圖片顯示為無效圖片圖示。但是如果沒有網路連線,應用程式會顯示空白畫面。

這無法提供良好的使用者體驗。在這項工作中,您需要新增基本的錯誤處理機制,讓使用者更好地瞭解發生的情況。如果未連線到網際網路,應用程式將顯示連線錯誤圖示。應用程式擷取 MarsProperty 清單時,會顯示載入動畫。

步驟 1:在檢視區塊模型中新增狀態

首先,您會在檢視區塊模型中建立 LiveData,代表網路要求的狀態。需要考慮的狀態有三種:載入、成功和故障。當您等待通話中的資料傳輸至 await() 時,畫面會顯示載入狀態。

  1. 開啟 overview/OverviewViewModel.kt。然後在檔案頂端 (匯入之後、類別定義之前) 新增 enum,代表所有可用狀態:
enum class MarsApiStatus { LOADING, ERROR, DONE }
  1. 在整個 OverviewViewModel 類別中,將內部和外部 _response 即時資料定義重新命名為 _status。由於您在本程式碼研究室稍早新增了 _properties LiveData 的支援功能,因此完整的網路服務回應一直未派上用場。您需要在此處使用 LiveData 追蹤目前狀態,因此只要重新命名現有變數即可。

此外,請將類型從 String 變更為 MarsApiStatus.

private val _status = MutableLiveData<MarsApiStatus>()

val status: LiveData<MarsApiStatus>
   get() = _status
  1. 向下捲動至 getMarsRealEstateProperties() 方法,並將 _response 更新為 _status。將 "Success" 字串變更為 MarsApiStatus.DONE 狀態,並將 "Failure" 字串變更為 MarsApiStatus.ERROR
  2. try {} 區塊頂端呼叫 await() 之前,新增 MarsApiStatus.LOADING 狀態。這是協同程式執行期間以及您在等待資料時的初始狀態。現在,完整的 try/catch {} 區塊如下所示:
try {
    _status.value = MarsApiStatus.LOADING
   var listResult = getPropertiesDeferred.await()
   _status.value = MarsApiStatus.DONE
   _properties.value = listResult
} catch (e: Exception) {
   _status.value = MarsApiStatus.ERROR
}
  1. catch {} 區塊中的錯誤狀態之後,將 _properties LiveData 設為空白清單。這會清除 RecyclerView
} catch (e: Exception) {
   _status.value = MarsApiStatus.ERROR
   _properties.value = ArrayList()
}

步驟 2:為 ImageView 狀態新增繫結轉接器

現在您在檢視模型中已有狀態,但這只是一組狀態。如何讓應用程式顯示這項資訊?在這個步驟中,您會使用與資料繫結連結的 ImageView,顯示載入和錯誤狀態的圖示。如果應用程式處於載入狀態或錯誤狀態,則應顯示 ImageView。應用程式載入完畢後,應不再顯示 ImageView

  1. 開啟 BindingAdapters.kt。新增名為 bindStatus() 的新繫結轉接器,以 ImageViewMarsApiStatus 值做為引數。依要求匯入 com.example.android.marsrealestate.overview.MarsApiStatus
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView, 
          status: MarsApiStatus?) {
}
  1. bindStatus() 方法中新增 when {},即可在不同狀態之間切換。
when (status) {

}
  1. when {} 中,新增載入狀態的案例 (MarsApiStatus.LOADING)。對於這個狀態,請將 ImageView 設為顯示,並指派載入動畫。這個動畫可繪項目與先前工作中 Glide 採用的可繪項目相同。依要求匯入 android.view.View
when (status) {
   MarsApiStatus.LOADING -> {
      statusImageView.visibility = View.VISIBLE
      statusImageView.setImageResource(R.drawable.loading_animation)
   }
}
  1. 新增錯誤狀態的案例,即 MarsApiStatus.ERROR。如同 LOADING 狀態,請將狀態 ImageView 設為顯示,並重複使用連線錯誤可繪項目。
MarsApiStatus.ERROR -> {
   statusImageView.visibility = View.VISIBLE
   statusImageView.setImageResource(R.drawable.ic_connection_error)
}
  1. 新增已完成狀態的案例,即 MarsApiStatus.DONE。您收到了正確的回應,請關閉狀態 ImageView 的瀏覽權限以隱藏該狀態。
MarsApiStatus.DONE -> {
   statusImageView.visibility = View.GONE
}

步驟 3:將狀態 ImageView 新增至版面配置

  1. 開啟 res/layout/fragment_overview.xml。在 RecyclerView 元素下方的 ConstraintLayout 內,新增下方顯示的 ImageView

    這個 ImageView 的限制與 RecyclerView 相同。不過,所設寬度和高度會使用 wrap_content 將圖片置中,而不是延展圖片來填滿檢視畫面。另請注意 app:marsApiStatus 屬性,當檢視區塊模型中的狀態屬性變更時,該屬性會讓檢視區塊呼叫 BindingAdapter
<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. 在模擬器或裝置上開啟飛航模式,模擬網路連線中斷。接著編譯並執行應用程式,請注意,畫面上會顯示錯誤圖片:

  1. 輕觸返回按鈕即可關閉應用程式,並關閉飛航模式。使用「最近使用」畫面返回應用程式。當應用程式查詢網路服務時,圖片開始載入之前,畫面上可能會極短暫地顯示載入旋轉圖示,具體視您的網路連線速度而定。

Android Studio 專案:MarsRealEstateGrid

  • 如要簡化圖片管理程序,請使用 Glide 程式庫,在應用程式中下載、緩衝處理、解碼及快取圖片。
  • 如要從網際網路載入圖片,Glide 需要兩項資訊:圖片的網址,以及要放置圖片的 ImageView 物件。如要指定這些選項,請搭配 Glide 使用 load()into() 方法。
  • 繫結轉接器是一種擴充方法,顯示在檢視畫面與檢視畫面的繫結資料之間。資料變更時,繫結轉接器會提供自訂行為,例如呼叫 Glide 將網址中的圖片載入 ImageView
  • 繫結轉接器是擴充方法,附有 @BindingAdapter 註解。
  • 如要為 Glide 要求新增選項,請使用 apply() 方法。舉例來說,使用 apply()placeholder() 指定載入中的可繪項目,並使用 apply()error() 指定錯誤的可繪項目。
  • 如要製作圖片格線,請將 RecyclerViewGridLayoutManager 搭配使用。
  • 如要在屬性變更時更新屬性清單,請使用 RecyclerView 和版面配置之間的繫結轉接器。

Udacity 課程:

Android 開發人員說明文件:

其他:

本節列出的作業可由課程講師指派給學習本程式碼研究室的學員。講師可自由採取以下行動:

  • 視需要指派作業。
  • 告知學員如何繳交作業。
  • 為作業評分。

講師可以視需求使用全部或部分建議內容,也可以自由指派任何其他合適的作業。

如果您是自行學習本程式碼研究室,不妨利用這些作業驗收學習成果。

回答以下問題

第 1 題

你會使用哪一種 Glide 方法來表示會包含已載入圖片的 ImageView

into()

with()

imageview()

apply()

第 2 題

如何指定在載入 Glide 時顯示的預留位置圖片?

▢ 搭配可繪項目使用 into() 方法。

▢ 使用 RequestOptions(),並呼叫包含可繪項目的 placeholder() 方法。

▢ 將 Glide.placeholder 屬性指派給可繪項目。

▢ 使用 RequestOptions(),並呼叫包含可繪項目的 loadingImage() 方法。

第 3 題

要如何指出方法實為繫結轉接器?

▢ 呼叫 LiveDatasetBindingAdapter() 方法。

▢ 將方法置入名為 BindingAdapters.kt 的 Kotlin 檔案。

▢ 在 XML 版面配置中使用 android:adapter 屬性。

▢ 使用 @BindingAdapter 為方法加上註解。

開始下一個課程:8.3 篩選網際網路資料並建立詳細資料檢視畫面

如要查看本課程其他程式碼研究室的連結,請參閱 Android Kotlin 基礎知識程式碼研究室登陸頁面