DataBinding其三

  1. 双向绑定
  2. DataBinding剩下的注解

很快啊,就来到了DataBinding的第三篇。在这里将介绍DataBinding中高级的知识点(自认为高级),下面就来瞧瞧这些高级的知识点。

1、双向绑定

其实早该在介绍绑定表达式的时候应该要介绍双向绑定的了,但是为了双向绑定完整,所以就留到了这,所谓双向绑定是这样的:

我们可以单向绑定数据和一些监听器(事件)。在更新数据的时候,绑定了的控件就会更新,而监听器发生改变的时候,我们就更新绑定的数据。

这就是双向绑定的原理

而DataBinding给我们提供了双向绑定的基本实现以及自定义。

1.1、可观察数据类型

并不是所有的数据类型都可以进行双向绑定,DataBinding给我们提供了一些:

可观察数据类型

大概是这么多。

而使用双向绑定的表达式也和普通绑定表达式有那么一点点不一样:

@={}这就是双向绑定表达式,比普通绑定表达式多了一个等号=

最常用到双向表达式的莫过于EditText控件了,当输入内容时,更新绑定的数据,但改变数据时,更新EditText控件的显示。

1.2、双向绑定特性

也并不是所有的属性都支持双向绑定,至于哪些属性(特性)可以进行双向绑定,官方也列出了一个表格:

双向数据绑定特性

1.3、自定义可观察数据类型(@Bindable)

如果DataBinding提供的可观察数据类型都不能够满足你的业务,那么还是可以自定义的。
新建一个类,继承BaseObservable,并创建一个属性,给这个属性加上@Bindable注解,然后重写该属性set方法即可,大概是这样的:

class Demo : BaseObservable() {
    @Bindable
    var name: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.name)
        }
}

@Bindable注解:该注解主要作用于get方法或者是字段本身,作用是生成一个BR值供notifyPropertyChanged方法调用以更新数据。

这时我们就可以对name这个属性进行双向绑定了:

<EditText
    android:text=“@={demo.name}”/>

但是你实际运行你会发现它报错了,报错的原因是类型不匹配。这时候该怎么办呢?

1.4、双向数据转换(@InverseMethod)

Databinding提供了数据转换的方案,单向和双向都有,我们先来说双向的。

定义一个静态类,提供两个方法。一个充当正向数据转换器,另外一个充当反向数据转换器,大概是这样的:

object Canversion{
    @InverseMethod("chToString")
    fun stringToCh(value: String): CharSequence {
          return value
    }

    fun chToString(value: CharSequence): String {
          return value.toString()
    }
}

事实上,两个顶级函数也是可以的。

InverseMethod注解:该注解修饰的就是正向转换器,而InverseMethod接收一个参数,就是反向数据转换器的名称。

还记得<import>标签吗,当转换器定义好之后,我们就可以通过该标签将这个类导入到xml中,然后使用正向转换器就可以了:

<import type=“…Conversion”/>

<EditText
    android:text=“@={Conversion.stringToCh(demo.name)}”/>

1.5、自定义双向绑定(InverseBindingAdapter)

如果DataBinding默认实现的双向绑定特性也不能满足需求的时候,就只能自定义了。
事实上,双向绑定就由两个单向绑定(数据绑定和监听器绑定)组成的,所以第一步就是建立两个单向绑定。下面重新实现android:text的双向绑定。
首先创建一个数据绑定

@BindingAdapter("app:test")
fun test(view: TextView, text: CharSequence?) {
    val oldText = view.text
    if (text == oldText || (oldText.isNullOrEmpty())) {
        return
    }
    if (text is Spanned) {
        if (text == oldText) {
            return; // No change in the spans, so don't set anything.
        }
    } else if (!haveContentsChanged(text, oldText)) {
        return; // No content changes, so don't set anything.
    }
    view.text = text
}

在赋值之前需要对数据进行判断:
只有是更新的数据才可以被赋值。防止无限循环的发生。

然后,创建一个事件监听绑定

@BindingAdapter("app:testAttrChanged")
fun setTextWatcher(view: TextView, textAttrChanged: InverseBindingListener?) {
    val newValue: TextWatcher? = if (textAttrChanged == null) {
        null
    } else {
        object : TextWatcher {
            //code..
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                textAttrChanged.onChange()
            }
            //code..
        }
    }
    if (newValue != null) {
        view.addTextChangedListener(newValue)
    }
}

这是一个事件监听,当EditText发生改变的时候,就会通过InverseBindingListener对象通知更改。

