适用于Android开发者的Databinding自定义view双向绑定

语言: CN / TW / HK

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言

本篇默认读者已经对databinding 有过一定的了解,如果不甚了解,可以看看东哥的DataBinding最全使用说明,很多人排斥databinding,但也有人喜欢使用。虽然它有争议,但是不妨碍我们去学习、了解。\ 本篇讲述的是自定义view如何来双向绑定数据,来实现在xml中使用自定义也能达到 app:customvalue="@={userName}"的效果

app:xx="@{userName}" 单向绑定\ app:xx="@={userName}" 双向绑定

引入Databingding

1、第一步 \ 引入kapt插件 image.png 2、第二步 \ 开启databinding(这里有 gradle新版跟旧版的区别,具体是哪个版本,有知道的评论区告诉我) 新版本的gradle buildFeatures.dataBinding = true image.png 旧版本的gradle android{ /.../ dataBinding { enabled = true; } }

实现代码

这里提供的实现方法肯定不是最优解,或者说是最好的,但是作为抛砖引玉的小知识,是比较适合的

1、先给大家看一下整个工程的目录

image.png

2、首先,新建一个CustomView、里面有一个TextView、EditText来作为演示。 ``` class CustomView(mContext: Context, attributeSet: AttributeSet?) : LinearLayout(mContext, attributeSet) { private var onChangeListener: InverseBindingListener? = null private var onInputChangeListener: InverseBindingListener? = null private var itemInput: EditText? = null private var itemText: TextView? = null var etInput = "" set(value) { val oldValue = field if (value == oldValue) { return; } field = value onInputChangeListener?.onChange() }

var tvValue = ""
    set(value) {
        val oldValue = field
        if (value == oldValue) {
            return;
        }
        field = value
        onChangeListener?.onChange()
    }

init {
    initView(mContext, attributeSet)
}


private fun initView(mContext: Context, attributeSet: AttributeSet?) {
    val view = inflate(mContext, R.layout.widget_custom_view, this)
    itemInput = view.findViewById(R.id.et_input)
    itemText = view.findViewById(R.id.tv_value)
    itemText?.setOnClickListener {
        tvValue = System.currentTimeMillis().toString()
        itemText?.text = tvValue
    }
    itemInput?.doOnTextChanged { text, start, before, count ->
        etInput = text.toString()
    }
}

internal fun setOnInputChangeListener(listener: InverseBindingListener) {
    if (onInputChangeListener == null) {
        this.onInputChangeListener = listener
    }
}

internal fun setOnValueChangeListener(listener: InverseBindingListener) {
    if (onChangeListener == null) {
        this.onChangeListener = listener
    }
}

} widget_custom_view.xml

3、新建一个BindAdapter管理类 DatabindComponent类来管理相关使用,也可以将其中的代码 通过BindMethod的方式写在CustomView中 object DataBindComponent {

@BindingAdapter("itemInput")
@JvmStatic
fun CustomView.setItemInputParams(value: String) {
    etInput = value
}

@InverseBindingAdapter(attribute = "itemInput", event = "itemInputAttrChanged")
@JvmStatic
fun getItemInputParams(view: CustomView): String {
    return view.etInput
}

@BindingAdapter(value = ["itemInputAttrChanged"], requireAll = false)
@JvmStatic
fun CustomView.itemPutChange(textAttrChanged: InverseBindingListener) {
    setOnInputChangeListener(textAttrChanged)
}

@BindingAdapter("itemValue")
@JvmStatic
fun CustomView.setItemValueParams(value: String) {
    tvValue = value
}

@InverseBindingAdapter(attribute = "itemValue", event = "itemValueAttrChanged")
@JvmStatic
fun getItemValueParams(view: CustomView): String {
    return view.tvValue
}

@BindingAdapter(value = ["itemValueAttrChanged"], requireAll = false)
@JvmStatic
fun CustomView.itemValueChange(textAttrChanged: InverseBindingListener) {
    setOnValueChangeListener(textAttrChanged)
}

} ``` 4、在xml中添加CustomView,并添加两个ObservableField 来进行数据的监听绑定

image.png

image.png

总结

没有贴GitHub代码仓库的原因是因为这些示例代码,是通过Moudle的方式写在project中,其中一些Moudle不适合放开。关键代码全都将其放了出来,只有一些使用的代码,通过图片形式展示。如果你刚好有将自定义view双向绑定的需求,以上示例,完全可以帮助到你,如果你有更好的写法,可以在评论区告诉我,让我学习