在介绍MVVM框架之前,先给大家简单介绍一下MVC、MVP框架(由于本博文主要讲解MVVM,所以MVC和MVP将简化介绍,如果需要我将在以后的博文中补充进来)。

MVC框架:

  • M-Model : 业务逻辑和实体模型(biz/bean)
  • V-View : 布局文件(XML)
  • C-Controller : 控制器(Activity)

相信大家都熟悉这个框架,这个也是初学者最常用的框架,该框架虽然也是把代码逻辑和UI层分离,但是View层能做的事情还是很少的,很多对于页面的呈现还是交由C实现,这样会导致项目中C的代码臃肿,如果项目小,代码臃肿点还是能接受的,但是随着项目的不断迭代,代码量的增加,你就会没办法忍受该框架开发的项目,这时MVP框架就应运而生。

MVP框架:

  • M-Model : 业务逻辑和实体模型(biz/bean)
  • V-View : 布局文件(XML)和Activity
  • P-Presenter : 完成View和Model的交互

MVP框架相对于MVC框架做了较大的改变,将Activity当做View使用,代替MVC框架中的C的是P,对比MVC和MVP的模型图可以发现变化最大的是View层和Model层不在直接通信,所有交互的工作都交由Presenter层来解决。既然两者都通过Presenter来通信,为了复用和可拓展性,MVP框架基于接口设计的理念大家自然就可以理解其用意。

但MVP框架也有不足之处:

1.接口过多,一定程度影响了编码效率。

2.业务逻辑抽象到Presenter中,较为复杂的界面Activity代码量依然会很多。

3.导致Presenter的代码量过大。

MVVM框架:

  • M-Model : 实体模型(biz/bean)
  • V-View : 布局文件(XML)
  • VM-ViewModel : DataBinding所在之处,对外暴露出公共属性,View和Model的绑定器

对比MVP和MVVM模型图可以看出,他们之间区别主要体现在以下两点:

1.    可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。 在Android中,布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化。

2.    低耦合。以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成(具体代码请看后面) 甚至都不需要再Activity、Fragment去findViewById()。这时候Activity、Fragment只需要做好的逻辑处理就可以了。

说了这么多理论知识,相信大家都有所厌烦了,下面就不来“虚”的了直接来“干”的,大家可能会问MVVM框架在Android怎么样使用?

Google在2015年的已经为我们提供DataBinding技术,以便让我们快速实现MVVM框架的实现。下面就详细讲解如何使用DataBinding?

由于本人使用的是AndroidStudio(以下简称AS),所以接下来都是关于AS相关使用规则:

1.检查你的AS版本,要求在1.3.0以上

2.Gradle 版本1.3.0-beta4以上

3.在工程根目录build.gradle文件加入如下配置:

dependencies {
classpath "com.android.tools.build:gradle:1.3.0-beta4"
classpath "com.android.databinding:dataBinder:1.0-rc1"
} allprojects {
repositories {
jcenter()
}
}

4.在app里的build.gradle文件加入如下配置:

apply plugin: 'com.android.application'
apply plugin: 'com.android.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>

l  先将根布局改为layout

l  在布局里引入的model 中的数据类:

<variable name="user" type="com.example.User"/>(还有一种写法将在后面代码中介绍)
l  设置布局属性值,通过@{}语法:

<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>

数据实体类User:

public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
} 在Activity中进行数据的绑定: @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}

这时你运行程序就会看到在界面上会显示你设置的测试用户数据,当然你还可以这样做去获取binding:

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

如果你使用的是ListView或者RecyclerView去显示界面,这时候在Items布局中使用Data Binding,在Adapter中你可以这样获取binding:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

接下来就是事件的实现,在我们以往的使用中对于事件的实现都是android:onClick或者在代码中使用View.setOnClickListener()来实现点击事件,在这里将有一种新的实现方式:

要将事件分配给它的处理程序,使用一个正常的绑定表达式,以值作为调用的方法名称。例如:你的数据对象有两种方法

public class MyHandlers {
public void onClickFriend(View view) { ... }
public void onClickEnemy(View view) { ... }
}

绑定表达式可以为视图指定单击事件监听器

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.Handlers"/>
<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}"
android:onClick="@{user.isFriend ?handlers.onClickFriend :handlers.onClickEnemy}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:onClick="@{user.isFriend ?handlers.onClickFriend :handlers.onClickEnemy}"/>
</LinearLayout>
</layout>

由于DataBinding对布局使用改动比较大,下面主要讲解一下布局:

①在布局中import导入

<data>
<import type="android.view.View"/>
</data>

之后在你的布局中通过View控件特性进行对其实现类隐藏和显示操作

