前提

之前讲过 Android 中的框架:传送门,里面有一个 MVVM 框架,在 MVVM 框架中就用到了 Data Binding,在这里我们详细说一下,Data Binding 的优势有什么呢?有下面几点:

  • 去掉了 Activity 和 Fragment 中的 UI 相关代码
  • 让 XML 变成 UI 的唯一真实来源
  • 不再需要 findViewById
  • 性能超过了手写代码,并且更加安全,不会因为 id 错而导致 crash
  • 所有的 UI 修改代码保证执行在主线程

Data Binding 使用

Data Binding 声明

我们要去使用 Data Binding 的话,就需要在 Android app 的 build.gradle 中声明使用 Data Binding。
build.gradle( app )

android {    dataBinding {        enabled = true    }}

如果使用的是 Kotlin 的话还需要添加下面代码

apply plugin: 'kotlin-kapt'kapt {    generateStubs = true}
Layout 文件改写

其实也很简单,就是在原有的 Layout 文件外再套上一层 标签

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <EditText            android:id="@+id/userName"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_margin="10dp"            android:hint="请输入用户名"            android:textSize="14sp" />        <Button            android:id="@+id/submit"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_margin="30dp"            android:text="提交" />        <TextView            android:id="@+id/result"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_margin="10dp"            android:textSize="14sp" />    LinearLayout>layout>

然后在 Activity 中修改导入 Layout 文件的部分代码,最后我们就可以直接通过 binding 去访问控件。
Activity

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);binding.result.setText("hello, world");
UI / 事件绑定

这里就要说到 xml 文件中的 标签了。我们需要在 xml 文件中定义变量,然后在 Activity 中绑定这个数据,最后直接在控件中使用该变量,就可以达到 Activity 中数据发生改变,控件中的数据也会随之改变的效果。然后因为我们不需要在外部通过 ID 去修改控件中的值了,所以为了防止这种情况的发生,我们也可以将 xml 中控件的 ID 删去。

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable            name="viewModel"            type="com.xjh.queryuser.mvvm.MVVMViewModel" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_margin="10dp"            android:hint="请输入用户名"            android:textSize="14sp" />        <Button            android:id="@+id/submit"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_margin="30dp"            android:text="提交" />        <TextView            android:id="@+id/result"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_margin="10dp"            android:text="@{viewModel.result}"            android:textSize="14sp" />    LinearLayout>layout>

然后方法我们是不是也可以这样做呢,当然是可以的,总共有两种绑定方式:

  1. 方法引用
  2. 监听器绑定
方法引用

我们只要传递进一个类,类中有我们需要调用的方法,我们通过控件的事件属性去调用这个方法就可以实现了,这样我们就可以使得外部更加干净。
事件属性如:

  • android:onClick
  • android:onLongClick
  • android:onTextChanged

下面就拿 android:onClick 为例吧:

<Button     android:id="@+id/submit"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_margin="30dp"     android:onClick="@{viewModel.getData}"     android:text="提交" />

然后我们在外部调用 setXXX() 方法,将我们需要的绑定的值传递进来就可以了。
Activity

ViewModel viewModel = new ViewModel();binging.setViewModel(viewModel);
监听器绑定

监听器绑定其实也是在类中声明一个方法,但是这个方法是需要参数传递的,我们也接着用 android:onClick 为例:

<Button    android:id="@+id/submit"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_margin="30dp"    android:onClick="@{() -> viewModel.getData(viewModel.userInput)}"    android:text="提交" />

Data Binding 源码分析

其实我们从之前的使用中可以发现,和 DataBinding 有关的主要是有下面三种:

  • android.binding 包下生成的相关代码
  • BR 类似与 R 文件,通过这个文件可以访问 XML 里面的控件,类似与 R 文件
  • XXXBinding XML 对应的自动生成的 Binding 类

实现我们可以看到我们的入口函数,我们是通过 DataBindingUtil 的 setContentView 方法去获取我们的 Binding 对象。

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,        int layoutId, @Nullable DataBindingComponent bindingComponent) {    activity.setContentView(layoutId);    View decorView = activity.getWindow().getDecorView();    ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);    return bindToAddedViews(bindingComponent, contentView, 0, layoutId);}

进入这个方法我们可以发现,他首先做的就是 Activity 的 setContentView 方法,也就是和一般的设置布局文件的操作是一样的,后面就是通过 Activity 拿到 DecorView ,再通过 DecorView 拿到对应的 ViewGroup,最后通过 bindToAddedViews 去生成对应的 Binding 类。

