当前位置:   article > 正文

Android databinding之数据单向与双向绑定详解与使用(三)_android databinding双向绑定

android databinding双向绑定

一、介绍

        通过前面两篇文档,我们大概了解了databinding的工作方式,view的初始化,recycleview的使用。但是这些UI都离不开数据的填充,数据的修饰。

在说到数据绑定,好多开发者平时在工作中也经常听到databinding的数据绑定有简单、单向绑定、双向绑定,玄幻莫测,不敢下手。甚至有些新手听完果然放弃。接下来我会通过代码讲解databinding的数据绑定和使用,包括map、list、和用户自定义类,让复杂的事件简单化,人人都可以掌握好并使用

数据绑定

        数据绑定分为两种,一种是系统支持的,还有一种是databind的数据,接下来我们分梁部分介绍

1、系统默认数据类型

基础数据

String、int、float、double,boolean

  1. <data class="MyDataInfo">
  2. <variable
  3. name="name"
  4. type="String" />
  5. <variable
  6. name="age"
  7. type="int" />
  8. <variable
  9. name="bodyH"
  10. type="float" />
  11. <variable
  12. name="income"
  13. type="double" />
  14. <variable
  15. name="sex"
  16. type="boolean" />
  17. </data>

简单类型,我们直接使用即可

 布局最终引用:

  1. <TextView
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="@{name}" />
  5. <TextView
  6. android:layout_width="match_parent"
  7. android:layout_height="wrap_content"
  8. android:text="@{String.valueOf(age)}" />
  9. <TextView
  10. android:layout_width="match_parent"
  11. android:layout_height="wrap_content"
  12. android:text="@{String.valueOf(bodyH)}" />
  13. <TextView
  14. android:layout_width="match_parent"
  15. android:layout_height="wrap_content"
  16. android:text="@{String.valueOf(income)}" />
  17. <TextView
  18. android:layout_width="match_parent"
  19. android:layout_height="wrap_content"
  20. android:text="@{String.valueOf(sex)}" />

注意:

任何在布局中的value都需要被处理成字符串类型,也就是说boolean或者double不能直接@{double}@{boolean},这种是错误的

正确:

  1. <TextView
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="@{String.valueOf(sex)}" />

错误:

  1. <TextView
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="@{sex}" />

注意:一些静态方法可以在布局中直接引用,这个后期会单独介绍

2、ArrayList、HashMap等聚合数据绑定

聚合列的数据,在data下方也是支持的,常用的有以下三种:map、list、sparseArry。

如何使用呢?因为data下方的数据都是要指定泛型的,所以这三种数据都是支持泛型,所以你必须要指定泛型。

泛型格式:

<variable
    name="list"
    type="ArrayList&lt;String&gt;" />

在type里面指定泛型。type格式=ArrayList&lt;String&gt;

这里面有人会不明白<和>是什么意思,

正常格式:ArrayList<String>

databind:ArrayList<String>

所以(<   为<)左括号,(>为>)右括号

  1. <data class="MyDataInfo">
  2. <import type="java.util.HashMap" />
  3. <import type="java.util.ArrayList" />
  4. <import type="android.util.SparseArray"/>
  5. <variable
  6. name="key"
  7. type="String" />
  8. <variable
  9. name="index"
  10. type="int" />
  11. <variable
  12. name="map"
  13. type="HashMap&lt;String,Object&gt;" />
  14. <variable
  15. name="list"
  16. type="ArrayList&lt;String&gt;" />
  17. <variable
  18. name="arry"
  19. type="SparseArray&lt;String&gt;" />
  20. </data>

在这里面,需要注意的是所有key和index最好动态设置,否则不方便业务开展

如何在view中绑定data数据:

  1. <TextView
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="@{arry.get(index)}" />
  5. <TextView
  6. android:layout_width="match_parent"
  7. android:layout_height="wrap_content"
  8. android:text="@{list.get(index)}" />
  9. <TextView
  10. android:layout_width="match_parent"
  11. android:layout_height="wrap_content"
  12. android:text="@{map[key]}" />

