DataBinding使用介绍

启用DataBinding

  首先设置使用DataBinding,在app module的build.gradle中添加如下代码即可:

android{
    ...
    dataBinding{
        enabled = true;
    }
}

布局绑定

  在使用DataBinding时就不能按照之前的方式来编写布局文件了,布局文件的根布局应该是layout,layout中同时存放要绑定的数据及布局,如下:

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

    <data>
        <variable name="title" type="java.lang.String"/>
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{title}"/>
    </LinearLayout>
</Layout>

layout为根布局,data节点中存放数据,下面就是常见的布局文件。data中的variable标签为变量,类似于我们定义了一个变量,name为变量名,type为变量全限定类型名,包括包名。布局中通过@{}来引用这个变量的值,{}中可以是任意java表达式,但不推荐使用过多代码。

我们可以使用import语法来导入类,以及使用alias设置别名:

<data>
    <import type="java.lang.String"/>
    <import type="包名.User" alias="ExUser"/>
    <variable name="title" type="String"/>
    <variable name="User" type="ExUser"/>
    </data>

也可以使用default字段设置默认值:

<TextView android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{title, default=my_default}"/>

绑定数据

  在新建一个类似上述实例代码中的DataBinding布局文件activity_main.xml之后,Gradle会根据布局创建一个ActivityMainBinding类,我们需要获取该对象来绑定数据,使用DataBinding时,不需要再按照之前的setContentView的方式来设置布局到Activity中,应该通过DataBindingUtil#setContentView来设置,该方法会返回对应的DataBinding对象,例如我们创建的布局文件为activity_main,那么生成的就是ActivityMainBinding,我们可以通过data节点使用class关键字改变这种默认的名字:

<data class=".MainActivityBinding">
...
</data/

"."号表示当前包名,也可以使用全限定包名指定。然后是获取绑定类:

override fun onCreate(savedInstanceState: Bundle?){
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.lifecycleOwner = this
    binding.title = "Title"
}

我们先获取 DataBinding 对象,然后设置 variable 数据,lifecycleOwner 适用于管理生命周期的方法,设置后 Databinding 可以感知到 Activity 的生命周期,保证数据在可见时才会更新,不可见时不会更新数据。
如果是在 Fragment/ListView/RecyclerView 中,我们可以通过下面的方法获取 DataBinding:

binding = ActivityMainBinding.inflate(layoutInflater, null, false)

绑定普通数据

  DataBinding可以绑定普通数据对象(非Observable/LiveData),例如上述例子中绑定了一个String类型的数据。绑定普通数据我们只需要按照上述的代码设置即可。

绑定可观察数据

  绑定可观察数据意味着当数据变化时UI会一起变化,绑定可观察数据由三种方式:object,field,collections。

对单个变量的绑定:fields

  对于一些数据类,如果我们不想继承BaseObservable或者只需要其中几个字段可观察,那么可以使用这种方式来创建可观察数据:

class User{
    var age = ObservableInt()
    var name = ObservableField<String>()
}

对于基本类型和Parcelable我们可以直接使用对应的包装类:

  • ObservableBoolean
  • ObservableByte
  • ObservableChar
  • ObservableShort
  • ObservableInt
  • ObservableLong
  • ObservableFloat
  • ObservableDouble
  • ObservableParcelable

引用类型使用带有泛型参数的ObservableField类来创建

var name = ObservableField<String>()

泛型参数为数据类型

对集合的绑定:collections

我们同样可以在布局中绑定集合中的某个元素,当集合中的数据发生变化后会同步更新到UI

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <import type="androidx.databinding.ObservableMap"/>
        <import type="androidx.databinding.ObservableList"/>
        <variable name="map" type="ObservableMap&lt;String, Object"/>
        <variable name="list" type="ObservableList&lt;Object"/>
    </data>
    ...
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{String.valueOf(map.count)}" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{String.valueOf(list[0])}" />
</layout>

绑定数据到UI中

val map = ObservableArrayMap<String, Any>.apply(){put("count", 0)}
binding.map = map
val list = ObservableArrayList<Any>().apply{add(0)}
binding.list = list

对于List来说,可以直接使用[]运算符(list[0])获取对应位置的元素。而Map就很有趣了,可以使用.运算符直接获取key对应的value:map.count,这还是很有意义的,我们如果不想定义一个数据实体,可以直接使用map替代。

绑定对象:objects

这是最常用的一种方式,需要绑定的数据实体类继承BaseObservable:

class Person : BaseObservable(){
    @get:Bindable
    var country: String = ""
        set(value){
            field = value
            notifyPropertyChanged(BR.country)
        }
    
    @get:Bindable
    var sex: String = ""
        set(value){
            field = value
            notifyPropertyChanger(BR.sex)
        }
}

首先在需要支持可观察的数据上添加@get:Bindable注解,然后重写set方法,在其中调用notifyPropertyChanged方法表示更新该数据,BR是自动生成的,包名跟当前报名一致,会根据Bindable注解的变量生成对应的值;也可以调用notifyChange()方法更新所有数据。

绑定LiveData

  LiveData目前也支持数据绑定,绑定方式跟上述介绍的一样:

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable name="desc"
            type="androidx.lifecycle.MutableLiveData&lt;String>" />
    </data>
...
    <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
           android:layout_gravity="center_vertical|end"
           android:textSize="18sp"
           android:text="@{desc}" />
</layout>

我们可以直接将LiveData赋值给text,然后绑定数据:

val desc = MutableLiveData<String>()
binding.desc = desc

需要注意的是,老版本的Gradle是不支持LiveData的绑定的,需要更新到Gradle3.4及以上。另外使用LiveData有个显示,更新到LiveData数据时必须在主线程才行。

双向绑定

上述的单向绑定是数据变化后更新UI,而双向绑定是指其中任意一个变化后都会同步更新到另一个。双向绑定使用@={}表达式来实现:

    <data>
...
   <variable name="input" type="androidx.databinding.ObservableField&lt;String"/>
    </data>
...
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={input}"/>

事件绑定

事件绑定其实跟数据绑定一样,本质上就是将监听器对象绑定到UI元素上:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
    <data class="">
    ...
        <variable
            name="handler"
            type="com.demo.jetpack.MainActivity.EventHandler"/>
    </data>
    ...
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:onClick="@{handler::onToastBtnClick}"
        android:text="ToastClick"
</layout>

然后我们写好监听事件,绑定到binding中即可

class MainActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?){
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        ...
        binding.handler = EventHandler()
    }
    
    ...
    inner class EventHandler{
        fun onToastBtnClick(v: View){
            Toast.makeText(this@MainActivity, "Click", Toast.LENGTH_SHORT).show()
        }
    }
}

自定义参数绑定: BindingAdapter

目前已支持的双向绑定的列表如下:

除了上述的参数外,我们也可以使用BindingAdapter创建自定义参数

例如我们需要使用Glide加载网络图片,可以先创建一个使用了BindingAdapter注解的函数,注解中的字段为参数名,函数的第一个参数必须为目标View或者其子类,因此使用Kotlin时我们可以定义为扩展函数,这样使用很方便。

@BindingAdapter("imageUrl")
fun ImageView.loadImage(url: String)= Glide.with(this.context).load(url).into(this)

然后我们就可以在布局代码中直接使用该参数加载图片

<ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_marginTop="10dp"
    imageUrl="@{imageUrl}"
    android:layout_gravity="center_horizontal"/>

posted @ 2020-11-17 10:58  Hyeri2333  阅读(1746)  评论(0编辑  收藏  举报