最后,将这两个绑定关联在一起:

@InverseBindingAdapter(attribute = "app:test", event = "app:testAttrChanged")
fun getTextString(view: TextView): CharSequence {
    return view.text // 返回控件的值
}

InverseBindingAdapter注解:该注解主要有两个参数,attribute指定数据绑定的名称,event指定事件绑定的名称。作用是将两者结合起来形成双向绑定,并且响应事件绑定,在事件发生时返回控件的值。

通过这三步,自定义双向绑定的属性就完成了:

<EditText
    app:test=“@={demo.name}”/>

自定义双向绑定的一个细节:
在自定义双向绑定中,对于app:testAttrChanged是可以识别的,规律就是:


数据绑定名称+AttrChanged。

所以在关联的时候event可以省略不写。

1.6、简化自定义双向绑定(InverseBindingMethods和InverseBindingMethod)

有没有更加简单的方式实现双向绑定呢?答案是没有的!!双向绑定就必须是一个数据绑定加上一个事件绑定。这是永远都没办法省略的。但是如果你有多个双向绑定需要实现,那么可以简化的只有InverseBindingAdapter注解修饰的方法了。

首先定义一个类:

@InverseBindingMethods(
    InverseBindingMethod(
        type = TextView::class,
        attribute = "app:test",
        method = "getText"
    )
)
object BindingAdapterImpl

InverseBindingMethods:该注解接收的参数是InverseBindingMethod数组。
而一个InverseBindingMethod对应的就是一个InverseBindingAdapter,下面我们来分析一下InverseBindingMethod

也就是说InverseBindingMethodsInverseBindingMethod的作用就是批量的InverseBindingAdapter

InverseBindingMethod该注解有四个参数:

type:是一个class对象,指定双向绑定的控件,比如TextView

attribute:和InverseBindingAdapter中同名的参数作用一样,指定数据绑定的方法。

event:和InverseBindingAdapter中同名的参数作用一样,指定事件绑定的方法。(可以看出,上面的例子我没有写该属性,是因为可以自动推测)

method:指定控件提供数据的方法。(事实上该参数也可以不写,当控件存在与attribute同名的get方法时,也就是说如果TextView中存在一个getTest方法,那么这个参数就可以缺省)

需要注意的是:这两个注解替代的是InverseBindingAdapter,所以数据绑定和事件绑定依旧需要实现。

一句话概括:

当可观察数据对象更改时,通过attribute指定的方法更新type的对象,当event事件发生时,将从method方法中获得数据返回给可观察数据对象。

2、DataBinding剩下的注解

终于说完双向绑定了,在说明双向绑定的时候也将和双向绑定有关的注解说了一下,接下来就说说DataBinding剩下的注解。

2.1、单向数据转换(BindingConversion)

上面提到数据转换有两种,双向的已经介绍了,现在来看单向的数据转换:

@BindingConversion
fun dateToString(time: Date): String{
    return SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time)
}

将Date类型转换成String类型输出。

<TextView
      android:text=“@{date}”/>//date是一个Date类型的对象

注意:

单向数据转换只需要输入(参数列表)输出(返回值)对上即可自动使用,无需手动调用。

再次注意:所有的数据转换都只能一对一。

2.2、简化单向绑定(BindingMethods和BindingMethod)

@BindingMethods({
        @BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask")
})

和双向绑定的差不多,BindingMethods接收的也是BindingMethod的数组。
来看看BindingMethod注解:

@BindingMethod注解有三个变量:
type:指定单向绑定的控件,比如:TextView
attribute:指定单向绑定的名称
method:指定响应单向绑定的set方法

BindingMethodsBindingMethod相当于批量的BindingAdapter

一句话概括:
attribute属性实现单向绑定,通过method方法将值设置给type类型的控件。

总结

最后一篇介绍了dataBinding的双向绑定和剩下的注解,到此Databinding算是基本讲完了。DataBinding主要分为三部分:xml部分,activity/fragment部分以及自定义绑定(单向绑定和双向绑定)部分。


到后面你会发现,xml之所以可以使用绑定表达式,是因为@BindingAdapter标签。当然了,这只是DataBinding的使用,真正的实现:为什么@BindingAdapter这么神奇,其背后的原理才是DataBinding真正的核心。这就是注解部分的内容了。对于日常开发来说,学完DataBinding的这几个注解就已经足够了。

对于学习,最重要的是要去写,只有真正写过,实现了,才能算掌握了。

因为真理都是在实践当中。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容