阅读 442

DataBinding 进阶篇二 BaseObservable

前面我们讲了,DataBinding的基础知识。现在讲DataBinding的单向绑定功能
DataBinding 基础篇一
DataBinding 进阶篇二 BaseObservable
DataBinding 进阶篇三 BindingAdapter以及BindingConversion
DataBinding 进阶篇四 双向数据绑定

三:使用可观察的数据对象(单向数据绑定)

实现数据变化,自动通知更新UI的方式有三种:BaseObservable、ObservableField、ObservableCollection

3.1 BaseObservable

实现Observable 接口具有添加和移除监听器的机制,但何时发送通知必须由您决定。为便于开发,数据绑定库提供了用于实现监听器注册机制的 BaseObservable 类。实现 BaseObservable 的数据类负责在属性更改时发出通知。具体操作过程是向 getter 分配 Bindable 注释,然后在 setter 中调用 notifyPropertyChanged() 方法。
如以下示例所示,创建一个ObserableUser类继承BaseObservable

class ObserableUser :BaseObservable(){

    @get:Bindable
    var name = ""
        set(value) {
            field = value
            // 只刷新当前属性
            notifyPropertyChanged(BR.name)
        }

    var price = 0f

    @get:Bindable
    var age = 0
        set(value) {
            field = value
            // 更新所有字段
            notifyChange()
        }
}
复制代码
  • @get:Bindable 通过Bindable注解get方法,生成BR类
  • notifyPropertyChanged() 方法是通知更新某个字段,上面是只更新BR.name,该 BR 的生成通过注释 @Bindable 生成
  • notifyChange() 是更新所有的字段

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:tools="http://schemas.android.com/tools">

    <data>
        <import type="com.example.jetpackdatabindingtestapp.ui.helper.ClickHelper" />
        <import type="com.example.jetpackdatabindingtestapp.ui.model.ObserableUser" />
        <variable name="obserableUser" type="ObserableUser" />
        <variable name="clickHelper"  type="ClickHelper" />
    </data>

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

         <TextView
            android:id="@+id/tv_obserable_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{obserableUser.name}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_obserable_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(obserableUser.age)+'岁'}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_obserable_name" />

        <TextView
            android:id="@+id/tv_obserable_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(obserableUser.price)+'元'}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_obserable_age" />

        <Button
            android:id="@+id/btn_change_user_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->clickHelper.onChangeName(obserableUser)}"
            android:text="改变名字"
            app:layout_constraintEnd_toStartOf="@+id/btn_change_user_age"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_obserable_price" />

        <Button
            android:id="@+id/btn_change_user_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->clickHelper.onChangeAge(obserableUser)}"
            android:text="改变年龄"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/btn_change_user_name"
            app:layout_constraintTop_toBottomOf="@+id/tv_obserable_price" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
复制代码

ClickHelper类

class ClickHelper(){

    fun onChangeName(userObserableUser: ObserableUser){
        userObserableUser.name +=userObserableUser.age
        userObserableUser.price++
    }

    fun onChangeAge(userObserableUser: ObserableUser){
        userObserableUser.age++
        userObserableUser.price++
    }

}
复制代码

MainActivity

class MainActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
        val observableUser = ObserableUser()
        observableUser.name = "ccm"
        observableUser.age = 21
        binding.obserableUser = observableUser
        binding.clickHelper = ClickHelper()
    }
}
复制代码

name的刷新没有同时刷新price,而age的刷新会同时刷新price。 当属性值改变的时候,会回调一个OnPropertyChangedCallback,我们可以添加OnPropertyChangedCallback进行监听

obserableUser.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
            override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
                when(propertyId){
                    BR.name->{}
                    BR._all->{}
                    BR.age->{}
                    else ->{}
                }
            }
        })
复制代码

3.2 ObservableField

继承于 Observable 类相对来说使用起来会复杂一些,且需要自己进行 notify 操作才能刷新UI,如果我们的属性定义比较简单,可以选择用ObservableField。ObservableField 其实是官方对 BaseObservable 中字段的注解和刷新等操作的封装,直接使用ObservableField,修改属性值,即可刷新UI ObservableField有如下几种:

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

也可以通过ObservableField<T>的形式,如ObservableField<String>,ObservableField<Int>
例子如下:创建一个Teacher类,有三个属性firstName,teacherAge,price

class Teacher{
    val firstName = ObservableField<String>()
    val teacherAge = ObservableInt()
    val price = ObservableDouble()
}
复制代码

