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

news2024/9/21 14:41:54

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

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

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

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

一、修改绑定表达式

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

        <TextView
            android:id="@+id/second"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.second}"
            android:textColor="#999"
            android:textSize="14sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/first" />

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

        <com.zfang.databindingstudy.widget.MyAppText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingHorizontal="12dp"
            android:paddingVertical="24dp"
            android:textSize="22sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:time="@={viewModel.time}" />

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

        <Target id="@+id/second" tag="binding_2" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="viewModel.second">
                    <Location endLine="35" endOffset="45" startLine="35" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="35" endOffset="43" startLine="35" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="41" endOffset="61" startLine="31" startOffset="8" />
        </Target>
        <Target tag="binding_3" view="com.zfang.databindingstudy.widget.MyAppText">
            <Expressions>
                <Expression attribute="app:time" text="viewModel.time">
                    <Location endLine="53" endOffset="40" startLine="53" startOffset="12" />
                    <TwoWay>true</TwoWay>
                    <ValueLocation endLine="53" endOffset="38" startLine="53" startOffset="25" />
                </Expression>
            </Expressions>
            <location endLine="53" endOffset="43" startLine="43" startOffset="8" />
        </Target>

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

二、监听UI变化

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

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

package com.zfang.databindingstudy.widget

import 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? = null
    fun 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 = timeDate
        setTime(AppTextBinds.formate(timeDate))
    }

    fun getTime() = timeDate!!
}

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

        相应的BindAdapter如下:

package com.zfang.databindingstudy.binds

import 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.module

import 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<String> = _first
    val second: LiveData<String> = _second

    var time :MutableLiveData<Date> = MutableLiveData(Date())
        set(date) {
            if (field == date) {
                return
            }
            field = date
        }
}

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

三、接收数据反馈

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

    // Inverse Binding Event Handlers
    private InverseBindingListener mboundView3timeAttrChanged = new InverseBindingListener() {
        @Override
        public 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;
            // viewModel
            SimpleViewModel viewModel = mViewModel;
            // viewModel.time
            MutableLiveData<Date> viewModelTime = null;
            // viewModel != null
            boolean viewModelJavaLangObjectNull = false;
            // viewModel.time != null
            boolean 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)),源代码在这里

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61484.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web前端-javascript-立即执行函数(说明、例子)

立即执行函数 /* (function(){alert("我是一个匿名函数~~~"); })(); */(function (a, b) {console.log("a " a);console.log("b " b); })(123, 456);1. 说明 函数定义完&#xff0c;立即被调用&#xff0c;这种函数叫做立即执行函数立即执…

Twitter群推解锁流量大门的钥匙

Twitter作为全球最知名的社交媒体平台之一&#xff0c;对海外营销有着巨大的影响力&#xff0c;是外贸企业进行群推、群发、引流必不可少的平台。那么要想通过推特群推、推特群发打开流量的大门&#xff0c;这里有几点值得大家注意&#xff0c;帮助你更好的驾驭流量&#xff1a…

虚拟机安装zookeeper集群

一、准备 克隆原先的虚拟机;因为是从原先已有jdk和zk的linux虚拟机克隆过来的,所以克隆的虚拟机上是一样的! 三台虚拟机,我采用的是:zk的ip不一样,端口一样 修改每台虚拟机上环境变量,zk配置文件 修改zookeeper配置文件,采用默认端口,配置主从节点

Bootstrap主页面搭建(十四)

创建主页面&#xff1a;index.jsp&#xff1a; 引入bootstrap依赖&#xff1a; 首先写导航条&#xff0c;复制代码更改&#xff1a; <!--导航条--> <nav class"navbar navbar-inverse"><div class"container-fluid"><!-- Brand and…

Nginx配置实例-动静分离

1、什么是动静分离 Nginx动静分离简单来说就是把动态跟静态请求分开&#xff0c;不能理解成只是单纯的把动态页面和 静态页面物理分离。严格意义上说应该是动态请求跟静态请求分开&#xff0c;可以理解成使用Nginx 处理静态页面&#xff0c;Tomcat处理动态页面。 动静分离从目…

Project joee 算法开发日志(一)

目录一. 下载并安装TensorRT1.1 下载安装TensorRT1.2 验证TensorRT安装是否成功二. 安装并测试Windows预测库2.1 安装cuda11.0_cudnn8.0_avx_mkl-trt7.2.1.6 预测库2.2 测试精度损失2.3 推理速度测试三. 总结开发机器配置&#xff1a;CPU: AMD5800 8core 16ThreadGPU: NVIDIA G…

微信支付回调,内网穿透详细过程

文章目录支付回调接口通过Ngrok进行内网穿透步骤1. 根据邮箱注册一个账号2. 获取隧道id3.下载Ngrok客户端4. 双击这个 Sunny-Ngrok启动工具.bat 文件5. 填写你的 隧道id 回车6.客户端启动成功7. 所以你的notify_url对应的value需要改为内网穿透的地址为8.支付成功之后微信平台会…

分面中添加直线

