# Android筆記--RecyclerView(循環視圖)(基礎篇)
>[!Note]前置閱讀:
>本篇會有很多用到[DataBinding](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_dataBinding)、[VIewBinding](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_viewBinding)的地方
RecyclerView 是 Android 中用來顯示大量資料的高效元件,具備高性能滾動與視圖重用機制。透過 ViewHolder,離開螢幕的項目會被回收再利用,節省記憶體並提升效能。
<font size=4><u>主要特色包括:</u><font>
- 支援多種佈局(如線性、網格、瀑布流)
- 可自訂項目樣式與動畫效果
- 可顯示多種類型資料
---
# ▼ RecyclerView三大主要部分及兩附件
layoutManager、ViewHolder、Adapter,以及兩個Layout佈局文件
<u>**1.layoutManager**</u>:
---
==layoutManager負責設定RecyclerView的滾動、排列方式==
:::spoiler <font size=4>**RecyclerView提供的三個版面配置管理工具**:</font>
<font size=4>1.[LinearLayoutManager](https://developer.android.com/reference/androidx/recyclerview/widget/LinearLayoutManager?hl=zh-tw) 會在一個維度清單中排列項目,用於實現線性佈局。</font>
---

- 支援水平或垂直方向的列表顯示。
- 適合顯示單列或單行的數據列表。
```kotlin=
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this) // 預設垂直
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) // 設置為水平
```
<font size=4>2.[GridLayoutManager](https://developer.android.com/reference/androidx/recyclerview/widget/GridLayoutManager?hl=zh-tw) 會在二維格線中排列項目,用於實現網格佈局。</font>
---

- 每行或每列可以容納多個項目。
- 可以針對特定項目設置跨行或跨列的效果。
- 如果網格是垂直排列的,GridLayoutManager 會嘗試讓每個資料列中的所有元素具有相同的寬度和高度,但不同的資料列高度不同。
- 如果格線已水平排列,GridLayoutManager 會嘗試讓每個資料欄的所有元素具有相同的寬度和高度,但不同資料欄寬度不同。
- `setSpanCount(spanCount: Int)`: 設置行或列的數量。
- `setSpanSizeLookup(object: SpanSizeLookup)`: 針對某些項目自定義跨行或跨列數量。
```kotlin=
//範例
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = GridLayoutManager(this, 3) // 每行 3 個項目
```
<font size=4>3.[StaggeredGridLayoutManager](https://developer.android.com/reference/androidx/recyclerview/widget/StaggeredGridLayoutManager?hl=zh-tw)類似於`GridLayoutManager`,但允許每個項目的寬高可以不一致,形成不規則的外觀,用於在 RecyclerView 中實現交錯式網格佈局。</font>
---

- 元素大小不必相同,呈現「交錯」效果。
- 適合展示帶有圖片的內容
- `setSpanCount(spanCount: Int)`: 設置列數(垂直方向)或行數(水平方向)
- `setGapStrategy(gapStrategy: Int)`: 設置空隙填充策略。例如: `GAP_HANDLING_NONE`或`GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS`
```kotlin=
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) // 2 列
```
:::
:::spoiler ※===layoutManager的基本範例===※
- 方案1.

- 方案2.==除了寫在Activity或Fragment外,也可以寫在xml中==

:::
:::spoiler ※===一些RecycleView的其他可用設定===※
參考: ==★==[Day 13:RecyclerView 基本資料列表顯示--RecyleView 更多設定](https://ithelp.ithome.com.tw/articles/10263176?sc=rss.iron)
- 添加項目分隔線(ItemDecoration):
```kotlin=
recyclerView.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
```
- 設定點擊事件監聽器(ClickListener):
```kotlin=
adapter.setOnItemClickListener { position ->
// 處理點擊事件
}
```
- 設定滾動監聽器(ScrollListener):
```kotlin=
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
// 在此處理滾動事件
}
}
```
- 設定項目動畫(Item Animation):
```kotlin=
recyclerView.itemAnimator = DefaultItemAnimator()
// 或者其他自定义的項目動畫
```
- 設定項目拖動和滑動(ItemTouchHelper):
```kotlin=
val itemTouchHelper = ItemTouchHelper(callback)
itemTouchHelper.attachToRecyclerView(recyclerView)
```
:::
>[!Tip]更多項目可參考[LayoutManager](https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.LayoutManager?hl=zh-tw)
<span id="ViewHolder"><u>**2.ViewHolder**</u></span>:
---
>[!Important]使用ViewHolder需繼承RecyclerView.ViewHolder(view)
ViewHolder==負責將==[ItemLayout](#4.兩個Layout佈局文件)==和RecyclerAdapter中對應的參數綁定起來==,並且儲存對[ItemLayout](#4.兩個Layout佈局文件)的引用。
當RecyclerView 需要顯示新項目時,Adapter 會在 onCreateViewHolder() 中建立 ViewHolder,並重複使用其中的資料與視圖參考。
也就是說,我們可以粗略將Holder理解為一個儲存View參照(View reference)的地方,也可以把它當成一個儲存 View 的 class。
==通常我們會將`ViewHolder`寫成`inner Class`==。
:::spoiler <font size=4><u>透過儲存引用,可以達到下列三點:</u></font>
- 保存對 [ItemLayout](#4.兩個Layout佈局文件) 內子視圖的引用,避免反覆使用 findViewById() 查找,提升效能。
- 負責將數據綁定到 [ItemLayout](#4.兩個Layout佈局文件),讓每個項目正確顯示內容。
- 支援 RecyclerView 的複用機制,減少資源浪費。
:::
:::spoiler ※===<span id="Holder的基本架構">Holder的基本架構</span>===※

```kotlin=
inner class FruitRecyclerViewHolder(private val binding: FruitItemViewBinding): RecyclerView.ViewHolder(binding.root){
val fruitName = binding.tvNameValue
val fruitDescription = binding.tvDescriptionValue
val fruitPrice = binding.tvPriceValue
}
```
:::
:::spoiler ※===<span id="Holder的實際案例">Holder的推薦做法</span>===※
推薦使用這種的架構,原因如下:
- 封裝性:
- ViewHolder 應該負責管理自己的視圖和事件處理
- 所有和視圖互動的邏輯都集中在一個地方
- 更好維護和測試
- 清晰的職責劃分:
- onBindViewHolder() 主要負責協調數據和 ViewHolder 的關係
- bind() 負責具體的視圖更新和事件處理邏輯

```kotlin=
// 改進版的 RecyclerView Adapter - 展示良好的封裝設計
class FruitRecyclerViewAdapter_2(private val fruitList: List<FruitItemDataClass>): RecyclerView.Adapter<FruitRecyclerViewAdapter_2.FruitRecyclerViewHolder2>() {
inner class FruitRecyclerViewHolder2(private val binding: FruitItemViewBinding): RecyclerView.ViewHolder(binding.root){
// 將 View 元件改為 private,提升封裝性
// 原本:val fruitName = binding.tvNameValue (public,外部可直接存取)
// 現在:private val fruitName = binding.tvNameValue (private,只有內部可存取)
private val fruitName = binding.tvNameValue
private val fruitDescription = binding.tvDescriptionValue
private val fruitPrice = binding.tvPriceValue
// 新增 bind() 方法,實現職責分離
// 目的:讓 ViewHolder 負責自己的數據綁定邏輯
// 好處:Adapter 不需要知道 ViewHolder 內部如何處理數據
fun bind(item: FruitItemDataClass) {
fruitName.text = item.name
fruitDescription.text = item.description
fruitPrice.text = item.price.toString()
// 未來擴展點:可以在這裡加入更多邏輯而不影響 Adapter
// 例如:點擊事件、格式化、條件顯示等
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitRecyclerViewHolder2 {
val inflater = LayoutInflater.from(parent.context)
val binding = FruitItemViewBinding.inflate(inflater, parent, false)
return FruitRecyclerViewHolder2(binding)
}
override fun onBindViewHolder(holder: FruitRecyclerViewHolder2, position: Int) {
// 簡化 onBindViewHolder 邏輯
// 原本:直接操作 holder 的各個 View 元件 (違反封裝)
// 現在:只需調用 holder.bind() 方法 (符合封裝原則)
val fruitItem = fruitList[position]
holder.bind(fruitItem) // 委託給 ViewHolder 處理,職責更清晰
}
override fun getItemCount(): Int {
return fruitList.size
}
}
```
:::
<font size=5><u>**3.Adapter:**</u></font>
---
Adapter負責管理數據邏輯,並將==資料與RecyclerView中的項目進行關聯,控制列表項目如何顯示==。
>[!Important]Adapter 必須覆寫`onCreateViewHolder()`、`onBindViewHolder`、`getItemCount()`
並繼承`RecyclerView.Adapter<yourViewHolder>`
- :::spoiler <u>**`onCreateViewHolder(parent:ViewGroup, viewType:Int): ViewHolder`**</u>: 負責創建及初始化 ViewHolder 及其相關聯的 View,最終須返回一個處理過的[ViewHolder](#ViewHolder)。
<u>**運作原理**</u>:
- RecyclerView 需要新項目時調用此方法創建 ViewHolder。
- 創建的 ViewHolder 傳遞給 onBindViewHolder() 進行數據綁定
- 使用完的 ViewHolder 進入重複利用池,避免重複創建er](#ViewHolder)的建構子進行初始化,最後返回這個 ViewHolder**。
- <u>**實作要點:創建 View → 初始化 ViewHolder → 返回 ViewHolder**</u>
```kotlin=
//在這裡創建及初始化 ViewHolder,須返回一個自定義的 ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitRecyclerViewHolder2 {
//使用 LayoutInflater 將項目佈局 XML 文件轉換為對應的 View。
val inflater = LayoutInflater.from(parent.context)
val binding = FruitItemViewBinding.inflate(inflater, parent, false)
return FruitRecyclerViewHolder2(binding)
}
```
- parent:ViewGroup: 傳入的 ViewGroup 是 RecyclerView 自己,用於提供上下文或佈局參數。
- viewType:Int:通過覆寫 getItemViewType() 返回的值,決定當前項目的佈局類型(用於多佈局)。
- 返回值ViewHolder: 必須返回一個自訂的 ViewHolder,該 ViewHolder 包含了創建的視圖。
:::
<span id="getItemCount()"></span>
- :::spoiler <u>**`getItemCount():Int`**</u>:負責回資料集的總數量,告知(返回) RecyclerView 需要顯示多少個項目。
RecyclerView 根據getItemCount()返回的數值決定調用 onBindViewHolder() 的次數
```kotlin=
//在這裡將資料集的大小傳遞給RecyclerView
override fun getItemCount(): Int {
return fruitList.size
}
```
:::
- :::spoiler <u>**`onBindViewHolder(viewHolder: ViewHolder, position: Int)`**</u>: 負責將資料集裡對應的資料匹配到ViewHolder上對應的位置,並更新視圖內容以反映數據的狀態。
- 此方法會將數據集合中對應位置的數據綁定到 ViewHolder 中的視圖。
- `onBindViewHolder()`是每一個 [Item](#4.兩個Layout佈局文件) 在建立的時候會跑的方法,也就是你的資料有多少筆,他就會跑幾次。
```kotlin=
override fun onBindViewHolder(holder: FruitRecyclerViewHolder, position: Int) {
holder.fruitName.text = fruitList[position].name
holder.fruitDescription.text = fruitList[position].description
holder.fruitPrice.text = fruitList[position].price.toString()
}
```
<u>**參數說明**</u>:
- `viewHolder: ViewHolder`: 當前項目的 ViewHolder,包含視圖引用
- `position: Int`: 資料集中的索引位置,範圍為 0 到 [getItemCount()-1](#getItemCount())
<u>**※===onBindViewHolder()中常用的方法===※**</u>
- `notifyItemRemoved(position)`: 通知 RecyclerView,從指定位置 position 刪除了一個項目。這會觸發 RecyclerView 的動畫,將該項目移除,並將該位置之後的所有項目位置向前移動一位。。
- `notifyItemRangeChanged(position, itemCount - position)`: 通知 RecyclerView 在 positionStart 和 positionStart + itemCount - 1 之間的項目已經改變。
- `notifyDataSetChanged()`: 通知RecyclerView要強制刷新。
:::
<font size=5><u><span id="4.兩個Layout佈局文件">**4.兩個Layout佈局文件:**</span></u></font>
---
使用RecycleView會需要用到至少兩個Layout佈局文件來呈現。Container Layout和 ItemLayout。
- :::spoiler **Container Layout**:定義 RecyclerView 在畫面中的位置和整體呈現,也就是放了RecyclerView元件的Layout。

:::success
:::spoiler 佈局文件範例
```xml=
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.12" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.97" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.05" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.95" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/materialCardView"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardBackgroundColor="#1B00BCD4"
app:cardCornerRadius="8dp"
app:contentPadding="2dp"
app:layout_constraintBottom_toBottomOf="@id/guideline_bottom"
app:layout_constraintEnd_toEndOf="@id/guideline_end"
app:layout_constraintStart_toStartOf="@id/guideline_start"
app:layout_constraintTop_toTopOf="@id/guideline_top">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_fruit_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
/>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoSizeTextType="uniform"
android:text="RecyclerView示範"
android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@+id/guideline_top"
app:layout_constraintEnd_toStartOf="@+id/guideline_end"
app:layout_constraintStart_toStartOf="@+id/guideline_start"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
```
:::
- :::spoiler **ItemLayou:** 負責表示單一一個Item的呈現方式。

```xml=
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:contentPadding="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 名稱 -->
<TextView
android:id="@+id/tv_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="12dp"
android:text="名稱:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_name_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="@{item.name}"
app:layout_constraintBottom_toBottomOf="@+id/tv_name_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_name_label"
app:layout_constraintTop_toTopOf="@id/tv_name_label" />
<!-- 描述 -->
<TextView
android:id="@+id/tv_description_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="8dp"
android:text="描述:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_name_label" />
<TextView
android:id="@+id/tv_description_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="@{item.description}"
app:layout_constraintBottom_toBottomOf="@+id/tv_description_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_description_label"
app:layout_constraintTop_toTopOf="@id/tv_description_label" />
<!-- 價格 -->
<TextView
android:id="@+id/tv_price_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="12dp"
android:text="價格:"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_description_label" />
<TextView
android:id="@+id/tv_price_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text='@{String.valueOf(item.price) + " 元"}'
app:layout_constraintBottom_toBottomOf="@+id/tv_price_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/tv_price_label"
app:layout_constraintTop_toTopOf="@id/tv_price_label" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
```
---
# ▼ RecyclerView 製作步驟總結:
1. <u>準備 Layout 文件</u>
- Container Layout:包含 RecyclerView 及其他 UI 元件的主要佈局
- Item Layout:定義單個項目的視圖結構
2. <u>建立數據類別</u>
- 創建用於存儲每個項目數據的 Data Class
3. <u>實作 Adapter</u>
- 繼承 RecyclerView.Adapter<ViewHolder>,實作三個核心方法:
- onCreateViewHolder():創建及初始化 ViewHolder
- onBindViewHolder():將數據綁定到 ViewHolder
- getItemCount():返回數據集總數量
4. <u>設計 ViewHolder</u>
- 繼承 RecyclerView.ViewHolder
- 包含 bind() 方法處理數據綁定(推薦做法)
- 將 View 元件設為 private 提升封裝性
5. <u>Activity/Fragment 設置</u>
- 初始化 RecyclerView
- 設置 LayoutManager(LinearLayoutManager、GridLayoutManager 等)
- 綁定 Adapter
- 準備數據並傳遞給 Adapter
:::spoiler 舊資料
▼ RecyclerView 製作步驟總結:
1.決定清單或格線的外觀。一般來說,您可以使用 RecyclerView 程式庫的標準版面配置管理工具。
2.設計清單中的每個元素外觀和行為。根據這個設計,擴充 ViewHolder 類別。您的 ViewHolder 版本提供清單項目的所有功能。檢視容器是 View 的包裝函式,而該檢視畫面由 RecyclerView 管理。
3.定義將資料與 ViewHolder 檢視畫面相關聯的 Adapter。
<iframe width="560" height="315" src="https://www.youtube.com/embed/5eXDba32rkI?si=67wviZfwRWsu3yg0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
:::
## ▼一些RecyclerView常用的方法
:::success
- 1.`recyclerView.adapter.notifyDataSetChanged()`: 通知適配器 (adapter) 整個資料集已經發生變化,並且需要重新繪製所有的項目。
:::info
:::spoiler 其他類似的方法
- `notifyItemInserted(position)`:當插入了一個新項目時通知。
- `notifyItemRemoved(position)`:當移除了某個項目時通知。
- `notifyItemChanged(position)`:當某個特定項目變化時通知。
- `notifyItemRangeChanged(positionStart, itemCount)`:當一個範圍內的項目變化時通知。
:::
- 2.`recyclerView.smoothScrollToPosition(position: Int)`: 是一個用來將 RecyclerView平滑的捲動到指定項目位置的方法(有動畫)。position是你希望捲動到的項目索引,這個索引起點為0。
:::info
:::spoiler 其他類似的方法
- `scrollToPosition`: 立即捲動到指定項目,沒有動畫過渡效果。
:::
:::
---
## RecyclerView的介紹影片(CC字幕):
### RecyclerView的工作原理:
<iframe width="560" height="315" src="https://www.youtube.com/embed/xjfBzI9qlwk?si=OfpAboDxaZBc_089" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
### RecyclerView的工作流程:
<iframe width="560" height="315" src="https://www.youtube.com/embed/ns3WC8HFx90?si=xDRXC7ieok205NLy" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
下一篇
---
[Android筆記–RecyclerView(循環視圖)(進階篇)](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_RecyclerView_advance)
GitHub專案
---
[MyPratice_RecyclerViewSample](https://github.com/PudCheetah/MyPratice_RecyclerViewSample): 乾淨的RecyclerView示範
[Task_13](https://github.com/PudCheetah/Task_13): RecyclerView和DataBinding運用練習,示範標準的RecyclerView
---
參考資料:
---
- [[Google Course] Android Basics in Kotlin(第6篇) — Display a scrollable list with RecyclerView](https://happyphoebe40090.medium.com/google-course-android-basics-in-kotlin-%E7%AC%AC6%E7%AF%87-display-a-scrollable-list-4e1fa4d3ef87)
- ==★==[AndroidDeveloper--使用 RecyclerView 建立動態清單](https://developer.android.com/develop/ui/views/layout/recyclerview?hl=zh-tw)
- ==★==[[Android 十全大補] RecyclerView](https://ithelp.ithome.com.tw/articles/10220196)
- [碼農日常-『Android studio』基本RecyclerView用法](https://thumbb13555.pixnet.net/blog/post/311803031)
- [Android -在ViewHolder中使用ViewBinding來優化建構子](https://medium.com/@Soda3752/android-%E5%9C%A8viewholder%E4%B8%AD%E4%BD%BF%E7%94%A8viewbinding%E4%BE%86%E5%84%AA%E5%8C%96%E5%BB%BA%E6%A7%8B%E5%AD%90-c16faa8cd0d2)
- ==★==[Day 13:RecyclerView 基本資料列表顯示--RecyleView 更多設定](https://ithelp.ithome.com.tw/articles/10263176?sc=rss.iron)
- ==★==[[Day 9] Android in Kotlin: 簡單的 Recycler View](https://ithelp.ithome.com.tw/articles/10241713?sc=rss.iron)
- [Android Kotlin 實作 Day 6 : ImageList(RecyclerView + LayoutInflater)](https://ithelp.ithome.com.tw/articles/10203735)
- ==★★★==[Android x Kotlin : RecyclerView(一)-嬰兒式基本用法速覽](https://ithelp.ithome.com.tw/articles/10238539)
舊資料
---
▼ 完整的MainActivity
:::danger
:::spoiler ※===完整的MainActivity1.0===※
```kotlin=
class MainActivity : AppCompatActivity() {
private lateinit var bindingActivityMain: ActivityMainBinding
private lateinit var bindingRowFunctionBinding: RowFunctionBinding
var functions = listOf<String>(
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q",
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindingActivityMain = ActivityMainBinding.inflate(layoutInflater)
setContentView(bindingActivityMain.root)
bindingActivityMain.recyclerView1.layoutManager = LinearLayoutManager(this)
// bindingActivityMain.recyclerViewMain1.setHasFixedSize(true)
bindingActivityMain.recyclerView1.adapter = FunctionAdapter()
}
inner class FunctionAdapter(): RecyclerView.Adapter<FunctionHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FunctionHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.row_function, parent, false)
// var bindingRowFunctionBinding = RowFunctionBinding.inflate(layoutInflater)
var bindingRowFunctionBinding = RowFunctionBinding.bind(view)
return FunctionHolder(bindingRowFunctionBinding)
}
override fun getItemCount(): Int {
return functions.size
}
override fun onBindViewHolder(holder: FunctionHolder, position: Int) {
holder.nameText.text = functions.get(position)
}
}
class FunctionHolder(var bindingRowFunctionBinding: RowFunctionBinding): RecyclerView.ViewHolder(bindingRowFunctionBinding.root){
var nameText: TextView = bindingRowFunctionBinding.name
}
}
```
:::
:::danger
:::spoiler ※===完整的MainActivity2.0===※
```kotlin=
class MainActivity : AppCompatActivity() {
private lateinit var bindingMainBinding: ActivityMainBinding
private lateinit var bindingRowFunctionBinding: RowFunctionBinding
var functions = listOf<String>(
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindingMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(bindingMainBinding.root)
bindingMainBinding.recyclerView1MainActivity.layoutManager = LinearLayoutManager(this)
bindingMainBinding.recyclerView1MainActivity.setHasFixedSize(true)
bindingMainBinding.recyclerView1MainActivity.adapter = FunctionAdapter(functions)
}
class FunctionAdapter(var recyclerViewList: List<String>): RecyclerView.Adapter<FunctionAdapter.FunctionHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FunctionHolder {
val bindingRowFunctionBinding = RowFunctionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
// val view = LayoutInflater.from(parent.context).inflate(R.layout.row_function, parent, false)
// var bindingRowFunctionBinding = RowFunctionBinding.bind(view)
return FunctionHolder(bindingRowFunctionBinding)
}
override fun getItemCount(): Int {
return recyclerViewList.size
}
override fun onBindViewHolder(holder: FunctionHolder, position: Int) {
holder.textView.text = recyclerViewList.get(position)
}
inner class FunctionHolder(bindingRowFunctionBinding: RowFunctionBinding): RecyclerView.ViewHolder(bindingRowFunctionBinding.root){
var textView: TextView = bindingRowFunctionBinding.textView1RowFunction
}
}
// class FunctionHolder(var bindingRowFunctionBinding: RowFunctionBinding): RecyclerView.ViewHolder(bindingRowFunctionBinding.root){
// var textView: TextView = bindingRowFunctionBinding.textView1RowFunction
// }
}
//具體變更:
//更改了onCreatViewHolder()中視圖綁定的方式
//將Holder()改為inner class放入Adapter中
//將Adapter由inner class改為普通class,並將串列作為參數傳入Adapter之中
```
:::