Android Jetpack之DataBinding(二)

前言

本文是在前文的基础上继续深入DataBinding的使用这一块,如果有不懂的地方,请移步上一篇
Android Jetpack之DataBinding(一)

DataBinding使用

观察者使用
使用可观察的bean
  1. 继承BaseObservable类,实现整体数据的可观察。
  2. 为属性添加@Bindable注解,生成BR对应属性。
  3. 重写set方法实现,属性改变时候动态唤醒刷新。
  4. 其余实现就是上一篇中基本的绑定过程。
    数据类代码如下:
class PersonObservable(): BaseObservable(){

    constructor(name:String,age:Int):this(){
        this.name = name
        this.age = age
    }

    @Bindable
    var name:String? = null
    set(value) {
        field = value
        //单个属性唤醒刷新,参数为编译生成的BR条目
        notifyPropertyChanged(BR.name)
        //整个对象的属性全部刷新
        notifyChange()
    }
    @Bindable
    var age:Int? = null
    set(value) {
        field = value
        notifyPropertyChanged(BR.age)
    }
}

xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable name="person" type="com.zgj.demojetpackapp.bean.PersonObservable"/>
        <variable name="listener" type="com.zgj.demojetpackapp.databinding.TestActivity3.EventListener"/>
    </data>
    <LinearLayout
            android:orientation="vertical"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".databinding.TestActivity">

        <TextView
                android:text="@{person.name}"
                android:padding="@dimen/dimen_10"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:padding="@dimen/dimen_10"
                android:text="@{person.age +`岁`}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="长大了一岁"
                  android:onClick="@{listener::onClick}"/>

    </LinearLayout>
</layout>

Activity相关调用代码如下 ,可以看到只需要改变绑定对象的属性值,不需要再将对象重新绑定即可实现刷新改变:

class TestActivity3 : AppCompatActivity() {

    val person = PersonObservable("张三", 23)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityTestDataBinding3Binding =
            DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding3)
        binding.person = person
        binding.listener = EventListener()
    }

    inner class EventListener {
        fun onClick(view: View) {
            person.age = person.age?.inc()
        }
    }

}

效果如下,点击按钮年龄随之增长:
效果1

使用可观察的属性

定义可观察的属性,访问使用get和set方法,代码如下:

class PersonObservable2{
    var name:ObservableField<String> = ObservableField()
    var age:ObservableInt = ObservableInt()
}

xml代码和上面一致,xml访问不需要使用get,可以直接使用。
activity调用代码如下:

class TestActivity3 : AppCompatActivity() {

    val person = PersonObservable2()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityTestDataBinding3Binding =
            DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding3)
        person.name.set("张三")
        person.age.set(23)
        binding.person = person
        binding.listener = EventListener()
    }

    inner class EventListener {
        fun onClick(view: View) {
            person.age.set(person.age.get().inc())
        }
    }

}

效果和上述一致。

问题:如果两个类的属性名相同会生成几个BR属性,会不会相互影响呢?
1、会生成1个BR属性。	
2、不会相互影响。

测试如下:
新建StudentObservable如下,属性和PersonObervable完全一样:

class StudentObservable(): BaseObservable(){

    constructor(name:String,age:Int):this(){
        this.name = name
        this.age = age
    }
    
    @Bindable
    var name:String? = null
    set(value) {
        field = value
        notifyPropertyChanged(BR.name)
        notifyChange()
    }
    
    @Bindable
    var age:Int? = null
    set(value) {
        field = value
        notifyPropertyChanged(BR.age)
    }
    
}

xml代码如下:


        <TextView
                android:text="@{`学生:`+student.name}"
                android:padding="@dimen/dimen_10"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:padding="@dimen/dimen_10"
                android:text="@{`学生:`+student.age +`岁`}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

activity调用代码如下:

class TestActivity3 : AppCompatActivity() {

    val person = PersonObservable("张三",23)
    val student = StudentObservable("张三",23)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityTestDataBinding3Binding =
            DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding3)

        binding.person = person
        binding.student = student
        binding.listener = EventListener()
    }


    inner class EventListener {
        fun onClick(view: View) {
            person.age = person.age?.inc()
            student.age = student.age?.plus(2)
        }
    }


}

效果2

原因分析:在绑定的时候传入了特定的发布者对象,即BaseObservable对象,根据对象和属性共同定位是那一个属性,如下:

原因1

DataBinding绑定列表

实现的列表效果如下:
列表效果
具体实现步骤:

  1. 自定义ViewHolder

    主要是将入参换成ViewDataBinding类型,将root传给super的构造器即可

