Google又更新了:實戰 MergeAdapte

1.什麼是 MergeAdapter ?

在最新的 recyclerview:1.2.0-alpha02[1] 中發佈了一個關於 Adapter 的新特性 MergeAdapter 。我們可以 “合併”Adapter,或者說給 Adapter “做加法”。

我簡單給你解釋下,以前我們要寫一個列表,比如有三部分組成:

頭部卡片+內容卡片+尾部卡片

正常我們會考慮多 itemtype 實現。

但是現在出來了一個新的技術方案。

你可以使用 3 個 Adapter 實現,例如 HeaderAdapter+ContentAdapter+FootAdapter,merge 到一塊成為一個 MergeAdapter,設置給 RecyclerView。

有什麼好處呢?

恩...看下文吧。

我們先來看下面的 RecyclerView 應該如何實現?

Google又更新了:實戰 MergeAdapte


源碼地址見文末。

實現起來其實很簡單,利用現有的知識,大部分人都能想到用多類型的 itemView 。

這裡要區分三種類型,Teacher ,Student 和 Foot 。

不同的類型要對應不同的佈局文件,同樣也對應不同的業務邏輯。長久以來我們一直都是這麼做的。

那麼,你有沒有想過這麼做有什麼不合理的地方嗎?

耦合度過高 。

上面的示例中一個 Adapter 需要負責三套視圖佈局的呈現,如果是四套,五套,甚至更多呢?從 擴展性 上來說,這個方案也不盡合理。

既然如此,那就讓每個 Adapter 只負責一套視圖佈局。

既降低了代碼耦合度,又便於擴展。

如果出現了新的佈局類型,再來一個 Adapter 就行了。

上面的示例中一共需要三個 Adapter,TeahcherAdapter,StudentAdapter,FootAdapter 。

  • TeahcherAdapter 負責展示列表最上面 Teacher 部分的視圖。
  • StudentAdapter 負責展示列表主體 Student 部分的視圖。
  • FootAdapter 負責展示列表底部加載狀態的視圖,包含加載中和無更多數據。

看起來很美好,各司其職,互不干擾。

然而問題是,你的 RecyclerView 可以接受幾個 Adapter ?

<code>public void setAdapter(@Nullable Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
processDataSetCompletelyChanged(false);
requestLayout();
}
/<code>

RecyclerView 顯然是 “一夫一妻制” 。通過 setAdapter() 方法,我們只能給 RecyclerView 設置一個 Adapter 。

在 recyclerview:1.2.0-alpha02 中,其實我們仍然只能設置一個 Adapter ,但是這個 Adapter 可以是 MergeAdapter ,一個可以做加法的 Adapter 。

直接上代碼。

<code>private val teacherAdapter by lazy { TeacherAdapter() }
private val studentAdapter by lazy { StudentAdapter() }

private val stateAdapter by lazy { StateAdapter() }

val mergeAdater = MergeAdapter(teacherAdapter, studentAdapter, footAdapter)

recyclerView.adapter = mergeAdapter
/<code>

使用方法就是如此的樸實無華,甚至有那麼一點枯燥。MergeAdapter 構造函數中的參數順序,就標識了列表中數據的顯示順序。

第一塊佈局是 Teacher 。

在實際開發中,常常可以用作 Header View 。

<code>class TeacherAdapter : ListAdapter<teacher>(TeacherDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TeacherViewHolder {
return TeacherViewHolder(
ItemTeacherBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}

override fun onBindViewHolder(holder: TeacherViewHolder, position: Int) {
holder.bind(getItem(position))
}

}

/<teacher>/<code>

第二塊佈局是 Student 。也就是實際開發中的真正的列表數據。

<code>class StudentAdapter : ListAdapter<student>(StudentDiffCallBack()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudentViewHolder {
return StudentViewHolder(
ItemStudentBinding.inflate(
LayoutInflater.from(parent.context),

parent,
false
)
)
}

override fun onBindViewHolder(holder: StudentViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
/<student>/<code>

最後一塊佈局是狀態佈局,也就是通常的 Footer 。包含正在加載,加載失敗和無更多數據,三種狀態。

<code>class FootAdapter : ListAdapter<loadstate>(StateDiffCallBack()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StateViewHolder {
return StateViewHolder(
ItemStateBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}

override fun onBindViewHolder(holder: StateViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
/<loadstate>/<code>

和平常的 Adapter 寫法並無二致。

2.有什麼好處 ?

眾所周知,RecyclerView 是一個設計極其精妙的類庫,從源碼裡可以發現很多設計模式的身影。MergeAdapter 也不例外。

將一個 Adapter 負責多套佈局,拆分為每個 Adapter 只處理一個佈局,大大降低代碼耦合,這是 單一職責原則 。

面對新的需求,需要給 RecyclerView 增加一個新類型的 View。我們需要做的僅僅只是添加新的 Adapter ,而無需修改之前的適配器代碼。對擴展開放,對修改封閉,這是 開閉原則 。

3.有什麼限制 ?

如上面的例子所示,MergeAdapter 的數據展示順序,是按照構造函數中的參數順序依次排列的,而且同類型的數據總是集中展示的。

所以,對於不確定性的,動態類型的複雜視圖,MergeAdapter 是無法處理的。

另外說一點,稱不上限制,應該說對 MergeAdapter 能力的期望。

如果能支持多 LayoutManager 那就更好了。

在多類型 RecyclerView 中,部分數據需要橫向滑動展示,部分數據需要縱向滑動展示,這種情況已經比較常見了。


分享到:


相關文章: