ViewBinding与DataBinding(视图绑定与数据双向绑定)

1,401 阅读15分钟

前言:心中纵是有所盼 严寒没有减 风很冷 我的手已渐蓝

binding.webp

前言

控件查找对于Android开发来说也是一部血泪史,一直为更有效的方案进行了多种方案的研究和探讨。findViewById() 过于繁琐,强制转换不安全;butterkniife 会存在众多臃肿的全局变量的控件,已不再维护;kotlin-android-extensions 通过引入布局可以直接使用资源 id 访问 View,但是也已被废弃了。Google 推出了新的解决方案:ViewBinding 和 DataBinding

目前 Jetpack 下的 MVVM 架构模式仍然是 Android 领域下的主流发展方向,DataBinding 可以理解为一种工具,它解决了 View 和数据之间的双向绑定,减少模版代码,释放Activity/Fragment,数据绑定空安全。用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。

一、ViewBinding 视图绑定

在模块中启用 ViewBinding 之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。也就是说,视图绑定会替代 findViewById。可以更轻松地编写可与视图交互的代码。

1.1 ViewBinding用法

build.gradle 文件中开启 ViewBinding:

android {
    buildFeatures {
        viewBinding = true
    }
}

如果不需要该布局文件生成绑定类,那么可以在该布局文件的根视图中添加属性 tools:viewBindingIgnore="true"

<androidx.constraintlayout.widget.ConstraintLayout 
    tools:viewBindingIgnore="true" > 
</androidx.constraintlayout.widget.ConstraintLayout>

ViewBinding 提供了三个绑定视图的方法:

// 绑定到视图 view 上
fun <T> bind(view : View) : T

// inflater 解析布局,再绑定到 View 上
fun <T> inflate(inflater : LayoutInflater) : T

// inflater 解析布局,再绑定到 View 上
fun <T> inflate(inflater : LayoutInflater, parent : ViewGroup?, attachToParent : Boolean) : T

在 Activity 中使用 ViewBinding:

class ViewBindingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 解析ActivityViewBindingBinding
        val binding = ActivityViewBindingBinding.inflate(layoutInflater)
        val contentView = binding.root
        setContentView(contentView)

        // 通过binding对象直接获取到xml中的控件
        binding.tvName.text = "苏火火苏火火"
        binding.tvName.setOnClickListener { 
    
        }
    }
}

1.2 ViewBinding原理

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户数据显示"/>
</androidx.constraintlayout.widget.ConstraintLayout>

启用 ViewBinding 后会在 /build/generated/data_binding_base_class_source_out 目录下生成类,因为被集成进 AndroidStudio 不需要手动编译会实时编译的:

binding位置.jpg ActivityViewBindingBinding 源码如下:

public final class ActivityViewBindingBinding implements ViewBinding {
  private final ConstraintLayout rootView;
  public final AppCompatTextView tvName;

  private ActivityViewBindingBinding(ConstraintLayout rootView,
      AppCompatTextView tvName) {
    this.rootView = rootView;
    this.tvName = tvName;
  }

  @Override
  @NonNull
  public ConstraintLayout getRoot() {
    return rootView;
  }

  @NonNull
  public static ActivityViewBindingBinding inflate(LayoutInflater inflater) {
    return inflate(inflater, null, false);
  }

  @NonNull
  public static ActivityViewBindingBinding inflate(LayoutInflater inflater,
      ViewGroup parent, boolean attachToParent) {
    View root = inflater.inflate(R.layout.activity_view_binding, parent, false);
    if (attachToParent) {
      parent.addView(root);
    }
    return bind(root);
  }

  @NonNull
  public static ActivityViewBindingBinding bind(View rootView) {
    int id;
    missingId: {
      id = R.id.tv_name;
      AppCompatTextView tvName = ViewBindings.findChildViewById(rootView, id);
      if (tvName == null) {
        break missingId;
      }

      return new ActivityViewBindingBinding((ConstraintLayout) rootView, tvName);
    }
    String missingId = rootView.getResources().getResourceName(id);
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }
}

主要是在 bind() 方法中,在 rootView 中通过 findChildViewById() 找到对应控件,创建 ActivityViewBindingBinding 类。所以我们能在 ActivityViewBindingBinding 实例中通过 binding.tvName 的形式获取到该控件。

二、DataBinding 双向绑定