<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

可能有人会问如果我导入的类与已导入的类的名字冲突怎么办?那么接下来就会解决这个问题!

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>

这里的“alias”属性就是别名的意思,你可以采用别名的方式解决这个问题。

②当你在布局中引用的变量是一个List集合,需将集合的左”<”使用转义字符输入,如下(想要了解转义字符具体表达形式,请自行查询):

<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List&lt;User>"/>
</data>

③类型转换

<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

④当导入的类中存在静态属性和方法时,你也是可以在布局中直接使用

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

<TextView
android:text="@{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

⑤属性Variables:

在数据元素中可以使用任意数量的变量元素。每个可变元素描述一个属性,该属性可以设置在布局文件中的绑定表达式中使用的布局上:

<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.example.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>

⑥自定义Binding类的类名:

<data class="CustomBinding"></data> 在app_package/databinding下生成CustomBinding;

<data class=".CustomBinding"></data> 在app_package下生成CustomBinding;

<dataclass="com.example.CustomBinding"></data> 明确指定包名和类名。

⑦include使用

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</LinearLayout>
</layout>

⑧DataBinding数据绑定不支持包括合并元素的直接子元素,例如下面的写法是不被允许的:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<merge>
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</merge>
</layout>

注意:name.xml 和 contact.xml都必须包含 <variable name="user" ../>

⑨DataBinding支持的表达式有:
数学表达式: + - / *%

字符串拼接 +

逻辑表达式&& ||

位操作符 & | ^

一元操作符 + - ! ~

位移操作符 >>>>> <<

比较操作符 == >< >= <=

instanceof

分组操作符 ()

字面量 -character, String, numeric, null

强转、方法调用

字段访问

数组访问 []

三元操作符 ?

聚合判断(Null Coalescing Operator)语法 ‘??’
例如:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

android:text="@{user.displayName ?? user.lastName}"

上面代码的意思是如果displayName为null,则显示lastName,否则显示displayName;

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

集合Collections

<data>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<import type="java.util.List"/>
<variable name="list" type="List&lt;String>"/>
<variable name="sparse" type="SparseArray&lt;String>"/>
<variable name="map" type="Map&lt;String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>

android:text="@{list[index]}"

android:text="@{sparse[index]}"

android:text="@{map[key]}"

①String literals(字符串常量)

当使用单引号围绕属性值时,在表达式中使用双引号是很容易的:

android:text='@{map["firstName"]}'

