Android-MVVM架构-Data Binding的使用
来源:互联网 发布:龙泉驾校网络授课 编辑:程序博客网 时间:2024/06/06 03:35
项目整体效果:
Awesome-Android-MVVM
- 什么是MVVM, 为什么需要 MVVM?
- 如何在Android中使用Data Binding实现MVVM架构?
什么是MVVM , 为什么需要MVVM?
MVVM是Model-View-ViewModel的简写. 它是有三个部分组成:Model、View、ViewModel。
Model:数据模型层。包含业务逻辑和校验逻辑。
View:屏幕上显示的UI界面(layout、views)。
ViewModel:View和Model之间的链接桥梁,处理视图逻辑。
MVVM功能图如下:
MVVM架构通过ViewModel隔离了UI层和业务逻辑层,降低程序的耦合度。
Android App 中MVC的不足
一般来说,我们开发Android App是基于MVC,由于MVC的普及和快速开发的特点,一个app从0开发一般都是基于MVC的。
Activity、Fragment相当于C (Controller), 布局相当于V(View), 数据层相当于M(Model)
随着业务的增长,Controller里的代码会越来越臃肿,因为它不只要负责业务逻辑,还要控制View的展示。也就是说Activity、Fragment杂糅了Controller和View,耦合变大。并不能算作真正意义上的MVC。
编写代码基本的过程是这样的,在Activity、Fragment中初始化Views,然后拉取数据,成功后把数据填充到View里。
假如有如下场景
:
我们基于MVC开发完第一版本,然后企业需要迭代2.0版本,并且UI界面变化比较大,业务变动较小,怎么办呢?
当2.0的所有东西都已经评审过后。这个时候,新建布局,然后开始按照新的效果图,进行UI布局。然后还要新建Activity、Fragment把相关逻辑和数据填充到新的View上。
如果业务逻辑比较复杂,需要从Activity、Fragment中提取上个版本的所有逻辑,这个时候自己可能就要晕倒了,因为一个复杂的业务,一个Activity几千行代码也是很常见的。千辛万苦做完提取完,可能还会出现很多bug。
一开始我尝试使用MVP架构, MVP功能图如下:
MVP把视图层抽象到View接口,逻辑层抽象到Presenter接口,提到了代码的可读性。降低了视图逻辑和业务逻辑的耦合。
但是有MVP的不足:
- 接口过多,一定程度影响了编码效率。
- 业务逻辑抽抽象到Presenter中,较为复杂的界面Activity代码量依然会很多。
- 导致Presenter的代码量过大。
这个时候MVVM就闪亮登场了。从上面的MVVM功能图我们知道:
- 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
在Android中,布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化。 - 低耦合。以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成(具体代码请看后面)
甚至都不需要再Activity、Fragment去findViewById。这时候Activity、Fragment只需要做好的逻辑处理就可以了。
现在我们回到上面从app1.0到app2.0迭代的问题,如果用MVVM去实现那就比较简单,这个时候不需要动Activity、Fragment,
只需要把布局按照2.0版本的效果实现一遍即可。因为视图逻辑和数据填充已经在布局里了,这就是上面提到的可重用性。
发展过程:
MVC->MVP->MVVP
Android中如何实现MVVM架构?
Google在2015年的已经为我们DataBinding技术。下面就详细讲解如何使用DataBinding。
环境准备
在工程根目录build.gradle文件加入如下配置,把Android Gradle 插件升级到最新:
dependencies { classpath 'com.android.tools.build:gradle:1.5.0'}
在app里的build.gradle文件加入如下配置,启用data binding 功能:
dataBinding { enabled true}
来个简单的例子
实现上面效果的“Data Binding Simple Sample”
data binding 布局格式和以往的有些区别:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
布局的根节点为
布局里使用的model 通过中的指定:
- 1
- 2
- 1
- 2
设置空间属性的值,通过@{}语法来设置:
- 1
- 2
- 1
- 2
下面是完整的布局实现:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
接下来实现数据模型类User:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
在Activity中 绑定数据
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
通过DataBindingUtil.setContentView设置布局,通过binding类设置数据模型:
- 1
- 1
布局详解
import导入
通过标签导入:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
如果产生了冲突可以使用别名的方式:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
集合泛型左尖括号需要使用转译:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
使用导入类的静态字段和方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
像Java一样,java.lang.*是自动导入的。
Variables
在节点中使用来设置。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
Binding类里将会包含通过variable设置name的getter和setter方法。如上面的setUser,getUser等。
如果控件设置了id,那么该控件也可以在binding类中找到,这样就不需要findViewById来获取View了。
自定义Binding类名(Custom Binding Class Names)
以为根节点布局,android studio默认会自动产生一个Binding类。类名为根据布局名产生,如一个名为activity_simple的布局,它的Binding类为ActivitySimpleBinding,所在包为app_package/databinding。
当然也可以自定义Binding类的名称和包名:
<data class="CustomBinding"></data>
在app_package/databinding下生成CustomBinding;<data class=".CustomBinding"></data>
在app_package下生成CustomBinding;<data class="com.example.CustomBinding"></data>
明确指定包名和类名。
Includes
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
name.xml 和 contact.xml都必须包含 <variable name="user" ../>
DataBinding Obervable
在上面的一个例子上,数据是不变,随着用户的与app的交互,数据发生了变化,如何更新某个控件的值呢?
有如下几种方案(具体实现下载代码,运行,点击DataBinding Observable 按钮):
- BaseObservable的方式
使User继承BaseObservable,在get方法上加上注解@Bindable,会在BR(BR类自动生成的)生成该字段标识(int)
set方法里notifyPropertyChanged(BR.field);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
如果数据发生变化通过set方法,view的值会自动更新,是不是很方便。
- 通过ObserableField来实现
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
布局中使用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
代码中设置/改变数据:
- 1
- 2
- 1
- 2
- Observable Collections方式:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
布局中使用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
下面通过DataBinding来实现列表
获取square公司retrofit代码贡献者数据列表,通过RecyclerView来实现。
RecyclerView的Adapter实现的核心方法为两个onCreateViewHolder、onBindViewHolder方法和Item的ViewHolder。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
通过setVariable方法来关联数据。
getBinding().setVariable(com.mvvm.BR.contributor, contributor)
大家看到BR.contributor的contributor常量是怎么产生的?布局里的中的name属性值。如: 那么就会自动生成BR.book。有点类似以前的R里面的id
。 有人会问了如果别的实体(model)也有相同的book属性怎么办?那他到底使用哪个呢?其实这是不会冲突,因为在不用的地方用,他的上下文(Binging)不一样,所以不会冲突。也是和以前的R里面的常量是一回事情。只是把它放到BR里面去了。所以我猜想BR的全称应该是(Binding R
(R就是以前我们用的常量类))虽然官方没有说明。
通过executePendingBindings强制执行绑定数据。
Item对应的VIewHolder
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
EL表达式(Expression Language)
DataBinding支持的表达式有:
数学表达式: + - / * %
字符串拼接 +
逻辑表达式 && ||
位操作符 & | ^
一元操作符 + - ! ~
位移操作符 >> >>> <<
比较操作符 == > < >= <=
instanceof
分组操作符 ()
字面量 - character, String, numeric, null
强转、方法调用
字段访问
数组访问 []
三元操作符 ?:
聚合判断(Null Coalescing Operator)语法 ‘??’
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" android:text="@{user.userName ?? user.realName}" android:textSize="12dp"/>
上面的意思是如果userName为null,则显示realName。
Resource(资源相关)
在DataBinding语法中,可以吧resource作为其中的一部分。如:
- 1
- 2
- 1
- 2
除了支持dimen,还支持color、string、drawable、anim等。
注意,对mipmap图片资源支持还是有问题,目前只支持drawable。
Event Binding (事件绑定)
事件处理器:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
布局中使用:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
在Activity实现该接口UserFollowEvent:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
效果如下所示:
点击按钮后:
Custom Setter(自定义Setter方法)
有些时候我们需要自定义binding逻辑,如:在一个TextView上设置大小不一样的文字,这个时候就需要我们自定义binding逻辑了.
在比如我们为ImageView加载图片,通过总是通过类似这样的的代码来实现:
- 1
- 1
如果我们自定Setter方法,那么这些都可以是自动的。怎么实现呢?
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
@BindingAdapter({“imageUrl”}) 这句话意味着我们自顶一个imageUrl属性,可以在布局文件中使用。当在布局文件中设置该属性的值发生改变,会自动
调用loadImage(ImageView view, String url)方法。
布局中使用:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
再来看下如何实现:在一个TextView上设置大小不一样的文字(其实是一样的)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
注意:使用自定义Setter,需要使用dataBinding语法。以下用法是不对的:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
其他的例子就不一一在这里介绍了,详情可以查看github上的代码。
完整源码下载 代码持续更新…
have Fun!
- Android-MVVM架构-Data Binding的使用
- Android-MVVM架构-Data Binding的使用
- Android-MVVM架构-Data Binding的使用
- MVVM架构以及Android Data Binding的学习
- android 的MVVM模型--Data Binding
- Google官方支持的MVVM架构框架Data Binding使用入门
- Google官方支持的MVVM架构框架Data Binding使用入门
- MVVM 和 Android Data Binding
- mvvm设计模式之Data Binding库的使用
- Android Data Binding代码实战,mvvm
- Android之MVC、MVP、MVVM(Data Binding)
- 【MVVM】Android Data Binding实战(一)
- Android Data Binding 使用
- MVVM Data Binding
- android中data binding的使用
- Data Binding的使用
- Android MVVM 开发模式插件库 Android Data Binding Library
- data binding——Android中的MVVM模式
- lvds屏幕的数据手册如何看
- linux下文件的三种时间属性和vim的三种模式
- Objective-C语言中的方法的参数,声明-实现-调用,类加载
- 使用线程Thread实现自动播放动画
- 优化iOS程序性能的25个方法
- Android-MVVM架构-Data Binding的使用
- VS2010搭建一个新的工程常见问题如下
- LINUX下通过系统调用的文件操作
- js制作页面返回、刷新和返回并刷新
- 安鑫 到底是什么决定了人与人之间收入的巨大差别
- SharePoint 2013 设置网站集为”只读”
- iOS7 background fetch 实战详解:让app自动/智能获取数据以优化用户体验
- 排序算法
- ELK(Windows下配置)