赞
踩
之前一直把ViewBinding当成了DataBinding,直到最近的学习中才发现他们不是一个东西。于是写下这篇笔记帮助理解和区分他们俩。
先来看看官方是怎么说的。
通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
在大多数情况下,视图绑定会替代
findViewById
在刚接触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类。
想要使用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最大的功能。
还是先看看官方的说法
数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。
这里官方的说话就有点看不懂了。那我就尝试来解释一下。
比如我们要修改一个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>
-
这样声明式的方式就将数据绑定了。具体的使用方法会在下面给出。
想要使用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最基础的用法。
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),否则会报错。
然后运行试一下,点击按钮,查看日志。
成功绑定。
前面的基础使用,我们只是知道了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都有了一定的了解。接下来就总结一下他们的区别。
目的不同。ViewBinding的出现仅仅是为了帮开发人员省去写findViewById的步骤;而DataBinding是用于绑定数据的,能够把视图的数据和代码变量绑定起来,并且实现自动更新。这个特性使得DataBinding能和MVVM框架进行很好的配合。
初始化方式不同。ViewBinding通过生成的Binding类的inflate方法来加载布局,然后还需要用Activity的setContentView()方法来绑定。而Databinding则是通过DataBindingUtil.setContentView()来绑定的。
包含关系。DataBinding也有ViewBinding的功能,也可以省去findViewById()方法。
本文从使用方面理解了什么是ViewBinding和DataBinding,并对他们做了区别分析,加深对他们俩的理解。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。