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

JetPack——databing

databing是什么?

数据绑定库是一个支持库,它允许你使用声明式格式而不是编程方式将布局中的UI组件绑定到应用程序中的数据源

databing解决什么问题?

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

当数据变化时,需要先找到控件才能设置数据

class MainActivity : AppCompatActivity() {

    data class User(val name: String, val age: Int)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val user = User("AAA", 18)
        findViewById<TextView>(R.id.tv).apply {
            text = user.name
        }

}

当控件数量增大时,会出现大量样板代码

databing实现

在应用模块的build.gradle中添加配置

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

根布局改为<layout>,可在<data>中创建<variable>指定变量和类型,通过@{变量.数据}引用数据,实现数据变化自动更新到UI

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.example.demo2_databinding2.MainActivity.User" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv"
            android:text="@{user.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>
</layout>

系统会自动生成以布局为名的Binding类,如activity_main.xml生成ActivityMainBinding,通过其加载布局

class MainActivity : AppCompatActivity() {

    data class User(var name: String, var age: Int)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        /*val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)*/
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val user = User("AAA", 18)
        binding.user = user
    }
}

如果要在Fragment、ListView、RecycleView中加载布局,可以使用xxxBinding或DataBindingUtil类中的inflate()方法

databing扩展

事件处理

  • 方法引用:在编译时生成,需要与被处理事件签名一致,否则会报错
  • 监听器绑定:在事件实际触发时才会生成
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="eventHandler"
            type="com.example.demo2_databinding2.MainActivity.EventHandle" />

        <variable
            name="user"
            type="com.example.demo2_databinding2.MainActivity.User" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{eventHandler::onTextClick}"
            android:onLongClick="@{(view)->eventHandler.onTextLongClick(view,user)}"
            android:text="@{user.name}" />

    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {

    data class User(var name: String, var age: Int)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val user = User("AAA", 18)
        binding.user = user
        binding.eventHandler = EventHandle(user)
    }

    inner class EventHandle(private val user: User) {
        fun onTextClick(view: View) {  //方法引用,签名需要和onclick()一致,若需要数据可以从外面传进来
            println("onImageClick  ${user.name}")
        }

        fun onTextLongClick(
            view: View,
            user: User
        ): Boolean {  //监听器绑定,返回值需要和onLongClick()一致,可以从参数中获取数据
            println("onLongClick  ${user.name}")
            return true
        }

    }
}

include布局处理

xml中的变量可以通过namespace传递给<include>布局,如下声明my:username传递user.name

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:my="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="user"
            type="com.example.demo2_databinding2.MainActivity.User" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include
            android:id="@+id/sub_layout"
            layout="@layout/sub_item"
            my:username="@{user.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</layout>

sub_item.xml中接收变量username

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="username"
            type="String" />
    </data>

    <TextView
        android:text="@{username}"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</layout>
class MainActivity : AppCompatActivity() {

    data class User(var name: String, var age: Int)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val user = User("AAA", 18)
        binding.user = user
    }
}

可观察的数据对象

上面例子中的只有user改变时才会更新UI,但user中的域改变不会更新UI,要想要让域更新时触发UI修改,需要

  • 继承BaseObservable()
  • get()加上@Bindable注解
  • set()改变值后调用notifyPropertyChanged(BR.xxx),BR为自动生成的类(若报错则在build.gradle中添加apply plugin: ‘kotlin-kapt’)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.example.demo2_databinding2.MainActivity.User" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/sub_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />
    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {

    class User(name: String, age: Int) : BaseObservable() {

        @get:Bindable
        var name: String = name
            set(value) {
                field = value
                notifyPropertyChanged(BR.name)
            }

        var age: Int = age
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val user = User("AAA", 18)
        binding.user = user
        Thread {
            Thread.sleep(3000)
            println("change")
            user.name = "BBB"
        }.start()
    }
}

或将域声明为ObservableField<T>,此时修改应调用内部的set()方法

class User(name: String, age: Int) {

    var name = ObservableField<String>(name)

    var age = age
}

自定义属性绑定

class MainActivity : AppCompatActivity() {