简介 这篇也是分享最近统计建模中所绘制的一副图形。总体而言和前面的几篇&#xff1a;xxx 类似。都是从“数据导入”到“基于分面的可视化”。但是本文的小技巧是&#xff0c;在不同的分面中添加直线。最后得到的图形如下&#xff1a; 注意&#xff1a;本文数据和代码在公众号…

交易所通用质押式回购

一、专业术语 逆回购&#xff1a;指资金融出方将资金融给资金融入方&#xff0c;收取有价证券作为质押&#xff0c;并在未来收回本息&#xff0c;并解除有价证券质押的交易行为。 债券通用质押式回购交易&#xff1a;&#xff08;简称“通用回购”&#xff09;是指资金融入方…

划分成绩ABCD

已知成绩等级划分为{“A”:[90~100],"B":[80~89],"c":[60~79],"D":[0~59]} 1、随机生成20个整数&#xff0c;范围0-100 2、按等级归类&#xff0c;输出成绩等级列表字典如下&#xff1a; {A: [96, 96, 97, 97, 100, 100], B: [86], C: [71, 7…

Python学习基础笔记二十二——生成器

一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值&#xff0c;但是yield又不同于return&#xff0c;return的执行意味着程序的结束&#xff0c;调用生成器函数不会得到返回的具体的值&#xff0c;而是得到一个可迭代的对象。每一次获取这个可迭代对…

微机原理与接口技术:数模转换和模数转换 详细笔记

文章目录1.数模转换1.1.数模转换原理1.1.1.权电阻D/A转换器1.1.2.R-2R T型电阻网络D/A转换器1.1.3.补充 D/A转换器的主要技术指标1.2.D/A转换芯片——DAC08321.2.1.引脚介绍1.2.2.工作方式直通输入方式单缓冲方式双缓冲方式2.模数转换2.1.信号变换中的采样、量化和编码2.1.1.采…

『NLP学习笔记』TextCNN文本分类原理及Pytorch实现

TextCNN文本分类原理及Pytorch实现 文章目录一. TextCNN网络结构1.1. CNN在文本分类上得应用1.2. 回顾CNN以及Pytorch解析1.2.1. CNN特点1.2.2. 一维卷积Conv1d1.2.3. 二维卷积 Conv2d1.2.3. 三维卷积 Conv3d1.2.4. 池化(pooling)操作1.2.4. nn.BatchNorm操作1.3. nn.ModuleLi…

大数据之数据的压缩与存储

文章目录前言一、Hive的压缩方式&#xff08;一&#xff09; 概念&#xff08;二&#xff09; 简介&#xff08;三&#xff09; 数据分层的压缩方式选择&#xff08;四&#xff09;开启Map输出阶段压缩&#xff08;五&#xff09;开启Reduce输出阶段压缩二、 Hive的数据存储格式…

再有人问你kafka 把这篇扔给他(建议收藏)

前言 最近重新读起kafka的内容&#xff0c;看到kafka官网文档里&#xff0c;有专门一栏讲kafka的设计&#xff0c;觉得很受益。我们常常会知道这个中间件是什么&#xff0c;是什么机制&#xff0c;这次想换个角度来聊&#xff0c;它在设计消息系统的时候&#xff0c;都做了哪些…

火山引擎虚拟数字人技术与应用

导读&#xff1a;火山引擎正在打造完善的虚拟数字人技术和应用体系&#xff0c;那么火山引擎是如何定义虚拟数字人的呢&#xff1f;火山引擎 2D 虚拟数字人和 3D 数字人采用了怎样先进的技术&#xff1f;火山引擎数字人有哪些应用和前景展望&#xff1f;今天我们就来一起探秘火…

【Transformer】Transformer理论知识

Transformer目录Transformer介绍Seq2seq结构Encoder结构Decoder结构Autoregressive Decoder&#xff08;AT&#xff09;&#xff1a;Encoder和Decoder对比和联系Cross attention&#xff1a;Non-autoregressive Decoder&#xff08;NAT&#xff09;&#xff1a;训练Seq2seq Mod…

基于ssm高校档案管理系统源码

档案管理作为企事业单位管理工作的基础&#xff0c;档案是企事业单位建设信息系统的重要组成部分&#xff0c;档案是提高企事业单位工作质量和工作效率的必要条件&#xff0c;甚至是维护历史真实面貌的一项重要工作。 1、档案信息数字化能够提供档案信息方便快捷的服务方式。 数…

量化风控的贷前实操课—详解的规则调优

风控全流程涉及贷前风控、贷中监控、贷后催收&#xff0c;每个模块都环环相扣&#xff0c;互相关联。而作为其中最关键模块的贷前风控&#xff0c;几乎是整个风控模块中最重要的部分&#xff0c;现金贷等互金产品重贷前、信用卡重贷中。 贷前风控是可以说整个防控中的第一道防线…

简单的CNN实现——MNIST手写数字识别

0.概述 此文章不涉及复杂的理论知识&#xff0c;仅仅只是利用PyTorch组建一个简单的CNN去实现MNIST的手写数字识别&#xff0c;用好的效果去激发学习CNN的好奇心&#xff0c;并且以后以此为基础&#xff0c;去进行一些改造。&#xff08;前提是把基础代码看明白&#xff09; 本…