DataBinding原理----双向绑定(4)
创始人
2024-03-17 23:52:37
0

        前面的几种文章分析了DataBinding单向数据绑定的原理,今天来看看双向数据绑定是怎么回事。

        我们知道单向绑定是在数据发生变化的时候能够通知到UI,让数据的变化能够及时反应到UI上;而双向绑定则是不仅要让数据的变化能够反馈到UI上,而且还要让UI的变化也能够反馈到数据上,前面已经分析了数据的变化如何反馈到UI上,所以这篇文章就只分析UI的变化是如何反馈到数据上。

        为了方便说明,我们使用如下的UI进行演示:

         界面下方有个格式化时间,它是一个TextView,这里要做的就是在点击该控件的时候把显示内容更新为当前时间,这个操作就对应到UI变化,此时会把当前时间保存到相应的LiveData中(也就是UI变化反馈到数据中)。接下来主要分三步来说明如何实现该效果。

一、修改绑定表达式

        在单向数据绑定中我们按如下方式使用:

        

        单向绑定表达式为:@{viewModel.second},而在双向绑定中按如下方式使用:

        

        双向绑定表达式为:@={viewModel.time},多了个“=”号,同时在生存的相关xml中也有所不同:

        falsetrue

        相应的true标签为true,而单向绑定中为false。

二、监听UI变化

        单向绑定中数据变化会通知到UI,使用到的是观察者模式;以LiveData为例,就是在LiveData变化的时候会执行相应的绑定表达式。

        而在双向绑定中,则需要监听UI变化,使用的则是事件或者控制提供的机制监听UI变化,以这里的TextView为例。

package com.zfang.databindingstudy.widgetimport android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.zfang.databindingstudy.binds.AppTextBinds
import java.text.SimpleDateFormat
import java.util.*class MyAppText(ctx: Context, attr: AttributeSet): AppCompatTextView(ctx, attr) {private var timeDate: Date? = nullfun timeChange(time: Date): Boolean {if (null == timeDate) {return true}return timeDate!! != time}private fun setTime(time: String) {text = time}fun setTime(timeDate: Date) {this.timeDate = timeDatesetTime(AppTextBinds.formate(timeDate))}fun getTime() = timeDate!!
}

        这是一个自定义TextView用于显示格式化时间,其中的timeChange方法用于判断时间是否有变化,如果有变化再更新显示时间(否则会引起无限循环)。

        相应的BindAdapter如下:

package com.zfang.databindingstudy.bindsimport android.util.Log
import androidx.databinding.*
import com.zfang.databindingstudy.widget.MyAppText
import java.text.SimpleDateFormat
import java.util.*
import kotlin.reflect.KClass
//@BindingMethods(
//    BindingMethod(type = MyAppText::class, attribute = "app:time", method = "setFormattedTime")
//)
class AppTextBinds {companion object {private val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())fun getDate(timeStr: String) = timeStr.apply {Log.e("zfang", "dateStr = ${this}")formatter.parse(this)}fun formate(date: Date) = formatter.format(date)@BindingAdapter("app:time")@JvmStatic fun setTime(view: MyAppText, newValue: Date) {Log.e("zfang", "setTime")// Important to break potential infinite loops.val timeStr = formatter.format(newValue)if (view.timeChange(newValue)) {view.setTime(newValue)}}/*** 双向绑定调用的方法(UI变化 -> 从UI获取数据)*/@InverseBindingAdapter(attribute = "app:time")@JvmStatic fun getTime(view: MyAppText) : Date {Log.e("zfang", "getTime")return view.getTime()}/*** 设置双向绑定调用时机*/@BindingAdapter("app:timeAttrChanged")@JvmStatic fun setListeners(view: MyAppText, attrChange: InverseBindingListener) {Log.e("zfang", "on UI change")view.apply {setOnClickListener {text = formate(Date())attrChange.onChange()}}}}
}

        其中的setListeners用于建立双向绑定的监听,它是由DataBinding调用的,在该方法中设置了View的点击监听,同时更新了UI上的显示数据,接着调用InverseBindingListener的onChange,该方法会更新相应的LiveData数据。

