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

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

前言

控件查找对于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:

kotlin 复制代码
android {
    buildFeatures {
        viewBinding = true
    }
}

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

xml 复制代码
<androidx.constraintlayout.widget.ConstraintLayout 
    tools:viewBindingIgnore="true" > 
</androidx.constraintlayout.widget.ConstraintLayout>

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

kotlin 复制代码
// 绑定到视图 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:

kotlin 复制代码
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 复制代码
<?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 不需要手动编译会实时编译的: ActivityViewBindingBinding 源码如下:

java 复制代码
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 文件中添加如下配置:

ini 复制代码
android {
    buildFeatures {
        dataBinding = true
    }
}

2.1 dataBinding用法

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

kotlin 复制代码
// 返回被绑定的视图对象
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 绑定视图的三个方法它都有:

kotlin 复制代码
// 绑定到视图 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):

java 复制代码
// 返回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:

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

Fragment 中使用 DataBindingUtil

kotlin 复制代码
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

kotlin 复制代码
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 布局:

转换后的布局,最外层变成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 复制代码
<?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 刷新。

kotlin 复制代码
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() 函数既可以刷新视图。
kotlin 复制代码
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 数据,如下图:

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

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

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

java 复制代码
BaseObservable,
ObservableBoolean,
ObservableByte,
ObservableChar,
ObservableDouble,
ObservableField,
ObservableFloat,
ObservableInt,
ObservableLong,
ObservableParcelable,
ObservableShort

2.4 数据双向绑定

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

xml 复制代码
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={student.stuName}"/>

双向绑定,数据变更自动更新 UI,UI 变更了也能自动更新 student 中的 nam 数据,比单向绑定多了个=

2.5 事件绑定

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

xml 复制代码
<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 中使用。

kotlin 复制代码
@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 的话,只要有一个属性被使用就能调用该方法。
xml 复制代码
<!--在布局文件中如下使用,便能实现图片圆角和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 原生控件属性

kotlin 复制代码
@BindingAdapter("android:text")
fun setText(view: Button, text: String) {
    view.text = "$text-改变原生控件属性"
}
xml 复制代码
<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 列表的撑开动画。
java 复制代码
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 复制代码
<?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" />
        <variable
            name="user"
            type="`com.sum.common.model.User" />
    </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 资源引用

xml 复制代码
<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 的值作为返回值:

xml 复制代码
<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 复制代码
<?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 复制代码
<?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 布局:

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 对象:

ini 复制代码
val viewStubBinding = binding.layoutBindingViewstub.viewStub?.inflate()

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

kotlin 复制代码
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 中实现:

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

布局文件增加 ViewModel 参数:

xml 复制代码
<?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!

相关推荐
开发者阿伟13 小时前
Android Jetpack DataBinding源码解析与实践
android·android jetpack
alexhilton6 天前
Android技巧:学习使用GridLayout
android·kotlin·android jetpack
Wgllss14 天前
轻松搞定Android蓝牙打印机,双屏异显及副屏分辨率适配解决办法
android·架构·android jetpack
alexhilton20 天前
群星闪耀的大前端开发
android·kotlin·android jetpack
一航jason1 个月前
Android Jetpack Compose 现有Java老项目集成使用compose开发
android·java·android jetpack
帅次1 个月前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
IAM四十二1 个月前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
Wgllss1 个月前
那些大厂架构师是怎样封装网络请求的?
android·架构·android jetpack
x0242 个月前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton2 个月前
深入理解观察者模式
android·kotlin·android jetpack