前言
本文是在前文的基礎上繼續深入DataBinding的使用這一塊,如果有不懂的地方,請移步上一篇
Android Jetpack之DataBinding(一)
DataBinding使用
觀察者使用
使用可觀察的bean
- 繼承BaseObservable類,實現整體數據的可觀察。
- 爲屬性添加@Bindable註解,生成BR對應屬性。
- 重寫set方法實現,屬性改變時候動態喚醒刷新。
- 其餘實現就是上一篇中基本的綁定過程。
數據類代碼如下:
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()
}
}
}
效果如下,點擊按鈕年齡隨之增長:
使用可觀察的屬性
定義可觀察的屬性,訪問使用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)
}
}
}
原因分析:在綁定的時候傳入了特定的發佈者對象,即BaseObservable對象,根據對象和屬性共同定位是那一個屬性,如下:
DataBinding綁定列表
實現的列表效果如下:
具體實現步驟:
-
自定義ViewHolder
主要是將入參換成ViewDataBinding類型,將root傳給super的構造器即可
class BindingViewHolder(var dataBinding:ViewDataBinding) :RecyclerView.ViewHolder(dataBinding.root){
}
- 定義Adapter/BaseAdapter
- 在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>
- 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(一)
-
創建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)
}
}
- 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
}
}
- 對應的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
- 新建可觀察的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)
}
- 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
}
}
- 對應的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):
源碼
源碼鏈接:源碼
總結
1、限於篇幅,本篇暫時到此,後續在繼續雙向綁定以及其他相關的內容。
2、本篇主要降到,觀察者使用、可變屬性、列表綁定、結合ViewModel、LiveData以及可觀察的ViewModel等內容。
3、本篇代碼均是實際運行之後貼出的代碼,但難免有什麼粗心或者遺漏問題,歡迎指出討論。
4、另外記錄了自己寫的過程中遇到的其他問題。
5、另外此篇內容主要是使用方面的,後續如果有時間研究了相關的原理再進行相關更新。