[Jetpack] DataBinding architecture component ⑤ (two-way binding between data model and view | BaseObservable for two-way binding | ObservableField for two-way binding)





1. Two-way binding between the data model Model and the view View




1. One-way binding between data model Model and view View


In a previous blog,

Bind the specified Field field in the data model Model to the component in the View view ,

In the actual case, the name field of String type in the Student class is bound to the TextView component in the layout file,

When the Student#name field changes,

The content displayed in the corresponding TextView component has also been modified accordingly;


The above binding method can be understood as one-way binding ,

Because the TextView component cannot be modified, it can only be displayed ,

Field modification in the data model can change the content displayed by TextView;

The TextView component cannot initiate changes to the data model;

insert image description here


2. Two-way binding is derived from one-way binding


If the component corresponding to the bound data model is an EditText text box,

The content of the EditText component can be modified by itself,

The data model can initiate modifications to the EditText component,

At the same time, EditText can also initiate modifications to the data model,

Then there will be a two-way binding problem;

insert image description here





2. BaseObservable implements two-way binding between data model Model and view View



Sample code: https://download.csdn.net/download/han1202012/87702558


1. Enable DataBinding


Before using DataBinding, data binding must be enabled,

In the build.gradle build script under Module , at the "android / defaultConfig" level , configure

        // 启用 DataBinding
        dataBinding {
    
    
            enabled = true
        }

content to enable data binding;


The code for the full hierarchy is as follows:

android {
    
    
    namespace 'kim.hsl.databinding_demo'
    compileSdk 32

    defaultConfig {
    
    
        applicationId "kim.hsl.databinding_demo"
        minSdk 21
        targetSdk 32

        // 启用 DataBinding
        dataBinding {
    
    
            enabled = true
        }
    }
}

2. Import the kotlin-kapt plugin


Whenever annotations are used in Kotlin, the kotlin-kapt plugin needs to be imported;

In the build.gradle build script under Module, import the kotlin-kapt plugin;

plugins {
    
    
    id 'kotlin-kapt'
}

3. Data model class


In the data class, it mainly encapsulates the data model;

package kim.hsl.databinding_demo

class Student(var name: String, var age: Int) {
    
    
}

4. BaseObservable realizes two-way binding (the core focus of this blog) ★


To realize the two-way binding class of data and view, you need to inherit BaseObservablethe class ;


class StudentViewModel: BaseObservable {
    
    
}

In this class, a data class object needs to be maintained , which is passed in the secondary constructor as follows;

    lateinit var student: Student

    constructor() {
    
    
        this.student = Student("Tom", 18)
    }

Implement a getXxx function and modify the function @Bindablewith annotations ,

At the same time, in the DataBinding layout, when setting the value for the EditText component, also use this function to set the value;

@BindableIf the annotation is set , as long as the name in the student object changes, the content in the bound component will change;

    /**
     * 只要 student 对象中的 name 发生了变化
     * 绑定的组件中的内容就会发生变化
     */
    @Bindable
    fun getStudentName(): String {
    
    
        return student.name
    }

If you want to achieve the effect of modifying the data model through EditText, you need to implement another setXxx function,

This function needs to correspond to the previous getXxx function decorated with @Bindableannotations , and Xxx must be the same;

After modification, you need to call notifyPropertyChanged(BR.xxx) to notify the data model to make changes;

    /**
     * 只要绑定的 EditText 组件内容发生变化
     * 就会自动调用该函数 修改 student 对象中的 name 字段
     */
    fun setStudentName(name: String): Unit {
    
    
        // 修改后的字符串不为空 且与之前的值不同 才更新数据模型数据
        if (name != null && !(name == student.name)) {
    
    
            student.name = name
            Log.i("StudentViewModel", "setStudentName : ${
      
      name}")

            // BR 是编译时自动生成的字段
            // studentName 对应的是 上面 被 @Bindable 修饰的 getStudentName 函数
            notifyPropertyChanged(BR.studentName)
        }
    }

The BR class BaseObservableis @Bindablegenerated by the annotation-modified function in the subclass;

The BR class is generated at app\build\generated\source\kapt\debug\kim\hsl\databinding_demo\BR.java;

insert image description here