可以理解为 dataBinding 是一种工具,它解决了 View 和数据之间的双向绑定。它有以下几点优势:

  1. 双向数据绑定:数据发生变化后,自动通知UI刷新页面,不再需要人工绑定最新数据到 View 上。UI 改变后也能同步给数据。
  2. 减少代码模板:不再需要写 findViewById,setOnClickListener 等枯燥生硬代码,大大提高开发效率,不再需要 ButterKnife。
  3. 释放Activity/Fragment:可以在 XML 中完成数据,事件绑定工作,让 Activity/Fragment 更加关心核心业务。
  4. 数据绑定空安全:在 XML 中绑定数据它是空安全的,因为 DataBinding 在数据绑定上会自动装箱和空判断,所以大大减少了 NPE 问题。

每个使用 DataBinding 的模块都需要在 Build.gradle 文件中添加如下配置:

android {
    buildFeatures {
        dataBinding = true
    }
}

2.1 dataBinding用法

自动生成的 DataBinding 类都继承自该类 ViewDataBinding:

// 返回被绑定的视图对象
View getRoot()

// 在Binding类中设置一个value值
abstract boolean setVariable(int variableId, Object value)

// 解绑绑定
void unbind()

// 添加绑定监听器
void addOnRebindCallback(OnRebindCallback listener)

// 删除绑定监听器
void removeOnRebindCallback(OnRebindCallback listener)

// 使所有的表达式无效,并请求新的重新绑定刷新UI(重置)
abstract void invalidateAll()

ViewBinding 绑定视图的三个方法它都有:

// 绑定到视图 view 上
fun <T> bind(view : View) : T

// inflater 解析布局,再绑定到 View 上
fun <T> inflate(inflater : LayoutInflater) : T

// inflater 解析布局,再绑定到 View 上
fun <T> inflate(inflater : LayoutInflater, parent : ViewGroup?, attachToParent : Boolean) : T

还提供了 DataBindingUtil 工具类,从 Layout 中创建 DataBinding,不仅可以绑定 Activity 还可以绑定视图内容(View):

// 返回View的binding,如果不存在则创建一个binding
static <T extends ViewDataBinding> T bind(View root)

// 解析一个binding布局,并返回该布局新创建的binding
static <T extends ViewDataBinding> T inflate(LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)

// 将给定的布局设置为Activity的内容View,并返回关联的bingding。给定的布局资源不能是<merge>布局
static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId)

通过 View 视图获取 binding:

val layoutView = LayoutInflater.from(this).inflate(R.layout.activity_data_binding, null)
val binding = DataBindingUtil.bind<ActivityDataBindingBinding>(layoutView)

Fragment 中使用 DataBindingUtil

class HomeFragment : Fragment {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val binding = DataBindingUtil.inflate<FragmentHomeBinding>(inflater, R.layout.fragment_home, container, false)
        return binding.root
    }
}

Activity 中使用 DataBindingUtil

class DataBindingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>(this, R.layout.activity_data_binding)
}

2.2 DataBinding在xml中使用

在布局文件中,选中根布局的标签,按住Alt+回车,点击Convert to data binding layout,转换成 dataBinding 布局:

image.png 转换后的布局,最外层变成layout标签,里面包裹了常规标签和常规布局元素。data 标签下定义参数,可以导入相关包或者类。

  • layout:布局根节点必须是 <layout> . 同时 layout 只能包含一个 View 标签,不能直接包含<merge>
  • data<data>标签的内容即 DataBinding 的数据,data 标签只能存在一个。元素用来声明在此布局使用到的变量和变量类型,以及类引用。
  • variable:声明布局中的一个变量。
  • name:变量名称。
  • type: 变量类型。

不是所有属性都能用 DataBinding 来绑定的。如果一个属性 xxx,在该类中有 setXxx 方法,我们才能使用 DataBinding 来绑定。比如android:layout_weightandroid:layout_height 就不能使用 DataBinding 来绑定,而 android:paddingLeftandroid:textSize 都是可以的。

<?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="com.sum.framework.utils.DisplayUtil" />
        <!--导入参数-->
        <variable
                name="student"
                type="com.sum.common.model.Student" />

        <variable
                name="activity"
                type="com.sum.demo.viewbinding.DataBindingActivity" />
    </data>

        <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{student.stuName}" />
</layout>

android:text="@{user.stuName} 等于 tvName.text = user.getStuName() 这样就将数据和 view 相关联了。

注意:xml 中的 @{} 只做赋值或者简单的三元运算或者判空等不要做复杂运算,否则违背解耦原则。

2.3 数据单向绑定

BaseObservable

如何实现 view 和数据的绑定呢?我们只需要让实体类 model 继承 BaseObservable,当字段发生变化,只需要调用 notifyPropertyChanged() 就可以让 UI 刷新。

data class Student(var name: String) : BaseObservable() {

