Android 之 Data Binding

595 阅读2分钟

运行环境

  • Android Studio 3.5
  • JDK 1.8.0_202
  • Kotlin

搭建环境(Build Environment)

申请 Kotlin-Kapt 插件, 开启数据绑定(Data Binding)

app模块build.gradle文件中添加 dataBinding 元素

//...
apply plugin: 'Kotlin-Kapt' //申请 Kotlin-Kapt 插件

android{
    //...
    dataBinding{
        enabled = true // 打开数据绑定
    }
}

数据对象(Data Object)

class MainData(private val context: Context){
    var name:String = "World"

    fun onClickMethodReference(view:View){
        Toast.makeText(context,
            "Method Reference: android:onClick=\"@{mainData::onClickMethodReference}\"",
            Toast.LENGTH_LONG).show()
    }

    fun onClickListenerBindings(){
        Toast.makeText(context,
            "Listener Bindings: android:onClick=\"@{()->mainData.onClickListenerBindings()}\"",
            Toast.LENGTH_LONG).show()
    }

    fun onClickListenerBindingsWithParamer(view:View, context:Context){
        Toast.makeText(context,
            "Listener Bindings: android:onClick=\"@{(view)->mainData.onClickListenerBindings(view, context)}\"",
            Toast.LENGTH_LONG).show()
    }
}

绑定数据(Binding Data)

为每个布局文件生成一个绑定类(binding class),默认的,绑定类名由布局文件名加Binding后缀并大写首字母构成

class MainActivity : AppCompatActivity() {

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

...

绑定适配器(Binding Adapters)

布局(Layout)属性适配器

object BindingAdapters{

    /**
    * Provide custom logic 
    * https://developer.android.google.cn/topic/libraries/data-binding/binding-adapters#custom-logic
    */
    @BindingAdapter("android:text")
    @JvmStatic fun setText(view:TextView, text: String){
        Log.d("---------", text)
        view.text = text
    }
}

MainActivity.kt

package com.buyi.jetpack

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.databinding.*
import com.buyi.jetpack.data.ObservableObjectUser
import com.buyi.jetpack.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

class MainData(private val context: Context){
    var name:String = "World"

    fun onClickMethodReference(view:View){
        Toast.makeText(context,
            "Method Reference: android:onClick=\"@{mainData::onClickMethodReference}\"",
            Toast.LENGTH_LONG).show()
    }

    fun onClickListenerBindings(){
        Toast.makeText(context,
            "Listener Bindings: android:onClick=\"@{()->mainData.onClickListenerBindings()}\"",
            Toast.LENGTH_LONG).show()
    }

    fun onClickListenerBindingsWithParamer(view:View, context:Context){
        Toast.makeText(context,
            "Listener Bindings: android:onClick=\"@{(view)->mainData.onClickListenerBindings(view, context)}\"",
            Toast.LENGTH_LONG).show()
    }
}


class MainDataObservable:BaseObservable(){

    var year:ObservableInt = ObservableInt(2019)

    // Kotlin 中 Any 相当于 Java 中的 Object
    var time:ObservableList<Any> = ObservableArrayList<Any>().apply {
        add("20")
        add("40")
    }

    fun onClickObservabelFields(context: Context){
        year.set(2020)
        Toast.makeText(context, "Observable Fields: Change year to 2020 success!!!",
            Toast.LENGTH_LONG).show()
    }

    fun onClickObservableCollections(context: Context){
        time[1] = "44"
        Toast.makeText(context, "Observable Collections: Change time to 20:44 success!!!",
            Toast.LENGTH_LONG).show()
    }


    @get:Bindable
    var greeting:String = "Hello World!"
    set(value) {
        field = value
        notifyPropertyChanged(BR.greeting)
    }

    fun changeGreeting(context: Context){
        greeting = "Hello Buyi!"
        Toast.makeText(context, "Observable Objects",
            Toast.LENGTH_LONG).show()
    }
}

activity_main.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="user"
            type="com.buyi.jetpack.data.ObservableObjectUser" />
        <variable
            name="mainDataObservable"
            type="com.buyi.jetpack.MainDataObservable" />
        <variable
            name="mainData"
            type="com.buyi.jetpack.MainData" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Layouts and binding expressions"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:onClick="@{mainData::onClickMethodReference}"
            android:text="Method Reference"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

        <Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:onClick="@{()->mainData.onClickListenerBindings()}"
            android:text="Listener Bindings"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button2" />

        <Button
            android:id="@+id/button4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:onClick="@{(view)->mainData.onClickListenerBindingsWithParamer(view, context)}"
            android:text="Listener Bindings With Parameter"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button3" />

        <!--        Work with observable data objects-->
        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            android:text="Work with observable data objects"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button4" />

        <Button
            android:id="@+id/button5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:onClick="@{()->mainDataObservable.onClickObservabelFields(context)}"
            android:text="Observable Fields"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2" />

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:text="@{String.valueOf(mainDataObservable.year)}"
            app:layout_constraintBottom_toBottomOf="@+id/button5"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@+id/button5" />

        <Button
            android:id="@+id/button7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:onClick="@{()->mainDataObservable.onClickObservableCollections(context)}"
            android:text="Observable Collections"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button5" />

        <TextView
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:text="@{mainDataObservable.time[0] + ` : ` + mainDataObservable.time[1]}"
            app:layout_constraintBottom_toBottomOf="@+id/button7"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@+id/button7" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:onClick="@{()->mainDataObservable.changeGreeting(context)}"
            android:text="Observable Objects"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button7" />

        <TextView
            android:id="@+id/hw"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:text="@{mainDataObservable.greeting}"
            app:layout_constraintBaseline_toBaselineOf="@+id/button"
            app:layout_constraintEnd_toEndOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>