# DataBindingSamples **Repository Path**: TsingFon/DataBindingSamples ## Basic Information - **Project Name**: DataBindingSamples - **Description**: 包含了 DataBinding 的大部分知识点 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: kotlin - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-01-11 - **Last Updated**: 2021-01-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现。借助该库,可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源,可以有效降低布局和业务代码之间的耦合性,使代码逻辑更加清晰。DataBinding 能够省去我们一直以来的 **findViewById()** 步骤,大量减少 **Activity** 内的代码,数据能够单向或双向绑定到 **layout** 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常 启用 DataBinding 的方法是在对应 Model 的 **build.gradle** 文件里加入以下代码,同步后就能引入对 DataBinding 的支持 ```groovy android { dataBinding { enabled = true } //或者是 buildFeatures { dataBinding = true } } ``` ### 一、基础入门 启用 DataBinding 后,这里先来看下如何在布局文件中绑定指定的变量 打开布局文件,选中根布局的 **ViewGroup**,按住 **Alt + 回车键**,点击 “**Convert to data binding layout**”,就可以自动生成 DataBinding 需要的布局规则 ![](https://images.xiaozhuanlan.com/photo/2020/30672cb9ed72c87ee0dad7dc7dbd74a5.jpg) ```xml ``` 和原始布局的区别在于多出了一个 **layout** 标签将原布局包裹了起来,**data** 标签用于声明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就需要把数据(Model)与 UI(View)进行绑定,**data** 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道 这里先来声明一个 Modle ```kotlin data class UserBean(var name: String, var password: String) ``` 在 **data** 标签里声明要使用到的变量名、类的全路径 ```java ``` 如果 User 类型要多处用到,也可以直接将之 **import** 进来,这样就不用每次都指明整个包名路径了,而 `java.lang.*` 包中的类会被自动导入,所以可以直接使用 ```java ``` 如果存在 **import** 的类名相同的情况,可以使用 **alias** 指定别名 ```xml ``` 这里声明了一个 UserBean 类型的变量 **userInfo**,我们要做的就是使这个变量与两个 TextView 控件挂钩,通过设置 userInfo 的变量值同时使 TextView 显示相应的文本 完整的布局代码如下所示 ```xml ``` 通过 `@{userInfo.name} ` 使 TextView 引用到相关的变量,DataBinding 会将之映射到相应的 **getter** 方法 之后可以在 Activity 中通过 `DataBindingUtil` 设置布局文件,省略原先 Activity 的 `setContentView()` 方法,并为变量 **userInfo** 赋值 ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMain2Binding = DataBindingUtil.setContentView(this, R.layout.activity_main2) val user = UserBean("leavesC", "123456") binding.userInfo = user } ``` ![](https://images.xiaozhuanlan.com/photo/2020/3309050468c13b99be6801a59c5a94c3.png) 由于 `@{userInfo.name}`在布局文件中并没有明确的值,所以在预览视图中什么都不会显示,不便于观察文本的大小和字体颜色等属性,此时可以为之设定默认值(文本内容或者是字体大小等属性都适用),默认值将只在预览视图中显示,且默认值不能包含引号 ```xml android:text="@{userInfo.name,default=defaultValue}" ``` 此外,也可以通过 ActivityMain2Binding 直接获取到指定 ID 的控件 ```kotlin binding.tvUserName.text = "leavesC" ``` 每个数据绑定布局文件都会生成一个绑定类,**ViewDataBinding** 的实例名是根据布局文件名来生成,将之改为首字母大写的驼峰命名法来命名,并省略布局文件名包含的下划线。控件的获取方式类似,但首字母小写 也可以通过如下方式自定义 ViewDataBinding 的实例名 ```xml ``` 此外,在绑定表达式中会根据需要生成一个名为`context`的特殊变量,`context`的值是根 **View** 的`getContext()`方法返回的`Context`对象, `context`变量会被具有该名称的显式变量声明所覆盖 Databinding 同样是支持在 **Fragment** 和 **RecyclerView** 中使用 。例如,可以看 Databinding 在 Fragment 中的使用 ```kotlin override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding: FragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false) binding.hint = "Hello" return binding.root } ``` **以上实现数据绑定的方式,每当绑定的变量发生变化的时候,都需要重新向 ViewDataBinding 传递新的变量值才能刷新 UI 。接下来看如何实现自动刷新 UI** ### 二、单向数据绑定 实现数据变化自动驱动 UI 刷新的方式有三种:`BaseObservable`、`ObservableField`、`ObservableCollection` #### 2.1、BaseObservable 一个纯净的 ViewModel 类被更新后,并不会让 UI 自动更新。而数据绑定后,我们自然会希望数据变更后 UI 会即时刷新,Observable 就是为此而生的概念 **BaseObservable** 提供了 **notifyChange()** 和 **notifyPropertyChanged()** 两个方法,前者会刷新所有的值域,后者则只更新对应 **BR** 的 **flag**,该 BR 的生成通过注释 **@Bindable** 生成,可以通过 **BR notify** 特定属性关联的视图 ```kotlin class GoodsBean : BaseObservable() { @Bindable var name = "" set(value) { field = value //只更新本字段 notifyPropertyChanged(BR.name) } @Bindable var details = "" set(value) { field = value //更新所有字段 notifyChange() } var price = 0F } ``` 在 **setName()** 方法中更新的只是本字段,而 **setDetails()** 方法中更新的是所有字段 添加两个按钮用于改变 goods 变量的三个属性值,由此可以看出两个 notify 方法的区别。当中涉及的按钮点击事件绑定,在下面也会讲到 ```xml