    //当使用name字段发生变更后,若想UI自动刷新,
    //要求方法名必须以get开头并且标记Bindable注解
    //注解才会自动在build目录BR类中生成entry
    @Bindable
    fun getStuName(): String {
        return name
    }

    fun setStuName(name: String) {
        this.name = name
        // 手动刷新
        notifyPropertyChanged(BR.stuName)
    }
}

当使用 name 字段发生变更后,若要UI自动刷新:

  1. 数据模型继承 BaseObservable
  2. 要求获取数据方法名必须以 get 开头并且标记 @Bindable 注解;
  3. 设置数据方法必须以 set 开头然后调用 notify() 函数既可以刷新视图。
class DataBindingActivity : AppCompatActivity() {
    var mStudent: Student? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 1.解析布局
        val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>(this, R.layout.activity_data_binding)
        // 2.构建数据
        mStudent = Student("姓名")
        // 3.给binding设置student数据
        binding.student = mStudent
    }
    ```
    //更新Student数据
    fun updateStudentName() {
        mStudent?.setStuName("苏火火~")
    }
}

通过 binding.student = mStudent 给 binding 设置数据,点击【更新Name数据】按钮,调用 updateStudentName() 更新 Name 数据,如下图:

单向绑定.gif

BR 类是 BaseObservable 子类中由 @Bindable 注解修饰的函数生成;BR 类生成位置在 build/generated/source/kapt/debug/com/sum/common/BR.java

如果你无法继承可以通过实现接口方式也可以,查看 BaseObservable 实现的接口自己实现即可。

除了 BaseObservable 还有很多其他的都可以拿来使用,都可以让一个对象一条普通的数据成为可观察的数据,只要它发生了变化,与之相关联的观察者就能监听到,从而做出刷新的动作:

BaseObservable,
ObservableBoolean,
ObservableByte,
ObservableChar,
ObservableDouble,
ObservableField,
ObservableFloat,
ObservableInt,
ObservableLong,
ObservableParcelable,
ObservableShort

2.4 数据双向绑定

双向绑定就是当数据改变时同时使视图刷新,而视图改变时也可以同时改变数据。通过表达式使用@=表达式就可以视图刷新的时候自动更新数据:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={student.stuName}"/>

比单向绑定多了个=,stuName 数据变更自动更新 EditText,EditText 输入数据也能自动更新 student 中的 stuName 数据。

双向绑定.gif

2.5 事件绑定

事件绑定也是一种变量绑定,只不过设置的变量是回调接口而已:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
                name="activity"
                type="com.sum.demo.viewbinding.DataBindingActivity" />
    </data>

    <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/tv_update_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="更新Name数据"
            android:onClick="@{()->activity.updateStudentName()}" />
</layout>

这里简单模拟点击时调用 Activity 中的方法。你也可以在 onClick 中调用管理类,ViewModel 等方法。

2.6 BindingAdapter

DataBinding 提供了 BindingAdapter 这个注解用于支持自定义属性或者修改原有属性。注解值可以是已有的 xml 属性,例如 android:srcandroid:text 等,也可以自定义属性然后在 xml 中使用。

@BindingAdapter(value = ["imageUrl", "radius"], requireAll = false)
fun setImageUrl(view: ImageView, imageUrl: String, radius: Int) {
    if (radius > 0) {
        view.setUrlRound(imageUrl, radius)
    } else {
        view.setUrl(imageUrl)
    }
}
  1. 需要定义成 public static,使用 @BindingAdapter注解 并标记;
  2. value 中的字段根据需要添加,与方法参数一一对应;
  3. requireAll 代表是否以下两个属性在 xml 中同时使用才会调用到该方法,为 false 的话,只要有一个属性被使用就能调用该方法。
<!--在布局文件中如下使用,便能实现图片圆角和URL绑定功能-->
<ImageView
    android:layout_width="match_parent"
    android:layout_height="@dimen/dp_120"
    app:imageUrl="@{activity.getImageUrl()}"
    app:radius="@{DisplayUtil.dpToPx(10)}" />

当 ImageView 控件的 url 属性值发生变化时,dataBinding 就会通过 setImageUrl() 方法,动态改变 ImageView 的相关属性。

BindingAdapter 更为强大的一点是可以覆盖 Android 原生控件属性

@BindingAdapter("android:text")
fun setText(view: Button, text: String) {
    view.text = "$text-改变原生控件属性"
}
<Button
   android:id="@+id/btn_text"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text='@{"button"}' />

当所有 Button 调用 setText() 方法时,数据都会变成$text-改变原生控件属性

dataBinding注意事项

  1. 在列表中谨慎使用,因为 dataBingding 数据绑定是延迟一帧的,如果在列表的 ItemView 的宽高需要计算后才能正确显示,或者显隐藏控制,不建议使用 Databinding 操作,否则会看到列表 ItemView 列表的撑开动画。
class ViewDataBinding {
    protected void requestRebind() {
        synchronized (this) {
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }
}

因为数据绑定或者数据变更刷新 UI 的时候,都会通过 requestRebind() 方法,然后调用 mChoreographer.postFrameCallback(mFrameCallback),最后通过 mUIThreadHandler.post(mRebindRunnable)等待下一次屏幕绘制的时候才会执行绑定工作,所以我们在显隐藏控制计算宽高的时候都会有个不及时的问题,但是一般情况下我们进行数据的绑定都是没有问题的。

  1. 对于以前不方便调试的问题,比如 ActivityDataBindingBinding.xml 布局,在编译时会生成ActivityDataBindingBindingImpl.class 我们可以搜索类 debug 跟进解决问题。

2.7 Array、List、Set、Map

DataBinding 也支持布局文件中使用,数组、list、set和Map,且在布局文件中都可以通过 list[index] 的形式获取元素,因为 xml 的特性,在声明 List<String> 的数据类型时,需要使用尖括号的转义字符:

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

    <data>
        <import type="java.util.List" />
        <import type="java.util.Set" />
        <import type="java.util.Map" />
        <import type="android.util.SparseArray" />
        <variable
            name="array"
            type="String[]" />
        <!--List<String>泛型的尖括号需要转义,否则会在编译阶段报错-->
        <variable
            name="list"
            type="List&lt;String&gt;" />
        <variable
            name="map"
            type="Map&lt;String, String&gt;" />
        <variable
            name="set"
            type="Set&lt;String&gt;" />
        <variable
            name="sparse"
            type="SparseArray&lt;String&gt;" />
        <variable
            name="index"
            type="int" />
        <variable
            name="key"
            type="String" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:text="@{array[1]}" />
        <TextView
            android:text="@{sparse[index]}" />
        <TextView
            android:text="@{list[index]}" />
        <TextView
            android:text="@{map[key]}" />
        <TextView
            android:text="@{map['苏火火']}" />
        <TextView
            android:text='@{set.contains("xxx")?"苏火火":key}' />
    </LinearLayout>

2.8 运算符

DataBinding 在 xml 中数据绑定支持的语法表达式也是非常丰富的,支持在布局文件中使用以下运算符,表达式和关键字

  • 算术:+ - * / %
  • 字符串链接:+
  • 逻辑:&& ||
  • 二元:& | ^
  • 一元:+ — ! ~
  • 移位:>> >>> <<
  • 比较:== > < >= <=(<需要被转义成 < >需要被转义为 >)
  • instanceof
  • 分组:()
  • 字面量:character, String, numeric, null
  • 类型转换 Cast,
  • 方法调用
  • Field 访问
  • Array 访问 []
  • 三元:?

目前不支持以下操作

  • this
  • super
  • new
  • 显示泛型调用

2.9 资源引用

<data>
    <variable
            name="isLeft"
            type="boolean" />
</data>

<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="@{isLeft ? @dimen/dp_20:@dimen/dp_12}"
        android:textAllCaps="false" />

注意:控件的宽高不能使用 DataBinding 动态绑定。

避免空指针异常

  1. DataBinding 也会自动帮助我们避免空指针异常 例如,如果 "@{student.age}"studentnull 的话,student.age 会被赋值为默认值 null,而不会抛出空指针异常。

  2. 空合并运算符  ??  会取第一个不为 null 的值作为返回值:

<TextView
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="@{student.stuName ?? student.age}" />
//等价于
<TextView
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="@{student.stuName != null ? student.stuName : student.age}" />

2.10 include 和 viewStub

include

对于 include 的布局文件,是支持通过 dataBinding 来进行数据绑定,需要在 include 的布局中使用 layout 标签,声明需要使用到的参数。

view_include.xml

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

        <import type="com.sum.common.model.Student" />

        <variable
                name="student"
                type="Student" />
    </data>

    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="20dp"
            android:text="@{student.stuName}" />
</layout>

在主布局文件中将相应的参数传递给 include 布局,从而使两个布局文件之间共享同一个参数:

<?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:bind="http://schemas.android.com/tools">

    <data>
        <!--导入参数-->
        <variable
                name="student"
                type="com.sum.common.model.Student" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        
        <include
                layout="@layout/layout_binding_include"
                bind:student="@{student}" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

viewStub

dataBinding 也支持 ViewStub 布局,在主布局文件中引用 viewStub 布局:

view_stub.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:bind="http://schemas.android.com/tools">

    <data>
        <!--导入参数-->
        <variable
                name="student"
                type="com.sum.common.model.Student" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        
        <ViewStub
                layout="@layout/layout_binding_include"
                bind:student="@{student}"
                android:layout="@layout/layout_binding_viewstub" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

如果需要为 ViewStub 绑定变量值,则 ViewStub 文件一样要使用 layout 标签进行布局,主布局文件使用自定义的 bind 命名空间将参数传递给 ViewStub,获取到 ViewStub 对象:

val viewStubBinding = binding.layoutBindingViewstub.viewStub?.inflate()

如果在 xml 中没有使用 bind:student="@{student}" 对 ViewStub 进行数据绑定,则可以在 ViewStub Inflate() 时再绑定,此时需要为 ViewStub 设置 setOnInflateListener 回调函数,在回调函数中进行数据绑定:

val viewStub = binding.layoutBindingViewstub.viewStub
viewStub?.setOnInflateListener { stub, inflated -> //如果在 xml 中没有使用 bind:student="@{student}" 对 viewStub 进行数据绑定
    //那么可以在此处进行手动绑定
    val viewStubBinding: LayoutBindingViewstubBinding? = DataBindingUtil.bind(stub)
    viewStubBinding?.student = mStudent
}

三、结合LiveData、ViewModel使用

使用 DataBinding 实现单向双向绑定时,model 必须要继承 BaseObservable 或者使用 ObservableField,还要添加 @Bindable 注解、调用 notifyPropertyChanged() 手动刷新,这样做代码入侵性比较强。

上一篇文章中介绍 LiveData,它实现数据驱动的,它包裹的 Student 并没有继承 BaseObservable。LiveData 可以代替 BaseObservable,ObservableField等,并且它还自动具备生命周期管理。不用侵入式的修改数据实体类了,直接使用 LiveData,同样支持 DataBinding 的数据绑定。

在 Activity 中实现:

    // 使用LiveData作为数据绑定来源,要设置LifecycleOwner
    binding.lifecycleOwner = this
    val viewModel = ViewModelProvider(this)[MainViewModel::class.java]
    // 给布局设置ViewModel参数
    binding.vm = viewModel

布局文件增加 ViewModel 参数:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
                name="vm"
                type="com.sum.demo.viewmodel.MainViewModel" />
    </data>

    <androidx.appcompat.widget.AppCompatTextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{vm.userLiveData}" />
</layout>
  1. 使用 LiveData 作为数据绑定来源,要给 Binding 设置 LifecycleOwner;
  2. 布局文件增加 ViewModel 参数,并且绑定数据;
  3. 给 Binding 布局设置 ViewModel 参数。

四、总结

相对于 findViewById、butterknife、kotlin-extension 而言,ViewBinding 和 DataBinding 都生成可用于直接引用视图的绑定类,能兼容 Kotlin、Java,使用更方便,减少Null安全,类型安全等问题。(无效 View ID 而引发 Null 异常,控件类型转换异常等)

但是它们也增加编译时间,因为 ViwBinding 是在编译时生成的,会产生额外的类,增加包的体积;include 的布局文件无法直接引用,需要给 include 给 id 值,然后间接引用。

ViewBinding与DataBinding的区别

  1. ViewBinding 旨在处理更简单的用例,最主要的表现是绑定后不用 findViewById 了,但不能进行数据绑定;不需要对原有的 XML 文件进行入侵。数据的绑定还是需要在宿主当中完成,简单易用。
  2. 编译效率来说,viewBinding 比 DataBinding 更快一些;因为 viewBinding 不需要处理注释,DataBinding 在编译阶段要生成很多类文件,耗时更长;
  3. 相对来说 DataBinding 功能更加强大,支持 布局变量或布局表达式,可以通过数据绑定的形式实时声明动态 UI 内容。

如果我们需要数据和 view 之间有联动绑定效果则可以使用 DataBinding,如果不需要数据绑定则选择 ViewBinding。整体来说 ViewBinding 与 DataBinding 的优点还是远远大于缺点的,所以推荐使用。

源码地址: github.com/suming77/Su…

点关注,不迷路

好了各位,以上就是这篇文章的全部内容了,很感谢您阅读这篇文章。我是suming,感谢支持和认可,您的点赞就是我创作的最大动力。山水有相逢,我们下篇文章见!

本人水平有限,文章难免会有错误,请批评指正,不胜感激 !

参考链接

希望我们能成为朋友,在 Github掘金 上一起分享知识,一起共勉!Keep Moving!