数据设置:

  1. var map = HashMap<String, Any>()
  2. map.put("name", "map你好")
  3. databind.key = "name"
  4. databind.map = map
  5. var list = ArrayList<String>()
  6. list.add("list0index")
  7. list.add("list1index")
  8. // databind.list = list
  9. var spar=SparseArray<String>()
  10. spar.put(0,"0 value SparseArray")
  11. spar.put(1,"1 value SparseArray")
  12. spar.put(100,"100 value SparseArray")
  13. databind.arry=spar
  14. databind.index = 100

说明:

        如果你的list没有设置,即在databind中为null,即使你设置了key或者index,也不会被引用,如果你设置了data,index会引起数组越界,但是不会抛空指针

二、databind数据介绍

        以上我们讲解的是通过系统数据取完成,但是我们在使用databinding的时候,是想使用他的数据特性,单向绑定、双向绑定。

        我们在上面介绍的数据绑定,都不涉及到,接下来我们要讲解通过databind提供的方式,进行单向绑定和双向绑定

1、单向绑定

什么叫单向绑定:

单向绑定就是data发生改变,会自动通知UI刷新,但是UI内容发生改变后,将不会引用data发生改变。

数据绑定有两种方法,第一种全家桶,第二种,用户自定义

第一种:全家桶模式BaseObservable、Bindable

BaseObservable:提供了数据更新的机制,可以通过notifyPropertyChanged(int field)和notifyChange()来完整数据的更新

Bindable:生成关联字段,形成关联图

如何使用:

1.继承:BaseObservable
2.通过Bindable注解绑定字段,改字段必须为public,否则绑定在get方法上

kotlin:

直接绑定在get方法上

  1. class MySchool : BaseObservable() {
  2. @get:Bindable
  3. var name: String = ""
  4. set(value) {
  5. field = value
  6. notifyPropertyChanged(BR.name)
  7. }
  8. }

Java:

  1. public class MySchool extends BaseObservable {
  2. private String schoolName="";
  3. @Bindable
  4. public String getSchoolName() {
  5. return schoolName;
  6. }
  7. public void setSchoolName(String schoolName) {
  8. this.schoolName = schoolName;
  9. notifyPropertyChanged(BR.schoolName);
  10. }
  11. }

关于notifyPropertyChanged()和 notifyChange()

notifyPropertyChanged:只刷新指定的字段

notifyChange:刷新对象下所有bindable的字段

注意:

        有人先写set方法,BR无法找到指定的字段,是因为该字段还没有被bindable注释绑定,生成对应的关系图,所以要先bindable,在更新,否则找不到对应的字段

字段更新监听:addOnPropertyChangedCallback

不仅可以单向绑定,我们可以坚定当前绑定的数据对应的字段:

BaseObservable提供了一个addOnPropertyChangedCallback回调,可以在这里设置字段监听
  1. var detail = MySchool()
  2. detail.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback(){
  3. override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
  4. // TODO("Not yet implemented")
  5. }
  6. })


2.自定义绑定字段ObservableField

通过上方,我们已知道全家桶配合的使用,但是databinding也提供了基础的绑定方法ObservableField,

BaseObservableField是基础类型,可以通过该类型指定泛型,也可以通过提供的其他数据类型进行绑定

ObservableFloat
ObservableBoolean
ObservableInt
ObservableParcelable
ObservableChar
聚合数据
ObservableArrayMap
ObservableArrayList

接下来我们先从最基础的BaseObservableField基础用起

数据源:

  1. class BaseFieldData {
  2. lateinit var name: ObservableField<String>
  3. lateinit var age: ObservableField<Int>
  4. lateinit var god: ObservableField<Dog>
  5. }

  1. class Dog() : Parcelable {
  2. lateinit var name: String
  3. lateinit var hostName: String
  4. constructor(parcel: Parcel) : this() {
  5. name = parcel.readString()!!
  6. hostName = parcel.readString()!!
  7. }
  8. override fun writeToParcel(parcel: Parcel, flags: Int) {
  9. parcel.writeString(name)
  10. parcel.writeString(hostName)
  11. }
  12. override fun describeContents(): Int {
  13. return 0
  14. }
  15. companion object CREATOR : Parcelable.Creator<Dog> {
  16. override fun createFromParcel(parcel: Parcel): Dog {
  17. return Dog(parcel)
  18. }
  19. override fun newArray(size: Int): Array<Dog?> {
  20. return arrayOfNulls(size)
  21. }
  22. }
  23. }

