浅谈MVVM之DataBinding
目录
1、 前言(DataBinding介绍)
2、 关于Android设计模式的简单介绍
3、 DataBinding能够解决的问题
4、 DataBinding的总体思路
5、 DataBinding的使用(实例讲解)
一、 基础入门
二、 单向数据绑定: BaseObservable、ObservableField、ObservableCollection
三、双向数据绑定
四、 事件绑定
6、github网址
7、参考资料
1、前言(DataBinding介绍)
DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现。MVVM 模式采用双向绑定(data-binding):可以实现View和Model的双向绑定交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力。
2、关于Android设计模式的简单介绍
讲到DataBinding,就有必要提起MVVM设计模式。
以下是关于在Android中常见的3个设计模式:
①MVC:(VIew-Model-Controller) 早期将VIew、Model、Controller代码块进行划分,使得程序大部分分离,降低耦合。
②MVP:(VIew-Model-Presenter)由于MVC中View和Model之间的依赖太强,导致Activity中的代码过于臃肿。为了他们可以绝对独立的存在,慢慢演化出了MVP。在MVP中View并不直接使用Model,它们之间的通信是通过 Presenter (MVC中的Controller)来进行的。
③MVVM:(Model–View–ViewModel) MVVM可以算是MVP的升级版,基本与MVP模式完全一致,将Presenter 改名为 ViewModel。唯一的区别是,它采用双向绑定(data-binding),当View有用户输入后,ViewModel通知Model更新数据,同理Model数据更新后,ViewModel通知View更新。用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。
3、DataBinding能够解决的问题
我们在平时项目开发中,通常需要些大量
①findViewById()、
②setXxx()
[setXxx()比如:
1.不用写 setOnClickListener 之类的响应 UI 命令的代码(响应 view 命令)
2.不用写 setText() 之类的控制 view 属性的代码(控制 view)
3、setOnXxxxListener()等无实际意义的代码。]
Databinding可以帮助我们省去这些步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。
而且:
①如果需要多次使用findViewById,降低了应用性能,令人厌烦
②更新UI数据需切换至UI线程,将数据分解映射到各个view比较麻烦
原来,第一个问题可以使用依赖注入框架 -ButterKnife,但是自从Android Studio升级3.0以来,ButterKnife一直受到Gradle API的影响,不能升级Gradle版本,这也算是一大诟病。
第二个问题,谷歌提供了Loop-Handler方案,还可以使用RxJava,EventBus等方案,但它们只是解决了线程切换的问题,却没有解决将数据分解映射到各个view的问题。
更好的解决方案来了!使用DataBinding可以同时解决这两个问题!同时,DataBinding的线程切换也是透明的,这是指,当你的Activity需要展示新的数据时,你可以在后台线程中获取数据,然后直接交给DataBinding就可以了,完全不需要关心线程切换的问题。
简单一句话,DataBinding 让你可以在布局文件中写 java 表达式,所以可以省略掉中间层。可以说 DataBinding 库是减少甚至完全代替 view 和 业务逻辑 之间的中间层 。
4、DataBinding的总体思路
DataBinding解决这些问题的思路非常简单。就是针对每个Activity的布局,在编译阶段,生成一个ViewDataBinding类的对象,该对象持有Activity要展示的数据和布局中的各个view的引用(这里已经解决了令人厌烦的findViewById问题)。
同时该对象还有如下可喜的功能:
①将数据分解到各个view
②在UI线程上更新数据
③监控数据的变化,实时更新
有了这些功能,你会感觉到,你要展示的数据已经和展示它的布局紧紧绑定在了一起,这就是该技术叫做DataBinding–数据绑定的原因。
简单来说,就是涉及3个,①model数据类②xml布局文件③Activity类,下面的讲解,也是围绕这三个文件的代码
综上所述,DataBinding的优点如下:
使用方式上避免过多的冗余代码;
监控数据的变化,实时更新;
事件处理上直接找到目标实例处理用户操作的事件,将数据与UI进行分离;
5、DataBinding的使用(实例讲解)
①我创建了一个项目名称为:DataBindingTest
②DataBinding是一个support library,所以它可以支持所有的android sdk,最低可以到android2.1(API7)。
③使用DataBinding需要Android Gradle插件的支持,版本至少在1.5以上,需要的Android studio的版本在1.3以上。
④升级Gradle版本:https://www.jianshu.com/p/00beddbe3dbc
步骤
启用 DataBinding 的方法是在对应 Model 的 build.gradle 文件里加入以下代码,就可以引入对 DataBinding 的支持。
android {
...
dataBinding {
enabled = true
}
}
一、基础入门
1、启用 DataBinding 后,这里先来看下如何在布局文件中绑定指定的变量
打开布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击 Convert to data binding layout,就可以生成 DataBinding 需要的布局规则
activity_main.xml:
和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 标签用于声明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就是要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道
2、这里先来声明一个 Modle
有的详细代码可以看我的github网址
package com.example.c7.databindingtest;
/**
* Created by c7 on 2019/5/29.
*/
public class User {
private String name;
private String password;
.............
}
3、在 刚才的布局文件中,data 标签里声明要使用到的变量名、类的全路径
<data>
<variable
name="userInfo"
type="com.example.c7.databindingtest.User"/>
</data>
如果 User 类型要多处用到,也可以直接将之 import 进来,这样就不用每次都指明整个包名路径了,而 java.lang.* 包中的类会被自动导入,所以可以直接使用,反正这里 type 必须传完整路径,或者用 import 方式也是可以的
<data>
<import type="com.example.c7.databindingtest.User"/>
<variable
name="userInfo"
type="User"/>
</data>
alias定义别名:
因为XML是不支持自定义导包的,所以通过import导包,如果存在 import 的类名相同的话可以通过alias进行区分:
<data>
<import type="com.example.c7.databindingtest.User"/>
<import
alias="TempUser"
type="com.alias.User" />
<variable
name="userInfo"
type="User"/>
<variable
name="tempUserInfo"
type="TempUser"/>
</data>
这里声明了一个 User 类型的变量 userInfo,我们要做的就是使这个变量与两个 TextView 控件挂钩,通过设置 userInfo 的变量值同时使 TextView 显示相应的文本
完整的布局代码如下所示:
<?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:tools="http://schemas.android.com/tools">
<data>
<import type="com.example.c7.databindingtest.User"/>
<!-- <import
alias="TempUser"
type="com.alias.User" />
-->
<variable
name="userInfo"
type="User"/>
<!-- <variable
name="tempUserInfo"
type="TempUser"/>
-->
</data>
<L