• DataBinding原理----双向绑定(4)


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

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

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

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

    一、修改绑定表达式

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

    1. <TextView
    2. android:id="@+id/second"
    3. android:layout_width="wrap_content"
    4. android:layout_height="wrap_content"
    5. android:text="@{viewModel.second}"
    6. android:textColor="#999"
    7. android:textSize="14sp"
    8. app:layout_constraintBottom_toBottomOf="parent"
    9. app:layout_constraintEnd_toEndOf="parent"
    10. app:layout_constraintStart_toStartOf="parent"
    11. app:layout_constraintTop_toBottomOf="@id/first" />

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

    1. <com.zfang.databindingstudy.widget.MyAppText
    2. android:layout_width="wrap_content"
    3. android:layout_height="wrap_content"
    4. android:paddingHorizontal="12dp"
    5. android:paddingVertical="24dp"
    6. android:textSize="22sp"
    7. android:textStyle="bold"
    8. app:layout_constraintBottom_toBottomOf="parent"
    9. app:layout_constraintEnd_toEndOf="parent"
    10. app:layout_constraintStart_toStartOf="parent"
    11. app:time="@={viewModel.time}" />

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

    1. <Target id="@+id/second" tag="binding_2" view="TextView">
    2. <Expressions>
    3. <Expression attribute="android:text" text="viewModel.second">
    4. <Location endLine="35" endOffset="45" startLine="35" startOffset="12" />
    5. <TwoWay>falseTwoWay>
    6. <ValueLocation endLine="35" endOffset="43" startLine="35" startOffset="28" />
    7. Expression>
    8. Expressions>
    9. <location endLine="41" endOffset="61" startLine="31" startOffset="8" />
    10. Target>
    11. <Target tag="binding_3" view="com.zfang.databindingstudy.widget.MyAppText">
    12. <Expressions>
    13. <Expression attribute="app:time" text="viewModel.time">
    14. <Location endLine="53" endOffset="40" startLine="53" startOffset="12" />
    15. <TwoWay>trueTwoWay>
    16. <ValueLocation endLine="53" endOffset="38" startLine="53" startOffset="25" />
    17. Expression>
    18. Expressions>
    19. <location endLine="53" endOffset="43" startLine="43" startOffset="8" />
    20. Target>

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

    二、监听UI变化

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

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

    1. package com.zfang.databindingstudy.widget
    2. import android.content.Context
    3. import android.util.AttributeSet
    4. import androidx.appcompat.widget.AppCompatTextView
    5. import com.zfang.databindingstudy.binds.AppTextBinds
    6. import java.text.SimpleDateFormat
    7. import java.util.*
    8. class MyAppText(ctx: Context, attr: AttributeSet): AppCompatTextView(ctx, attr) {
    9. private var timeDate: Date? = null
    10. fun timeChange(time: Date): Boolean {
    11. if (null == timeDate) {
    12. return true
    13. }
    14. return timeDate!! != time
    15. }
    16. private fun setTime(time: String) {
    17. text = time
    18. }
    19. fun setTime(timeDate: Date) {
    20. this.timeDate = timeDate
    21. setTime(AppTextBinds.formate(timeDate))
    22. }
    23. fun getTime() = timeDate!!
    24. }

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

            相应的BindAdapter如下:

    1. package com.zfang.databindingstudy.binds
    2. import android.util.Log
    3. import androidx.databinding.*
    4. import com.zfang.databindingstudy.widget.MyAppText
    5. import java.text.SimpleDateFormat
    6. import java.util.*
    7. import kotlin.reflect.KClass
    8. //@BindingMethods(
    9. // BindingMethod(type = MyAppText::class, attribute = "app:time", method = "setFormattedTime")
    10. //)
    11. class AppTextBinds {
    12. companion object {
    13. private val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
    14. fun getDate(timeStr: String) = timeStr.apply {
    15. Log.e("zfang", "dateStr = ${this}")
    16. formatter.parse(this)
    17. }
    18. fun formate(date: Date) = formatter.format(date)
    19. @BindingAdapter("app:time")
    20. @JvmStatic fun setTime(view: MyAppText, newValue: Date) {
    21. Log.e("zfang", "setTime")
    22. // Important to break potential infinite loops.
    23. val timeStr = formatter.format(newValue)
    24. if (view.timeChange(newValue)) {
    25. view.setTime(newValue)
    26. }
    27. }
    28. /**
    29. * 双向绑定调用的方法(UI变化 -> 从UI获取数据)
    30. */
    31. @InverseBindingAdapter(attribute = "app:time")
    32. @JvmStatic fun getTime(view: MyAppText) : Date {
    33. Log.e("zfang", "getTime")
    34. return view.getTime()
    35. }
    36. /**
    37. * 设置双向绑定调用时机
    38. */
    39. @BindingAdapter("app:timeAttrChanged")
    40. @JvmStatic fun setListeners(view: MyAppText, attrChange: InverseBindingListener) {
    41. Log.e("zfang", "on UI change")
    42. view.apply {
    43. setOnClickListener {
    44. text = formate(Date())
    45. attrChange.onChange()
    46. }
    47. }
    48. }
    49. }
    50. }

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

            相应的LiveData如下:

    1. package com.zfang.databindingstudy.module
    2. import androidx.lifecycle.LiveData
    3. import androidx.lifecycle.MutableLiveData
    4. import androidx.lifecycle.ViewModel
    5. import java.util.*
    6. class SimpleViewModel: ViewModel() {
    7. private val _first = MutableLiveData("Alice")
    8. private val _second = MutableLiveData("Bob")
    9. val first: LiveData = _first
    10. val second: LiveData = _second
    11. var time :MutableLiveData = MutableLiveData(Date())
    12. set(date) {
    13. if (field == date) {
    14. return
    15. }
    16. field = date
    17. }
    18. }

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

    三、接收数据反馈

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

    1. // Inverse Binding Event Handlers
    2. private InverseBindingListener mboundView3timeAttrChanged = new InverseBindingListener() {
    3. @Override
    4. public void onChange() {
    5. // Inverse of viewModel.time.getValue()
    6. // is viewModel.time.setValue((Date) callbackArg_0)
    7. //上面定义的方法,获取时间
    8. Date callbackArg_0 = AppTextBinds.getTime(mboundView3);
    9. // localize variables for thread safety
    10. // viewModel.time.getValue()
    11. Date viewModelTimeGetValue = null;
    12. // viewModel
    13. SimpleViewModel viewModel = mViewModel;
    14. // viewModel.time
    15. MutableLiveData viewModelTime = null;
    16. // viewModel != null
    17. boolean viewModelJavaLangObjectNull = false;
    18. // viewModel.time != null
    19. boolean viewModelTimeJavaLangObjectNull = false;
    20. viewModelJavaLangObjectNull = (viewModel) != (null);
    21. if (viewModelJavaLangObjectNull) {
    22. viewModelTime = viewModel.getTime();
    23. viewModelTimeJavaLangObjectNull = (viewModelTime) != (null);
    24. if (viewModelTimeJavaLangObjectNull) {
    25. //设置UI数据到LiveData中
    26. viewModelTime.setValue(((Date) (callbackArg_0)));
    27. }
    28. }
    29. }
    30. };

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

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

  • 相关阅读:
    基于Java毕业设计药品管理系统源码+系统+mysql+lw文档+部署软件
    【目标检测】Faster R-CNN的几点理解
    【JS】Chapter14-深入面向对象
    【picgo】【typora】自己服务器搭建自定义图床
    【jvm如何在设计层面实现线程实现停顿时间控制的思考】
    HTML5期末考核大作业:基于Html+Css+javascript的网页制作(化妆品公司网站制作)
    【小月电子】FPGA开发板(XLOGIC_V1)系统学习教程-LESSON1点亮LED灯
    第08章 Tableau在线服务器
    三角函数画图
    MBR主引导记录
  • 原文地址:https://blog.csdn.net/www586089/article/details/128176912