    companion object {
        @BindingAdapter("android:paddingLeft")
        fun setPaddingLeft(view: View, padding: Int) {
            view.setPadding(
                padding,
                view.getPaddingTop(),
                view.getPaddingRight(),
                view.getPaddingBottom()
            )
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
    }
}

如上,使用@BindingAdapter声明android:paddingLeft,可以直接设置左边距

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="100dp"
            android:text="AAA" />
    </LinearLayout>
</layout>

自定义类型转换

如下android:background接收一个Drawable,但传递的是int

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="isError"
            type="Boolean" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <View
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@{isError ? @color/black : @color/white}" />
    </LinearLayout>
</layout>

通过@BindingConversion注解类型转换方法,其会自动调用

class MainActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.isError = false
    }
}

@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)

和ViewModel、LiveData一起使用

class UserViewModel : ViewModel() {
    val name = MutableLiveData<String>()
}

布局引用UserViewModel的name

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewmodel"
            type="com.example.demo2_databinding2.UserViewModel" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:text="@{viewmodel.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</layout>

当LiveData改变时,会更新到UI,注意需要设置binding.lifecycleOwner = this才能生效

class MainActivity : AppCompatActivity() {

    private val userViewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        binding.viewmodel = userViewModel
        binding.lifecycleOwner = this
        userViewModel.name.observe(this, Observer {
            println("${it}")
        })
        Thread{
            Thread.sleep(2000)
            userViewModel.name.postValue("AAA")
        }.start()
    }
}

双向数据绑定

如下代码针对CheckBox,使用LiveData实现数据到UI的更新,使用监听器实现UI到数据的更新

class MyViewModel : ViewModel() {
    val rememberMe = MutableLiveData<Boolean>(false)

    fun rememberMeChanged(view: CompoundButton, change: Boolean) {
        if (rememberMe.value == change) {   //数据更新触发UI更新,UI更新又会触发数据更新,故若是数据更新导致的UI更新则跳过
            return
        }
        rememberMe.postValue(change)
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewmodel"
            type="com.example.demo2_databinding2.MyViewModel" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <CheckBox
            android:id="@+id/rememberMeCheckBox"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:checked="@{viewmodel.rememberMe}"
            android:onCheckedChanged="@{(view,change)->viewmodel.rememberMeChanged(view,change)}" />
    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {

    private val myViewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        binding.viewmodel = myViewModel
        binding.lifecycleOwner = this
        myViewModel.rememberMe.observe(this, Observer {
            println("${it}")
        })
        Thread {
            Thread.sleep(2000)
            myViewModel.rememberMe.postValue(true)
        }.start()
    }
}

若使用双向数据绑定,可以省略监听器,将@{viewmodel.rememberMe}改为@={viewmodel.rememberMe},即多加一个=

class MyMutableLiveData<T>(value: T) : MutableLiveData<T>(value) {

    override fun postValue(value: T) {
        if (value != getValue()) {
            super.postValue(value)
        }
    }

    override fun setValue(value: T) {
        if (value != getValue()) {   //数据更新触发UI更新,UI更新又会触发数据更新,故若是数据更新导致的UI更新则跳过
            super.setValue(value)
        }
    }
}

class MyViewModel : ViewModel() {
    val rememberMe = MyMutableLiveData<Boolean>(false)
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewmodel"
            type="com.example.demo2_databinding2.MyViewModel" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <CheckBox
            android:id="@+id/rememberMeCheckBox"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:checked="@={viewmodel.rememberMe}" />
    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {

    private val myViewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        binding.viewmodel = myViewModel
        binding.lifecycleOwner = this
        myViewModel.rememberMe.observe(this, Observer {
            println("${it}")
        })
        Thread {
            Thread.sleep(2000)
            myViewModel.rememberMe.postValue(true)
        }.start()
    }
}

自定义双向数据绑定

在res/value下新建attrs.xml,自定义属性numberValue

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="InputNumberView">
        <attr name="numberValue" format="integer" />
    </declare-styleable>
</resources>

自定义控件布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#44000000">

    <Button
        android:id="@+id/minBtn"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="-" />

    <EditText
        android:id="@+id/numberValueEt"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:text="0" />

    <Button
        android:id="@+id/addBtn"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="+" />
</LinearLayout>

通过如下自定义控件模拟购物车加减功能

class InputNumberView : LinearLayout {