一个activity_field.xml布局,三个文本,三个按钮。三个文本分别显示firstName,teacherAge,price的值,三个按钮分别改变firstName,teacherAge,price的值

<?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>

        <import type="com.example.jetpackdatabindingtestapp.ui.model.Teacher" />
        <import type="com.example.jetpackdatabindingtestapp.ui.helper.ClickTeacherHelper"/>

        <variable
            name="teacher"
            type="Teacher" />

        <variable
            name="clickTeacherHelper"
            type="ClickTeacherHelper" />

    </data>

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

        <TextView
            android:id="@+id/tv_teacher_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{teacher.firstName}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_teacher_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(teacher.teacherAge)+`岁的老师`}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_teacher_name" />

        <TextView
            android:id="@+id/tv_teacher_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(teacher.price)+`元`}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_teacher_age" />

        <Button
            android:id="@+id/btn_change_teacher_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="改变名字"
            android:onClick="@{()->clickTeacherHelper.onChangeName(teacher)}"
            app:layout_constraintEnd_toStartOf="@+id/btn_change_teacher_age"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_teacher_price" />

        <Button
            android:id="@+id/btn_change_teacher_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="改变年龄"
            android:onClick="@{()->clickTeacherHelper.onChangeAge(teacher)}"
            app:layout_constraintEnd_toStartOf="@+id/btn_change_teacher_price"
            app:layout_constraintStart_toEndOf="@+id/btn_change_teacher_name"
            app:layout_constraintTop_toBottomOf="@+id/tv_teacher_price" />

        <Button
            android:id="@+id/btn_change_teacher_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="改变价格"
            android:onClick="@{()->clickTeacherHelper.onChangePrice(teacher)}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/btn_change_teacher_age"
            app:layout_constraintTop_toBottomOf="@+id/tv_teacher_price" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
复制代码

一个处理事件的类ClickTeacherHelper

class ClickTeacherHelper {
    fun onChangeName(teacher: Teacher){
        var name = teacher.firstName.get()?:""
        teacher.firstName.set(name+name)
    }

    fun onChangeAge(teacher: Teacher){
        var age = teacher.teacherAge.get()
        teacher.teacherAge.set(++age)
    }

    fun onChangePrice(teacher: Teacher){
        var price = teacher.price.get()
        teacher.price.set(++price)
    }
}
复制代码

一个Activity类

class FieldActivity :AppCompatActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityFieldBinding>(this,R.layout.activity_field)
        val teacher = Teacher()
        teacher.firstName.set("陈春明啊")
        teacher.teacherAge.set(11)
        teacher.price.set(10.0)
        binding.teacher = teacher
        binding.clickTeacherHelper = ClickTeacherHelper()
    }
}
复制代码

3.3 ObservableCollection

DataBinding也给集合List跟Map提供了,ObservableList跟ObservableMap。当数据改变时候,可以自动更新UI,例子如下: 一个Activity类

class FieldActivity :AppCompatActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityFieldBinding>(this,R.layout.activity_field)
     
        val observableArrayList = ObservableArrayList<String>()
        observableArrayList.add("语文")
        binding.list = observableArrayList
        binding.index = 0

        val observableArrayMap = ObservableArrayMap<String,String>()
        observableArrayMap.put("course","语文")
        binding.map = observableArrayMap
        binding.key = "course"

        binding.btnChangeList.setOnClickListener{
            observableArrayList.clear()
            observableArrayList.add("数学")
            observableArrayMap.clear()
            observableArrayMap.put("course","数学")
        }
    }

}
复制代码

一个布局文件

<?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>

        <import type="com.example.jetpackdatabindingtestapp.ui.model.Teacher" />

        <import type="com.example.jetpackdatabindingtestapp.ui.helper.ClickTeacherHelper" />

        <import type="androidx.databinding.ObservableArrayMap" />

        <import type="androidx.databinding.ObservableArrayList" />

        <variable
            name="list"
            type="ObservableArrayList&lt;String&gt;" />

        <variable
            name="map"
            type="ObservableArrayMap&lt;String,String&gt;" />

        <variable
            name="index"
            type="int" />

        <variable
            name="key"
            type="String" />
    </data>

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

        <TextView
            android:id="@+id/tv_list_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list[index]}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_map_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map[key]}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_list_name" />


        <Button
            android:id="@+id/btn_change_list"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="改变集合"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_map_name" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
复制代码
文章分类
Android