Article directory
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;
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;
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 BaseObservable
the 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 @Bindable
with 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;
@Bindable
If 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 @Bindable
annotations , 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 BaseObservable
is @Bindable
generated 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
;
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:
Delete Tom letter by letter and enter Jack ;
Finally, the following log is printed:
setStudentName : To
setStudentName : T
setStudentName :
setStudentName : Jack
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 ;Student
name
fun getStudentName(): String? {
return studentObservableField.get()?.name
}
Define setStudentName()
the function , set the property of the objectObservableField<Student>
in the object ;Student
name
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;