在以前的文章 0xA05 Android 10 源碼分析:Dialog 加載繪製流程以及在 Kotlin、DataBinding 中的使用 分析了 Dialog 加載繪製流程、設計模式,以及基於 DataBinding 封裝的 DataBindingDialog 的基礎庫 JDataBinding,這篇文章主要講基於 DataBinding 封裝的基礎庫 JDataBindingandroid
JDataBinding 源碼地址:https://github.com/hi-dhl/JDataBindinggit
JDataBinding 是基於 DataBinding 封裝的 DataBindingActivity、DataBindingFragment、DataBindingDialog、DataBindingListAdapter 基礎庫,歡迎 start
github
DataBinding 是什麼?查看 Google官網,會有更詳細的介紹
算法
DataBinding 是 Google 在 Jetpack 中推出的一款數據綁定的支持庫,利用該庫能夠實如今頁面組件中直接綁定應用程序的數據源編程
利用 Kotlin 的 inline、reified、DSL 等等語法, 結合着 DataBinding,能夠設計出更加簡潔並利於維護的代碼設計模式
DataBindingListAdapter 是基於 ListAdapter 封裝的,使用更少的代碼快速實現 RecyclerView adapter and ViewHolderbash
什麼是 ListAdapter?app
ListAdapter 是 Google 推出的一個新的類庫,相比傳統的 Adapter,它可以用較少的代碼實現更多的 RecylerView 的動畫,而且能夠自動存儲以前的 list,ListAdapter 還加入了 DiffUtil 的工具類,只有當 items 變化的時候進行刷新,而不用刷新整個 list,大大提升 RecyclerView 的性能ide
什麼是 DiffUtil?函數
DiffUtil 主要在後臺計算 list 是否相同,而後回到回主線程刷新數據,主要用了 Myers Diff Algorithm, 而咱們平常使用的 git diff 就用到了該算法
好了介紹完基礎概念以後,來看一下 DataBindingListAdapter 是如何使用的,爲何我會說使用更少的代碼快速實現 RecyclerView adapter and ViewHolder
Step1: 繼承 BaseViewHolder
建立一個自定義的 ViewHolder 類,繼承 DataBindingListAdapter,經過 viewHolderBinding 能夠快速實現 DataBinding 的綁定
class TestViewHolder(view: View) : BaseViewHolder<Model>(view) {
val binding: RecycieItemTestBinding by viewHolderBinding(view)
override fun bindData(data: Model) {
binding.apply {
model = data
executePendingBindings()
}
}
}
複製代碼
Step2: 繼承 DataBindingListAdapter
實現帶頭部和尾部的 Adapter,建立自定義的 Adapter,繼承 DataBindingListAdapter
class TestAdapter : DataBindingListAdapter<Model>(Model.CALLBACK) {
override fun viewHolder(layout: Int, view: View): DataBindingViewHolder<Model> = when (layout) {
R.layout.recycie_item_header -> HeaderViewHolder(view)
else -> TestViewHolder(view)
}
override fun layout(position: Int): Int = when (position) {
0 -> R.layout.recycie_item_header
getItemCount() - 1 -> R.layout.recycie_item_footer
else -> R.layout.recycie_item_test
}
override fun getItemCount(): Int = super.getItemCount() + 2
}
複製代碼
構造方法傳入了 Model.CALLBACK,Model.CALLBACK 實現了 DiffUtil.ItemCallback,用於計算 list 的兩個非空 item 的不一樣。具體要寫兩個抽象方法 areItemsTheSame 和 areContentsTheSame
val CALLBACK: DiffUtil.ItemCallback<Model> = object : DiffUtil.ItemCallback<Model>() {
// 判斷兩個Objects 是否表明同一個item對象, 通常使用Bean的id比較
override fun areItemsTheSame(oldItem: Model, newItem: Model): Boolean =
oldItem.id == newItem.id
// 判斷兩個Objects 是否有相同的內容。
override fun areContentsTheSame(oldItem: Model, newItem: Model): Boolean = true
}
複製代碼
Step3: 綁定 RecyclerView 和 Adapter
<data>
<variable
name="viewModel"
type="com.hi.dhl.jdatabinding.demo.ui.MainViewModel" />
<variable
name="testAdapter"
type="com.hi.dhl.jdatabinding.demo.ui.TestAdapter" />
</data>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:adapter="@{testAdapter}"
app:adapterList="@{viewModel.mLiveData}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
複製代碼
這裏用到了 DataBinding 的自定義數據綁定部分,能夠百度、Google 具體的用法,具體實現能夠參考 demo 下面 fragment_test.xml 文件
在 Kotlin 中應該儘可能避免使用構建者模式,使用 Kotlin 的具名可選參數構造類,實現構建者模式,代碼更加簡潔
在 "Effective Java" 書中介紹構建者模式時,是這樣子描述它的:本質上 builder 模式模擬了具名的可算參數,就像 Ada和 Python中的同樣
幸運的是,Kotlin 是一門擁有具名可選參數的變成語言,DataBindingDialog 在使用 Kotlin 的具名可選參數構造類實現 Dailog 構建者模式的基礎上,用 DataBinding 進行二次封裝,加上 DataBinding 數據綁定的特性,使 Dialog 變得更加簡潔、易用
Step1: 繼承 DataBindingDialog
class AppDialog(
context: Context,
val title: String? = null,
val message: String? = null,
val yes: AppDialog.() -> Unit
) : DataBindingDialog(context, R.style.AppDialog) {
private val mBinding: DialogAppBinding by binding(R.layout.dialog_app)
init {
requireNotNull(message) { "message must be not null" }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
mBinding.apply {
setContentView(root)
display.text = message
btnNo.setOnClickListener { dismiss() }
btnYes.setOnClickListener { yes() }
}
}
}
複製代碼
Step2: 簡潔的調用方式
AppDialog(
context = this@MainActivity,
message = msg,
yes = {
// do something
}).show()
複製代碼
Kotlin 中的函數和構造器都支持具名可選參數,在使用上更加靈活,在 DataBindingActivity 中使用 Kotlin 的 inline、reified 強大的特性,將類型參數實化,初始化 View 更加簡潔
繼承 DataBindingActivity
class MainActivity : DataBindingActivity() {
private val mBinding: ActivityMainBinding by binding(R.layout.activity_main)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding.apply {
dialog.setOnClickListener {
val msg = getString(R.string.dialog_msg)
AppDialog(
context = this@MainActivity,
message = msg,
yes = {
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show()
}).show()
}
}
}
}
複製代碼
在 Fragment 當中如何使用 Kotlin 的 inline、reified 初始化 View,能夠查看DataBindingFragment
繼承自 DataBindingFragment
class FragmentTest : DataBindingFragment() {
val testViewModel: MainViewModel by viewModel()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding<FragmentTestBinding>(
inflater,
R.layout.fragment_test, container
).apply {
viewModel = testViewModel
testAdapter = TestAdapter()
lifecycleOwner = this@FragmentTest
}.root
}
}
複製代碼
關於基於 DataBinding 封裝的 DataBindingActivity、DataBindingFragment、DataBindingDialog、DataBindingListAdapter 基礎庫,點擊 JDataBinding 前往查看,歡迎start
JDataBinding 源碼地址:https://github.com/hi-dhl/JDataBinding
致力於分享一系列 Android 系統源碼、逆向分析、算法相關的文章,每篇文章都會反覆推敲,結合新的技術,帶來一些新的思考,若是你同我同樣喜歡 coding,一塊兒來學習,期待與你一塊兒成長