private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component, ViewGroup parent, int startChildren, int layoutId) {    final int endChildren = parent.getChildCount();    final int childrenAdded = endChildren - startChildren;    if (childrenAdded == 1) {        final View childView = parent.getChildAt(endChildren - 1);        return bind(component, childView, layoutId);    } else {        final View[] children = new View[childrenAdded];        for (int i = 0; i < childrenAdded; i++) {            children[i] = parent.getChildAt(i + startChildren);        }        return bind(component, children, layoutId);    }}

其实就可以发现最后返回的都是通过对应类的 bind 函数去获取的 Binding 类。然后一系列调用 bind 函数过后就到了 DataBinderMapper 的实现类 DataBinderMapperImpl 的 getDataBinder 方法中。

@Overridepublic ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {  int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);  if(localizedLayoutId > 0) {    final Object tag = view.getTag();    if(tag == null) {      throw new RuntimeException("view must have a tag");    }    switch(localizedLayoutId) {      case  LAYOUT_ACTIVITYMVVM: {        if ("layout/activity_mvvm_0".equals(tag)) {          return new ActivityMvvmBindingImpl(component, view);        }        throw new IllegalArgumentException("The tag for activity_mvvm is invalid. Received: " + tag);      }    }  }  return null;}

首先先去比对 layoutId,如果是 LAYOUT_ACTIVITYMVVM 的话,再去比较对应的 tag 是否相同,如果相同则 new 出 ActivityMvvmBindingImpl 这个实现类。