    private var listener: OnNumberValueChangeListener? = null
    private lateinit var minBtn: Button
    private lateinit var numberValueEt: TextView
    private lateinit var addBtn: Button
    private var currentNumberValue: Int = 0

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context, attrs, defStyleAttr
    ) {
        initAttr(context, attrs)
        initView()
        initEvent()
    }

    fun setNumber(value: Int) {
        post(object : Runnable {
            override fun run() {
                serCurrentNumberValue(value)
            }
        })
    }

    fun getNumber(): Int {
        return currentNumberValue
    }

    private fun initEvent() {
        minBtn.setOnClickListener(object : OnClickListener {
            override fun onClick(v: View?) {
                serCurrentNumberValue(--currentNumberValue)
            }
        })
        numberValueEt.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

            override fun afterTextChanged(s: Editable?) {
                val text = s.toString().toInt()
                if (text != currentNumberValue) {
                    currentNumberValue = text
                    listener?.onNumberValueChange(currentNumberValue)
                }
            }

        })

        addBtn.setOnClickListener(object : OnClickListener {
            override fun onClick(v: View?) {
                serCurrentNumberValue(++currentNumberValue)
            }
        })
    }

    private fun serCurrentNumberValue(value: Int) {
        currentNumberValue = value
        numberValueEt.text = String.format("%s", currentNumberValue)
        listener?.onNumberValueChange(currentNumberValue)
    }

    private fun initView() {
        LayoutInflater.from(context).inflate(R.layout.input_number_layout, this)
        minBtn = findViewById<Button>(R.id.minBtn)
        numberValueEt = findViewById<EditText>(R.id.numberValueEt)
        addBtn = findViewById<Button>(R.id.addBtn)

        numberValueEt.text = String.format("%s", currentNumberValue)
    }

    private fun initAttr(context: Context, attrs: AttributeSet?) {
        val ta: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.InputNumberView)
        currentNumberValue = ta.getInt(R.styleable.InputNumberView_numberValue, 0)
        ta.recycle()
    }

    interface OnNumberValueChangeListener {
        fun onNumberValueChange(currentNumber: Int)
    }

    fun setOnNumberChangeListener(listener: OnNumberValueChangeListener) {
        this.listener = listener
    }
}

常规用法

<?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">

    <data>

    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.example.demo2_databinding2.InputNumberView
            android:id="@+id/input_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:numberValue="10" />

    </LinearLayout>
</layout>

Goods的number更改时通知UI,UI更改时通过监听器通知number

class MainActivity : AppCompatActivity() {


    companion object {
        private const val TAG: String = "MainActivity"
    }

    class Goods(var number: Int)

    private var goods = Goods(10)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.inputNumber.setOnNumberChangeListener(object :
            InputNumberView.OnNumberValueChangeListener {
            override fun onNumberValueChange(currentNumber: Int) {
                Log.d(TAG, "onNumberChange: $currentNumber")
                goods.number = currentNumber
            }
        })
        Thread {
            Thread.sleep(2000)
            goods.number = 20
            binding.inputNumber.setNumber(goods.number)
        }.start()

    }
}

databing单向数据绑定

对属性numberValue和numberValueChangeListener新增单项绑定

object InputNumberViewBindingAdapter {

    @JvmStatic
    @BindingAdapter("numberValue")
    fun setValue(view: InputNumberView, value: Int) {
        if (view.getNumber() != value) {
            view.setNumber(value)
        }
    }

    @JvmStatic
    @BindingAdapter("numberValueChangeListener")
    fun setListener(view: InputNumberView, listener: InputNumberView.OnNumberValueChangeListener) {
        view.setOnNumberChangeListener(listener)
    }
}

使用ViewModel和LiveData处理逻辑,此时number更改时通知UI,UI更改时还是通过监听器通知number

class GoodsViewModel : ViewModel() {
    val number = MutableLiveData<Int>(10)

    fun getNumberValueChangeListener(): InputNumberView.OnNumberValueChangeListener {
        return object : InputNumberView.OnNumberValueChangeListener {
            override fun onNumberValueChange(currentNumber: Int) {
                if (currentNumber != number.value) {
                    Log.d("song", "onNumberValueChange: $currentNumber")
                    number.postValue(currentNumber)
                }
            }
        }
    }
}

