Android App 開發實戰系列 Part 5. Epoxy on RecyclerView
Part 5. 要來介紹呈現電影列表的套件 Epoxy,主要是用這個套件來呈現比較複雜的列表,像是我們的首頁,穿插夾雜了橫滑和直滑的列表,用嵌套的 RecyclerView 實作技術上來說一定做得到,只是你要多花時間心力,而 Epoxy 套件提供一個更容易的實作方式,讓我們來看看如何實作。
完整程式碼 https://github.com/enginebai/MovieHunt 已經釋出,可以下載程式碼邊看程式碼邊學習,歡迎給星 ⭐ 支持。這一系列文章是有連貫性的,如果還沒看過前面文章,建議先去看過前面的章節。傳送門:(Part 1.) (Part 2.) (Part 3.) (Part 4.)
還沒使用 Epoxy 之前
假設我們現在列表第一筆資料希望比較突顯,要放大圖片和文字,和其他地方用不同方式呈現,以我們傳統的 RecyclerView
作法就會是宣告兩個不同的介面檔 item_movie_large.xml
/ item_movie_normal.xml
,然後在 Adapter
宣告不同的 View Type 分開使用:
如果今天再複雜一點,像是下列需求:
- 列表有分頁載入,當滑到列表最底下需要呈現載入中的
ProgressBar
,像是 MovieHunt 的垂直列表。 - 列表要呈現不同分類的橫滑列表,且橫滑列表要支援分頁載入,載入中要呈現
ProgressBar
,像是 MovieHunt 的首頁。 - 列表一開始要呈現另一個橫滑的列表、或是不同的橫滑項目列表,像是 FunNow。
- 列表要呈現不同分類的橫滑列表,像是 Google Play 呈現方式。
這樣的介面在傳統的 RecyclerView
寫起來肯定是複雜非常多,垂直 RecyclerView
裡面要嵌套橫向 RecyclerView
,要定義不同的 Adapter
,裡面要支援不同的 ViewType ... 等,需要很多的元件模板和配置才能達到,以幾個知名的預定 App 來說需求肯定比上述幾個來要來的複雜,Airbnb 的工程團隊推出了 Epoxy 來解決這樣的問題。
Epoxy 是?
Epoxy 簡單來說就是讓你可以用輕鬆的方式來建立複雜的列表,你只需要定義
- Step 1. 各別每一種 item view 長的樣子。
- Step 2. 怎麼用 Step 1. 定義的 item view 在列表中排列出來。
對應到 Epoxy 的話,就是定義下面兩種不同元件:
EpoxyModel
:以傳統RecyclerView
的作法來說,這個EpoxyModel
就是RecyclerView.ViewHolder
的角色,在 Model 裡面給定資料、定義資料如何在介面上呈現、要怎麼互動 ... 等等。EpoxyController
:把定義好不同的EpoxyModel
在這邊組合起來成為列表的樣子。
以圖來說明這兩個元件:
以我們文章一開始提到「列表第一筆資料希望圖片和文字放大」需求來說,我們會宣告 LargeEpoxyModel
& NormalEpoxyModel
,然後實作 EpoxyController
,程式碼大致是這樣(這邊先著重了解概念就好,等等會講細節怎麼宣告和使用):
對於 Epoxy 有一個大致的了解後,我們就來針對 MovieHunt 專案的使用來細說 EpoxyModel
和 EpoxyController
用法。
Epoxy Model
這就代表了列表中每一個 item 的實作, EpoxyModel
支援三種不同的實作方式:
- Custom View
- Data Binding
- View Holder
用法是你依照這三種不同的方式來實作 Model 類別,然後 建置專案 Make Project 後, EpoxyModel
會幫你自動產生以底線 _ 結尾的類別或者 Kotlin DSL Builder,你就可以在 EpoxyController
裡面使用這些自動產生的類別。
注意:你只要新增一個
EpoxyModel
或者對EpoxyModel
類別裡面做什麼變動,都要 建置專案,Epoxy 才會自動重新幫你建立新的類別,你才可以在EpoxyController
使用。
我們依序來看怎麼在專案中使用不同的實作:
Custom Views
我們專案比較沒用到這種模式,這種比較多用在客製化的介面上,我提供一個簡單的範例,假設我們要顯示一個客製化下拉選單,選單是使用 RecyclerView
來實作,裡面的 item view 需要客製化,選單項目要可以選取或取消選取,選取起來要顯示特別的圖示,那麼我們會這樣宣告那個 custom view:
@ModelView, @TextProp, @ModelProp, @CallbackProp, @AfterPropsSet
都是 Epoxy 針對 Custom View 所提供的標注,分別是用在 1. 定義 Custom View 類別 2. 定義文字屬性 3. 定義一般屬性 3. 定義 Callback 屬性 4. 在屬性設定後呼叫的方法,更多更詳細的用法可以參考 官方 Wiki 。
Data Binding
我們對於資料綁定本身在 Android 的設定不多做說明,我們假設你已經設定好且可以正常使用 Android data binding,這邊我們只講解如何在 Epoxy 使用,以常見一般情況來說,我們可以做一個設定,然後就可以靠 Epoxy 來幫我們自動產生 EpoxyModel
。
以 MovieHunt 專案來解說怎麼設定,我們會到 package 根目錄也就是 com.enginebai.moviehunt
新增一個檔案名稱是 package-info.java
。
這個檔案裡面要把 R.class
和你想要讓 Epoxy 幫你自動產生 EpoxyModel
的 layout 檔名前綴加進去:
注意:這邊 import 是放在 package 宣告之後。
檔案 package-info.java
加入後,之後建置專案時,Epoxy 就會自動幫你抓檔名以 item 為開頭的 layout 檔來產生 EpoxyModel
。首頁列表的 Item View 我們會使用資料綁定的方式來實作 item_movie_home_normal.xml
:
完成後建置專案,你就可以在 EpoxyController
使用 :
View Holder
這個實作方式和原本的 RecyclerView.ViewHolder
非常的接近,你就像平常一樣定義介面檔,然後實作一個繼承 EpoxyModelWithHolder<T>
的抽象類別:
- 這邊
@EpoxyModelClass(layout = R.layout.holder_movie_landscape)
指定你的介面 xml 檔 - Model 裡面會用
@EpoxyAttribute
來定義不同的屬性,好讓你在bind()
方法可以用來顯示資料 class Holder : EpoxyHolder()
則是定義 Holder 以及 View 元件。
Model 宣告的部份就到這邊,更詳盡的用法可以參考 官方文件,文件上介紹更多用法和注意事項(像是宣告 ID,ID 是用來做 Model 的 Diff 和狀態儲存 … 等),接下來我們要來講解如何在 EpoxyController
使用這些 EpoxyModel
來建立你的列表。
Epoxy Controller
這類別控制列表要如何呈現,我們會建立一個類別來繼承 EpoxyController
,然後實作唯一的方法 buildModels()
:
我們列表是使用 PagedList
,所以是繼承 PagedListEpoxyController
,概念上是一樣的,最後一個 addModels()
方法則是當我們滑到底部要載入下一頁資料時,可以在底部呈現載入中的介面,這邊 順序是非常重要的,在 Controller 裡面如何擺放 Model 就 直接決定了最後介面呈現的樣子,如果在 Controller.buildModel()
這樣設定:
結語
Epoxy 可以幫助開發者建構出複雜的頁面,降低 RecyclerView 顯示不同介面的實作複雜度,同時可以提高介面元件的重複利用性,大致上的用法就這樣,更多注意事項和選項非常建議到 官方 Wiki 好好看過。
你看完這篇可能會非常好奇首頁是如何實作的?要如何在垂直列表裡面新增不同的水平列表、直的列表裡面還有很多橫的列表該怎麼實作?這部份屬於進階用法超過這章節的深度,礙於篇幅我會在後續章節提及怎麼實作,敬請鎖定這一系列文章。
如果你有任何和此專案相關的疑問,歡迎留言給我交流或討論。完整程式碼: https://github.com/enginebai/MovieHunt 歡迎 Fork + Star ⭐ 支持。