private ActivityMvvmBindingImpl(android.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {    super(bindingComponent, root, 1        , (android.widget.TextView) bindings[3]        , (android.widget.Button) bindings[2]        , (android.widget.EditText) bindings[1]        );    this.mboundView0 = (android.widget.LinearLayout) bindings[0];    this.mboundView0.setTag(null);    this.result.setTag(null);    this.submit.setTag(null);    this.userName.setTag(null);    setRootTag(root);    // listeners    mCallback1 = new com.xjh.queryuser.generated.callback.OnClickListener(this, 1);    invalidateAll();}

这里我们就能发现,我们将控件放入对应的数组中,方便之后进行调用。
整体的主要流程就是:

开始编译 处理 Layout 文件 解析表达式 Java 编译 解析依赖 找到 setter

Data Binding 特性

自动空指针检查

Data Binding 会对变量进行空判断,假如说未对给定的变量赋值的话,就会给予变量一个默认的值,比如:
{ user.name } -> null
{ user.age } -> 0

include

Data Binding 支持 include 传递变量,如:

但是 Data Binding 并不支持 direct child,例如引入的 layout 根标签为 merge

动态更新数据

现在我们去更新数据的话,就需要查询传递一个类进去,将这个类中的所有值重新进行赋值,这样其实是不好的,效率很低,所以说 Data Binding 提供了几种手段去只刷新更新的值。

BaseObservable

我们的类需要继承 BaseObservable。然后在元素的 get 和 set 方法做出修改,get 方法需要添加注解 @Bindable ,而 set 方法中需要加入刷新显示的代码 notifyPropertyChanged(BR.XXX)

package com.xjh.queryuser.mvvmimport android.databinding.BaseObservableimport android.databinding.Bindableimport android.view.Viewimport com.xjh.queryuser.bean.Accountimport com.xjh.queryuser.callback.MCallbackimport com.xjh.queryuser.BRclass MVVMViewModel : BaseObservable() {    private val mvvmModel: MVVMModel = MVVMModel()    @get:Bindable    var userInput: String? = null        set(userInput) {            field = userInput            notifyPropertyChanged(BR.userInput)        }    @get:Bindable    var result: String? = null        set(result) {            field = result            notifyPropertyChanged(BR.result)        }    fun getData(view: View) {        this.userInput?.let {            mvvmModel.getAccountData(it, object : MCallback {                override fun onSuccess(account: Account) {                    result = "用户账号:" + account.name + " | " + "用户等级:" + account.level                }                override fun onFailed() {                    result = "获取数据失败"                }            })        }    }}
Observable Fields

如果我们只有简单的几个变量需要传递的话,为这几个变量封装一个类的话,他的消耗会比较大,那我们应该怎么去做呢。其实就是是在 变量类型前加上 Observable,比如:ObservableBoolean,ObservableByte,ObservableChar … ObservableParcelable。然后修改或者获取值的话就要调用他的 get 或者 set 方法,这样的话,就可以做到动态更新变量。

Observable Collection

如果说我们需要用到一些容器类的话怎么办呢,和 Observable Fields 一样,我们只需要使用 ObservableArrayMap 和 ObservableArrayList 就可以了。

刷新

类或者 Observable 发生改变后,会在下一个帧进行绑定的时候发生改变,如果需要立即刷新的话,可以执行 executePendingBindings() 方法去进行立即执行。
Data Binding 会在本地化变量或者值域,以避免同步的问题发生,但是对于 Collection 是不会的。

生成

Data Binding 生成 Binding 类的规则有两种,一种就是之前说的那种,直接默认生成,下划线分割,大写开头,比如:

activity_main - > ActivityMainBinding

第二种方法的化就是自定义 class:

<layout>...data>layout>

这样就可以生成我们想要的名字的类,直接使用 XXX 就可以了。

RecycleView

我们如何在 RecycleView 中使用 DataBinding 功能呢,这样的话我们就可以省略掉对 viewholder 的控件赋值的一系列操作了,只需要对数据源做相对应的改变就可以实现。
首先我们新建三个布局文件,这样我们就能模拟出两个不同样式的 View 在同一个 list 中的显示了。
activity_list.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">    <data>        <variable            name="presenter"            type="com.xjh.databinding.list.ListActivity.Presenter" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        tools:context=".list.ListActivity">        <Button            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:onClick="@{presenter::onClickAddItem}"            android:text="增加" />        <Button            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:onClick="@{presenter::onClickRemoveItem}"            android:text="删除" />        <android.support.v7.widget.RecyclerView            android:id="@+id/recyclerView"            android:layout_width="match_parent"            android:layout_height="match_parent">        android.support.v7.widget.RecyclerView>    LinearLayout>layout>

item_employee.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable            name="item"            type="com.xjh.databinding.Employee" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="50dp"            android:layout_height="wrap_content"            android:gravity="left"            android:text="@{item.firstName}" />        <TextView            android:layout_width="50dp"            android:layout_height="wrap_content"            android:gravity="left"            android:text="@{item.lastName}" />    LinearLayout>layout>

item_employee_off.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable            name="item"            type="com.xjh.databinding.Employee" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="50dp"            android:layout_height="wrap_content"            android:gravity="left"            android:text="@{item.firstName}" />        <TextView            android:layout_width="50dp"            android:layout_height="wrap_content"            android:gravity="left"            android:text=" is fired" />    LinearLayout>layout>

这两个 item 的数据都是通过同一个数据源进行传递的,直接在代码中进行调用这个类的相关属性。
Employee.kt

package com.xjh.databindingimport android.databinding.BaseObservableimport android.databinding.Bindableimport android.databinding.ObservableBooleanclass Employee constructor(firstName: String, lastName: String, isFired: Boolean) : BaseObservable() {    @get:Bindable    var firstName: String? = null        set(userInput) {            field = userInput            notifyPropertyChanged(BR.firstName)        }    @get:Bindable    var lastName: String? = null        set(result) {            field = result            notifyPropertyChanged(BR.lastName)        }    var isFired = ObservableBoolean()    init {        this.firstName = firstName        this.lastName = lastName        this.isFired.set(isFired)    }}

既然用到了 RecyclerView,那首先就要去实现他的 ViewHolder,使用了 Data Binding 的 ViewHolder 和原有使用的地方有一些不同,不再是传递 View 去进行展示了,而是传递 Binding 对象去进行实现,展现的时候就直接调用 Binding 对象的 root 对象。
BindingViewHolder.kt

package com.xjh.databinding.listimport android.databinding.ViewDataBindingimport android.support.v7.widget.RecyclerViewclass BindingViewHolder<T : ViewDataBinding>(private val mBinding: T) : RecyclerView.ViewHolder(mBinding.root) {    fun getmBinding(): T {        return mBinding    }}

在不使用 DataBinding 的 Adapter 中可能需要重新一项一项的加载数据进去,但是如果使用了 Data Binding 的话,就直接在 set 更新后的对象进行就可以了,其他的功能 Data Binding 都帮我们做好了。
EmployeeAdapter.kt

package com.xjh.databinding.listimport android.content.Contextimport android.databinding.DataBindingUtilimport android.databinding.ViewDataBindingimport android.support.v7.widget.RecyclerViewimport android.view.LayoutInflaterimport android.view.ViewGroupimport com.xjh.databinding.BRimport com.xjh.databinding.Employeeimport com.xjh.databinding.Rimport java.util.*class EmployeeAdapter() : RecyclerView.Adapter<BindingViewHolder<*>>() {    companion object {        private const val ITEM_VIEW_TYPE_ON = 1        private const val ITEM_VIEW_TYPE_OFF = 2    }    private lateinit var mLayoutInflater: LayoutInflater    private var mListener: OnItemClickListener? = null    private lateinit var mEmployeeList: ArrayList<Employee>    constructor(context: Context) : this() {        mLayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater        mEmployeeList = ArrayList()    }    override fun getItemViewType(position: Int): Int {        val employee = mEmployeeList.get(position)        if (employee.isFired.get()) {            return ITEM_VIEW_TYPE_OFF        }        return ITEM_VIEW_TYPE_ON    }    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder<*> {        var binding: ViewDataBinding = if (viewType == ITEM_VIEW_TYPE_ON) {            DataBindingUtil.inflate(mLayoutInflater, R.layout.item_employee, parent, false)        } else {            DataBindingUtil.inflate(mLayoutInflater, R.layout.item_employee_off, parent, false)        }        return BindingViewHolder(binding)    }    override fun onBindViewHolder(holder: BindingViewHolder<*>, position: Int) {        val employee = mEmployeeList[position]        holder.getmBinding().setVariable(BR.item, employee)        holder.getmBinding().executePendingBindings()        holder.itemView.setOnClickListener {            mListener?.onEmployeeClick(employee)        }    }    override fun getItemCount(): Int {        return mEmployeeList.size    }    fun setListener(listener: OnItemClickListener) {        mListener = listener    }    fun addAll(employees: List<Employee>) {        mEmployeeList.addAll(employees)        notifyDataSetChanged()    }    val mRandom = Random(System.currentTimeMillis())    fun add(employee: Employee) {        val position = mRandom.nextInt(mEmployeeList.size + 1)        mEmployeeList.add(position, employee)        notifyItemInserted(position)    }    fun remove() {        if (mEmployeeList.size != 0) {            val position = mRandom.nextInt(mEmployeeList.size + 1)            mEmployeeList.removeAt(position)            notifyItemRemoved(position)        }    }    interface OnItemClickListener {        fun onEmployeeClick(employee: Employee)    }}

如何使用的话就和一般的 RecycleView 一样进行使用就可以了
ListActivity.kt

package com.xjh.databinding.listimport android.databinding.DataBindingUtilimport android.support.v7.app.AppCompatActivityimport android.os.Bundleimport android.support.v7.widget.LinearLayoutManagerimport android.view.Viewimport android.widget.Toastimport com.xjh.databinding.Employeeimport com.xjh.databinding.Rimport com.xjh.databinding.databinding.ActivityListBindingclass ListActivity : AppCompatActivity() {    lateinit var mBinding: ActivityListBinding    lateinit var mEmployeeAdapter: EmployeeAdapter    inner class Presenter {        fun onClickAddItem(view: View) {            mEmployeeAdapter.add(Employee("X", "JH", false))        }        fun onClickRemoveItem(view: View) {            mEmployeeAdapter.remove()        }    }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_list)        mBinding.presenter = Presenter()        mBinding.recyclerView.layoutManager = LinearLayoutManager(this)        mEmployeeAdapter = EmployeeAdapter(this)        mEmployeeAdapter.setListener(object : EmployeeAdapter.OnItemClickListener {            override fun onEmployeeClick(employee: Employee) {                Toast.makeText(this@ListActivity, employee.firstName, Toast.LENGTH_SHORT).show()            }        })        var list = ArrayList<Employee>()        list.add(Employee("Xiong1", "JH1", false))        list.add(Employee("Xiong2", "JH2", false))        list.add(Employee("Xiong3", "JH3", true))        list.add(Employee("Xiong4", "JH4", false))        mEmployeeAdapter.addAll(list)        mBinding.recyclerView.adapter = mEmployeeAdapter    }}

