給 Adapter 作 「加法」 —— 實戰 MergeAdapter

什麼是 MergeAdapter ?

在最新的 recyclerview:1.2.0-alpha02 中發佈了一個關於 Adapter 的新特性 MergeAdapter 。咱們能夠 「合併」Adapter,或者說給 Adapter 「作加法」。java

聽起來可能不是那麼容易理解。咱們先來看下面的 RecyclerView 應該如何實現?android

Merge Adapter

源碼地址見文末。git

實現起來其實很簡單,利用現有的知識,大部分人都能想到用多類型的 itemView 。這裏要區分三種類型,Teacher ,Student 和 Foot 。不一樣的類型要對應不一樣的佈局文件,一樣也對應不一樣的業務邏輯。github

長久以來咱們一直都是這麼作的。那麼,你有沒有想過這麼作有什麼不合理的地方嗎?耦合度太高 。上面的示例中一個 Adapter 須要負責三套視圖佈局的呈現,若是是四套,五套,甚至更多呢?從 擴展性 上來講,這個方案也不盡合理。設計模式

既然如此,那就讓每一個 Adapter 只負責一套視圖佈局。既下降了代碼耦合度,又便於擴展。若是出現了新的佈局類型,再來一個 Adapter 就好了。上面的示例中一共須要三個 Adapter,TeahcherAdapterStudentAdapterFootAdapteride

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

看起來很美好,各司其職,互不干擾。然而問題時,你的 RecyclerView 能夠接受幾個 Adapter ?佈局

public void setAdapter(@Nullable Adapter adapter) {
        // bail out if layout is frozen
        setLayoutFrozen(false);
        setAdapterInternal(adapter, false, true);
        processDataSetCompletelyChanged(false);
        requestLayout();
    }
複製代碼

RecyclerView 顯然是 「一夫一妻制」 。經過 setAdapter() 方法,咱們只能給 RecyclerView 設置一個 Adapter 。post

recyclerview:1.2.0-alpha02 中,其實咱們仍然只能設置一個 Adapter ,可是這個 Adapter 能夠是 MergeAdapter ,一個能夠作加法的 Adapter 。學習

直接上代碼。

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
複製代碼

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

第一塊佈局是 Teacher 。在實際開發中,經常能夠用做 Header View 。

class TeacherAdapter : ListAdapter<Teacher, TeacherViewHolder>(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))
    }

}
複製代碼

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

class StudentAdapter : ListAdapter<Student, StudentViewHolder>(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))
    }
}
複製代碼

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

class FootAdapter : ListAdapter<LoadState, StateViewHolder>(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))
    }
}
複製代碼

和日常的 Adapter 寫法並沒有二致。

有什麼好處 ?

衆所周知,RecyclerView 是一個設計極其精妙的類庫,從源碼裏能夠發現不少設計模式的身影。MergeAdapter 也不例外。

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

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

有什麼限制 ?

如上面的例子所示,MergeAdapter 的數據展現順序,是按照構造函數中的參數順序依次排列的,並且同類型的數據老是集中展現的。因此,對於不肯定性的,動態類型的複雜視圖,MergeAdapter 是沒法處理的。

另外說一點,稱不上限制,應該說對 MergeAdapter 能力的指望。若是能支持多 LayoutManager 那就更好了。在多類型 RecyclerView 中,部分數據須要橫向滑動展現,部分數據須要縱向滑動展現,這種狀況已經比較常見了。

最後

MergeAdapter 的一些學習資源。

Android 開發團隊的相關介紹:

medium.com/androiddeve…

掘金譯文:

juejin.im/post/5e86ff…

文中示例源碼地址:

github.com/lulululbj/S…


這裏是秉心說,歡迎關注個人公衆號,第一時間爲你帶來 Android 最新動態。

相關文章
相關標籤/搜索