1. 使用流程對比
-
ListView:
- 布局XML: 在布局文件中放置
<ListView>
控件,指定id
(如android:id="@+id/listView"
)。 - 數據適配器 (Adapter): 繼承
BaseAdapter
或ArrayAdapter
/CursorAdapter
/SimpleAdapter
。- 重寫
getCount()
:返回數據項總數。 - 重寫
getItem(int position)
:返回指定位置的數據對象。 - 重寫
getItemId(int position)
:返回指定位置項的 ID(通常就是position
)。 - 核心:重寫
getView(int position, View convertView, ViewGroup parent)
:- 檢查
convertView
是否為空(是否有可復用的視圖)。為空則通過LayoutInflater
從布局 XML 文件inflate
一個新視圖。 - 查找視圖中的子控件(如
TextView
,ImageView
)。 - 根據
position
獲取數據對象。 - 將數據對象綁定到子控件上。
- 手動處理視圖復用:
convertView
機制是實現復用的關鍵,開發者需要自己管理。
- 檢查
- 重寫
- 設置適配器: 在 Activity/Fragment 中,
findViewById
獲取ListView
實例,調用setAdapter(adapter)
設置適配器。 - (可選) 設置監聽器: 如
setOnItemClickListener
,setOnItemLongClickListener
。
- 布局XML: 在布局文件中放置
-
RecyclerView:
- 布局XML: 在布局文件中放置
<androidx.recyclerview.widget.RecyclerView>
控件,指定id
(如android:id="@+id/recyclerView"
)。 - ViewHolder 模式 (強制): 創建一個繼承自
RecyclerView.ViewHolder
的內部類。- 在構造方法中
findViewById
,持有 Item 布局中子控件的引用。
- 在構造方法中
- 適配器 (Adapter): 繼承
RecyclerView.Adapter<YourViewHolder>
。- 重寫
onCreateViewHolder(ViewGroup parent, int viewType)
:- 使用
LayoutInflater
從布局 XML 文件inflate
Item 視圖。 - 創建并返回一個
YourViewHolder
實例(傳入剛 inflate 的視圖)。
- 使用
- 重寫
onBindViewHolder(YourViewHolder holder, int position)
:- 根據
position
獲取數據對象。 - 通過
holder
對象訪問其持有的子控件引用。 - 將數據對象綁定到這些子控件上。
- 根據
- 重寫
getItemCount()
:返回數據項總數。 - (可選) 重寫
getItemViewType(int position)
: 用于處理多種類型的 Item。
- 重寫
- 設置布局管理器 (LayoutManager - 必需):
RecyclerView
必須 設置一個LayoutManager
。- 常用實現:
LinearLayoutManager
: 線性列表(垂直或水平)。GridLayoutManager
: 網格布局。StaggeredGridLayoutManager
: 瀑布流布局。
- 代碼:
recyclerView.setLayoutManager(new LinearLayoutManager(context));
- 設置適配器:
recyclerView.setAdapter(yourAdapter);
- (可選) 設置 Item 裝飾 (ItemDecoration) 和 Item 動畫 (ItemAnimator): 提供分割線、間隔、增刪改動畫等。
- (可選) 處理點擊事件:
RecyclerView
沒有內置OnItemClickListener
。需要在ViewHolder
的構造函數中或在onBindViewHolder
里為 Item 視圖或其子控件設置點擊監聽 (setOnClickListener
)。
- 布局XML: 在布局文件中放置
流程關鍵差異:
- ViewHolder 強制化:
RecyclerView
強制使用 ViewHolder 模式,將findViewById
的開銷從頻繁調用的onBindViewHolder
移到了只調用幾次的onCreateViewHolder
中,顯著提升性能。 - 布局分離:
RecyclerView
通過LayoutManager
將布局策略(線性、網格、瀑布流)完全解耦,無需為不同布局重寫整個適配器。 - 事件處理:
ListView
提供內置 Item 點擊監聽,RecyclerView
需要手動實現,靈活性更高(可以監聽 Item 內任意子控件的點擊)。 - 復用機制:
ListView
的復用 (convertView
) 需要開發者在getView
中手動管理。RecyclerView
的復用由系統通過Adapter
(onCreateViewHolder
/onBindViewHolder
) 和LayoutManager
自動處理,開發者只需遵循 ViewHolder 模式。
2. 應用場景對比
-
ListView (當前適用場景非常有限):
- 維護非常老舊的 Android 項目(API level < 21 或未引入支持庫)。
- 需要實現極其簡單、靜態、單類型且性能要求不高的短列表。
- 不推薦在新項目中使用。
-
RecyclerView (現代首選):
- 絕大多數列表/網格/瀑布流需求。
- 需要復雜布局(多種 Item 類型)。
- 需要高度定制化的布局(如水平滑動列表、網格、交錯網格、自定義排列)。
- 需要精細控制項目動畫(增、刪、改、移動)。
- 需要添加項目裝飾(如分割線、間隔、高亮)。
- 需要高性能處理超長列表或復雜 Item 布局。
- 需要局部更新數據(
notifyItemChanged()
,notifyItemInserted()
等),避免全局刷新。 - 所有新項目都應使用 RecyclerView。
3. 實現原理對比
-
ListView:
- 繼承自
AbsListView
。 - 核心在
Adapter
的getView()
: 系統在需要顯示 Item 時調用此方法。position
指明位置,convertView
是可能可復用的舊視圖(由系統管理一個復用池 -RecycleBin
),parent
是ListView
本身。 - 復用池 (
RecycleBin
):ListView
內部維護一個有限的視圖復用池。當 Item 滾出屏幕時,其視圖可能被放入池中。當需要新的 Item 視圖時,系統嘗試從池中取 (getScrapView()
) 一個同類型的視圖 (convertView
) 給getView()
復用。開發者負責在getView()
中檢查convertView
并重置內容。 - 布局:
ListView
自身負責垂直(或水平)堆疊排列子視圖。不支持網格或瀑布流(除非自定義或使用GridView
,但GridView
也有類似限制)。 - 測量與布局: 在
onMeasure
和onLayout
中,ListView
會遍歷所有需要顯示的 Item(或預估),調用它們的measure
和layout
。
- 繼承自
-
RecyclerView:
- 核心設計哲學:關注點分離 (Separation of Concerns)
Adapter
: 負責提供數據 (getItemCount
,getItem
) 和創建/綁定ViewHolder
(onCreateViewHolder
,onBindViewHolder
)。ViewHolder
: 持有 Item 視圖及其子控件的引用,避免重復findViewById
。LayoutManager
: 核心創新點! 完全負責 Item 的測量和布局。決定 Item 在屏幕上的擺放位置(線性、網格、瀑布流、自定義)。它管理著視圖的附加/分離和復用策略。RecyclerView
本身不知道如何布局。Recycler
: 由LayoutManager
使用。LayoutManager
在需要視圖時會向Recycler
請求 (getViewForPosition
)。Recycler
管理著多級緩存池 (Scrap, Cache, ViewCacheExtension, RecycledViewPool),優先從緩存中提供視圖。如果緩存中沒有,則要求Adapter
創建新的ViewHolder
(onCreateViewHolder
)。ItemAnimator
: 負責 Item 的增、刪、改、移動動畫。ItemDecoration
: 負責在 Item 周圍繪制裝飾(分割線、間隔、邊框等),不影響 Item 的測量和布局。
- 工作流程簡述:
RecyclerView
被測量和布局時,將任務委托給LayoutManager
。LayoutManager
開始遍歷需要顯示的位置。- 對于每個位置,
LayoutManager
向Recycler
請求該位置的視圖 (getViewForPosition
)。 Recycler
檢查各級緩存:- Scrap: 當前布局過程中臨時分離但很快會重新附加的視圖(如正在滾出但尚未完全離開屏幕的 Item)。
- Cache: 剛剛滾出屏幕的視圖(
mAttachedScrap
和一級緩存)。類型匹配可直接復用。 - ViewCacheExtension (可選): 開發者自定義的緩存層。
- RecycledViewPool: 最終的共享池。存放被完全移除且類型相同的視圖。
onBindViewHolder
會被重新調用。
- 如果緩存中找到視圖,
Recycler
返回它(可能需要重新綁定數據)。 - 如果緩存中沒有,
Recycler
要求Adapter
創建新的ViewHolder
(onCreateViewHolder
)。 LayoutManager
將獲取到的視圖添加到RecyclerView
中并測量、布局它。- 當 Item 滾出屏幕,
LayoutManager
將其視圖回收到Recycler
的緩存中(通常是 Cache 或 RecycledViewPool)。
- 核心設計哲學:關注點分離 (Separation of Concerns)
原理關鍵差異:
- 架構:
ListView
是相對單一的整體。RecyclerView
是高度模塊化的設計(Adapter, ViewHolder, LayoutManager, ItemAnimator, ItemDecoration, Recycler),職責清晰分離,擴展性極強。 - 布局控制:
ListView
自身處理布局。RecyclerView
將布局職責完全委托給可插拔的LayoutManager
,這是實現多樣化布局(網格、瀑布流)的基礎。 - 復用機制:
ListView
使用相對簡單的兩級復用池 (ActiveView
+ScrapView
)。RecyclerView
使用更精細、可擴展的四級緩存機制 (Scrap
+Cache
+ViewCacheExtension
+RecycledViewPool
),并由LayoutManager
和Recycler
緊密協作管理,效率更高,尤其對復雜布局和多類型 Item。 - ViewHolder 強制化:
RecyclerView
將ListView
中推薦的最佳實踐 (ViewHolder) 變為強制要求,從架構上保證了性能優化的基礎。
4. 緩存機制對比
-
ListView (RecycleBin):
ActiveView
: 當前屏幕上完全可見的 Item 視圖。這些視圖是“活躍”的。ScrapView
: 剛剛滾出屏幕的 Item 視圖(通常存儲在mScrapViews
或mRecycler
中)。當新 Item 需要進入屏幕時,系統優先嘗試從ScrapView
中獲取同類型的視圖作為convertView
給getView()
復用。如果ScrapView
中沒有匹配的,可能會新建視圖。ScrapView
是 ListView 主要的復用來源。- 特點:
- 兩級緩存(Active + Scrap)。
- 緩存以 Item 位置 (Position) 為主要標識(雖然也看類型
itemType
,但不如RecyclerView
嚴格)。 - 復用發生在
getView()
方法內部,開發者手動處理convertView
。 - 沒有不同
ListView
實例間的視圖共享機制。
-
RecyclerView (Recycler):
- Scrap (Attached Scrap & Changed Scrap):
Attached Scrap
: 在布局過程中(如onLayout
)臨時從父視圖分離但不需要重新綁定的視圖。通常是因為布局調整(如滾動一點距離)暫時移出但很快會放回的視圖。不需要調用onBindViewHolder
。Changed Scrap
: 在布局過程中被標記為需要更新的視圖(調用了notifyItemChanged
)。它們會被優先復用,并且復用時會調用onBindViewHolder
(帶 payloads 如果有的話)。
- Cache (View Cache):
- 剛剛滾出屏幕的視圖。它們被保留在內存中,類型匹配且位置在屏幕附近。當用戶反向滾動時,可以非常快地重新附加,且不需要重新綁定數據 (
onBindViewHolder
不會被調用),因為數據假設未變。這是RecyclerView
流暢滾動體驗的關鍵之一。緩存大小通常有限(默認 2 個)。
- 剛剛滾出屏幕的視圖。它們被保留在內存中,類型匹配且位置在屏幕附近。當用戶反向滾動時,可以非常快地重新附加,且不需要重新綁定數據 (
- ViewCacheExtension (可選 - 開發者擴展):
- 開發者可以繼承
ViewCacheExtension
實現自定義的緩存層。可以存儲特定類型的視圖或應用特殊邏輯。較少使用。
- 開發者可以繼承
- RecycledViewPool (回收視圖池):
- 核心優勢點! 存儲的是完全移除(既不在屏幕也不在 Cache/Scrap 中)且按類型 (
viewType
) 分類的視圖 (ViewHolder
)。 - 當
LayoutManager
請求一個視圖,且 Scrap/Cache/Extension 都沒有時,會到RecycledViewPool
中查找相同viewType
的視圖。 - 如果找到,該視圖會被返回給
Adapter
進行數據重新綁定(調用onBindViewHolder
)。 - 如果
RecycledViewPool
中也沒有,則Adapter
會創建新的ViewHolder
(onCreateViewHolder
)。 - 關鍵特性:
- 按
viewType
存儲: 嚴格區分不同類型 Item 的視圖。 - 可跨
RecyclerView
實例共享: 多個RecyclerView
實例(即使是不同列表)可以設置同一個RecycledViewPool
(setRecycledViewPool
)。這對于具有相同 Item 類型的多個列表(如標簽選擇器、多 Tab 下的同類型列表)是巨大的性能優化,避免了重復創建視圖的開銷。 - 池大小可配置: 可以為每種
viewType
設置最大緩存數量 (setMaxRecycledViews
)。
- 按
- 核心優勢點! 存儲的是完全移除(既不在屏幕也不在 Cache/Scrap 中)且按類型 (
- Scrap (Attached Scrap & Changed Scrap):
緩存機制關鍵差異:
- 層級與精細度:
RecyclerView
擁有更復雜、更精細的四級緩存(特別是分離的 Scrap 和 Cache),且嚴格按viewType
區分,復用更精準高效。 - Cache 層:
RecyclerView
獨有的 Cache 層避免了附近 Item 滾回時昂貴的onBindViewHolder
調用,極大提升回滾流暢度。 - RecycledViewPool:
RecyclerView
獨有的RecycledViewPool
實現了跨列表的視圖復用,是ListView
完全不具備的能力,對復雜 UI 優化意義重大。 - 位置 vs 類型:
ListView
緩存更依賴位置信息,RecyclerView
緩存則強依賴viewType
,后者在數據動態變化(增刪)時更健壯。 - 管理方式:
ListView
緩存復用邏輯需要開發者在getView
中參與(檢查convertView
)。RecyclerView
的緩存完全由系統 (Recycler
+LayoutManager
) 自動管理,開發者只需遵循 ViewHolder 模式。
5. 優化方案對比
-
ListView 優化 (本質是優化
getView()
):- 利用
convertView
: 這是最重要的優化!務必檢查convertView != null
并復用,避免不必要的inflate
。 - 應用 ViewHolder 模式 (非強制但必須做): 在
convertView
的tag
中存儲子控件引用 (setTag
/getTag
),避免每次findViewById
。這是RecyclerView
強制化的原因。 - 減少 Item 布局層次和復雜度: 使用
Hierarchy Viewer
或Layout Inspector
分析,避免嵌套過深。使用ConstraintLayout
替代多層嵌套的LinearLayout
/RelativeLayout
。 - 圖片加載優化: 使用
Picasso
,Glide
或Coil
等庫異步加載、緩存和正確處理圖片回收。 - 避免在
getView()
中做耗時操作: 如網絡請求、復雜計算、頻繁 I/O。只做數據綁定。 - 分批加載/分頁: 對于超長列表,實現滾動到底部加載更多數據。
- 使用
android:scrollingCache="false"
(謹慎): 禁用滾動時的顏色緩存,可能略微提升滾動性能(但可能犧牲視覺平滑度)。需測試效果。 - 使用
android:fastScrollEnabled="true"
(僅視覺): 啟用快速滾動滑塊,方便用戶快速導航長列表。
- 利用
-
RecyclerView 優化 (充分利用其架構優勢):
- 遵循 ViewHolder 模式: 這是基礎,已在架構中保證。
- 合理使用
notify
方法族: 絕對避免在數據變化時總是調用notifyDataSetChanged()
!使用精細化的方法:notifyItemInserted(position)
notifyItemRemoved(position)
notifyItemMoved(fromPosition, toPosition)
notifyItemChanged(position)
notifyItemRangeInserted(positionStart, itemCount)
等。- 使用 Payloads: 當 Item 只有部分內容變化時,在
notifyItemChanged(position, payload)
中傳遞變化的 payload 對象。在Adapter
的onBindViewHolder(VH holder, int position, List<Object> payloads)
中根據payloads
進行增量更新,避免重繪整個 Item。這是RecyclerView
獨有的高級優化。
- 優化
onCreateViewHolder
和onBindViewHolder
:onCreateViewHolder
:盡量高效,只做inflate
和創建ViewHolder
。避免耗時操作。onBindViewHolder
:只做數據綁定。避免在此創建新對象、做耗時操作。利用 payloads 進行局部更新。
- 減少 Item 布局層次和復雜度: 同
ListView
優化 3。使用高效布局和ConstraintLayout
。 - 圖片加載優化: 同
ListView
優化 4。庫通常能很好配合RecyclerView
。 - 預加載 (Prefetching):
RecyclerView
內置了預取機制(默認開啟)。LayoutManager
會在空閑時間預取即將進入屏幕的 Item 視圖。通常不需要手動干預。確保LayoutManager
支持(LinearLayoutManager
/GridLayoutManager
支持)。 - 配置
RecycledViewPool
:- 對于同類型 Item 的多個列表,共享同一個
RecycledViewPool
(setRecycledViewPool
) 是極其重要的優化。 - 根據應用場景和 Item 類型內存占用,調整每種
viewType
的緩存池大小 (setMaxRecycledViews
)。避免過大(內存浪費)或過小(頻繁創建 ViewHolder)。
- 對于同類型 Item 的多個列表,共享同一個
- 使用
setItemViewCacheSize(int size)
: 適當增大 Cache 層的大小(默認通常是 2)。增大它可以讓更多剛滾出屏幕的 Item 視圖保留在 Cache 中,提升回滾性能(避免重新綁定)。但會增加內存占用。需根據 Item 復雜度和設備內存平衡。 - 使用
setHasFixedSize(true)
: 如果RecyclerView
自身的大小不會隨 Adapter 內容的變化而改變(即寬高固定或match_parent
),調用此方法可以跳過不必要的自身測量步驟,優化性能。 - 合理使用 DiffUtil: 當數據集發生復雜變化(多個增刪改操作混合)時,使用
DiffUtil
類計算新舊數據集差異,并自動調用最合適的精細notify...
方法。比手動計算和調用更高效且不易出錯。尤其適合配合Paging
庫或后臺數據更新。 - 選擇高效的
LayoutManager
: 對于超大數據集或復雜 Item,LinearLayoutManager
通常是最優解。StaggeredGridLayoutManager
在布局計算上可能稍重。 - 優化 Item 動畫: 如果不需要默認動畫,設置
DefaultItemAnimator
為null
(setItemAnimator(null)
)。自定義復雜動畫也可能影響性能。
優化關鍵差異:
- 精細化更新:
RecyclerView
通過notifyItem...
系列方法和DiffUtil
支持局部更新,是ListView
的notifyDataSetChanged()
無法比擬的。 - Payloads:
RecyclerView
獨有的增量更新機制,對復雜 Item 優化效果顯著。 - 緩存配置:
RecyclerView
提供setItemViewCacheSize
和RecycledViewPool
的精細控制(包括跨列表共享),優化手段更豐富。 - 預加載:
RecyclerView
內置預取機制提升流暢度。 - 固定尺寸優化:
setHasFixedSize(true)
是RecyclerView
特有的優化點。 - ViewHolder 基礎:
ListView
優化需手動實現 ViewHolder,RecyclerView
已強制使用。
總結
特性 | ListView | RecyclerView | 結論與優勢 |
---|---|---|---|
年代/狀態 | 較老,基本被棄用 | 現代,官方推薦,持續更新 | RecyclerView 是未來 |
架構 | 相對單一 | 高度模塊化 (Adapter, VH, LayoutManager, Animator, Decoration, Recycler) | RecyclerView 更靈活、可擴展、職責清晰 |
布局能力 | 僅垂直/水平線性列表 | 通過 LayoutManager 支持線性、網格、瀑布流及任意自定義布局 | RecyclerView 布局能力碾壓 |
ViewHolder | 推薦使用但非強制 | 強制使用 | RecyclerView 從架構保證性能基礎 |
使用流程 | getView() 中手動處理 convertView 和 findViewById | 分離 onCreateViewHolder (創建/找控件) 和 onBindViewHolder (綁定數據) | RecyclerView 代碼更清晰,性能更好 (findViewById 開銷低) |
緩存機制 | 兩級 (Active + Scrap),按位置為主 | 四級緩存 (Scrap, Cache, Extension, Pool),嚴格按 viewType | RecyclerView 緩存更精細、高效 |
緩存復用 | 無跨列表共享 | RecycledViewPool 支持跨列表共享同類型視圖 | RecyclerView 對多列表場景優化顯著 |
數據更新 | 主要 notifyDataSetChanged() (全局刷新) | 精細 notifyItem...() 系列方法 + DiffUtil | RecyclerView 局部更新效率極高 |
增量更新 | 不支持 | 支持 Payloads (部分綁定) | RecyclerView 優化復雜 Item 更新的利器 |
Item 動畫 | 內置簡單動畫 | 強大且可定制的 ItemAnimator | RecyclerView 動畫效果豐富靈活 |
裝飾 | 需自定義或第三方庫實現分割線等 | 內置 ItemDecoration 機制 | RecyclerView 添加裝飾更標準方便 |
點擊事件 | 內置 OnItemClickListener 等 | 需在 ViewHolder 或 Adapter 中手動實現 | ListView 簡單,RecyclerView 更靈活 (監聽子控件) |
優化點 | 優化 getView() (復用, ViewHolder, 布局扁平化) | 優化 onBindViewHolder , 使用精細 notify, Payloads, DiffUtil, 配置緩存池/大小, 預加載, 共享 Pool, setHasFixedSize | RecyclerView 提供更多、更強大的內置和可配置優化手段 |
應用場景 | 維護舊代碼,極簡單短列表 | 所有現代列表/網格/瀑布流需求,復雜布局,高性能長列表 | 新項目無腦選 RecyclerView |