自定义属性

在 Android 的开发中我们会用到很多的自定义 View,也就会有一些自定义属性会让我们用到,而这些属性如果 Data Binding 不支持的话我们怎么去做呢?

自动 set 方法

一种就是系统中有方法去 set 这个值,但是在 XML 中没有属性可以去设置,这样的话 Data Binding 就会去自动寻找我们需要的 set 方法了。

<android.support.v4.widget.DrawerLayout    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:scrimColor ="@{@color/scrimColor}"/>

这样的话 Data Binding 就会去调用 setScrimColor 方法去改变属性的值。

Binding 方法

就是在一些属性上,他所对应的 set 方法并不是以该属性名为后缀的,使用在 DataBinding 中没有办法顺利的访问到这个方法,所以这里我们就需要对该属性和对应的 set 方法进行映射,我们只需要在一个类之前加下 @BindingMethods 注解,就可以定义这样的一个映射了。
Java 版本

@BindingMethods({    @BindingMethod(        type = ImageView.class,        attribute = "android:tint",        method = "setImageTintList"),})

Kotlin 版本

@BindingMethods(    BindingMethod(        type = ImageView::class,        attribute = "android:tint",        method = "setImageTintList"    ))

这样的话,DataBinding 就知道在 XML 文件中使用 android:tint 的属性对应的是应该调用 setImageTintList 方法了。