        相应的LiveData如下:

package com.zfang.databindingstudy.moduleimport androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.util.*class SimpleViewModel: ViewModel() {private val _first = MutableLiveData("Alice")private val _second = MutableLiveData("Bob")val first: LiveData = _firstval second: LiveData = _secondvar time :MutableLiveData = MutableLiveData(Date())set(date) {if (field == date) {return}field = date}
}

        数据流路径为:onClick -> InverseBindingListener.onChange -> 设置LiveData time的值,需要注意的是此时time的变化会导致requestRebind的调用,重而更新UI,此时需要判断数据是否发生变化再设置相应的LiveData数据,否则会产生死循环。

三、接收数据反馈

        接着上面说到的InverseBindingListener.onChange调用,其实现如下:

    // Inverse Binding Event Handlersprivate InverseBindingListener mboundView3timeAttrChanged = new InverseBindingListener() {@Overridepublic void onChange() {// Inverse of viewModel.time.getValue()// is viewModel.time.setValue((Date) callbackArg_0)//上面定义的方法,获取时间Date callbackArg_0 = AppTextBinds.getTime(mboundView3);// localize variables for thread safety// viewModel.time.getValue()Date viewModelTimeGetValue = null;// viewModelSimpleViewModel viewModel = mViewModel;// viewModel.timeMutableLiveData viewModelTime = null;// viewModel != nullboolean viewModelJavaLangObjectNull = false;// viewModel.time != nullboolean viewModelTimeJavaLangObjectNull = false;viewModelJavaLangObjectNull = (viewModel) != (null);if (viewModelJavaLangObjectNull) {viewModelTime = viewModel.getTime();viewModelTimeJavaLangObjectNull = (viewModelTime) != (null);if (viewModelTimeJavaLangObjectNull) {//设置UI数据到LiveData中viewModelTime.setValue(((Date) (callbackArg_0)));}}}};

        上面带注释的两处即是更新了相应数据中的值(数据是从UI中获取,在当前场景中也就是TextView)。当然这里的代码是DataBinding生存的,我们需要做的是实现AppTextBinds 中SetListener方法,监听UI的变化并回调InverseBindingListener.onChange,这样就实现UI的变化反馈到数据中。

        需要了解单向绑定的可以点这里(DataBinding原理----单向数据绑定(3)),源代码在这里

相关内容

热门资讯

广东建工:公司与广州地铁不存在... 证券之星消息,广东建工(002060)12月25日在投资者关系平台上答复投资者关心的问题。 投资者提...
“政策找人”暖民心 服务下沉“... 城乡居民基本医疗保险作为我国覆盖人群最多的基本医疗保险,是基本医疗保障体系中的重要一环。为切实维护人...
我市举办调解工作专题培训 为深入贯彻法治政府建设要求,全面提升基层矛盾纠纷化解能力,12月19日,市司法局牵头举办了全市调解工...
河南将优化涉企法律服务,营造法... 【大河财立方 记者 朱娟 见习记者 岳炎霖】12月26日,大河财立方记者在省政府新闻办召开的《河南省...
新版河南省优化营商环境条例哪些... 【大河财立方 记者 朱娟 见习记者 岳炎霖】12月26日,河南省政府新闻办召开《河南省优化营商环境条...
原创 中... 12月25日,家纺企业富安娜披露了关于中信证券固定收益类理财产品逾期兑付的进展公告。公告显示,公司近...
封关临近!海南自贸港政策红利释... 交易所数据显示,2025年12月26日09时47分,京粮控股当前价格为8.92元,涨幅为9.99%,...
字节跳动通报:120名员工被辞... 12月25日,字节跳动披露2025年三季度内部违规案例的处理情况。通报显示,三季度共有120名员工因...
上亿理财难收回,家纺龙头富安娜... 12月25日晚,家纺龙头企业深圳市富安娜家居用品股份有限公司(以下简称富安娜,002327.SZ)发...
华院计算取得法律要素图谱辅助类... 国家知识产权局信息显示,华院计算技术(上海)股份有限公司取得一项名为“一种法律要素图谱辅助类案推荐方...