如何在项目中封装 Kotlin + Android Databinding

code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群

作者:HiDhl
链接:https://juejin.im/post/5e9c434a51882573663f6cc6
声明:本文已获HiDhl授权发表,转发等请联系原作者授权

DataBindingDialog

文章首发于掘金 https://juejin.im/post/5e9c434a51882573663f6cc6

在之前的文章 https://juejin.im/post/5e9199db6fb9a03c7916f635 分析了 Dialog 加载绘制流程、设计模式,以及基于 DataBinding 封装的 DataBindingDialog 的基础库 JDataBinding,这篇文章主要讲基于 DataBinding 封装的基础库 JDataBinding

JDataBinding 源码地址:https://github.com/hi-dhl/JDataBinding

JDataBinding 是基于 DataBinding 封装的 DataBindingActivity、DataBindingFragment、DataBindingDialog、DataBindingListAdapter 基础库,欢迎 start

DataBinding 是什么?查看 Google官网,会有更详细的介绍

DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源

利用 Kotlin 的 inline、reified、DSL 等等语法, 结合着 DataBinding,可以设计出更加简洁并利于维护的代码

DataBindingListAdapter

DataBindingListAdapter 是基于 ListAdapter 封装的,使用更少的代码快速实现 RecyclerView adapter and ViewHolder

什么是 ListAdapter?

ListAdapter 是 Google 推出的一个新的类库,相比传统的 Adapter,它能够用较少的代码实现更多的 RecylerView 的动画,并且可以自动存储之前的 list,ListAdapter 还加入了 DiffUtil 的工具类,只有当 items 变化的时候进行刷新,而不用刷新整个 list,大大提高 RecyclerView 的性能

什么是 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 文件

DataBindingDialog

在 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()

DataBindingActivity

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()
            }
        }
    }
}

DataBindingFragment

在 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

参考文献

https://github.com/..BaseRecyclerViewAdapter

结语

致力于分享一系列 Android 系统源码、逆向分析、算法相关的文章,每篇文章都会反复推敲,结合新的技术,带来一些新的思考,如果你同我一样喜欢 coding,一起来学习,期待与你一起成长

相关阅读

Kotlin + Mvp + RxJava + Retrofit 心得体会
用 Kotlin 编写的 MVP+RxJava+Retrofit 黄历 demo
Android 使用 LiveData 实现 EventBus
Glide 源码解析之监听生命周期
深入浅出 Android 屏幕刷新原理

如果你有写博客的好习惯

欢迎投稿

点个在看,小生感恩❤️

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值