实现:

  1. fun initData() {
  2. var name = ObservableField<String>("我的名字")
  3. var age = ObservableField<Int>(100)
  4. var dog = Dog()
  5. dog.name = "小黑"
  6. dog.hostName = name.get()!!;
  7. var isDog = ObservableField<Dog>(dog)
  8. var baseData = BaseFieldData()
  9. baseData.age = age
  10. baseData.god = isDog
  11. baseData.name = name
  12. dataBind.data = baseData
  13. dataBind.testClick.setOnClickListener {
  14. name.set("新名字")
  15. var newDog=Dog()
  16. newDog.name="我是小白"
  17. newDog.hostName=name.get().toString()
  18. isDog.set(newDog)
  19. }
  20. }

通过这样,我们已完成了担心绑定,每次我们只要更新name和age的变量值,UI会自动刷新

每个field通过set()来设置泛型参数,通过get来回去。

为什么我们没手动更新,系统确能自动完成UI的更新?

我们看下ObservableField的set()就可以知道

  1. public void set(T value) {
  2. if (value != mValue) {
  3. mValue = value;
  4. notifyChange();
  5. }
  6. }

当我们调用set()的时候,默认也调用的全局刷新。

第二种:采用databind封装好的

分析:

        封装好的ObservableChar已继承了BaseObservableField

public class ObservableChar extends BaseObservableField implements Parcelable, Serializable

所以只是在构造器类指定了内容类型,这样我们在使用的时候,不再需要设置泛型参数。

var size=ObservableDouble(12.0)
  1. <import type="androidx.databinding.ObservableDouble"/>
  2. <variable
  3. name="size"
  4. type="ObservableDouble" />
  5. <TextView
  6. android:layout_width="match_parent"
  7. android:layout_height="wrap_content"
  8. android:text="@{String.valueOf(size)}" />
  1. dataBind.testClick.setOnClickListener {
  2. size.set(123.2)
  3. }

这些都很好处理,接下我们将介绍ObservableArrayList和ObservableArrayMap

三、数据集合:

ObservableArrayList和ObservableArrayMap

 介绍一个ObservableArrayMap:

布局中的data:

  1. <import type="androidx.databinding.ObservableArrayMap" />
  2. <variable
  3. name="map"
  4. type="ObservableArrayMap&lt;String,String&gt;" />
  5. <variable
  6. name="key"
  7. type="String" />
  8. <TextView
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:text="@{map.get(key)}" />

代码中实现:

  1. var map = ObservableArrayMap<String, String>()
  2. map.put("name", "zhangshan")
  3. dataBind.map = map
  4. dataBind.key = "name"
  5. dataBind.testClick.setOnClickListener {
  6. map.put("name", "修改过的")
  7. }
 

同理,map的put方法如下:

public V put(K k, V v) {
    V val = super.put(k, v);
    notifyChange(k);
    return v;
}

也是调用了全局刷新,同理,ArryList的add方法也是这样:

介绍完以上用法,会发现,这些好像都是单向绑定,什么才是双向绑定?其实很简单

四、双向绑定

        介绍完单向绑定,其实大家已掌握了双向绑定。用周董的话说,全球音乐看话语,中文才是最屌。

双向绑定比单向绑定在view绑定的时候,多一个=号

单向:

  1. <EditText
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="@{dbValue}" />

双向:

  1. <EditText
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="@={dbValue}" />

这边采用了

var name = ObservableField<String>("我的名字")

进行测试。

效果图

 总结:

               只要我们掌握了单向绑定,双向绑定自然也会解决。但是,在使用双向的时候需要注意,不同的场景如果存在多处引用,会导致数据错乱。所以,在使用的时候需要格外小心。

        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/265076
推荐阅读
相关标签
  

闽ICP备14008679号