Article directory
I. Introduction
The use of expressions recorded above DataBinding
, this article continues to record the use of observable objects
Second, the observable field
Here is the introduction of the official website
Observability refers to the ability of an object to inform other objects of changes to its data. With data binding libraries, you can make objects, fields, or collections observable.
Any plain-old object can be used for data binding, but modifying the object does not automatically update the interface. With data binding, data objects can notify other objects, or listeners, when their data changes. There are three different types of observable classes: objects , fields , and collections .
When one of the observable data objects is bound to the interface and the properties of that data object change, the interface updates automatically.
Here is an example to illustrate the role of observability. Let's first look at the code that does not use observable fields. Here, ordinary classes are used for data binding to display content.
activity_main.xml
<?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:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="change"
type="String" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/update_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@{change}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="value" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private var changeValue = "你好啊"
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.change = changeValue
changeValue = "我不好"
}
}
The code first xml
binds with the string sum and then changes the string later. The result is to be expected, the content changed later will not be displayed on the UI layout, which is a normal phenomenon. But in the project, assuming that this field needs to be changed many times, it binding.change = changeValue
will be very cumbersome to re-execute each time after the assignment. So the function of observable field is introduced here, let's see the modified code below
activity_main.xml
<?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:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="change"
type="androidx.databinding.ObservableField<String>" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/update_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@{change}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="value" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/update_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@{input.text}"
app:layout_constraintTop_toBottomOf="@+id/input"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val changeValue = ObservableField<String>("你好啊")
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.change = changeValue
changeValue.set("我不好")
}
}
In this way, the UI can be changed in real time when the content of the string is changed.
DataBinding
The basic observable fields provided in are as follows
There is some work to be done when creating a class that implements the Observable
interface , but it doesn't make much sense if your class has only a few properties. In this case, you can use the generic Observable
class and the following primitive-specific classes to make the field observable:
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
For example, if we create a new class to store user information, we can fill in the observable variables.
class User {
val firstName = ObservableField<String>()
val lastName = ObservableField<String>()
val age = ObservableInt()
}
3. Observable collection
In addition to observable fields, there are also observable collections, mainly ObservableArrayMap
andObservableArrayList
ObservableArrayMap<String, Any>().apply {
put("firstName", "Google")
put("lastName", "Inc.")
put("age", 17)
}
ObservableArrayList<Any>().apply {
add("Google")
add("Inc.")
add(17)
}
<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap<String, Object>"/>
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user2" type="ObservableList<Object>"/>
</data>
…
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@{String.valueOf(1 + (Integer)user.age)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{user2[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user2[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
4. Observable objects
Sometimes the officially provided method cannot meet the needs and needs to be expanded by itself. At this time, it can be implemented Observable
to complete this function.
class User : BaseObservable() {
@get:Bindable
var firstName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.firstName)
}
@get:Bindable
var lastName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.lastName)
}
}
The following is quoted from the official documentation
Data binding generates
BR
a that contains the ID of the resource used for data binding. During compilation,Bindable
annotations generate an entry in theBR
class file. If the base class of the data class cannot be changed, theObservable
interface can be implemented using anPropertyChangeRegistry
object to effectively register and notify listeners.
How to implement Observable
the interface
class User : Observable {
private val property : PropertyChangeRegistry by lazy {
//默认是线程安全的
PropertyChangeRegistry()
}
@get:Bindable
var change: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.change)
}
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
property.add(callback)
}
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
property.remove(callback)
}
fun notifyChange(){
property.notifyCallbacks(this, 0, null)
}
fun notifyPropertyChanged(fieldId : Int) {
property.notifyCallbacks(this, fieldId, null);
}
}