修改布局让属性numberValue和NumberValueChangeListener和ViewModel绑定

<?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">

    <data>
        <variable
            name="goodsVM"
            type="com.example.demo2_databinding2.GoodsViewModel" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.example.demo2_databinding2.InputNumberView
            android:id="@+id/input_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:numberValue="@{goodsVM.number}"
            app:NumberValueChangeListener="@{goodsVM.numberValueChangeListener}"/>

    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {

    private val goodsViewModel: GoodsViewModel by viewModels()

    companion object {
        private const val TAG: String = "MainActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.goodsVM = goodsViewModel
        binding.lifecycleOwner = this

        goodsViewModel.number.observe(this, Observer {
            Log.d(TAG, "onCreate: $it")
        })

        Thread {
            Thread.sleep(2000)
            goodsViewModel.number.postValue(1000)
        }.start()
    }
}

databing双向数据绑定

新增双向数据绑定,需要注意避免死循环

  • @InverseBindingAdapter(attribute = “numberValue”)定义获取数据方法
  • @BindingAdapter(“numberValueAttrChanged”)定义何时触发数据监听
object InputNumberViewBindingAdapter {

    @JvmStatic
    @BindingAdapter("numberValue")
    fun setValue(view: InputNumberView, value: Int) {
        if (view.getNumber() != value) {
            Log.d("song", "setValue: $value")
            view.setNumber(value)
        }
    }

    @JvmStatic
    @InverseBindingAdapter(attribute = "numberValue")
    fun getValue(view: InputNumberView): Int {
        Log.d("song", "getValue: " + view.getNumber())
        return view.getNumber()
    }

    @JvmStatic
    @BindingAdapter("numberValueAttrChanged")
    fun setListeners(view: InputNumberView, inverseBindingListener: InverseBindingListener) {
        view.setOnNumberChangeListener(object : InputNumberView.OnNumberValueChangeListener {
            override fun onNumberValueChange(currentNumber: Int) {
                inverseBindingListener.onChange()
            }

        })
    }
}

ViewModel中number更改时通知UI,UI更改时由内部的InverseBindingListener通知number

class GoodsViewModel : ViewModel() {
    val number = MutableLiveData<Int>(10)
}

修改布局为双向监听

<?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">

    <data>

        <variable
            name="goodsVM"
            type="com.example.demo2_databinding2.GoodsViewModel" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.example.demo2_databinding2.InputNumberView
            android:id="@+id/input_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:numberValue="@={goodsVM.number}" />

    </LinearLayout>
</layout>

databing源码解析

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="eventHandler"
            type="com.example.demo2_databinding2.MainActivity.EventHandle" />

        <variable
            name="user"
            type="com.example.demo2_databinding2.MainActivity.User" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{eventHandler::onTextClick}"
            android:onLongClick="@{(view)->eventHandler.onTextLongClick(view,user)}"
            android:text="@{user.name}" />

    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {

    data class User(var name: String, var age: Int)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val user = User("AAA", 18)
        binding.user = user
        binding.eventHandler = EventHandle(user)
    }

    inner class EventHandle(private val user: User) {
        fun onTextClick(view: View) {  //方法引用,签名需要和onclick()一致,若需要数据可以从外面传进来
            println("onImageClick  ${user.name}")
        }

        fun onTextLongClick(
            view: View,
            user: User
        ): Boolean {  //监听器绑定,返回值需要和onLongClick()一致,可以从参数中获取数据
            println("onLongClick  ${user.name}")
            return true
        }

    }
}

以上面的布局和代码为例,系统在build目录会自动生成相关类,ActivityMainBinding类如下

  • 会为布局中每个带id的控件生成实例
  • 创建布局中的变量
  • 将inflate()和bind()转发给父类ViewDataBinding
public abstract class ActivityMainBinding extends ViewDataBinding {
  @NonNull
  public final LinearLayout main;

  @NonNull
  public final TextView tv;

  @Bindable
  protected MainActivity.EventHandle mEventHandler;

  @Bindable
  protected MainActivity.User mUser;

  protected ActivityMainBinding(Object _bindingComponent, View _root, int _localFieldCount,
      LinearLayout main, TextView tv) {
    super(_bindingComponent, _root, _localFieldCount);
    this.main = main;
    this.tv = tv;
  }

