天天看點

當 Adapter 遇上 Kotlin DSL,無比簡單的調用方式

早在去年的時候我就提到過使用工廠的方式擷取 Adapter 而不是為每個 Adapter 定義一個類檔案。這樣的好處是,對于不是那麼複雜的 Adapter 可以節省大量的代碼,提升開發效率和解放雙手,同時更好的支援多類型布局效果。

1、Kotlin DSL 和 Adapter 工廠方法

可以把 Kotlin DSL 當作建構者使用。這裡有一篇不錯的文章,想了解的可以閱讀下,

https://www.ximedes.com/2020-04-21/kotlin-dsl-tutorial/

Kotlin DSL 是拓展函數的延申,比如我們常用的 with 等函數就是函數的拓展,

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
           

這裡是泛型 T 的拓展。這裡的 T 可以類比到 Java 建構者模式中的 Builder,通過方法接收外部參數之後調用

build()

方法建立一個最終的對象即可。

對于 Adapter 工廠方法,之前我是通過如下方式使用的,

fun <T> getAdapter(
    @LayoutRes itemLayout:Int,
    converter: (helper: BaseViewHolder, item: T) -> Unit,
    data: List<T>
): Adapter<T> = Adapter(itemLayout, converter, data)

class Adapter<T>(
    @LayoutRes private val layout: Int,
    private val converter: (helper: BaseViewHolder, item: T) -> Unit,
    val list: List<T>
): BaseQuickAdapter<T, BaseViewHolder>(layout, list) {
    override fun convert(helper: BaseViewHolder, item: T) {
        converter(helper, item)
    }
}
           

也就是每次想要得到 Adapter 的時候隻要調用

getAdapter()

方法即可。這種封裝方式比較簡陋,支援的功能有限。後來慢慢采用了 Kotlin DSL 之後,我封裝了 Kotlin DSL 風格的工廠方法。采用 Kotlin DSL 風格之後更加優雅和友善快捷,同時更好的支援多類型布局效果。

2、使用

2.1 引入依賴

首先,該項目依賴于 BRVAH,是以,你需要引入該庫之後菜可以使用。BRVAH 可以說是目前開源的最好用的 Adapter,我們沒必要再另起爐竈自己再造輪子。這個架構設計最好地方在于通過 SpareArray 收集了 ViewHolder 控件,進而避免了自定義 ViewHolder,這是我們架構設計的基礎思想。

該項目已經上傳到了 MavenCentral,你需要先在項目中引入該倉庫,

allprojects {
    repositories {
        mavenCentral()
    }
}
           

然後在項目中添加如下依賴,

implementation "com.github.Shouheng88:xadapter:${latest_version}"
           

2.2 使用 Adapter 工廠方法

使用 xAdapter 之後,當你需要定義一個 Adapter 的時候,你無需單獨建立一個類檔案,隻需要通過

createAdapter()

方法擷取一個 Adapter,

adapter = createAdapter {
    withType(Item::class.java, R.layout.item_eyepetizer_home) {
        // Bind data with viewholder.
        onBind { helper, item ->
            helper.setText(R.id.tv_title, item.data.title)
            helper.setText(R.id.tv_sub_title, item.data.author?.name + " | " + item.data.category)
            helper.loadCover(requireContext(), R.id.iv_cover, item.data.cover?.homepage, R.drawable.recommend_summary_card_bg_unlike)
            helper.loadRoundImage(requireContext(), R.id.iv_author, item.data.author?.icon, R.mipmap.eyepetizer, 20f.dp2px())
        }
        // Item level click and long click events.
        onItemClick { _, _, position ->
            adapter?.getItem(position)?.let {
                toast("Clicked item: " + it.data.title)
            }
        }
    }
}
           

在這種新的調用方式中,你需要通過

withType()

方法指定資料類型及其對應的布局檔案,然後在

onBind()

方法中即可實作資料到 ViewHolder 的綁定操作。這裡的

onBind()

方法的使用與 BRVAH 中的

convert()

