Android DataBinding 自定义View实现数据双向绑定
看不懂的可以先看看单向数据绑定:Android DataBinding数据变化时自动更新界面_皮皮高的博客-CSDN博客
然后再确定已经启动了dataBinding的情况下,按下面的顺序来:
首先创建一个自定义View:
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
class MyView(context: Context, attr: AttributeSet) : View(context, attr) {
var number = 0
set(value) {
field = value
invalidate()
}
private val onNumberChangeListenerList = ArrayList<OnNumberChangeListener>()
private val paint = Paint()
init {
setOnClickListener {
number ++
invalidate()
for (item in onNumberChangeListenerList) {
item.onChange(number)
}
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas!!
paint.color = Color.RED
canvas.drawRect(Rect(0, 0, width, height), paint)
paint.color = Color.YELLOW
paint.textSize = resources.displayMetrics.density * 20
canvas.drawText(number.toString(), width / 2f, height / 2f, paint)
}
fun addOnNumberChangeListener(listener: OnNumberChangeListener) {
onNumberChangeListenerList.add(listener)
}
fun removeOnNumberChangeListener(listener: OnNumberChangeListener) {
onNumberChangeListenerList.remove(listener)
}
interface OnNumberChangeListener {
fun onChange(number: Int)
}
}
代码很简单,就是在界面上显示一个矩形,然后里面有个文本,用来显示被点击了多少次。
接着实现双向数据绑定逻辑:
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import androidx.databinding.adapters.ListenerUtil
object ViewAdapter {
@BindingAdapter("number")
@JvmStatic fun setNumber(view: MyView, number: Int){
if (view.number == number) {
return
}
view.number = number
}
@InverseBindingAdapter(attribute = "number")
@JvmStatic fun getNumber(view: MyView): Int{
return view.number
}
@BindingAdapter("numberAttrChanged")
@JvmStatic fun setNumberListener(view : MyView, listener: InverseBindingListener?) {
val newListener = object : MyView.OnNumberChangeListener {
override fun onChange(number: Int) {
listener?.onChange()
}
}
val oldListener = ListenerUtil.trackListener(view, newListener, R.id.onNumberChangeListener)
oldListener?.apply {
view.removeOnNumberChangeListener(this)
}
view.addOnNumberChangeListener(newListener)
}
}
总的来说只要实现三个方法就行了,现在来说下每个方法的含义:
setNumber() 用于把数据设置到View上,这里还需要添加判断数据是否重复,重复了就return,不然有概率会死循环
getNumber() 用于给框架提供数据,也就是要返回用于数据双向绑定的值。
setNumberListener() 用于给框架设置数据变化监听,当监听到变化时,框架就会调用getNumber()来获取数据并应用到ViewMode上。(方法内部调用了一个ListenerUtil.trackListener()方法,这是官方的推荐的写法,用于监听器类型是集合的情况下,如果是set/get之类的那就直接set新的监听器即可。)
然后创建ViewMode:
import androidx.databinding.ObservableField
class UserObservable {
val number: ObservableField<Int> by lazy {
ObservableField<Int>()
}
}
创建Activity,并实现一些基础的显示逻辑:
import android.app.Activity
import android.os.Bundle
import android.view.View
import androidx.databinding.DataBindingUtil
import com.cc.databingdingtest.databinding.MainBinding
class MainActivity: Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: MainBinding = DataBindingUtil.setContentView(this, R.layout.main)
val userViewMode = UserObservable()
userViewMode.number.set(0)
binding.user = userViewMode
findViewById<View>(android.R.id.content).setOnClickListener {
userViewMode.number.set(1000)
}
}
}
测试一下:
先点击View,然后打断点看看ViewMode里的数据是否会变化。
成功。
然后再看看修改ViewMode数据是否能自动应用到界面上
成功的实现了双向数据绑定 。