Android DataBinding学习(二)

1,557 阅读5分钟

注意:本篇笔记学习基于官方文档使用可观察数据对象部分,点击这里查看原文

使用可观察数据对象

可观察性是指一个对象将其数据变化通知给其它对象的能力。通过数据绑定库,可以让对象,字段或集合变为可观察。

任何plain-old对象都可用于数据绑定,但修改对象不会自动使界面更新。通过数据绑定,数据对象可在其数据发生更改时通知其它对象,即监听器。可观察类有三种不同的类型:对象,字段和集合。

当其中一个数据对象绑定到界面并且该数据对象的属性发生更改时,界面会自动更新。

可观察字段

在创建实现Observable接口的类时要完成一些操作,但是如果类只有少数几个属性,则去实现Observable接口意义不大。在这种情况下,可以使用通用Observable类和以下基于特定基元的类,将字段设为可观察字段:

  • ObservableBoolean
  • ObservableByte
  • ObservableChar
  • ObservableShort
  • ObservableInt
  • ObservableLong
  • ObservableFloat
  • ObservableDouble
  • ObservableParcelable

可观察字段是具有单个字段的自包含可观察对象,原语版本避免在访问操作期间封箱和开箱。如:

class Person {
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
    val height = ObservableFloat()
    val isBoy = ObservableBoolean()
}

接下来,我们将这个类使用到布局文件中:

<data>
    <variable
        name="person"
        type="com.project.databinding_moudle.data.Person"
        />
</data>

<TextView
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/layout_title"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:textColor="@android:color/black"
    android:text='@{"我是"+person.firstName+person.lastName+",年龄"+person.age+"岁,是个"+(person.isBoy ? "男孩": "女孩")+",身高:"+person.height+"cm!"}'
    android:padding="20dp"
    />

没有设置任何数据的时候,显示如下:

可观察字段学习
可观察字段学习

接下来在Activity中创建对象并设置:

override fun initData() {
    val person = Person()
    mBinding.person = person
    person.firstName.set("Bob")
    person.lastName.set("Smith")
    person.age.set(10)
    person.height.set(178.0f)
    person.isBoy.set(true)
}

注意上面的设置顺序,首先创建了Person对象,然后将Person对象设置到布局文件中,最后才对Person对象进行赋值,最后显示如下:

可观察字段学习
可观察字段学习

为了能够更加准确的演示,现在添加一个按钮,点击按钮的时候改变数据

<Button
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/tv_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:text="修改数据"
    android:layout_marginTop="20dp"
    android:onClick="doClick"
    android:id="@+id/btn_change_data"
    />

在Activity中修改:

if(view?.id == R.id.btn_change_data){
    person.age.set(20)
    person.height.set(188.0f)
}

可以看到,在Activity中点击按钮之后,只是重新设置了值,并没有对布局文件中的数据进行重新赋值。

可观察字段学习
可观察字段学习

可观察集合

如果应用使用动态结构来保存数据,那么在这里可以使用可观察集合,它允许使用键来访问这些数据。当键为引用类型(如 String)时,ObservableArrayMap类非常有用,如:

<TextView
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/btn_change_data"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="20dp"
    android:padding="10dp"
    android:textColor="@android:color/black"
    android:text='@{"我是:"+userMap["firstName"]+userMap["lastName"]+",今年"+userMap["age"]+"岁!"}'
    />

当键为整数的时候,ObservaleArrayList类非常有用,如:

val array = ObservableArrayList<Any>().apply {
    add("李")
    add("四")
    add(15)
}
mBinding.userArray = array

在布局文件中使用:

<variable
    name="userArray"
    type="androidx.databinding.ObservableArrayList&lt;Object>"
    />
<TextView
    android:id="@+id/tv_content2"
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/tv_content1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="20dp"
    android:padding="10dp"
    android:textColor="@android:color/black"
    android:text='@{"我是:"+userArray[0]+userArray[1]+",今年"+userArray[2]+"岁!"}'
    />

可观察对象

实现Observable接口的类允许注册监听器,以便它们接收有关可观察对象的属性更改的通知。

Observable接口具有添加和移除监听器的机制,但何时发送通知则必须由自己决定。为便于开发,数据绑定库提供了用于实现监听器机制的BaseObservable类。实现BaseObservable的数据类负责在属性更改时发出通知。具体操作过程是向getter分配@Bindable注释,然后在setter中调用notifyPropertyChanged()方法。如:

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

    @get:Bindable
    var age: Int = 0
        set(value) {
            field = value
            notifyPropertyChanged(BR.age)
        }
}

需要注意的是,在上面由于使用了BR这个自动生成的类,可能会出现Unresolved reference: BR这个错误,这是由于dataBindingkotlin插件有冲突造成的,解决方案如下:

在对用modulebuild.gradle文件下添加如下配置:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

dependencies {
//注意:版本号和项目build.gradle中的kotlin-gradle-plugin版本相同,AndroidStudio3.0以上可以不添加这个
kapt 'com.jakewharton:butterknife-compiler:8.4.0''
}

kapt {
    generateStubs = true
}

添加完成之后即可正常使用BR

现在可以在布局文件中使用Student类:

<variable
    name="student"
    type="com.project.databinding_moudle.data.Student"
    />

<TextView
    android:id="@+id/tv_content3"
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/tv_content2"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="20dp"
    android:padding="10dp"
    android:textColor="@android:color/black"
    android:text="@{@string/format_student(student.name,student.age)}"
    />

Activity中对student进行赋值:

val student: Student = Student()
mBinding.student = student
student.name = "小明"
student.age = 20

数据绑定在模块包中生成一个名为BR的类,该类包含用于数据绑定的资源的ID,在编译期间,Bindable注释会在BR类文件中生成一个条目。

另外,由于我们创建的类可能需要继承别的类,导致没有办法继承BaseObservable这个类,但是又需要这个功能,此时可以通过实现Observable接口同时配合PropertyChangeRegistry实现,如:

class Teacher : Observable {

    private val registry = PropertyChangeRegistry()

    @get:Bindable
    var name = ""
        set(value) {
            field = value
            registry.notifyChange(this, BR.name)
        }
    @get:Bindable
    var course = ""
        set(value) {
            field = value
            registry.notifyChange(this,BR.course)
        }

    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        registry.add(callback)
    }

    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        registry.remove(callback)
    }
}

在布局文件中使用:

<variable
    name="teacher"
    type="com.project.databinding_moudle.data.Teacher"
    />  
    
<TextView
    android:id="@+id/tv_content4"
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/btn_change_student_data"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="20dp"
    android:padding="10dp"
    android:textColor="@android:color/black"
    android:text="@{@string/format_teacher(teacher.name,teacher.course)}"
    />

Activity中赋值:

val teacher = Teacher()
mBinding.teacher = teacher
teacher.name = "王五"
teacher.course = "体育"