方法使用一緻,可以通過閱讀該庫了解如何使用。總之,xAapter 在 BRVAH 的基礎上做了二次封裝,可以說,比簡單更簡單。

xAdapter 支援為每個 ViewHolder 綁定點選和長按事件,同時也支援為 ViewHolder 上的某個單獨的 View 添加點選和長按事件。使用方式如上所示,隻需要添加

onItemClick()

方法并實作自己的邏輯即可。其他的點選事件可以參考項目的示例代碼。

效果,

當 Adapter 遇上 Kotlin DSL,無比簡單的調用方式

2.3 使用多類型 Adapter

多類型 Adapter 的使用方式非常簡單,類似于上面的調用方式,隻需要在

createAdapter()

内再添加一個

withType()

方法即可。下面是一個寫起來可能相當複雜的 Adapter,但是采用了 xAdpater 的調用方式之後,一切變得非常簡單,

private fun createAdapter() {
    adapter = createAdapter {
        withType(MultiTypeDataGridStyle::class.java, R.layout.item_list) {
            onBind { helper, item ->
                val rv = helper.getView<RecyclerView>(R.id.rv)
                rv.layoutManager = GridLayoutManager(context, 3)
                val adapter = createSubAdapter(R.layout.item_home_page_data_module_1, 1)
                rv.adapter = adapter
                adapter.setNewData(item.items)
            }
        }
        withType(MultiTypeDataListStyle1::class.java, R.layout.item_home_page_data_module_2) {
            onBind { helper, item ->
                converter.invoke(helper, item)
            }
            onItemClick { _, _, position ->
                (adapter?.getItem(position) as? MultiTypeDataListStyle1)?.let {
                    toast("Clicked style[2] item: " + it.item.data.title)
                }
            }
        }
        withType(MultiTypeDataListStyle2::class.java, R.layout.item_list) {
            onBind { helper, item ->
                val rv = helper.getView<RecyclerView>(R.id.rv)
                rv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
                val adapter = createSubAdapter(R.layout.item_home_page_data_module_4, 3)
                rv.adapter = adapter
                adapter.setNewData(item.items)
            }
        }
        withType(MultiTypeDataListStyle3::class.java, R.layout.item_home_page_data_module_3) {
            onBind { helper, item ->
                converter.invoke(helper, item)
            }
            onItemClick { _, _, position ->
                (adapter?.getItem(position) as? MultiTypeDataListStyle3)?.let {
                    toast("Clicked style[4] item: " + it.item.data.title)
                }
            }
        }
    }
}
           

xAdapter 對多類型布局方式的支援是在 BRVAH 之上進行的改造,在這種封裝方式中,資料類無需實作任何類和接口。Adpater 内部通過 Class 區分各個 ViewHolder.

效果,

當 Adapter 遇上 Kotlin DSL,無比簡單的調用方式

總結

相對于為各種類型的資料定義 Adapter 的使用方式,以上封裝方式的優勢是:

  1. 借助 BRVAH 的優勢,封裝了大量的方法,進一步簡化了 Adapter 的使用;
  2. 通過工廠和 DSL 封裝,簡化了調用 Adapter 的方式,你無需為資料類型定義 Adapter 檔案,減少了項目中需要維護的代碼和類檔案數量;
  3. 通過以上封裝,使用 Adapter 更加簡潔,節省了大量的代碼,提升開發效率和解放雙手;
  4. 自由地在單一類型布局和多類型布局之間進行切換,但是少了沒必要的工廠方法。

當有更加簡潔的使用方式的時候,繼續采用複雜的調用方式無異于抱殘守缺,對于程式員而言,做這種重複而沒有太大價值的工作,付出再多的汗水都不值得同情。以上是部分功能和代碼的展示,可以通過閱讀源碼了解更多。後續我參考其他優秀的庫的設計思想,支援更多 Adapter 特性的封裝來實作快速調用。

項目已開源,感興趣的可以直接閱讀項目源碼,源碼位址:https://github.com/Shouheng88/xAdapter