当前位置: 首页 > article >正文

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数据是否能自动应用到界面上

成功的实现了双向数据绑定  。


http://www.kler.cn/a/3531.html

相关文章:

  • 等变即插即用图像重建
  • 变频器硬件接线
  • Vue平台开发三——项目管理页面
  • 2. CSS 中的单位
  • Kafka-常见的问题解答
  • 《探秘鸿蒙Next:非结构化数据处理与模型轻量化的完美适配》
  • 中国蚁剑AntSword实战
  • 【Linux】Linux下权限的理解
  • 大公司为什么禁止SpringBoot项目用Tomcat?
  • 学习typeScript(weakMap,weakSet,set,map)
  • 动态规划---线性dp和区间dp
  • STM32外设-定时器详解
  • QT之QSysInfo(查看电脑信息)
  • 【springcloud 微服务】Spring Cloud Alibaba Nacos使用详解
  • 如何成为优秀的程序员
  • 并发粗略测算
  • 6.3 归并排序Mergesort
  • 【深度强化学习】(3) Policy Gradients 模型解析,附Pytorch完整代码
  • 51单片机8*8 LED点阵实现原理讲解
  • echarts地图不同地区设置不同的颜色
  • 手机验证发送及其验证(基于springboot+redis)保姆级
  • Docker【基本使用】
  • 你还不会递归?告别困惑,我来教你
  • 多线程(三):Thread 类的基本属性
  • USB键盘实现——字符串描述符(四)
  • JNI原理及常用方法概述