The source code of the BaseObservable class is as follows:

package kim.hsl.databinding_demo

import android.util.Log
import android.view.View
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable

class StudentViewModel: BaseObservable {
    
    

    lateinit var student: Student

    constructor() {
    
    
        this.student = Student("Tom", 18)
    }

    /**
     * 只要 student 对象中的 name 发生了变化
     * 绑定的组件中的内容就会发生变化
     */
    @Bindable
    fun getStudentName(): String {
    
    
        return student.name
    }

    /**
     * 只要绑定的 EditText 组件内容发生变化
     * 就会自动调用该函数 修改 student 对象中的 name 字段
     */
    fun setStudentName(name: String): Unit {
    
    
        // 修改后的字符串不为空 且与之前的值不同 才更新数据模型数据
        if (name != null && !(name == student.name)) {
    
    
            student.name = name
            Log.i("StudentViewModel", "setStudentName : ${
      
      name}")

            // BR 是编译时自动生成的字段
            // studentName 对应的是 上面 被 @Bindable 修饰的 getStudentName 函数
            notifyPropertyChanged(BR.studentName)
        }
    }
}

5. Layout file settings (emphasis)


In the DataBinding layout file, you need to introduce an object of type StudentViewModel in the "data / variable" tag ;

When assigning a value to the EditText component, you need to use to android:text="@={student.studentName}"assign a value. Note that the value @={student.studentName}is an equal sign more than the previous data binding;


Layout code example:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="student"
            type="kim.hsl.databinding_demo.StudentViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <EditText
            android:id="@+id/imageView"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="@={student.studentName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

6. Activity component code (key point)


In the Activity component, the object type set in the layout is the StudentViewModel type, not the Student type;

package kim.hsl.databinding_demo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import kim.hsl.databinding_demo.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)

        // 设置布局文件
        // 布局文件是 activity_main.xml
        // 该类名称生成规则是 布局文件名称 + Binding
        var activityMainBinding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)

        // 为布局 设置 数据
        activityMainBinding.student = StudentViewModel()
    }
}

7. Execution results


Displayed after execution:

insert image description here

Delete Tom letter by letter and enter Jack ;

Finally, the following log is printed:

setStudentName : To
setStudentName : T
setStudentName : 
setStudentName : Jack

insert image description here

insert image description here





3. ObservableField implements two-way binding between data model Model and view View (the core focus of this blog) ★



Sample code:


ObservableField implements two-way binding between data model Model and view View

and

BaseObservable implements two-way binding between data model Model and view View

comparing ,

Except for StudentViewModel, other codes are the same;

Focus on the StudentViewModel class;


Define the data model class Student as a generic class of ObservableField;

    lateinit var studentObservableField: ObservableField<Student>

In the constructor, create a Student object and set it to ObservableField<Student>the object ;

    constructor() {
    
    
        var student: Student = Student("Tom", 18)
        studentObservableField = ObservableField()
        studentObservableField.set(student)
    }

Define getStudentName()the function to get the properties of the objectObservableField<Student> in the object ;Studentname

    fun getStudentName(): String? {
    
    
        return studentObservableField.get()?.name
    }

Define setStudentName()the function , set the property of the objectObservableField<Student> in the object ;Studentname

    fun setStudentName(name: String): Unit {
    
    
        studentObservableField.get()?.name = name
        Log.i("StudentViewModel", "setStudentName : ${
      
      name}")
    }

The complete code is as follows:

package kim.hsl.databinding_demo

import android.util.Log
import androidx.databinding.ObservableField

class StudentViewModel {
    
    
    lateinit var studentObservableField: ObservableField<Student>

    constructor() {
    
    
        var student: Student = Student("Tom", 18)
        studentObservableField = ObservableField()
        studentObservableField.set(student)
    }

    fun getStudentName(): String? {
    
    
        return studentObservableField.get()?.name
    }

    fun setStudentName(name: String): Unit {
    
    
        studentObservableField.get()?.name = name
        Log.i("StudentViewModel", "setStudentName : ${
      
      name}")
    }
}

Executing the above code can also achieve the same effect as BaseObservable two-way binding;
insert image description here

Guess you like

Origin blog.csdn.net/han1202012/article/details/130228644