Android Kotlin 基礎課程 08.2:從網際網路載入及顯示圖片

本程式碼研究室是 Android Kotlin 基礎課程的一部分。使用程式碼研究室逐步完成程式碼課程後,您將能充分發揮本課程的潛能。所有課程程式碼研究室清單均列於 Android Kotlin 基礎程式碼研究室到達網頁

簡介

在先前的程式碼研究室中,您已學會如何從網路服務取得資料,並將回應剖析為資料物件。在本程式碼研究室中,你可以利用這項知識來載入和顯示網址中的相片。您也可以回顧如何建構 RecyclerView 並使用它在總覽頁面上顯示圖片格線。

須知事項

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

您將會瞭解的內容

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

要執行的步驟

  • 修改 MarsRealEstate 應用程式以取得 Mars 屬性資料中的圖片網址,然後使用 Glide 載入並顯示該圖片。
  • 在應用程式中新增載入動畫和錯誤圖示。
  • 使用 RecyclerView 即可顯示包含火星屬性圖片的格線。
  • RecyclerView 新增狀態和錯誤處理機制。

在這個程式碼研究室 (及相關的程式碼研究室) 中,您使用了名為 MarsRealEstate 的應用程式,在 Mars 上展示要銷售的房產。應用程式會連線至網際網路伺服器,以擷取和顯示屬性資料,包括價格、是否販售或出租等詳細資料。每個資源代表的圖片都是從 NASA 所拍攝的火星漫遊的實景照片。

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

顯示來自網址的相片聽起來可能很簡單,但卻需要很多工程技術才能成功。圖片必須下載、緩衝處理,以及從壓縮格式中解碼為 Android 可使用的圖片。圖片應快取至記憶體內快取或儲存空間型快取,或同時快取至兩者。這需要在低優先順序背景執行緒中完成,這樣 UI 才不會回應。此外,為了獲得最佳網路和 CPU 效能,建議您一次擷取並解碼多張圖片。學習如何從網路有效載入圖片,可能是程式碼研究室本身。

幸好,您可以使用社群開發的 Glide 程式庫下載、緩衝處理、解碼及快取圖片。相較於單純從頭開始完成的工作,滑行作業可以省下許多心力。

滑行基本上需要兩件事情:

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

在這項工作中,您會學到如何使用 Glide 顯示房地產網路服務中的單一圖片。您可以在網路服務傳回的屬性清單中,呈現第一個火星屬性的圖片。以下是前後對照的螢幕截圖:

步驟 1:新增 Glide 依附元件

  1. 從最後一個程式碼研究室開啟 MarsRealEstate 應用程式。(如果您沒有這個應用程式,可以在這裡下載 MarsRealEstateNetwork)。
  2. 執行應用程式以查看其用途。(內容會顯示在 Tus 上提供假設的資源屬性詳細資料)。
  3. 開啟 build.gradle (Module: app)
  4. dependencies 區段中,為 Glide 程式庫新增這一行:
implementation "com.github.bumptech.glide:glide:$version_glide"


請注意,專案編號已在專案 Gradle 檔案中另行定義。

  1. 請按一下 [立即同步處理],以新的依附元件重新建立專案。

步驟 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 為函式加上註解。當 XML 項目具有 imageUrl 屬性時,@BindingAdapter 註解會告知資料繫結您想要這個繫結轉接程式。

    可要求匯入 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. app:imageUrl 屬性新增至 ImageView 元素,以使用新的圖片載入繫結轉接程式:
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。但應用程式的目標是顯示格線格線,所以你想將 RecyclerViewGridLayoutManager 搭配使用。

步驟 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> 元素,這個元素會針對單一項目使用 GridLayoutManagergrid_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's 參照等式運算子 (===)。如果 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. 在呼叫 await() 之前,於 try {} 區塊頂端加上 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 應隱藏。

  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 設為顯示,並指派載入動畫。這與您在前一項工作中用於滑行時使用的動畫繪圖功能相同。在要求時匯入 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

    這個 ImageViewRecyclerView 的限制相同。不過,寬度和高度會使用 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 程式庫來下載、緩衝處理、解碼及快取應用程式中的圖片。
  • 他必須從網際網路載入圖片中的兩個項目:圖片網址,以及要放置圖片的 ImageView 物件。若要指定這些選項,請搭配 Glide 使用 load()into() 方法。
  • 繫結轉接器是一種擴充方法,顯示在檢視畫面與檢視畫面的繫結資料之間。舉例來說,呼叫 Glide 即可呼叫 Glide,將圖片從網址載入至 ImageView,以提供自訂行為。
  • 繫結轉接器是加上 @BindingAdapter 註解的擴充功能方法。
  • 如要在 Glide 要求中新增選項,請使用 apply() 方法。舉例來說,使用 apply() 搭配 placeholder() 即可指定載入的可繪項目,使用 apply() 搭配 error() 來指定可繪製的錯誤。
  • 如要產生圖片格線,請使用 RecyclerView 搭配 GridLayoutManager
  • 如要在屬性變更時更新屬性清單,請在 RecyclerView 和版面配置之間使用繫結轉接器。

Udacity 課程:

Android 開發人員說明文件:

其他:

這個部分會列出在代碼研究室中,受老師主導的課程作業的可能學生作業。由老師自行決定要執行下列動作:

  • 視需要指派家庭作業。
  • 告知學生如何提交家庭作業。
  • 批改家庭作業。

老師可視需要使用這些建議,並視情況指派其他合適的家庭作業。

如果您是自行操作本程式碼研究室,歡迎透過這些家庭作業來測試自己的知識。

回答這些問題

第 1 題

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

into()

with()

imageview()

apply()

第 2 題

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

▢ 使用可繪製的 into() 方法。

▢ 使用 RequestOptions(),然後以可繪製的方式呼叫 placeholder() 方法。

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

▢ 使用 RequestOptions(),然後以可繪製的方式呼叫 loadingImage() 方法。

第 3 題

如何指明方法為繫結轉接程式?

▢ 呼叫 LiveData 上的 setBindingAdapter() 方法。

▢ 將此方法放入名為 BindingAdapters.kt 的 Kotlin 檔案中。

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

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

開始下一堂課:8.3 使用網際網路資料篩選及詳細檢視

如要瞭解本課程中其他程式碼研究室的連結,請參閱 Android Kotlin 基礎程式碼程式碼到達網頁