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

1,588 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="100dp"
  android:orientation="vertical">

  <TextView
    android:id="@+id/tv_value"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@color/purple_200"
    android:gravity="center"
    android:hint="请选择"
    android:textColor="@color/white"
    android:textColorHint="@color/white"
    android:textSize="16dp" />

  <EditText
    android:id="@+id/et_input"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@color/teal_700"
    android:gravity="center"
    android:hint="请输入"
    android:textColor="@color/white"
    android:textColorHint="@color/white"
    android:textSize="16dp" />
</LinearLayout>

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双向绑定的需求,以上示例,完全可以帮助到你,如果你有更好的写法,可以在评论区告诉我,让我学习