  public abstract void setEventHandler(@Nullable MainActivity.EventHandle eventHandler);

  @Nullable
  public MainActivity.EventHandle getEventHandler() {
    return mEventHandler;
  }

  public abstract void setUser(@Nullable MainActivity.User user);

  @Nullable
  public MainActivity.User getUser() {
    return mUser;
  }

  @NonNull
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot) {
    return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
  }

  @NonNull
  @Deprecated
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot, @Nullable Object component) {
    return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, root, attachToRoot, component);
  }

  @NonNull
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, DataBindingUtil.getDefaultComponent());
  }
  
  @NonNull
  @Deprecated
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable Object component) {
    return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, null, false, component);
  }

  public static ActivityMainBinding bind(@NonNull View view) {
    return bind(view, DataBindingUtil.getDefaultComponent());
  }


  @Deprecated
  public static ActivityMainBinding bind(@NonNull View view, @Nullable Object component) {
    return (ActivityMainBinding)bind(component, view, R.layout.activity_main);
  }
}

ActivityMainBinding的实现类ActivityMainBindingImpl

  • 为布局中变量实现set方法
  • 通过Flag判断当前调用的方法
public class ActivityMainBindingImpl extends ActivityMainBinding implements com.example.demo2_databinding2.generated.callback.OnLongClickListener.Listener {

    @Nullable
    private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
    @Nullable
    private static final android.util.SparseIntArray sViewsWithIds;
    static {
        sIncludes = null;
        sViewsWithIds = null;
    }
    // views
    // variables
    @Nullable
    private final android.view.View.OnLongClickListener mCallback1;
    // values
    // listeners
    private OnClickListenerImpl mEventHandlerOnTextClickAndroidViewViewOnClickListener;
    // Inverse Binding Event Handlers

    public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds));
    }
    private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 0
            , (android.widget.LinearLayout) bindings[0]
            , (android.widget.TextView) bindings[1]
            );
        this.main.setTag(null);
        this.tv.setTag(null);
        setRootTag(root);
        // listeners
        mCallback1 = new com.example.demo2_databinding2.generated.callback.OnLongClickListener(this, 1);
        invalidateAll();
    }

    @Override
    public void invalidateAll() {
        synchronized(this) {
                mDirtyFlags = 0x4L;
        }
        requestRebind();
    }

    @Override
    public boolean hasPendingBindings() {
        synchronized(this) {
            if (mDirtyFlags != 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
        if (BR.user == variableId) {
            setUser((com.example.demo2_databinding2.MainActivity.User) variable);
        }
        else if (BR.eventHandler == variableId) {
            setEventHandler((com.example.demo2_databinding2.MainActivity.EventHandle) variable);
        }
        else {
            variableSet = false;
        }
            return variableSet;
    }

    public void setUser(@Nullable com.example.demo2_databinding2.MainActivity.User User) {
        this.mUser = User;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }
    public void setEventHandler(@Nullable com.example.demo2_databinding2.MainActivity.EventHandle EventHandler) {
        this.mEventHandler = EventHandler;
        synchronized(this) {
            mDirtyFlags |= 0x2L;
        }
        notifyPropertyChanged(BR.eventHandler);
        super.requestRebind();
    }

    @Override
    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        switch (localFieldId) {
        }
        return false;
    }

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        java.lang.String userName = null;
        com.example.demo2_databinding2.MainActivity.User user = mUser;
        android.view.View.OnClickListener eventHandlerOnTextClickAndroidViewViewOnClickListener = null;
        com.example.demo2_databinding2.MainActivity.EventHandle eventHandler = mEventHandler;

        if ((dirtyFlags & 0x5L) != 0) {



                if (user != null) {
                    // read user.name
                    userName = user.getName();
                }
        }
        if ((dirtyFlags & 0x6L) != 0) {



                if (eventHandler != null) {
                    // read eventHandler::onTextClick
                    eventHandlerOnTextClickAndroidViewViewOnClickListener = (((mEventHandlerOnTextClickAndroidViewViewOnClickListener == null) ? (mEventHandlerOnTextClickAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mEventHandlerOnTextClickAndroidViewViewOnClickListener).setValue(eventHandler));
                }
        }
        // batch finished
        if ((dirtyFlags & 0x6L) != 0) {
            // api target 1

            this.tv.setOnClickListener(eventHandlerOnTextClickAndroidViewViewOnClickListener);
        }
        if ((dirtyFlags & 0x4L) != 0) {
            // api target 1

            this.tv.setOnLongClickListener(mCallback1);
        }
        if ((dirtyFlags & 0x5L) != 0) {
            // api target 1

            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv, userName);
        }
    }
    // Listener Stub Implementations
    public static class OnClickListenerImpl implements android.view.View.OnClickListener{
        private com.example.demo2_databinding2.MainActivity.EventHandle value;
        public OnClickListenerImpl setValue(com.example.demo2_databinding2.MainActivity.EventHandle value) {
            this.value = value;
            return value == null ? null : this;
        }
        @Override
        public void onClick(android.view.View arg0) {
            this.value.onTextClick(arg0); 
        }
    }
    // callback impls
    public final boolean _internalCallbackOnLongClick(int sourceId , android.view.View callbackArg_0) {
        // localize variables for thread safety
        // user
        com.example.demo2_databinding2.MainActivity.User user = mUser;
        // eventHandler
        com.example.demo2_databinding2.MainActivity.EventHandle eventHandler = mEventHandler;
        // eventHandler != null
        boolean eventHandlerJavaLangObjectNull = false;
        // eventHandler.onTextLongClick(view, user)
        boolean eventHandlerOnTextLongClickCallbackArg0User = false;



        eventHandlerJavaLangObjectNull = (eventHandler) != (null);
        if (eventHandlerJavaLangObjectNull) {




            eventHandlerOnTextLongClickCallbackArg0User = eventHandler.onTextLongClick(callbackArg_0, user);
        }
        return eventHandlerOnTextLongClickCallbackArg0User;
    }
    // dirty flag
    private  long mDirtyFlags = 0xffffffffffffffffL;
    /* flag mapping
        flag 0 (0x1L): user
        flag 1 (0x2L): eventHandler
        flag 2 (0x3L): null
    flag mapping end*/
    //end
}

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

