本文是在前文的基础上继续深入DataBinding的使用这一块,如果有不懂的地方,请移步上一篇
Android Jetpack之DataBinding(一)
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代码如下:
<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()
}
}
}
定义可观察的属性,访问使用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())
}
}
}
效果和上述一致。
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)
}
}
}
原因分析:在绑定的时候传入了特定的发布者对象,即BaseObservable对象,根据对象和属性共同定位是那一个属性,如下:
自定义ViewHolder
主要是将入参换成ViewDataBinding类型,将root传给super的构造器即可
class BindingViewHolder(var dataBinding:ViewDataBinding) :RecyclerView.ViewHolder(dataBinding.root){
}
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布局文件如下:
<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>
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代码如下:
<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>
LiveData如果不熟悉,请移步LiveData
ViewModel如果不熟悉,请移步ViewModel
DataBinding基础不熟悉,请移步DataBinding(一)
创建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)
}
}
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
}
}
<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只会会变到最终的效果):
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)
}
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
}
}
<?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>
源码链接:源码
1、限于篇幅,本篇暂时到此,后续在继续双向绑定以及其他相关的内容。
2、本篇主要降到,观察者使用、可变属性、列表绑定、结合ViewModel、LiveData以及可观察的ViewModel等内容。
3、本篇代码均是实际运行之后贴出的代码,但难免有什么粗心或者遗漏问题,欢迎指出讨论。
4、另外记录了自己写的过程中遇到的其他问题。
5、另外此篇内容主要是使用方面的,后续如果有时间研究了相关的原理再进行相关更新。