BindingAdapter

假如我们完全自定义一个 View,我们有自己的属性和方法,我们应该怎么做呢,这里就要使用 BindingAdapter 来实现了。
首先就要定义一个适配器,来进行方法的实现:
DemoBindingAdapter.kt

package com.xjh.databinding.expressionimport android.databinding.BindingAdapterimport android.graphics.drawable.Drawableimport android.widget.ImageViewimport com.bumptech.glide.Glideclass DemoBindingAdapter {    companion object {        @BindingAdapter("app:imageUrl", "app:placeholder")        @JvmStatic        fun loadImageFromUrl(view: ImageView, url: String, drawable: Drawable) {            Glide.with(view.context).load(url).placeholder(drawable).into(view)        }    }}

这样就可以利用这两个属性传递相关变量,然后完成对图片进行展示。
activity_expression.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">    <data>        <variable            name="employee"            type="com.xjh.databinding.Employee" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">        <ImageView            android:id="@+id/imageView"            android:layout_width="150dp"            android:layout_height="150dp"            app:imageUrl="@{employee.avatar}"            app:placeholder="@{@drawable/ic_launcher}" />    LinearLayout>layout>

ExpressionActivity.kt

package com.xjh.databinding.expressionimport android.databinding.DataBindingUtilimport android.support.v7.app.AppCompatActivityimport android.os.Bundleimport com.xjh.databinding.Employeeimport com.xjh.databinding.Rimport com.xjh.databinding.databinding.ActivityExpressionBindingclass ExpressionActivity : AppCompatActivity() {    private lateinit var binding: ActivityExpressionBinding    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        binding = DataBindingUtil.setContentView(this, R.layout.activity_expression)        val employee = Employee("X", "JH", false)        employee.avatar = "https://avatar.csdnimg.cn/D/4/D/1_xjh_shin.jpg"        binding.employee = employee    }}
BindingConversion

有些时候 Android 系统中帮我们实现了相关方法,但是传入的参数并不是符合方法要求的,这时候我们就要将属性进行转换,这里就要用到 BindingConversion 方法了。

<Viewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="@{isError ? @color/red : @color/white}" />

Java 版本

@BindingConversionpublic static ColorDrawable convertColorToDrawable(int color) {return new ColorDrawable(color);}

Kotlin 版本

companion object {    @BindingConversion    @JvmStatic    fun convertColorToDrawable(color: Int): ColorDrawable {        return ColorDrawable(color)    }}

双向绑定

假如我们想要监听输入框的值怎么办呢?这里单纯的用单向绑定就没有办法实现效果了,就要使用到 DataBinding 的双向绑定,其实现在 DataBinding 实现双向绑定其实很简单,就是将 XML 文件中的 @ 改为 @= 就实现了数据的双向绑定。

<EditTextandroid:id="@+id/userName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:hint="请输入用户名"android:text="@={viewModel.userInput}"android:textSize="14sp" />

绑定方式就是和单向绑定是一样的,这里就不再重复说明了。
但是双向绑定并不是可以支持所有属性,他主要是用于那些带有额外事件的属性,比如:text,checked,year,month,hour,rating,progress 等。

表达式链

重复表达式