class BindingViewHolder(var dataBinding:ViewDataBinding) :RecyclerView.ViewHolder(dataBinding.root){
}
  1. 定义Adapter/BaseAdapter
  2. 在Adapter中进行数据绑定
BaseAdapter 实现绑定 同时抽象一个方法传入layoutId,如下:
abstract class BaseAdapter<T>(var data :MutableList<T> = mutableListOf()) : RecyclerView.Adapter<BindingViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
        return BindingViewHolder(
            DataBindingUtil.inflate(
                LayoutInflater.from(parent.context),
                getLayoutId(),
                parent,
                false
            )
        )
    }

    override fun getItemCount(): Int {
        return  data.size
    }


    abstract fun getLayoutId(): Int

}
RecycleDemoAdapter实现具体的业务,如下:
class RecycleDemoAdapter() : BaseAdapter<RecycleListAnimalBean>() {

    override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
//        holder.dataBinding.setVariable(BR.animal,data.get(position))
        (holder.dataBinding as ItemRecycleBinding).animal = data[position]
    }

    override fun getLayoutId(): Int {
        return R.layout.item_recycle
    }
}

对应的bean数据如下:
class RecycleListAnimalBean {

    /**
     * 名称、类型、年龄、描述
     */
    val name:ObservableField<String> = ObservableField()
    val type:ObservableInt = ObservableInt()
    val age:ObservableInt = ObservableInt()
    val typeDesc:ObservableField<String> = ObservableField()

}

item_recycle对应的xml布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="animal" type="com.zgj.demojetpackapp.bean.RecycleListAnimalBean"/>
    </data>
    <LinearLayout
            android:padding="@dimen/dimen_10"
            android:orientation="vertical"
            android:layout_marginTop="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <TextView
                android:id="@+id/tv_name"
                android:text="@{animal.name,default=`名称`}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>


        <TextView
                android:id="@+id/tv_age"
                android:text="@{`年龄:`+animal.age,default=`0`}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:id="@+id/tv_type"
                android:text="@{`类型:`+animal.type,default=`类型`}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:id="@+id/tv_desc"
                android:text="@{animal.typeDesc,default=`描述`}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

    </LinearLayout>
</layout>
  1. activity设置相关的数据和adapter,代码如下
class TestActivity4() : AppCompatActivity() {

    var binding: ActivityTestDataBinding4Binding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding4)
        init()
    }

    private fun init() {
        binding?.rvContent?.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        val animalBean = RecycleListAnimalBean()
        animalBean.name.set("老虎")
        animalBean.age.set(10)
        animalBean.type.set(1)
        animalBean.typeDesc.set("老虎是一种动物,和猫比较像")

        val animalBean1 = RecycleListAnimalBean()
        animalBean1.name.set("长颈鹿")
        animalBean1.age.set(20)
        animalBean1.type.set(2)
        animalBean1.typeDesc.set("长颈鹿是一种动物,脖子非常长")
        val data: MutableList<RecycleListAnimalBean> = mutableListOf(animalBean, animalBean1)

        var adapter = RecycleDemoAdapter()
        adapter.data = data
        binding?.rvContent?.adapter = adapter

    }

}

activity_test_data_binding4对应的xml代码如下:

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


    </data>
    <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <TextView
                android:text="动物列表"
                android:gravity="center"
                android:layout_width="match_parent"
                android:padding="@dimen/dimen_10"
                android:layout_height="wrap_content"/>

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

    </LinearLayout>
</layout>
DataBinding结合ViewModel和LiveData使用/ DataBinding结合ViewModel和可观察的数据类型

LiveData如果不熟悉,请移步LiveData
ViewModel如果不熟悉,请移步ViewModel
DataBinding基础不熟悉,请移步DataBinding(一)

  1. 创建ViewModel如下

    ViewModel分两种情况 一种是LiveData管理普通的类studentData,一种是LiveData管理可观察数据类studentObservable,同时下
    面附上了两个类的代码。
    ViewModel主要进行了两件事情,分别为
    1)初始化了LiveData属性
    2)延迟1s钟之后进行相关属性改变(普通类通过postValue方法,可观察数据类型直接改变值即可)

class Test5ViewModel : ViewModel() {


   init {
       Thread(Runnable {
           Thread.sleep(1000)
           val student = Student("李四", 20)
           studentData.postValue(student)

           studentObservable.name = "赵六"
           studentObservable.age = 60
       }).start()
   }

