通过前面两篇文档,我们大概了解了databinding的工作方式,view的初始化,recycleview的使用。但是这些UI都离不开数据的填充,数据的修饰。
在说到数据绑定,好多开发者平时在工作中也经常听到databinding的数据绑定有简单、单向绑定、双向绑定,玄幻莫测,不敢下手。甚至有些新手听完果然放弃。接下来我会通过代码讲解databinding的数据绑定和使用,包括map、list、和用户自定义类,让复杂的事件简单化,人人都可以掌握好并使用
数据绑定分为两种,一种是系统支持的,还有一种是databind的数据,接下来我们分梁部分介绍
String、int、float、double,boolean
简单类型,我们直接使用即可
任何在布局中的value都需要被处理成字符串类型,也就是说boolean或者double不能直接@{double}@{boolean},这种是错误的
注意:一些静态方法可以在布局中直接引用,这个后期会单独介绍
聚合列的数据,在data下方也是支持的,常用的有以下三种:map、list、sparseArry。
如何使用呢?因为data下方的数据都是要指定泛型的,所以这三种数据都是支持泛型,所以你必须要指定泛型。
这里面有人会不明白<和>是什么意思,
正常格式:ArrayList
databind:ArrayList<String>
所以(< 为<)左括号,(>为>)右括号
在这里面,需要注意的是所有key和index最好动态设置,否则不方便业务开展
如何在view中绑定data数据:
var map = HashMap()map.put("name", "map你好")databind.key = "name"databind.map = mapvar list = ArrayList()list.add("list0index")list.add("list1index")
// databind.list = listvar spar=SparseArray()spar.put(0,"0 value SparseArray")spar.put(1,"1 value SparseArray")spar.put(100,"100 value SparseArray")databind.arry=spardatabind.index = 100
如果你的list没有设置,即在databind中为null,即使你设置了key或者index,也不会被引用,如果你设置了data,index会引起数组越界,但是不会抛空指针
以上我们讲解的是通过系统数据取完成,但是我们在使用databinding的时候,是想使用他的数据特性,单向绑定、双向绑定。
我们在上面介绍的数据绑定,都不涉及到,接下来我们要讲解通过databind提供的方式,进行单向绑定和双向绑定
单向绑定就是data发生改变,会自动通知UI刷新,但是UI内容发生改变后,将不会引用data发生改变。
数据绑定有两种方法,第一种全家桶,第二种,用户自定义
第一种:全家桶模式BaseObservable、Bindable
BaseObservable:提供了数据更新的机制,可以通过notifyPropertyChanged(int field)和notifyChange()来完整数据的更新
Bindable:生成关联字段,形成关联图
1.继承:BaseObservable
2.通过Bindable注解绑定字段,改字段必须为public,否则绑定在get方法上
直接绑定在get方法上
class MySchool : BaseObservable() {@get:Bindablevar name: String = ""set(value) {field = valuenotifyPropertyChanged(BR.name)}}
public class MySchool extends BaseObservable {private String schoolName="";@Bindablepublic String getSchoolName() {return schoolName;}public void setSchoolName(String schoolName) {this.schoolName = schoolName;notifyPropertyChanged(BR.schoolName);}
}
关于notifyPropertyChanged()和 notifyChange()
notifyPropertyChanged:只刷新指定的字段
notifyChange:刷新对象下所有bindable的字段
有人先写set方法,BR无法找到指定的字段,是因为该字段还没有被bindable注释绑定,生成对应的关系图,所以要先bindable,在更新,否则找不到对应的字段
不仅可以单向绑定,我们可以坚定当前绑定的数据对应的字段:
BaseObservable提供了一个addOnPropertyChangedCallback回调,可以在这里设置字段监听
var detail = MySchool()detail.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback(){override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
// TODO("Not yet implemented")}})
通过上方,我们已知道全家桶配合的使用,但是databinding也提供了基础的绑定方法ObservableField,
BaseObservableField是基础类型,可以通过该类型指定泛型,也可以通过提供的其他数据类型进行绑定ObservableFloat ObservableBoolean ObservableInt ObservableParcelable ObservableChar 聚合数据 ObservableArrayMap ObservableArrayList
接下来我们先从最基础的BaseObservableField基础用起
class BaseFieldData {lateinit var name: ObservableFieldlateinit var age: ObservableFieldlateinit var god: ObservableField}
class Dog() : Parcelable {lateinit var name: Stringlateinit var hostName: Stringconstructor(parcel: Parcel) : this() {name = parcel.readString()!!hostName = parcel.readString()!!}override fun writeToParcel(parcel: Parcel, flags: Int) {parcel.writeString(name)parcel.writeString(hostName)}override fun describeContents(): Int {return 0}companion object CREATOR : Parcelable.Creator {override fun createFromParcel(parcel: Parcel): Dog {return Dog(parcel)}override fun newArray(size: Int): Array {return arrayOfNulls(size)}}}
fun initData() {var name = ObservableField("我的名字")var age = ObservableField(100)var dog = Dog()dog.name = "小黑"dog.hostName = name.get()!!;var isDog = ObservableField(dog)var baseData = BaseFieldData()baseData.age = agebaseData.god = isDogbaseData.name = namedataBind.data = baseDatadataBind.testClick.setOnClickListener {name.set("新名字")var newDog=Dog()newDog.name="我是小白"newDog.hostName=name.get().toString()isDog.set(newDog)}}
通过这样,我们已完成了担心绑定,每次我们只要更新name和age的变量值,UI会自动刷新
每个field通过set()来设置泛型参数,通过get来回去。
为什么我们没手动更新,系统确能自动完成UI的更新?
我们看下ObservableField的set()就可以知道
public void set(T value) {if (value != mValue) {mValue = value;notifyChange();}
}
当我们调用set()的时候,默认也调用的全局刷新。
封装好的ObservableChar已继承了BaseObservableField
public class ObservableChar extends BaseObservableField implements Parcelable, Serializable
所以只是在构造器类指定了内容类型,这样我们在使用的时候,不再需要设置泛型参数。
var size=ObservableDouble(12.0)
dataBind.testClick.setOnClickListener {size.set(123.2)}
这些都很好处理,接下我们将介绍ObservableArrayList和ObservableArrayMap
布局中的data:
代码中实现:
var map = ObservableArrayMap()map.put("name", "zhangshan")dataBind.map = mapdataBind.key = "name"dataBind.testClick.setOnClickListener {map.put("name", "修改过的")}
public V put(K k, V v) {V val = super.put(k, v);notifyChange(k);return v; }
也是调用了全局刷新,同理,ArryList的add方法也是这样:
介绍完以上用法,会发现,这些好像都是单向绑定,什么才是双向绑定?其实很简单
介绍完单向绑定,其实大家已掌握了双向绑定。用周董的话说,全球音乐看话语,中文才是最屌。
双向绑定比单向绑定在view绑定的时候,多一个=号
这边采用了
var name = ObservableField("我的名字")
进行测试。
只要我们掌握了单向绑定,双向绑定自然也会解决。但是,在使用双向的时候需要注意,不同的场景如果存在多处引用,会导致数据错乱。所以,在使用的时候需要格外小心。