Note: This note is based on the official documentation using observable data objects section. Click here to view the original text

Use observable data objects

Observability refers to the ability of one object to notify other objects of changes in its data. Data binding libraries allow you to make objects, fields, or collections observable.

Any plain-old object can be used for data binding, but modifying an object does not automatically update the interface. With data binding, data objects can notify other objects, namely listeners, when their data changes. There are three different types of observable classes: objects, fields, and collections.

When one of the data objects is bound to the interface and the properties of the data object change, the interface updates automatically.

Observable field

There are a few things to do when creating a class that implements the Observable interface, but implementing it doesn’t make much sense if the class has only a few properties. In this case, you can use the general Observable class and the following primitive-based classes to make the field Observable:

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

Observable fields are self-contained observables with a single field, and the primitive version avoids boxed and unboxed during access operations. Such as:

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

Next, we use this class in the layout file:

<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='@{" I am "+person.firstName+person.lastName+", age "+person.age+", is a "+(person.isBoy? "+person. Height +"cm!" } '
    android:padding="20dp"
    />
Copy the code

If no data is set, the following output is displayed:

Observable field learning

Next create an object in the Activity and set it:

override fun initData() {
    val person = Person()
    mBinding.person = person
    person.firstName.set("Bob")
    person.lastName.set("Smith") person. Age. The set (10) person. Height. Set (178.0 f) person. IsBoy. Set (true)}Copy the code

Note the order in which the Person object is first created, then set to the layout file, and then assigned to the Person object, as shown below:

Observable field learning

For a more accurate demonstration, now add a button that changes the data when clicked

<Button
    style="@style/match_width"
    app:layout_constraintTop_toBottomOf="@id/tv_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:text="Modify data"
    android:layout_marginTop="20dp"
    android:onClick="doClick"
    android:id="@+id/btn_change_data"
    />
Copy the code

Change it in the Activity:

if(view? .id == r.i.b.tn_change_data){person.age.set(20) person.height.set(188.0f)}Copy the code

As you can see, after clicking the button in the Activity, you just reset the value, not reassign the data in the layout file.

Observable field learning

Observable set

If your application uses dynamic structures to store data, you can use observable collections here, which allow you to access the data using keys. The ObservableArrayMap class is useful when the key is of a reference type, such as String, for example:

<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='@ {" I is "+ userMap [" firstName"] + userMap (" lastName ") + ", "this year + userMap (" age") + "years old!" } '
    />
Copy the code

The ObservaleArrayList class is useful when the key is an integer, as in:

val array = ObservableArrayList<Any>().apply {
    add("Li")
    add("Four")
    add(15)
}
mBinding.userArray = array
Copy the code

Use:

<variable
    name="userArray"
    type="androidx.databinding.ObservableArrayList< 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='@ {" I is "+ userArray userArray [0] + [1] +", "this year + userArray [2] + years old!" " } '
    />
Copy the code

Observable object

Classes that implement the Observable interface allow listeners to be registered so that they receive notifications about Observable property changes.

The Observable interface has mechanisms for adding and removing listeners, but it must decide when to send notifications. For development purposes, the data binding library provides a BaseObservable class that implements the listener mechanism. The data class that implements a BaseObservable notifies properties when they change. This is done by assigning the @bindable annotation to the getter and then calling the notifyPropertyChanged() method in the setter. Such as:

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)
        }
}
Copy the code

Unresolved Reference: BR error: Unresolved reference: BR error: Unresolved reference: BR error: Unresolved reference: BR error: Unresolved reference: BR error: Unresolved reference: BR

Add the following configuration to the build.gradle file of the opposing module:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'Gradle: kotlin-gradle-plugin: kotlin-gradle-plugin: kotlin-gradle-plugin: kotlin-gradle-plugin: kotlin-gradle-plugin: kotlin-gradle-plugin'com. Jakewharton: butterknife - compiler: 8.4.0'' } kapt { generateStubs = true }Copy the code

Once added, you can use the BR as normal.

You can now use the Student class in the layout file:

<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)}"
    />
Copy the code

Assign to student in Activity:

val student: Student = Student()
mBinding.student = student
student.name = "Xiao Ming"
student.age = 20
Copy the code

Data binding generates a class called BR in the module package that contains the ID of the resource used for data binding, and at compile time the Bindable annotation generates an entry in the BR class file.

In addition, the class we create may need to inherit other classes, so we cannot inherit the BaseObservable class, but we need this function. In this case, we can implement the Observable interface together with PropertyChangeRegistry, for example:

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)
    }
}
Copy the code

Use:

<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)}"
    />
Copy the code

Assign to the Activity:

val teacher = Teacher()
mBinding.teacher = teacher
teacher.name = "Fifty"
teacher.course = "Sports"
Copy the code