当我们有很多的 View 需要用到同一个表达式运算的结果进行显示的话,我们可能需要在这些 View 的属性上重复的写同一个表达式,这样的话就导致代码较为累赘,其实我们就可以直接用之前计算好的属性给他进行赋值,这样的话就避免了多次的计算。

<?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>        <import type="android.view.View" />        <variable            name="employee"            type="com.xjh.databinding.Employee" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">        <ImageView            android:id="@+id/avatar"            android:layout_width="150dp"            android:layout_height="150dp"            android:visibility="@{employee.isFired() ? View.INVISIBLE : View.VISIBLE}"            app:imageUrl="@{employee.avatar}"            app:placeholder="@{@drawable/ic_launcher}" />        <TextView            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="@{employee.firstName}"            android:visibility="@{avatar.visibility}" />        <TextView            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="@{employee.lastName}"            android:visibility="@{avatar.visibility}" />    LinearLayout>layout>
隐式更新

如果我们需要绑定两个 View,一个 View 的样式改变依赖于另一个 View 的结果,这样的话我们就需要去监听这个 View 的值,然后手动去改变另一个 View,在 Data Binding 中我们就直接使用隐式更新就可以了。

<?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>        <import type="android.view.View" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">                <CheckBox            android:id="@+id/seeAds"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:visibility="@{seeAds.checked ? View.INVISIBLE : View.VISIBLE}" />    LinearLayout>layout>

动画

当有一个 View 要随着选择的状态进行显示的时候,如果我们直接进行刷新的话就会让整个用户的体验很差,所以要使用动画的效果来优化整个体验,而 DataBinding 已经帮我们实现了动画效果,只需要我们实现 OnRebindCallback 回调就可以进行实现了。
activity_animation.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <import type="android.view.View" />        <variable            name="presenter"            type="com.xjh.databinding.animation.AnimationActivity.Presenter" />        <variable            name="showImage"            type="boolean" />    data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">        <ImageView            android:layout_width="150dp"            android:layout_height="150dp"            android:src="@drawable/ic_launcher"            android:visibility="@{showImage ? View.VISIBLE : View.GONE}" />        <CheckBox            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:onCheckedChanged="@{presenter.onCheckedChanged}"            android:text="show Image" />    LinearLayout>layout>

AnimationActivity.kt

package com.xjh.databinding.animationimport android.databinding.DataBindingUtilimport android.databinding.OnRebindCallbackimport android.os.Buildimport android.support.v7.app.AppCompatActivityimport android.os.Bundleimport android.support.annotation.RequiresApiimport android.transition.TransitionManagerimport android.view.Viewimport android.view.ViewGroupimport com.xjh.databinding.Rimport com.xjh.databinding.databinding.ActivityAnimationBindingclass AnimationActivity : AppCompatActivity() {    private lateinit var binding: ActivityAnimationBinding    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        binding = DataBindingUtil.setContentView(this, R.layout.activity_animation)        binding.presenter = Presenter()        binding.addOnRebindCallback(object : OnRebindCallback<ActivityAnimationBinding>() {            @RequiresApi(Build.VERSION_CODES.KITKAT)            override fun onPreBind(binding: ActivityAnimationBinding?): Boolean {                val view = binding?.root as ViewGroup                TransitionManager.beginDelayedTransition(view)                return true            }        })    }    inner class Presenter {        fun onCheckedChanged(view: View, isChecked: Boolean) {            binding.showImage = isChecked        }    }}

项目GitHub地址:传送门

更多相关文章

  1. Android大厂一面面试:记录第一次跳槽经历
  2. Android(安卓)用户界面---XML布局
  3. Android面试(13): Android(安卓)中返回数据给上一个活动---start
  4. [Android]Fragment自定义动画、动画监听以及兼容性包使用
  5. Android(安卓)home键和back键区别
  6. Android数据存储之SQLite
  7. Android(安卓)Hook 机制之实战模拟
  8. Android开发学习之路--Content Provider之初体验
  9. Android(安卓)自定义SeekBar样式

随机推荐

  1. 第1个Android应用程序 Android制作简单单
  2. Android中的visibility属性的区别
  3. android开发 加载so库的解析和出现的各种
  4. android 用代码画虚线边框背景
  5. android-详解Android中的屏幕方向
  6. Android 字体颜色变化(点击)
  7. 怎么在button上写两行文字
  8. Android开发中立即停止AsyncTask和Thread
  9. Android中的预定义样式
  10. Android ListView getView()方法重复调用