   val student: Student = Student("张三", 23)

   var studentData: MutableLiveData<Student> = MutableLiveData(student)

   val studentObservable: StudentObservable = StudentObservable("王五", 16)



} 

Student类
data class Student (var name:String,var age:Int)
StudentObservable类
class StudentObservable(): BaseObservable(){

   constructor(name:String,age:Int):this(){
       this.name = name
       this.age = age
   }

   @Bindable
   var name:String? = null
   set(value) {
       field = value
       notifyPropertyChanged(BR.name)
       notifyChange()
   }
   @Bindable
   var age:Int? = null
   set(value) {
       field = value
       notifyPropertyChanged(BR.age)
   }


}
  1. activity绑定如下
    主要做了两件事情:
    1)binding关联生命周期,跟LiveData发消息相关
    2)进行ViewModel的实例化并且绑定到相关的xml
class TestActivity5 : AppCompatActivity() {

    var binding: ActivityTestDataBinding5Binding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding5)
        binding?.lifecycleOwner = this

        val model: Test5ViewModel = ViewModelProviders.of(this).get(Test5ViewModel::class.java)

        binding?.nameViewModel = model

    }

}
  1. 对应的xml使用如下
    声明ViewModel对象直接使用即可,注意此处nameViewModel.studentData.name 最后一步点属性是没有提示的 一定要写对
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="nameViewModel" type="com.zgj.demojetpackapp.databinding.Test5ViewModel"/>
    </data>
    <LinearLayout
            android:orientation="vertical"
            android:padding="@dimen/dimen_10"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <TextView
                android:text="@{`姓名:`+nameViewModel.studentData.name}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:text="@{`年龄:`+nameViewModel.studentData.age}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>


        <TextView
                android:text="可观察的数据类型"
                android:layout_margin="@dimen/dimen_10"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:text="@{`姓名:`+nameViewModel.StudentObservable.name}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:text="@{`年龄:`+nameViewModel.StudentObservable.age}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>


    </LinearLayout>
</layout>

整体效果如下(初始值为ViewModel的初始化值,1s只会会变到最终的效果):
结合效果

DataBinding结合可观察的ViewModel
  1. 新建可观察的Test6ViewModel /ObservableViewModel如下
    同样继承于ViewModel,同时实现Observable接口
baseViewModel
open class ObservableViewModel :ViewModel(),Observable{


    private val callbacks:PropertyChangeRegistry = PropertyChangeRegistry()

    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        callbacks.remove(callback)
    }

    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        callbacks.add(callback)
    }

    fun  notifyChange(){
        callbacks.notifyCallbacks(this,0,null)
    }

    fun notifyPropertyChanged(fieldId: Int) {
        callbacks.notifyCallbacks(this, fieldId, null)
    }

}
具体ViewModel
class Test6ViewModel : ObservableViewModel() {

    init {
        Thread(Runnable {
            Thread.sleep(2000)
            student.name = "李四"
            student.age = 33
            notifyChange()

        }).start()
    }
    val student: Student = Student("张三", 23)

}
  1. activity绑定如下,和前面一致
class TestActivity6 : AppCompatActivity() {

    var binding: ActivityTestDataBinding6Binding? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding6)
        binding?.lifecycleOwner = this

        val model: Test6ViewModel = ViewModelProviders.of(this).get(Test6ViewModel::class.java)

        binding?.nameViewModel = model

    }

}
  1. 对应的xml如下,直接视同student即可
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="nameViewModel" type="com.zgj.demojetpackapp.databinding.Test6ViewModel"/>
    </data>
    <LinearLayout
            android:orientation="vertical"
            android:padding="@dimen/dimen_10"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <TextView
                android:text="@{`姓名:`+nameViewModel.student.name}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

        <TextView
                android:text="@{`年龄:`+nameViewModel.student.age}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

    </LinearLayout>
</layout>

效果如下(从最初的张三,23 在2s之后变成李四,33):
可观察ViewModel

源码

源码链接:源码

总结

1、限于篇幅,本篇暂时到此,后续在继续双向绑定以及其他相关的内容。
2、本篇主要降到,观察者使用、可变属性、列表绑定、结合ViewModel、LiveData以及可观察的ViewModel等内容。
3、本篇代码均是实际运行之后贴出的代码,但难免有什么粗心或者遗漏问题,欢迎指出讨论。
4、另外记录了自己写的过程中遇到的其他问题。
5、另外此篇内容主要是使用方面的,后续如果有时间研究了相关的原理再进行相关更新。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章