相关文章:

  • 粒子群优化 (PSO, Particle Swarm Optimization) 算法详解及案例分析
  • C++ 强化记忆
  • 初学stm32 --- flash模仿eeprom
  • nginx 配置域名前缀访问 react 项目
  • Python语言的编程范式
  • BIO、NIO、AIO
  • Ubuntu20.04取消root账号自动登录的方法,触觉智能RK3568开发板演示
  • 基于微服务的在线校园便民服务站的设计与实现
  • 无降智o1 pro——一次特别的ChatGPT专业模式探索
  • 【C#】将信息输出到 Visual Studio 的输出窗口的几个方式
  • VSCode注释高亮(# NOTE;# TODO;# FIXME;#XXX;# HACK;# BUG)
  • 基于SpringBoot+Vue旅游管理系统的设计和实现(源码+文档+部署讲解)
  • 2025 年 Java 最新学习资料与学习路线——从零基础到高手的成长之路
  • Java 17 新特性详解与代码示例
  • 【Flink系列】1.概述
  • AWS云平台上生成式AI通过项目文档内容分析获知项目风险
  • vue集成高德地图API实现坐标拾取功能
  • 快速开发:用AI构造AI —— 打造属于个人的Copilot(M-聪明AI)
  • 为AI聊天工具添加一个知识系统 之46 蒙板程序设计(第一版):Facet六边形【意识形态:操纵】
  • 【常见BUG】Spring Boot 和 Springfox(Swagger)版本兼容问题
  • 5步打造完善的物联网IoT测试体系
  • 【机器学习实战】kaggle 欺诈检测---使用生成对抗网络(GAN)解决欺诈数据中正负样本极度不平衡问题
  • React 第二十一节 useDeferredValue 开发中用法注意事项
  • Web3 数字资产如何更有趣?解锁 Ultiland 融合 MeMe 与 RWA 的技术路径
  • Cyber Security 101-Defensive Security-Digital Forensics Fundamentals(数字取证基础知识)
  • Linux下进程间通信方式 进程间传递文件描述符——sockpair()函数