ViewBinding和DataBinding的理解和区别

Posted 我的猫叫冰彬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ViewBinding和DataBinding的理解和区别相关的知识,希望对你有一定的参考价值。

之前一直把ViewBinding当成了DataBinding,直到最近的学习中才发现他们不是一个东西。于是写下这篇笔记帮助理解和区分他们俩。

一、ViewBinding

1.什么是ViewBinding

先来看看官方是怎么说的。

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

在大多数情况下,视图绑定会替代 findViewById

来源:视图绑定 | Android 开发者 | Android Developers (google.cn)

在刚接触android的时候,获取布局里的一个组件是通过findViewById去获取的。比如获取一个Button,那么写法就是

val btn: Button = findViewById(R.id.btn)

于是当组件很多的时候,就需要大量的用findViewById来获取,这是很繁琐的。然后在学习郭霖老师的《第一行代码时》,郭神书里提到了kotlin-android-extensions这个插件。该插件能够帮我们省去findViewById,在用Kotlin写的时候可以直接通过视图组件的Id来获取。

比如视图里有一个id为btn的Button组件,那么在Acitivy中就会有一个btn变量。这个插件帮我们简化了上面的步骤。

但是这个插件很快就被Google废弃了,在AndroidStudio中引入会出现警告。

 

取而代之的则是ViewBinding。

而Viewbinding就是会给每一个xml布局生成一个对应的binding类。比如activity_main.xml布局,就会生成一个ActivityMainBinding类。

2.怎么使用ViewBinding

想要使用ViewBinding,首先需要在build.gradle里加入配置。

android 
    ...
    buildFeatures 
        viewBinding true
    

然后创建一个MainActivity,并写下布局文件activity_main.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
​
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
​
    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="textView"
        android:textColor="@color/black"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

然后在Activity里的使用

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    

ViewBinding帮我们生成了一个ActivityMainBinding类,我们通过ActivityMainBinding.inflate()来加载布局。

然后我们就可以通过binding来操作布局了。

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.btn.setOnClickListener 
            Toast.makeText(this, "click", Toast.LENGTH_SHORT).show()
        
        binding.tv.text = "setText"
    

这样就帮我们省去了FindViewById的步骤,这也是ViewBinding最大的功能。

二、DataBinding

1.什么是DataBinding

还是先看看官方的说法

数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。

来源:数据绑定库 | Android 开发者 | Android Developers (google.cn)

这里官方的说话就有点看不懂了。那我就尝试来解释一下。

比如我们要修改一个TextView的text值,之前都是在代码里获取到TextView组件,然后通过textView.text去赋值。比如

val str = "setText"
binding.tv.text = str

或者当我们要获取一个TextView的文本值时,也需要通过textView.text来获取

val value = binding.tv.text.toString()

这样的操作就是程序化,要在逻辑代码里去赋值或取值。如果我们可以在xml布局文件里就声明该组件的值和哪个变量绑定,就能方便很多。

利用DataBinding就能做到这一点,比如

<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@user.firstName"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@user.lastName"/>
       </LinearLayout>
    </layout>
    

这样声明式的方式就将数据绑定了。具体的使用方法会在下面给出。

2.怎么使用DataBinding

2.1 基础用法

想要使用DataBinding,同样需要现在build.gradle里加入配置

android 
    ...
    buildFeatures 
        dataBinding true
    

然后我们先创建一个User的实体类,用于绑定数据

class User(var firstName: String, var lastName: String) 

然后写布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@user.firstName"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@user.lastName"/>
       </LinearLayout>
    </layout>

注意这里的格式, 最外层是一个Layout标签,里面包含了data标签和LinearLayout标签,这是Databinding的表示方式,LinearLayout其实就是这里的根布局。

如何形成这样的文件呢?在原本的xml文件里,将光标移动到根布局的位置,然后按alt+enter就会出现选项转换到DataBinding的格式。

 

然后再来分析布局文件的代码。

        <data>
           <variable name="user" type="com.example.User"/>
       </data>

data标签里表示的是数据源,name是名称,type是类型。这里的type就是我们前面写的User实体类。

    <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@user.firstName"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@user.lastName"/>
       </LinearLayout>

然后在TextView里的text属性,通过@的格式来获取data标签里声明的变量。

最后回到Activity中来绑定数据。

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.user = User("Test", "User")
    

首先是定义DataBinding时和ViewBinding有所不同,DataBinding是通过DataBindingUtil.setContentView来绑定布局的。

最后一行就是执行了数据绑定。

运行后结果如下:

 

这就是databinding最基础的用法。

2.2 绑定点击事件

databinding不止能绑定数据,还能绑定一些事件,比如点击事件。

