MVVM(Model - View - ViewModel)最初是在2005年由微軟提出的一個(gè)UI框架的概念。相比MVP模式,MVVM將Presenter改為了ViewModel,同時(shí)實(shí)現(xiàn)的View和ViewModel的雙向數(shù)據(jù)綁定,View層的變化會(huì)自動(dòng)導(dǎo)致ViewModel發(fā)生變化,ViewModel的數(shù)據(jù)發(fā)生變化也會(huì)自動(dòng)實(shí)現(xiàn)View的刷新,開發(fā)者可以不用直接處理View和數(shù)據(jù)的更新操作,MVVM框架會(huì)完成這一切,MVVM模式不同層關(guān)系如下

Data Binding
在Google I/O2015大會(huì)上,Android開發(fā)團(tuán)隊(duì)發(fā)布了官方的MVVM支持函數(shù)庫(kù)Data Binding Library,要求,Gradle的版本大于1.5.0-alphal,Android最低版本Android4.0
Databinding 是一個(gè)實(shí)現(xiàn)數(shù)據(jù)和UI綁定的框架,是一個(gè)實(shí)現(xiàn) MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的實(shí)現(xiàn)MVVM開發(fā)模式。
官網(wǎng)
起步
- 配置Gradle
在app/在build.gradle使用如下配置android { ... dataBinding { enabled = true } } - 數(shù)據(jù)綁定
使用@{}語(yǔ)法可以輕松綁定數(shù)據(jù)<TextView android:text="@{model.userName}"/>
數(shù)據(jù)綁定
- 布局文件(View)
Data binding 的布局文件與傳統(tǒng)布局文件有一點(diǎn)不同。它以一個(gè)layout標(biāo)簽作為根節(jié)點(diǎn),里面是data標(biāo)簽與view標(biāo)簽。view標(biāo)簽的內(nèi)容就是不使用 Data Binding 時(shí)的普通布局文件內(nèi)容<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="book" type="app.mrquan.databindingdemo.pojo.Book"/> </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="@{book.name}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{String.valueOf(book.price)}"/> </LinearLayout> </layout> - 數(shù)據(jù)對(duì)象(Model)
public class Book { public String name; public Integer price; public Book(String name, Integer price) { this.name = name; this.price = price; } /** * Constructor * ... */ }
從 Data Binding 的角度看,這兩個(gè)類是一樣的。public class Book { private String name; private Integer price; public Book(String name, Integer price) { this.name = name; this.price = price; } /** * Constructor * getter setter * ... */ }
在前一個(gè)類中表達(dá)式@{book.name}用于android:text訪問(wèn)name屬性,在后一個(gè)類中用于訪問(wèn)getName()方法。 另外,兩者都存在,它會(huì)優(yōu)先解析為getName()方法。 - 綁定數(shù)據(jù)(ViewModel)
在默認(rèn)情況下,會(huì)基于布局文件生成一個(gè)繼承于ViewDataBinding的Binding類。例如,布局文件叫 main_activity.xml,所以會(huì)生成一個(gè) MainActivityBinding 類。這個(gè)類包含了布局文件中所有的綁定關(guān)系,會(huì)根據(jù)綁定表達(dá)式給布局文件賦值。綁定方式如下@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ActivityMainBinding 類是自動(dòng)生成的 ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main); // 所有的 set 方法也是根據(jù)布局中 variable 名稱生成的 binding.setBook(new Book("Thinking in JAVA",100)); } - 表達(dá)式語(yǔ)言
data binding提供了大量的表達(dá)式方法 鏈接
事件處理
有兩種實(shí)現(xiàn)方式:
方法引用(Method References)
-
監(jiān)聽綁定(Listener bindings)
方法引用和監(jiān)聽綁定之間的主要區(qū)別在于監(jiān)聽器是在綁定數(shù)據(jù)時(shí)創(chuàng)建的,而不是在觸發(fā)事件時(shí)創(chuàng)建的。如果希望在事件觸發(fā)時(shí)解析表達(dá)式,則應(yīng)使用監(jiān)聽綁定
方法引用
事件可以直接綁定到處理函數(shù)方法,類似于activity的
android:onClick. 它的優(yōu)勢(shì)在于表達(dá)式會(huì)在編譯時(shí)處理,如果函數(shù)不存在或者函數(shù)簽名不對(duì),編譯將會(huì)報(bào)錯(cuò)。方法引用如下public class MyHandler { public void onClick( View view ) { ... } }<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="book" type="app.mrquan.databindingdemo.pojo.Book"/> <variable name="handler" type="app.mrquan.databindingdemo.MyHandler"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:text="@{book.name}" android:onClick="@{handler::onClick}"/> <!-- 函數(shù)調(diào)用也可以使用 `.` , 如handler.onClickFriend , 不過(guò)已棄用 --> </LinearLayout> </layout>注:表達(dá)式中方法名必須與監(jiān)聽器對(duì)象中方法名完全一致。監(jiān)聽綁定
在監(jiān)聽綁定中,只要返回值與監(jiān)聽器對(duì)象的預(yù)期返回值相匹配即可??梢允褂脦в卸鄠€(gè)參數(shù)的lambda表達(dá)式,監(jiān)聽綁定如下
public class MyHandler { public void onClickTwo(View view, Book book) { ... } }<Button android:text="監(jiān)聽綁定" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:onClick="@{(view) -> handler.onClickTwo(view,book)}" />注:此功能在 Android Gradle Plugin version 2.0 或更新版本上可用.
深入Layout文件
- 導(dǎo)入(Imports)
data 標(biāo)簽內(nèi)可以有多個(gè) import 標(biāo)簽。可以在布局文件中像使用 Java 一樣導(dǎo)入引用<data> <import type="android.view.View"/> </data>
當(dāng)類名發(fā)生沖突時(shí),可以使用 alias<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="com.example.real.estate.View" alias="Vista"/> - includes
引用變量可以傳遞到任何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>注:name.xml 與 contact.xml 中都需要聲明 user 變量。 - Data Binding 進(jìn)階