zoukankan      html  css  js  c++  java
  • 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"/>
    
    
  • 相关阅读:
    剑指offer-替换空格
    Python replace方法并不改变原字符串
    退出循环break,在while、for、do...while、循环中使用break语句退出当前循环,直接执行后面的代码。
    do{}while()
    while循环
    for循环
    switch用法
    Javascript获取select下拉框选中的的值
    js关于a++ 与++a
    onload属性使用方法
  • 原文地址:https://www.cnblogs.com/hyeri/p/13993108.html
Copyright © 2011-2022 走看看