本程式碼研究室是 Android Kotlin 基礎課程的一部分。使用程式碼研究室逐步完成程式碼課程後,您將能充分發揮本課程的潛能。所有課程程式碼研究室清單均列於 Android Kotlin 基礎程式碼研究室到達網頁。
簡介
本程式碼研究室可教導如何使用 RecyclerView
顯示項目清單。使用前一代程式碼研究室的睡眠追蹤應用程式進行建構,您就能透過採用建議架構的 RecyclerView
,以更靈活且更多種的方式顯示資料。
須知事項
您應該很熟悉:
- 運用活動、片段和視圖來建構基本的使用者介面 (UI)。
- 在片段之間瀏覽,並使用
safeArgs
在片段之間傳送資料。 - 使用檢視模型、檢視模型工廠、轉換,以及
LiveData
及其觀測器。 - 建立
Room
資料庫、建立 DAO 以及定義實體。 - 使用協同程式處理資料庫工作和其他長時間執行的工作。
課程內容
- 如何使用具有
Adapter
和ViewHolder
的RecyclerView
來顯示項目清單。
執行步驟
- 將「TrackMySleepquality」應用程式從上一個課程變更為使用
RecyclerView
顯示睡眠品質資料。
在這個程式碼研究室中,您將為追蹤睡眠品質的應用程式建立 RecyclerView
部分。這個應用程式會使用 Room
資料庫儲存一段時間內的睡眠資料。
啟動睡眠追蹤器應用程式有兩個畫面,以片段表示,如下圖所示。
第一個畫面 (如左側所示) 為開始和停止追蹤的按鈕。這個畫面也會顯示使用者的睡眠資料。[清除] 按鈕永久刪除應用程式已收集的所有資料。第二個螢幕則是根據選取睡眠品質評分的方式。
這個應用程式使用簡化的架構搭配 UI 控制器 ViewModel
和 LiveData
。應用程式也會使用 Room
資料庫,將睡眠資料永久保留下來。
第一個畫面中顯示的睡眠之夜功能可正常運作,但內容不完全正確。此應用程式使用複雜的格式化工具,為文字檢視建立文字字串,並為品質提供數字。此外,這項設計不會擴充透過這個程式碼研究室修正所有問題後,最終應用程式會提供相同的功能,且主畫面的外觀如下:
顯示清單或格線的資料是 Android 最常用的使用者介面工作之一。清單的內容十分簡單,文字檢視清單可能會顯示簡單資料,例如購物清單。複雜的清單 (例如以註解的度假目的地清單) 可能會在包含捲動標頭的捲動格狀空間中,向使用者顯示許多詳細資料。
為了支援上述所有用途,Android 提供了 RecyclerView
小工具。
「RecyclerView
」最大的優點在於,對於大型清單而言,效果極佳:
- 根據預設,
RecyclerView
只會處理或繪製目前顯示在畫面上的項目。舉例來說,如果清單中有 1,000 個元素,但只有 10 個元素可以顯示,RecyclerView
只會使用 10 個項目來在畫面上擷取 10 個項目。當使用者捲動畫面時,RecyclerView
會判斷螢幕上顯示的新項目,而只要花一些時間就能顯示這些項目。 - 當項目捲動離開畫面時,會回收該項目的檢視。這表示該項目填入了可以捲動在畫面上的新內容。這種
RecyclerView
行為可大幅縮短處理時間,並有助於建立順暢的捲動清單。 - 當項目變更時,
RecyclerView
不會重整整份清單,而是直接更新該項目。顯示複雜項目清單時,能大幅提升效率!
在下方的序列中,您可以看到一個已填入資料 ABC
的檢視畫面。在該畫面捲動至螢幕外之後,RecyclerView
會讓新資料 (XYZ
) 重複使用檢視畫面。
轉接器圖案
如果您經常在不同使用不同電源插座的國家/地區旅行,或許知道如何使用轉接器將裝置插入插座。此轉接器可讓您將一種類型的插座轉換為另一個,也就是將一個介面轉換為另一個介面。
軟體工程的調整模式可協助物件與其他 API 搭配運作。RecyclerView
使用轉接程式將應用程式資料轉換成 RecyclerView
顯示的內容,而不會變更應用程式儲存及處理資料的方式。針對睡眠追蹤器應用程式,您將建立轉接器,將 Room
資料庫中的資料調整為RecyclerView
的顯示內容,而不變更 ViewModel
。
實作 RecyclerView
如要在 RecyclerView
中顯示您的資料,您必須符合以下條件:
- 要顯示的資料。
- 在版面配置檔案中定義的
RecyclerView
執行個體,做為資料檢視的容器。 - 單一資料項目的版面配置。
如果所有清單項目看起來都一樣,則可對所有項目使用相同的版面配置,但這並非強制性的。項目版面配置必須與片段版面配置分開建立,因此可以一次建立一個項目檢視並填入資料。 - 版面配置管理工具。
版面配置管理員會在資料檢視中處理 UI 元件的機構 (版面配置)。 - 視圖架。
檢視架可延伸ViewHolder
類別。內含顯示項目版面配置顯示項目的檢視資訊。資料檢視擁有者也會新增RecyclerView
使用的資訊,可有效移動畫面瀏覽。 - 轉接器。
轉接器會將資料連接至RecyclerView
。可調整資料,以便在ViewHolder
中顯示資料。RecyclerView
使用轉接頭來瞭解如何在螢幕上顯示資料。
在這項工作中,您將 RecyclerView
新增至版面配置檔案,並設定 Adapter
來將睡眠資料提供給 RecyclerView
。
步驟 1:使用 LayoutManager 新增 RecyclerView
在這個步驟中,您將 fragment_sleep_tracker.xml
檔案中的 ScrollView
替換為 RecyclerView
。
- 從 GitHub 下載 RecyclerViewFundamentals-Starter 應用程式。
- 建構並執行應用程式。請注意資料如何以純文字顯示。
- 開啟 Android Studio 中「設計」分頁中的
fragment_sleep_tracker.xml
版面配置檔案。 - 在「元件樹狀結構」窗格中刪除
ScrollView
。這個動作會一併刪除ScrollView
中的TextView
。 - 在「Palette」窗格中,捲動左側的元件類型清單以尋找「Containers」(容器),然後選取該元件。
- 將
RecyclerView
從「Pelette」窗格拖曳至「Component Tree」窗格。將RecyclerView
置於ConstraintLayout
內。
- 如果畫面上出現對話方塊,詢問您是否要新增依附元件,請按一下 [確定],讓 Android Studio 將
recyclerview
依附元件加入您的 Gradle 檔案。應用程式可能需要幾秒鐘的時間才能完成同步處理,
- 開啟模組
build.gradle
檔案,捲動至結尾,然後記下新的依附元件,如下所示:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- 切換回
fragment_sleep_tracker.xml
。 - 在「Text」分頁中,找出下列
RecyclerView
程式碼:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 向「
RecyclerView
」提供sleep_list
的id
。
android:id="@+id/sleep_list"
- 讓
RecyclerView
佔據ConstraintLayout
內部畫面的剩餘部分。方法是將RecyclerView
的頂部限制在 [開始] 按鈕、底部的 [清除] 按鈕,以及每一側的父項。在版面配置編輯器或 XML 中,使用以下程式碼將版面配置寬度和高度設為 0 dp:
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button"
- 在
RecyclerView
XML 中加入版面配置管理工具。每個「RecyclerView
」都需要版面配置管理員,說明如何將清單中的項目排序。Android 提供的是LinearLayoutManager
,根據預設,系統會按照完整寬度列的垂直清單顯示項目。
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- 切換至「設計」分頁,並留意增加的限制會導致「
RecyclerView
」展開以填滿可用空間。
步驟 2:建立清單項目版面配置和文字檢視工具
RecyclerView
只是容器。在這個步驟中,您會建立要在 RecyclerView
內呈現項目的版面配置和基礎架構。
如要盡快前往 RecyclerView
,你使用的只是簡易清單項目,而且只能以數字形式顯示睡眠品質。您必須擁有 TextItemViewHolder
的檢視者才能執行這項操作。您也需要資料檢視 TextView
,才能檢視資料。(在後續的步驟中,您可以進一步瞭解觀看擁有人,以及如何制定所有睡眠資料)。
- 建立名為「
text_item_view.xml
」的版面配置檔案。不論使用哪個元素,都不會影響根元素,因為您將取代範本程式碼。 - 在
text_item_view.xml
中,刪除所有指定的程式碼。 - 新增在開頭和結尾加上
16dp
的TextView
,並將文字大小設為24sp
。讓寬度與父項保持一致,而高度會納入內容。由於這個資料檢視位於RecyclerView
內,因此您不需要將檢視放在ViewGroup
中。
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- 開啟
Util.kt
。捲動至結尾,並新增下方所述的定義,建立TextItemViewHolder
類別。將程式碼放在檔案最後的大括號之後。這個存取碼為Util.kt
,因為這個檢視畫面保留程式目前只是暫時的,因此之後即可取代。
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
- 如果出現系統提示,請匯入
android.widget.TextView
和androidx.recyclerview.widget.RecyclerView
。
步驟 3:建立 SleepNightAdapter
執行 RecyclerView
的核心工作是建立轉接程式。您有一個項目檢視的簡易檢視區塊,以及每個項目的版面配置。您現在可以建立轉接程式。轉接程式會建立檢視器並填入資料以顯示 RecyclerView
。
- 在
sleeptracker
套件中,新建名為SleepNightAdapter
的 Kotlin 類別。 - 讓
SleepNightAdapter
類別延長RecyclerView.Adapter
。這個類別稱為SleepNightAdapter
,因為它會將SleepNight
物件調整為RecyclerView
可使用的物件。轉接器需要知道要使用的檢視器,請傳入TextItemViewHolder
。按照系統提示匯入必要元件後,您會看到錯誤訊息,因為必須實作必要方法。
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
- 在
SleepNightAdapter
的頂層建立listOf
SleepNight
變數,以存放資料。
var data = listOf<SleepNight>()
- 在
SleepNightAdapter
中覆寫getItemCount()
,以傳回data
中的睡眠夜晚清單大小。「RecyclerView
」需要瞭解轉接器的數量,且需呼叫getItemCount()
。
override fun getItemCount() = data.size
- 在
SleepNightAdapter
中覆寫onBindViewHolder()
函式,如下所示。RecyclerView
會呼叫onBindViewHolder()
函式,以顯示單一清單項目在指定位置的資料。因此onBindViewHolder()
方法會使用兩個引數:檢視區塊,以及要繫結的資料位置。在這個應用程式中,持有人是TextItemViewHolder
,而排名是清單中的排名。
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
- 在
onBindViewHolder()
中,為資料中的某個位置建立變數。
val item = data[position]
- 您建立的
ViewHolder
具有名為textView
的屬性。在onBindViewHolder()
內,將textView
的text
設為睡眠品質數字。這個代碼只會顯示數字清單,不過這個簡單的範例可讓您查看轉接程式如何將資料匯入資料檢視持有者,並在螢幕上顯示。
holder.textView.text = item.sleepQuality.toString()
- 在
SleepNightAdapter
中,覆寫並實作onCreateViewHolder()
,當RecyclerView
需要檢視區塊來代表項目時,就會呼叫此。
此函式會使用兩個參數,並傳回ViewHolder
。parent
參數是擁有資料檢視資料檢視的資料檢視群組,一律為RecyclerView
。當同一個RecyclerView
中有多個檢視表時,系統會使用viewType
參數。舉例來說,如果您將文字檢視、圖片和影片等全部放在同一個RecyclerView
中,onCreateViewHolder()
函式就必須知道要使用的檢視類型。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
- 在
onCreateViewHolder()
中,建立LayoutInflater
的執行個體。
版面配置選項可幫助你瞭解如何透過 XML 版面配置建立檢視畫面。context
包含關於如何正確增加視圖的資訊。在回收再生視圖的轉接頭中,您一律可以進入parent
檢視群組,也就是RecyclerView
。
val layoutInflater = LayoutInflater.from(parent.context)
- 在
onCreateViewHolder()
中,要求layoutinflater
建立膨脹,然後建立view
。
可傳入資料檢視的 XML 版面配置,以及資料檢視的parent
資料檢視群組。第三個布林引數是attachToRoot
。這個引數必須是false
,因為RecyclerView
會在其時間將您加入檢視階層。
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView
- 在
onCreateViewHolder()
中,傳回由「view
」製作的TextItemViewHolder
。
return TextItemViewHolder(view)
- 適配器需要通知
RecyclerView
data
在RecyclerView
它只會知道轉接程式提供的檢視區塊。
如要讓RecyclerView
在其顯示資料變更時通知RecyclerView
,請在SleepNightAdapter
類別頂端的data
變數中加入一個自訂設定器。在設定集中,為data
提供一個新的值,然後呼叫notifyDataSetChanged()
以使用新資料重新觸發清單。
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}
步驟 4:告知 RecyclerView 關於轉接器
「RecyclerView
」需要知道轉接程式,才能取得檢視者。
- 開啟
SleepTrackerFragment.kt
。 - 在
onCreateview()
建立轉接器。請在建立ViewModel
模型後 (在return
陳述式之前) 加入這段程式碼。
val adapter = SleepNightAdapter()
- 將
adapter
與RecyclerView
建立關聯。
binding.sleepList.adapter = adapter
- 清理並重建專案以更新
binding
物件。
如果binding.sleepList
或binding.FragmentSleepTrackerBinding
左右仍持續發生錯誤,請撤銷快取並重新啟動。(選取 [檔案 > 撤銷快取/重新啟動])。
如果現在執行應用程式,就表示沒有任何錯誤,但如果您依序輕觸 [開始] 和 [停止],系統並不會顯示任何資料。
步驟 5:將資料新增至轉接頭
您目前已有一個轉接程式,以及將資料從轉接器插入 RecyclerView
的方法。現在您必須從「ViewModel
」中將資料新增至轉接頭。
- 開啟
SleepTrackerViewModel
。 - 找出
nights
變數,該變數會儲存所有睡眠晚數,這是顯示的資料。nights
變數是透過在資料庫中呼叫getAllNights()
而設定。 - 將「
private
」從「nights
」中移除,因為您要建立需要存取這個變數的觀測器。您的宣告看起來應該像這樣:
val nights = database.getAllNights()
- 在
database
套件中開啟SleepDatabaseDao
。 - 找出
getAllNights()
函式。請注意,此函式會傳回SleepNight
值清單做為LiveData
。這表示nights
變數包含LiveData
。這個變數會由Room
持續更新。您可以透過nights
瞭解資料何時有所變更。 - 開啟
SleepTrackerFragment
。 - 在
onCreateView()
的adapter
建立下方,針對nights
變數建立觀測器。
請提供片段的viewLifecycleOwner
做為生命週期擁有者,可確保只有當RecyclerView
出現在螢幕上時,這個觀測器才會啟用。
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})
- 在觀察器內,每當你收到非空值的值 (
nights
) 時,請將這個值指派給轉接程式的data
。這是觀測器完成設定和設定資料的程式碼:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})
- 建構並執行程式碼。
如果變壓器可以使用,你會看到睡眠品質數字清單。系統會在您輕觸 [開始] 後顯示左側的螢幕截圖 -1。在您輕觸 [停止] 並選取品質評分後,右側螢幕截圖會顯示更新後的睡眠品質數據。
步驟 6:探索檢視模式的回收業者
RecyclerView
「回收」檢視持有人,這表示它會重複使用這些憑證。當畫面捲動離開畫面時,RecyclerView
會重複使用一個檢視畫面,讓畫面顯示在畫面上。
由於這些檢視區塊已回收,請確認 onBindViewHolder()
已設定或重設先前在檢視項目上設定的所有項目。
舉例來說,如果某位通話品質等級低於 1 或代表睡眠品質不佳,此時就可以將文字顏色設為紅色。
- 在
SleepNightAdapter
類別中,將下列程式碼加進onBindViewHolder()
的結尾。
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}
- 執行應用程式。
- 新增一些睡眠品質低落的資料,且數字為紅色。
- 為睡眠品質加上高評分,直到畫面上出現紅色的高數字為止。
由於RecyclerView
會重複使用觀看人數,最終只會讓其中一個紅色觀看者擁有高品質的評分。高分等級會以紅色標示。
- 如要修正這個問題,請新增「
else
」陳述式,以便將顏色設為黑色或等於不等於一個顏色。
將上述兩種條件都設為「清晰」時,檢視者會針對每個項目使用正確的文字顏色。
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}
- 執行應用程式,且所有號碼應使用正確的顏色。
恭喜!現在,您的基本功能 RecyclerView
可正常運作。
在這項工作中,您需使用簡單的檢視畫面來取代顯示睡眠資料的其他資料。
您添加到 Util.kt
的簡單 ViewHolder
只是將 TextView
包覆在 TextItemViewHolder
。
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
為什麼 RecyclerView
不是直接使用 TextView
?這行程式碼可以提供許多功能。ViewHolder
說明項目在 RecyclerView
中的檢視項目及其中繼資料。「RecyclerView
」必須使用這項功能才能隨著清單捲動而正確定位檢視區塊,以及在 Adapter
中新增或移除項目時產生一些有趣的內容,例如動畫檢視。
如果 RecyclerView
需要存取儲存在 ViewHolder
中的資料檢視,可使用資料檢視擁有者的 itemView
屬性。RecyclerView
將項目繫結至螢幕上以便顯示項目、在邊框周圍繪製裝飾,以及實作協助工具時,會使用 itemView
。
步驟 1:建立項目版面配置
在這個步驟中,您會建立一個商品項目的版面配置檔案。版面配置包含 ConstraintLayout
和睡眠品質 ImageView
,以及 TextView
的睡眠長度,以及 TextView
的文字內容。您先前曾完成版面配置,因此請複製及貼上系統提供的 XML 程式碼。
- 建立新的版面配置資源檔案,並命名為
list_item_sleep_night
。 - 請將檔案中的所有程式碼替換成以下程式碼。接著,請熟悉您剛建立的版面配置。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/quality_image"
android:layout_width="@dimen/icon_size"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_sleep_5" />
<TextView
android:id="@+id/sleep_length"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_image"
app:layout_constraintTop_toTopOf="@+id/quality_image"
tools:text="Wednesday" />
<TextView
android:id="@+id/quality_string"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@+id/sleep_length"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/sleep_length"
app:layout_constraintTop_toBottomOf="@+id/sleep_length"
tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 切換至 Android Studio 中的「設計」分頁。在設計檢視模式中,您的版面配置如下所示。在藍圖檢視中,看似右側螢幕截圖。
步驟 2:建立 ViewHolder
- 開啟
SleepNightAdapter.kt
。 - 在
SleepNightAdapter
中建立名為ViewHolder
的類別,讓該類別延伸RecyclerView.ViewHolder
。
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
ViewHolder
內取得資料檢視參照。您需要參照此ViewHolder
將會更新的檢視。每次繫結此「ViewHolder
」時,都需要存取圖片和兩種文字檢視。(您之後會將此程式碼轉換為使用資料繫結)。
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)
步驟 3:在 SleepNightAdapter 中使用 ViewHolder
- 在
SleepNightAdapter
定義中,請使用TextItemViewHolder
,不要使用您剛剛建立的SleepNightAdapter.ViewHolder
。
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {
更新 onCreateViewHolder()
:
- 變更
onCreateViewHolder()
的簽名以傳回ViewHolder
。 - 將版面配置充氣器變更為使用正確的版面配置資源
list_item_sleep_night
。 - 將投放內容移除至
TextView
。 - 傳回
ViewHolder
檔案,而不是傳回TextItemViewHolder
。
以下是完成的onCreateViewHolder()
函式:
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater =
LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night,
parent, false)
return ViewHolder(view)
}
更新 onBindViewHolder()
:
- 變更
onBindViewHolder()
的簽名,使holder
參數是ViewHolder
而非TextItemViewHolder
。 - 在
onBindViewHolder()
內,除了item
的定義以外,請刪除所有程式碼。 - 定義一個
val
res
,用來存放此資料檢視的resources
。
val res = holder.itemView.context.resources
- 將
sleepLength
文字檢視的文字設為持續時間。複製以下程式碼,該程式碼會呼叫 和範例程式碼所提供的格式設定函式。
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
- 由於需要定義
convertDurationToFormatted()
,因此發生錯誤。開啟「Util.kt
」並取消註解該程式碼與相關的匯入項目。(選取 [程式碼 &註解 (含註解)])。 - 返回
onBindViewHolder()
,使用convertNumericQualityToString()
設定畫質。
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
- 您可能需要手動匯入這些函式。
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
- 為品質設定正確的圖示。範例程式碼中會顯示新的
ic_sleep_active
圖示。
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
- 以下是已完成的
onBindViewHolder()
函式,用於設定ViewHolder
的所有資料:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
- 執行您的應用程式。顯示畫面應如下圖所示,顯示睡眠品質圖示,以及睡眠時長與睡眠品質的文字。
「RecyclerView
」現已完成!您已經瞭解如何實作 Adapter
和 ViewHolder
,並且成功整理出包含 RecyclerView
Adapter
的清單。
目前您的程式碼顯示建立轉接程式和檢視持有者的程序。不過,您可以改善這個程式碼。要顯示的程式碼和管理資料檢視持有者的程式碼會混合,onBindViewHolder()
也知道如何更新 ViewHolder
。
在正式版應用程式中,您可能有多個檢視點、更複雜的轉接程式,以及多位開發人員進行變更。您應該妥善設計程式碼,讓所有與資料檢視擁有者相關的項目都只屬於資料檢視持有者。
步驟 1:重構 onBindViewHolder()
在這個步驟中,您會重構程式碼,並將所有檢視器功能移至 ViewHolder
。這項重構的目的在於改變應用程式的呈現方式,不讓開發人員以更輕鬆、安全的方式處理程式碼。幸好,Android Studio 提供了多項實用工具,
- 在
SleepNightAdapter
中,選取onBindViewHolder()
以外的陳述式以宣告變數item
。 - 按一下滑鼠右鍵,然後選取 [重新計算] > [擷取 & 函式]。
- 將函式命名為
bind
並接受建議的參數。按一下 [確定]。bind()
函式位於onBindViewHolder()
下方。
private fun bind(holder: ViewHolder, item: SleepNight) {
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
- 將遊標移到
bind()
的holder
參數文字holder
上。按下Alt+Enter
(在 Mac 上為Option+Enter
) 開啟意圖選單。選取 [轉換參數到接收器],轉換成具備下列簽名的擴充功能函式:
private fun ViewHolder.bind(item: SleepNight) {...}
- 剪下
bind()
函式並貼到ViewHolder
。 - 將
bind()
設為公開。 - 視需要將
bind()
匯入轉接程式。 - 因為它為了在
ViewHolder
中,所以您可以移除簽章的ViewHolder
部分。以下是ViewHolder
類別中bind()
函式的最終程式碼。
fun bind(item: SleepNight) {
val res = itemView.context.resources
sleepLength.text = convertDurationToFormatted(
item.startTimeMilli, item.endTimeMilli, res)
quality.text = convertNumericQualityToString(
item.sleepQuality, res)
qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
步驟 2:重構 onCreateViewHolder
轉接器中的 onCreateViewHolder()
方法目前會從 ViewHolder
的版面配置資源開放視圖。但是,通貨膨脹不適用於適配器,以及如何使用 ViewHolder
。通貨膨脹將於 ViewHolder
發生。
- 在
onCreateViewHolder()
中,選取函式主體中的所有程式碼。 - 按一下滑鼠右鍵,然後選取 [重新計算] > [擷取 & 函式]。
- 將函式命名為
from
並接受建議的參數。按一下 [確定]。 - 將遊標移到函式名稱
from
上。按下Alt+Enter
(在 Mac 上為Option+Enter
) 開啟意圖選單。 - 選取 [移至隨附物件]。
from()
函式必須在隨附物件中,以便可在ViewHolder
類別中呼叫,而非在ViewHolder
執行個體上呼叫。 - 將
companion
物件移到ViewHolder
類別中。 - 將
from()
設為公開。 - 在
onCreateViewHolder()
中,變更return
陳述式以傳回ViewHolder
類別中呼叫from()
的結果。
您已完成的onCreateViewHolder()
和from()
方法應如下方所示,且您的程式碼應順利執行並執行。
override fun onCreateViewHolder(parent: ViewGroup, viewType:
Int): ViewHolder {
return ViewHolder.from(parent)
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
return ViewHolder(view)
}
}
- 變更
ViewHolder
類別的簽名,使其建構函式設為不公開。由於from()
現在是傳回新ViewHolder
執行個體的方法,因此任何人都不想再呼叫ViewHolder
建構函式。
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
- 執行應用程式。您的應用程式必須如往常一樣建立及執行,也就是重組後產生的結果。
Android Studio 專案:RecyclerViewFundamentals
- 顯示清單或格線的資料是 Android 最常用的使用者介面工作之一。
RecyclerView
即使設計極大清單,也可以提高效率。 RecyclerView
只會處理處理或繪製目前顯示在畫面上的項目。- 當項目捲動離開畫面時,其檢視畫面的檢視畫面會再度使用。這表示該項目填入了可以捲動在畫面上的新內容。
- 軟體工程的調整模式可協助物件與其他 API 搭配運作。
RecyclerView
使用轉接程式將應用程式資料轉換為其顯示內容,而不需變更應用程式儲存及處理資料的方式。
如要在 RecyclerView
中顯示您的資料,您必須符合以下條件:
- RecyclerView
如要建立RecyclerView
例項,請在版面配置檔案中定義<RecyclerView>
元素。 - LayoutManager
RecyclerView
使用LayoutManager
來整理RecyclerView
中項目的版面配置,例如排列在網格或線性清單中。
在版面配置檔案的<RecyclerView>
中,將app:layoutManager
屬性設為版面配置管理工具 (例如LinearLayoutManager
或GridLayoutManager
)。
您也可以透過程式為RecyclerView
設定LayoutManager
。(我們會在後續的程式碼研究室中說明此技術)。 - 每個項目的版面配置
為 XML 版面配置檔案中的單一項目建立版面配置。 - 轉接器
建立轉接程式,準備資料以顯示在ViewHolder
中。將轉接程式與RecyclerView
建立關聯。RecyclerView
執行時,系統會使用轉接程式來辨識如何在螢幕上顯示資料。
轉接程式要求你採用以下方法:
–getItemCount()
可傳回項目數量。
–onCreateViewHolder()
為清單中的項目傳回ViewHolder
。
–onBindViewHolder()
可配合清單中的項目檢視畫面調整資料。 - ViewHolder
ViewHolder
包含查看資訊,用於顯示項目版面配置中的某個項目。 - 轉接器中的
onBindViewHolder()
方法會根據資料自動調整視圖。您可以隨時覆寫這個方法。一般來說,onBindViewHolder()
會增加項目的版面配置,並將資料放入版面配置的檢視畫面中。 - 由於
RecyclerView
不知道這項資料,因此Adapter
會在資料變更時通知RecyclerView
。使用notifyDataSetChanged()
通知Adapter
資料已變更。
Udacity 課程:
Android 開發人員說明文件:
這個部分會列出在代碼研究室中,受老師主導的課程作業的可能學生作業。由老師自行決定要執行下列動作:
- 視需要指派家庭作業。
- 告知學生如何提交家庭作業。
- 批改家庭作業。
老師可視需要使用這些建議,並視情況指派其他合適的家庭作業。
如果您是自行操作本程式碼研究室,歡迎透過這些家庭作業來測試自己的知識。
回答這些問題
第 1 題
RecyclerView
如何顯示項目?請選取所有適用選項。
▢ 以清單或格線顯示項目。
▢ 垂直或水平捲動。
▢ 使用較大型的裝置 (例如平板電腦) 沿著對角線捲動。
▢ 清單或格狀檢視功能的設計不足,導致內容無法滿足這項需求。
第 2 題
使用 RecyclerView
有哪些好處?請選取所有適用選項。
▢ 有效顯示大型清單。
▢ 自動更新資料。
▢ 更新項目時,可盡量減少更新、刪除或新增到項目時重新整理的機會。
▢ 重複使用會捲動在畫面上的視圖,以顯示下一個項目在畫面上。
第 3 題
使用轉接程式的幾個好處是什麼?請選取所有適用選項。
▢ 區分問題有助於更輕鬆地變更及測試程式碼。
▢ RecyclerView
與顯示的資料無關。
▢ 資料處理層不需要擔心資料的顯示方式。
▢ 應用程式執行速度更快。
第 4 題
以下關於「ViewHolder
」的敘述何者正確?請選取所有適用選項。
▢ ViewHolder
版面配置是在 XML 版面配置檔案中定義。
▢ 資料集中的每個資料單位都有一個 ViewHolder
。
▢ 您在 RecyclerView
中可以有多個 ViewHolder
。
▢ Adapter
將資料繫結至 ViewHolder
。