一.基本使用
1.让项目支持databining
plugins {
id 'kotlin-kapt'
}
android {
buildFeatures {
dataBinding true
}
}
2.布局和绑定表达式
选中xml文件的根目录,按住alt+enter 选择covert to data binding layout
<?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>
<variable
name="dataModel"
type="com.seven.databinding.DataModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{dataModel.str}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
3.对象与xml的单向绑定方式
3.1 变量单次修改时修改控件的单向绑定
class DataModel {
var str: String? = null
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val contentView =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val dataModel = DataModel()
dataModel.str = "abc"
contentView.dataModel=dataModel
//通过上面的数据绑定textView中显示的内容就为dataModel的str这个属性值,但是只有一次,后面就不会修改了
}
}
3.2 变量修改时修改控件的单向绑定
使用ObservableField或继承自BaseObservable方式,这样后续数据变化,控件的显示就会跟踪变化
/**
* 单向绑定 数据层单向给view赋值,当数据层发生修改时,view产生修改
* 1.
* 2.ObservableField方式
* 3.extends Observable方式
*
*/
class DataModel1 {
var str:String? ="default"
}
class DataModel2 {
var str: ObservableField<String> = ObservableField("default")
}
class DataModel3 : BaseObservable() {
@Bindable
var str2: String?=null
set(value) {
field = value
println("value:$value")
notifyPropertyChanged(BR.str2)
}
}
class MainActivity : AppCompatActivity() {
lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val data2 = DataModel2()
binding.datamodel2 = data2
data2.str.addOnPropertyChangedCallback(object : OnPropertyChangedCallback(){
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
//因为是单向绑定,所以当延时2秒去修改view是,这个里面就收不到
println("delay after propertyId$propertyId,${binding.text.text},str2:${data2.str.get()}")
}
})
thread {
Thread.sleep(1000)
runOnUiThread {
println("delay single bingding field change ui")
data2.str.set("cde")
}
}
thread {
Thread.sleep(2000)
println("delay run---------> ")
runOnUiThread {
binding.text.text = "fgh"
}
}
}
/**
* 记得销毁,防止内存泄漏
*/
override fun onDestroy() {
super.onDestroy()
binding.unbind()
}
}
<?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>
<variable
name="datamodel2"
type="com.seven.databinding.DataModel2" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={datamodel2.str}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
4.双向绑定
修改上面的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> <variable name="datamodel2" type="com.seven.databinding.DataModel2" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@={datamodel2.str}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
二.事件绑定
1.写一个Event处理器
package com.seven.databinding
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.databinding.*
import androidx.databinding.Observable.OnPropertyChangedCallback
import com.seven.databinding.databinding.ActivityMainBinding
import kotlin.concurrent.thread
/**
* 一.单向绑定 数据层单向给view赋值,当数据层发生修改时,view产生修改
* 1.常规变量
* 2.ObservableField方式
* 3.extends Observable方式
* 二.双向绑定
* 修改xml文件即可
* 三.事件的绑定
*
*
*/
class EventHandler(private val context: Context) {
fun onBtnClick(view: View) {
Toast.makeText(context, "view on click", Toast.LENGTH_SHORT).show()
}
}
class DataModel1 {
var str:String? ="default"
}
class DataModel2 {
var str: ObservableField<String> = ObservableField("default")
}
class DataModel3 : BaseObservable() {
@Bindable
var str2: String?=null
set(value) {
field = value
println("value:$value")
notifyPropertyChanged(BR.str2)
}
}
class MainActivity : AppCompatActivity() {
lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val data2 = DataModel2()
binding.datamodel2 = data2
data2.str.addOnPropertyChangedCallback(object : OnPropertyChangedCallback(){
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
//因为是单向绑定,所以当延时2秒去修改view是,这个里面就收不到
println("delay after propertyId$propertyId,${binding.text.text},str2:${data2.str.get()}")
}
})
thread {
Thread.sleep(1000)
runOnUiThread {
println("delay single bingding field change ui")
data2.str.set("cde")
}
}
thread {
Thread.sleep(2000)
println("delay run---------> ")
runOnUiThread {
binding.text.text = "fgh"
}
}
//设置eventHandler
val eventHandler = EventHandler(this)
binding.eventHandler = eventHandler
}
/**
* 记得销毁,防止内存泄漏
*/
override fun onDestroy() {
super.onDestroy()
binding.unbind()
}
}
2.在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>
<variable
name="datamodel2"
type="com.seven.databinding.DataModel2" />
<variable
name="eventHandler"
type="com.seven.databinding.EventHandler" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:clickable="true"
android:onClick="@{(view)->eventHandler.onBtnClick(view)}"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={datamodel2.str}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
3.带参数的Event处理器
修改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>
<variable
name="datamodel2"
type="com.seven.databinding.DataModel2" />
<variable
name="eventHandler"
type="com.seven.databinding.EventHandler" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:clickable="true"
android:onClick="@{(view)->eventHandler.onBtnClick(view)}"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={datamodel2.str,default=`ccc`}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:onClick="@{(view)->eventHandler.onBtnClick(datamodel2.str.toString(),view)}"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={datamodel2.str,default=`eeee`}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
4.添加带参数的点击事件
class EventHandler(private val context: Context) {
fun onBtnClick(view: View) {
Toast.makeText(context, "view on click", Toast.LENGTH_SHORT).show()
}
/**
* 带参数的点击事件
*/
fun onBtnClick(str:String,view: View) {
Toast.makeText(context, "view on click $str", Toast.LENGTH_SHORT).show()
}
}
三.常用绑定适配、binding的传递、一些表达式的写法
1.RecycleView搭配databinding
编写xml布局:1.需要新增一个命名空间;2.配置自定的适配器方法sob:setImage="@{item.url}"
<?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:sob="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.seven.databinding.DBBean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:src="@{item.url}"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.201"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent"
sob:setImage="@{item.url}" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent"
sob:srcCompat="@drawable/ic_launcher_background" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
2. 设置自定义holder
1.将databinding传入ViewHolder中去;2.自定义适配器加载图片
data class DBBean(val name: String, val url: String)
class Adapter: RecyclerView.Adapter<Adapter.VH>() {
val data: MutableList<DBBean> by lazy {
mutableListOf()
}
//伴生对象做加载图像是适配器
companion object {
@BindingAdapter("setImage")
@JvmStatic
fun loadImage(view: ImageView?, url: String?) {
println("loadImage->>>>>>>$url")
// view?.setImageResource(R.drawable.ic_launcher_foreground)
}
@BindingAdapter("android:src")
@JvmStatic
fun loadImage2(view: ImageView?, url: String?) {
println("loadImage2->>>>>>$url")
// view?.setImageResource(R.drawable.ic_launcher_foreground)
}
}
inner class VH(item: View, binding: ItemRvBinding): RecyclerView.ViewHolder(item)
/**
* 作为局部变量设置进去,不能作为全局变量哟,全局变量会一直是同一个
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val binding = DataBindingUtil.inflate<ItemRvBinding>(
LayoutInflater.from(parent.context), R.layout.item_rv, parent, false
)
return VH(binding.root, binding)
}
override fun onBindViewHolder(holder: VH, position: Int) {
}
override fun getItemCount(): Int =data.size
}
3.databinding的传递
<!--一级界面-->
<?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>
<variable
name="eventHandler"
type="com.seven.databinding.MainActivity3.EventHandler" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:onClick="@{(view)->eventHandler.onBtnClick(view)}"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
app:event="@{eventHandler}"
android:id="@+id/sub"
layout="@layout/sub_activity_main3"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<!--二级界面-->
<?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>
<variable
name="event"
type="com.seven.databinding.MainActivity3.EventHandler" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:onClick="@{(view)->event.onBtnClick(view)}"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
4.一些表达式的写法
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/cardview_shadow_start_color"
android:text="@{db.str}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:src="@{@drawable/ic_launcher_background}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/format_name('1','2','3')}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView4" />
四.binding结合liveData使用
1.结合liveData使用有两个好处
1.liveData不用在添加观察者;
2.数据类不需要继承自BaseObServable或者使用ObservableField
2.怎么使用?
1.新建一个ViewModel
2.在viewModel中添加liveData
3.在xml中引用这个vm
4.在Activity中给binding设置这个vm,注意需要设置lifecycleOwner!!!
class DataModel3Vm :ViewModel(){
val liveData= MutableLiveData<DataModel3>()
}
class DataModel3 {
var str2:String?=null
}
/**
* binding.dataModelVm=DataModel3Vm()这样就不会
* 为DataModel3Vm中的liveData去添加观察者,但是
* 需要给其设置lifecycleOwner,通过binding.lifecycleOwner
* 传递过去的
*
*/
val dataModel3Vm=DataModel3Vm()
binding.lifecycleOwner=this
binding.dataModelVm=dataModel3Vm
thread {
var dataModel3= DataModel3()
dataModel3.str2="abc"
dataModel3Vm.liveData.postValue(dataModel3)
Thread.sleep(2000)
dataModel3.str2="cde"
dataModel3Vm.liveData.postValue(dataModel3)
}
<?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>
<variable
name="dataModel"
type="com.seven.databinding.DataModel" />
<variable
name="dataModel2"
type="com.seven.databinding.DataModel2" />
<variable
name="dataModelVm"
type="com.seven.databinding.DataModel3Vm" />
<variable
name="eventHandler"
type="com.seven.databinding.EventHandler" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="30dp"
android:onClick="@{eventHandler.onBtnClick}"
android:text="@={dataModel.str, default=`name`}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/cardview_dark_background"
android:onClick="@{(view)->eventHandler.onBtnClick2(dataModel2.str2.toString(),view)}"
android:onLongClick="@{(view)->eventHandler.onBtnClickLong(dataModel2.str2.toString(),view)}"
android:text="@={dataModel2.str2, default=`name`}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/cardview_dark_background"
android:text="@={dataModelVm.liveData.str2}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
<include
layout="@layout/sub_main"
app:db="@{dataModel}"
android:id="@+id/sub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sub" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>