②也可以使用双引号来环绕属性值。当你这样做的时候,String literals要么用&quot;或反引号(`):

android:text="@{map[`firstName`}"
android:text="@{map[&quot;firstName&quot;]}"

Resources资源:

在DataBinding语法中,可以把resource作为其中的一部分:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
除了支持dimen,还支持color、string、drawable、anim等。

注意:对mipmap图片资源支持还是有问题,目前只支持drawable。

一些资源要求需明确的类型赋值:

Type

Normal Reference

Expression Reference

String[]

@array

@stringArray

int[]

@array

@intArray

TypedArray

@array

@typedArray

Animator

@animator

@animator

StateListAnimator

@animator

@stateListAnimator

Color   int

@color

@color

ColorStateList

@color

@colorStateList

到这里基本的属性使用方法介绍就结束了,下面大家应该对数据变化时,UI如何呈现很迷惑,好了,现在开始讲数据的变化如何让UI的更新?
任何普通的java对象(POJO)可用于数据绑定,但修改一个POJO不会造成UI更新。数据绑定(DataBinding)的真正力量可以通过给你的数据对象在数据改变时通知你来使用。有三种不同的数据变化通知机制,Observable objects, observable fields, and observable collections。
下面来逐个讲解:
Observable Objects
一个实现可观察到的接口的类,将允许绑定到绑定一个单一的监听器绑定到一个绑定对象,以监听该对象上的所有属性的更改;观察到的接口有一个机制来添加和删除监听器,但通知是由开发人员来进行的。为使开发更容易,一个基类,baseobservable,是为了实现监听器注册机制。数据类实现者仍然是负责通知时的性能变化。这是通过分配一个绑定注释getter和setter进行通知:

private static class User extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}

注意:BR类自动生成的

好了,现在你就会发现当通过set方法改变数据后,UI就会自动更新!

ObservableFields

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

在代码中设置数据:

user.firstName.set("Google");
int age = user.age.get(); Observable Collections
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", );

布局中使用:

<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap&lt;String, Object>"/>
</data>

<TextView
android:text='@{user["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

如果集合的key是Integer,可以使用ObservableArrayList代替ObservableArrayMap:

ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add();

在布局中使用:

<data>
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user" type="ObservableList&lt;Object>"/>
</data>

<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

文章写到这里,DataBinding的一些基本用法已经介绍完毕,如果您想要了解更加详细、全面的内容,请自行去sdk的Doc文档中阅读或者关注我以后的博文。

MVVM简介与运用的更多相关文章

  1. MVVM简介

    如果你对MVVM的概念还是不了解,可以参看下面链接:http://baike.baidu.com/view/3507915.htm 我们以WPF+MVVM的本地桌面程序为背景,这样一来我们可以不去操心 ...

  2. MV、MVC、MVP、MVVM简介,对MVC不确定了。

    参考: http://www.cnblogs.com/changxiangyi/archive/2012/07/16/2594297.html http://www.jcodecraeer.com/a ...

  3. MVVM 简介

    转:https://objccn.io/issue-13-1/ 所以,MVVM 到底是什么?与其专注于说明 MVVM 的来历,不如让我们看一个典型的 iOS 是如何构建的,并从那里了解 MVVM: 我 ...

  4. 架构 MVC MVP MVVM 简介 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  5. [转载]MVVM模式原理分析及实践

    没有找到很好的MVVM模式介绍文章,简单找了一篇,分享一下.MVVM实现了UI\UE设计师(Expression Blend 4设计界面)和软件工程师的合理分工,在SilverLight.WPF.Wi ...

  6. WPF MVVM 学习总结(一)

    ---恢复内容开始--- 1. MVVM简介 在WPF中,MVVM(View-ViewModel-Model)开发模型用的很多,它具有低耦合,可重用行,相对独立的设计和逻辑.所以备受广大开发者的喜爱. ...

  7. WPF+MVVM学习总结 DataGrid简单案例

    一.WPF概要 WPF(Windows Presentation Foundation)是微软推出的基于Windows 的用户界面框架,属于.NET Framework 3.0的一部分.它提供了统一的 ...

  8. MVVM模式原则

    1.MVVM简介 这个模式的核心是ViewModel,它是一种特殊的model类型,用于表示程序的UI状态.它包含描述每个UI控件的状态的属性.例如,文本输入域的当前文本,或者一个特定按钮是否可用.它 ...

  9. WPF学习07:MVVM 预备知识之数据绑定

    MVVM是一种模式,而WPF的数据绑定机制是一种WPF内建的功能集,两者是不相关的. 但是,借助WPF各种内建功能集,如数据绑定.命令.数据模板,我们可以高效的在WPF上实现MVVM.因此,我们需要对 ...

随机推荐

  1. 024_mac配置屏保命令

    注意吃饭等离开工位的时候养成随时开启屏保的功能,养成信息保护的好习惯,mac如何配置屏幕保护呢? 一. 通过mac"设置"里的"Desktop & Screen ...

  2. ogg 单表拆分合并进程

    metalink文档:1320133.1和1512633.1 map scott.emp1, target scott.emp1 ,FILTER(@RANGE(1,3));  --拆分 map sco ...

  3. HTML5 移动端Meta设置

    1.   强制让文档的宽度与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览. <meta name="viewport" content= ...

  4. linux线程 join/detach

    linux中创建线程时,可以通过 __attr 指定线程的属性 extern int pthread_create (pthread_t *__thread, __const pthread_attr ...

  5. 两条命令,实现ssh免密登陆

    ssh-keygenssh-copy-id -i 目标服务器ip

  6. 图的最小环floyed

    最优的路线 问题描述 学校里面有N个景点.两个景点之间可能直接有道路相连,用Dist[I,J]表示它的长度:否则它们之间没有直接的道路相连.这里所说的道路是没有规定方向的,也就是说,如果从I到J有直接 ...

  7. Gradle 同步时报错,Could not find com.android.support.constraint:constraint-layout:1.0.0-alpha8的解决方法

    Error:Could not find com.android.support.constraint:constraint-layout:1.0.0-alpha8. 原因: SDK 中可能是没有安装 ...

  8. tp5 修改默认的分页url

    默认分页url:xx.com/xxx?page=1 个人主要感觉不美观,想变成xx.com/xxx/list_1.html这样的 框架本身默认使用的boostrap分页类,目录位置 simplewin ...

  9. SpringBoot动态配置加载

    1.SpringBoot对配置文件集中化进行管理,方便进行管理,也可以使用HttpClient进行对远程的配置文件进行获取. 创建一个类实现EnvironmentPostProcessor 接口,然后 ...

  10. python 项目实例

    参考:  https://blog.csdn.net/yz764127031/article/details/71522161 https://www.cnblogs.com/linuxprobe/p ...