上文记录了DataBinding
的表达式的使用,这一篇继续对可观察对象的使用进行记录
这里引用一下官网的介绍
可观察性是指一个对象将其数据变化告知其他对象的能力。通过数据绑定库,您可以让对象、字段或集合变为可观察。
任何 plain-old 对象都可用于数据绑定,但修改对象不会自动使界面更新。通过数据绑定,数据对象可在其数据发生更改时通知其他对象,即监听器。可观察类有三种不同类型:对象、字段和集合。
当其中一个可观察数据对象绑定到界面并且该数据对象的属性发生更改时,界面会自动更新。
这里举个例子来说明可观察性的作用,先看下没有使用可观察字段的代码,这里使用普通的类进行数据绑定,用来显示个内容
activity_main.xml
<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 = "我不好"
}
}
这个代码先使用字符串和xml
进行绑定,后续又更改这个字符串。结果是可以预料的,后面改变的内容不会显示到UI布局上,这是很正常的现象。但是在项目中,假设这个字段需要更改很多次,那么每次都要在赋值之后重新执行一下binding.change = changeValue
会显的很繁琐。所以这里引入了可观察字段的功能,下面看修改后的代码
activity_main.xml
<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" />
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("你好啊")
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("我不好")
}
}
这样修改完就可以做到当字符串内容更改后UI就实时更改了。
在DataBinding
中提供的基础可观察字段有以下几种
在创建实现 Observable
接口的类时要完成一些操作,但如果您的类只有少数几个属性,这样操作的意义不大。在这种情况下,您可以使用通用 Observable
类和以下特定于基元的类,将字段设为可观察字段:
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
例如我们新建一个保存用户信息的类,可以将可观察到变量填充进去
class User {
val firstName = ObservableField<String>()
val lastName = ObservableField<String>()
val age = ObservableInt()
}
除了可观察字段外,还有一些可观察集合,主要是ObservableArrayMap
和ObservableArrayList
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" />
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user2" type="ObservableList/>
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"/>
有时候官方提供的方式不能满足需求,需要自己拓展,这时候可以实现Observable
来完成这一功能
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)
}
}
以下引用自官方文档
数据绑定在模块包中生成一个名为
BR
的类,该类包含用于数据绑定的资源的 ID。在编译期间,Bindable
注释会在BR
类文件中生成一个条目。如果数据类的基类无法更改,Observable
接口可以使用PropertyChangeRegistry
对象实现,以便有效地注册和通知监听器。
实现Observable
接口的方式
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);
}
}