我们先创建一个Handlers,里面添加一个onClick()方法,用于点击事件回调。

class Handlers 
​
    fun myOnClick(view: View) 
        Log.d("Handlers", "onClick()")
    

然后修改刚才的布局文件,添加一个Button组件,然后在data标签里声明刚才写的Handlers。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="handler" type="com.example.example.Handlers" />
        <variable name="user" type="com.example.example.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        ...
        
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@handler::onClick"
            android:text="Button"/>
​
    </LinearLayout>
​
​
</layout>

注意Button的onClick属性。依然是通过@来调用变量,并且对于方法的调用是"::"。

需要注意的是,自定义的这个方法的签名必须和监听器对象方法中的签名完全一样。比如点击事件是View.OnClickListner的onClick(view: View),那么自定义的方法参数也必须保持一致:myOnClick(view: View),否则会报错。

然后运行试一下,点击按钮,查看日志。

 

成功绑定。

2.3 使用可观察的数据对象

前面的基础使用,我们只是知道了DataBinding如何声明式而非程序式的赋值。但是这样的情况下,如果我们要修改视图里的值,我们依然需要通过在代码里赋值的方式才能修改。

数据绑定,我们更希望的是让组件属性的值直接绑定到一个变量中,当变量发生改变时组件属性的值相应发生改变,而不需要我们再去进行赋值。

那么要实现这种当一个值发生改变,另一个值相应发生改变的效果,我们很容易想到观察者模式。那我们就可以将变量变成一个可观察的数据对象。

如果去实现Observable接口的话,对于一些简单的类型来说比较麻烦。所以基础类型的变量可以用以下的类来声明

  • ObservableBoolean

  • ObservableByte

  • ObservableChar

  • ObservableShort

  • ObservableInt

  • ObservableLong

  • ObservableFloat

  • ObservableDouble

  • ObservableParcelable

接下来就来实践测试一下。

我们先修改一个User实体类

class User 
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()

注意这里声明变量我们使用了val,是因为要使用Observable要尽量避免开箱或封箱操作,在Java里声明也应该是pubc final属性

private static class User 
        public final ObservableField<String> firstName = new ObservableField<>();
        public final ObservableField<String> lastName = new ObservableField<>();
        public final ObservableInt age = new ObservableInt();
    

这里有人可能会有疑问,用val定义了我怎么修改变量的值?其实Observable的实现类里都提供了get()和set()函数来修改具体的值。

接着在布局里新增一个TextView来展示age变量。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="handler" type="com.example.example.Handlers" />
        <variable name="user" type="com.example.example.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@user.firstName"/>
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@user.lastName"/>
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@String.valueOf(user.age)"/>
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@handler::onClick"
            android:text="Change"/>
    </LinearLayout>
</layout>
​

然后修改一下Handlers的点击事件逻辑,点击按钮修改user的变量值。

class Handlers(private val user: User) 
​
    fun onClick(view: View) 
        user.firstName.set("Luka")
        user.lastName.set("Dončić")
        user.age.set(23)
        Log.d("Handlers", "onClick()")
    

最后在MainActivity里绑定

package com.example.example
​
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import com.example.example.databinding.ActivityMainBinding
​
class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        val user = User()
        user.firstName.set("Stephen")
        user.lastName.set("Curry")
        user.age.set(34)
        binding.user = user
        binding.handler = Handlers(user)
    

最终的效果如下

 

这样我们就不用再去给组件属性做赋值操作,只需要修改绑定的变量的值即可。

三、区别

看到这里,相信你对ViewBinding和DataBinding都有了一定的了解。接下来就总结一下他们的区别。

  1. 目的不同。ViewBinding的出现仅仅是为了帮开发人员省去写findViewById的步骤;而DataBinding是用于绑定数据的,能够把视图的数据和代码变量绑定起来,并且实现自动更新。这个特性使得DataBinding能和MVVM框架进行很好的配合。

  2. 初始化方式不同。ViewBinding通过生成的Binding类的inflate方法来加载布局,然后还需要用Activity的setContentView()方法来绑定。而Databinding则是通过DataBindingUtil.setContentView()来绑定的。

  3. 包含关系。DataBinding也有ViewBinding的功能,也可以省去findViewById()方法。

四、总结

本文从使用方面理解了什么是ViewBinding和DataBinding,并对他们做了区别分析,加深对他们俩的理解。

本文主要参考资料为Android 开发者 | Android Developers (google.cn)

Android:DataBinding 和 ViewBinding 的区别

【中文标题】Android:DataBinding 和 ViewBinding 的区别【英文标题】:Android : Difference between DataBinding and ViewBinding 【发布时间】:2020-01-22 05:52:56 【问题描述】:

自 Jetpack 发布以来,我们一直在使用 DataBinding。 Android 文档表明 ViewBinding 是在 Android Studio 3.6 Canary 11+ 中添加的。

我阅读了文档,但它看起来类似于 DataBinding。

谁能解释这两个概念有什么区别?

【问题讨论】:

粗略地说,视图绑定是数据绑定的一个子集。您没有获得数据绑定表达式,但您确实获得了生成的绑定类,您可以使用它来处理 findViewById() 调用,并为您提供对布局中小部件的类型安全和空值安全访问。 @CommonsWare 如果 DataBinding 可以完成所有这些以及更多操作,那么使用 ViewBinding 有什么优势? @IgorGanapolsky:数据绑定减慢了构建过程。视图绑定的构建过程并没有那么糟糕。 【参考方案1】:

根据official docs:

视图绑定

仅将视图绑定到代码。

数据绑定

将数据(来自代码)绑定到视图 + ViewBinding(将视图绑定到代码)

三个重要的区别

    使用视图绑定,布局不需要布局标签

    您不能使用视图绑定将布局与 xml 中的数据绑定 (没有绑定表达式,没有 BindingAdapters 也没有与视图绑定的双向绑定)

    视图绑定的主要优点是速度和效率。它具有更短的构建时间,因为它避免了与数据绑定相关的开销和性能问题,因为注释处理器会影响数据绑定的构建时间。

简而言之,没有任何视图绑定可以做数据绑定不能做的事情(尽管代价是更长的构建时间),而且数据绑定可以做很多视图绑定不能做的事情

【讨论】:

ViewBinding最大的优势是什么? 速度和效率 好的,建议我一件事。我应该使用什么?还是我应该同时使用两者?我说的是任何一般项目。是的,我想使用 MVVM 结构。请提出建议。 您可以使用 VIEWBINDING 有效地替换 findviewbyid()。但是,如果您的项目更复杂,并且您需要添加“糖”和功能(例如将数据绑定到视图、绑定适配器等),请使用 DATABINDING。 您也不需要同时使用两者。如果您使用的是DATABINDING,则无需添加VIEWBINDING【参考方案2】:

ViewBinding VS 数据绑定

这就是我想一起解释ViewBindingDataBinding 的原因。

如您所见,ViewBindingDataBinding 库的一种子集,这意味着ViewBindingDataBiding 在绑定布局方面可以完成相同的工作。这也意味着DataBinding,你可能不需要ViewBinding,因为它会做ViewBinding应该做的事情,还提供一些额外的功能,比如2way绑定,以及在XML中使用变量文件。

那么,这可能会引出一个问题

“那么我们就使用DataBinding,因为它听起来更花哨!”

等一下。尽管听起来很花哨,但它是一个负载非常重的库,可能会导致更长的编译时间。因此,如果您不打算仅使用 DataBinding 功能,那么考虑使用 ViewBinding 可能会更好,因为它在构建时间和 apk 大小方面确实具有一些优势。

For more detail read this article

【讨论】:

【参考方案3】:

按照官方定义,

视图绑定使我们能够更轻松地编写与视图交互的代码。一旦在模块中启用视图绑定,它就会为该模块中存在的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。

数据绑定库是一个支持库,可让您使用声明性格式而非编程方式将布局中的 UI 组件绑定到应用中的数据源。

视图绑定和数据绑定的区别

    View Binding 库比 Data Binding 库快,因为它是 不使用下面的注释处理器,当涉及到 编译时速度 View Binding 更高效。

    View Binding 的唯一功能就是将视图绑定在 代码。虽然数据绑定提供了更多选项,例如绑定 表达式,它允许我们编写表达式连接 布局中视图的变量。

    Data Binding 库与 Observable Data 对象一起工作,但你不需要 当底层数据发生变化时,不必担心刷新 UI。

    数据绑定库为我们提供了绑定适配器。

    Data Binding 库为我们提供了两种方式的 Data Binding,这是 一种将对象绑定到 xml 布局的技术,这样两者 对象和布局可以互相发送数据。

要详细了解视图绑定和数据绑定,您可以阅读这些文章,

https://medium.com/geekculture/android-viewbinding-over-findviewbyid-389401b41706

https://anubhav-arora.medium.com/android-view-binding-v-s-data-binding-5862a27524e9

【讨论】:

以上是关于ViewBinding和DataBinding的理解和区别的主要内容,如果未能解决你的问题,请参考以下文章

视图绑定(ViewBinding )与数据绑定(Databinding)

找不到 androidx.databinding:viewbinding:4.1.0

DataBinding的双向绑定实现原理

Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions

Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions

Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions