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

news2024/11/18 8:30:25

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

在这里插入图片描述

前言

控件查找对于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 不需要手动编译会实时编译的:
在这里插入图片描述

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 布局:
在这里插入图片描述
转换后的布局,最外层变成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 数据,如下图:
在这里插入图片描述

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 数据。
在这里插入图片描述

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 的优点还是远远大于缺点的,所以推荐使用。

源码地址: https://github.com/suming77/SumTea_Android

点关注,不迷路

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

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

参考链接

  • ViewBinding官网

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1322706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【【UART 传输数据实验】】

UART 传输数据实验 通信方式在日常的应用中一般分为串行通信&#xff08;serial communication&#xff09;和并行通信&#xff08;parallel communication&#xff09;。 我们再来了解下串行通信的特点。串行通信是指数据在一条数据线上&#xff0c;一比特接一比特地按顺序传…

随笔记录-springboot_LoggingApplicationListener+LogbackLoggingSystem

环境&#xff1a;springboot-2.3.1 加载日志监听器初始化日志框架 SpringApplication#prepareEnvironment SpringApplicationRunListeners#environmentPrepared EventPublishingRunListener#environmentPrepared SimpleApplicationEventMulticaster#multicastEvent(Applicati…

字符设备驱动的加载与卸载

一. 简介 前面几篇文章编写了 字符设备驱动模块加载与卸载框架代码&#xff0c;设置了开发板启动方式。文章地址如下&#xff1a; 字符设备驱动框架的编写-CSDN博客 字符设备驱动模块的编译-CSDN博客 字符设备驱动的加载与卸载前工作-CSDN博客 本文学习如何加载与卸载驱动…

windows10 固定电脑IP地址操作说明

windows10 固定电脑IP地址操作说明 一、无线网络的IP地址设置方法二、有线网络的IP地址设置方法 本文主要介绍&#xff0c;windows10操作系统下&#xff0c;不同的网络类型&#xff0c;对应的电脑IP地址设置方法。 一、无线网络的IP地址设置方法 在桌面右下角&#xff0c;点击…

st.pp.normalize_total(data) # NOTE: no log1p

这段代码在使用 stlearn 包中的 st.pp.normalize_total 函数对数据进行总体计数标准化。标准化后&#xff0c;每个细胞的总计数都将等于 median(total_counts)。 NOTE: no log1p 这行注释表示在标准化后&#xff0c;数据不会进行 log1p 转换。log1p 转换将每个计数值增加 1&a…

Java如何创建线程?到底有几种方式创建线程?

文章目录 继承Thread类实现Runnable接口实现Callable接口匿名内部类形式的线程创建实现接口 VS 继承Thread到底有几种创建线程的方式&#xff1f;参考 继承Thread类 定义一个线程类&#xff0c;重写实现run方法(因为 Thread类也实现了 Runable接口)&#xff0c;在其中定义线程…

Pytorch神经网络的参数管理

目录 一、参数访问 1、目标参数 2、一次性访问所有参数 3、从嵌套块收集参数 二、参数初始化 1、内置初始化 2、自定义初始化 3、参数绑定 在选择了架构并设置了超参数后&#xff0c;我们就进入了训练阶段。此时&#xff0c;我们的目标是找到使损失函数最小化的模型参数…

矩阵式键盘实现的电子密码锁

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit P14P1^4; //将P14位定义为P1.4引脚 sbit P15P1^5; //将P15位定义为P1.5引脚 sbit P16P1^6; //将P16位定义为P1.6引脚 sbit P17P1^7; //将P17位定义为P1.7引脚 sbit soundP3^7; //将so…

新媒体宣传与广州迅腾文化传播有限公司:品牌知名度提升的新动力

新媒体宣传与广州迅腾文化传播有限公司&#xff1a;品牌知名度提升的新动力 随着科技的飞速发展和互联网的普及&#xff0c;新媒体已经成为现代社会不可或缺的一部分。新媒体平台具有传播速度快、覆盖面广的特点&#xff0c;为企业品牌宣传提供了前所未有的机会。广州迅腾文化…

黑马点评07 秒杀优化 加阻塞队列

实战篇-22.秒杀优化-异步秒杀思路_哔哩哔哩_bilibili 1.流程回顾 1.1超卖问题 判断秒杀时间&#xff0c;加乐观锁&#xff08;比较标记/版本&#xff09;&#xff0c;检查库存是否大于0 1.2一人一单问题 看看数据库里有没有这个这个人下的订单&#xff1a; 1.单机模式中…

自动化测试 (五) 读写64位操作系统的注册表

自动化测试经常需要修改注册表 很多系统的设置&#xff08;比如&#xff1a;IE的设置&#xff09;都是存在注册表中。 桌面应用程序的设置也是存在注册表中。 所以做自动化测试的时候&#xff0c;经常需要去修改注册表 Windows注册表简介 注册表编辑器在 C:\Windows\regedit…

第二百一十五回 如何创建单例模式

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"沉浸式状态样相关的内容&#xff0c;本章回中将介绍 如何创建单例模式.闲话休提&#xff0c;让我们一起Talk Flutter吧。 …

@KafkaListener 注解配置多个 topic

见如下示例 主要见 KafkaListener 中 topics 属性的配置 其中 ${xxxx.topic1} 为从springBoot 配置文件中读取的属性值 KafkaListener(topics {"${xxxx.topic1}", "${xxxx.topic2}"}, groupId "${xxxx.groupId}",containerFactory "xxx…

易点易动打通OA系统,实现固定资产高效管理

近年来,随着信息化建设的不断深入,OA系统在企业管理工作中的应用也日趋广泛。传统的固定资产管理存在数据分散,管理效率低等问题。深度整合易点易动和OA系统,可以打通各系统之间的数据通道,实现固定资产通过OA系统的全流程管理。这不仅可以提升管理效率,减轻人工管理成本,也更方…

部署LVS的NAT模式

实验准备 #负载调度器# 192.168.116.40 #内网 12.0.0.100 #外网 先添加双网卡 #web服务器# 192.168.116.20 #web1 192.168.116.30 #web2 #nfs共享服务# 192.168.116.10 #nfs systemctl stop firewalld setenforce 0 1.nfs共享文件 1…

Python-Selenium-使用 pywinauto 实现 Input 上传文件

当前环境&#xff1a;Win10 Python3.7 pywinauto0.6.8&#xff0c;selenium3.14.1 示例代码 from pywinauto import Desktop import osapp Desktop() dialog app[打开] dialog[Edit].set_edit_text(os.getcwd() .\\example-01.jpg) dialog[Button].click() 其他方法&…

接口测试的工具(3)----postman+node.js+newman

1.安装newman&#xff1a;输入命令之后 一定注意 什么都不要操作 静静的等待结束就行了。 2.安装失败的对此尝试不行 在用下面的方法 解压一下就行了 3.验证是否成功 多次尝试是可以在线安装成功的

SpringCloudAliBaba篇之Seata:分布式事务组件理论与实践

1、事务简介 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中&#xff0c;一个事务由一组SQL语句组成&#xff0c;事务具有4个属性&#xff1a;原子性、一致性、隔离性、持久性。这四个属性通常称为ACID原则。 原子性(atomici…

在非联网、无网络环境下,fpm的安装和生成RPM包的使用案例

文章目录 前言1、安装fpm1.1、安装Ruby环境1.2、gem 安装 fpm 2、fpm使用2.1、fpm常用参数2.2、fpm使用案例2.2.1、fpmFirstDemo文件夹2.2.3、编写脚本文件2.2.4、生成RPM包2.2.5、RPM安装与卸载测试 前言 由于fpm采用Ruby语言开发&#xff0c;因此在使用之前需要先在您的虚拟…

AI日报:OpenAI扩大创业基金计划

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 文章目录 OpenAI拓宽Converge启动程序变压器模型背后的思想建立启动融资新闻AutoGen AI支点其他 OpenAI拓宽Converge启动程序 ChatGPT制造商OpenAI正在扩大其Converge AI创业计划。 OpenAI的Converge产品于2022年12月首次…