1、MVVM
MVVM 模式,即指 Model-View-ViewModel。它將 View 的狀態(tài)和行為完全抽象化,把邏輯與界面的控制完全交給 ViewModel 處理
優(yōu)點(diǎn):
降低耦合:一個(gè)ViewModel層可以綁定不同的View層,當(dāng)Model變化時(shí)View可以不變。
可重用性:可以把一些視圖邏輯放在ViewModel層中,讓很多View重用這些視圖邏輯。
2、關(guān)系流程圖
半自動(dòng)生成,首先用戶自定義Model后,框架會(huì)為Model綁定ViewModel。
- View:指的是layout文件,主要進(jìn)行視圖控件的一些初始設(shè)置,不應(yīng)該有任何的數(shù)據(jù)邏輯操作。
- ViewModel:作為連接 View 與 Model 的中間重要橋梁,ViewModel 與 Model 直接交互,處理完業(yè)務(wù)邏輯后,通過(guò) DataBinding 將數(shù)據(jù)變化反應(yīng)到用戶界面上。(DataBinding會(huì)根據(jù)layout中的布局文件來(lái)進(jìn)行數(shù)據(jù)的綁定并自動(dòng)生成class文件)
- Model:定義實(shí)體類(lèi),以及獲取業(yè)務(wù)數(shù)據(jù)模型,比如通過(guò)數(shù)據(jù)庫(kù)或者網(wǎng)絡(luò)來(lái)操作數(shù)據(jù)等。javaBean里面定義了ObservableField的屬性,ObservableField中提供了get和set方法,還有刷新屬性方法。
2、實(shí)踐
首先,介紹一下DataBinding 與 MVVM 之間的關(guān)系 。
MVVM是一種思想,一種架構(gòu)模式,而DataBinding是谷歌推出的方便實(shí)現(xiàn) MVVM 的工具。
在 DataBinding 庫(kù)之前,我們經(jīng)常會(huì)寫(xiě)一些重復(fù)性很高而且毫無(wú)營(yíng)養(yǎng)的代碼,比如:findViewById()、setText()、setOnClickListener() 等。直到2015谷歌 I/O大會(huì)推出了 DataBinding,一個(gè)實(shí)現(xiàn)視圖和數(shù)據(jù)雙向綁定的工具。使用 DataBinding 庫(kù)以后,可以使用聲明式布局文件來(lái)減少粘結(jié)業(yè)務(wù)邏輯和布局文件的膠水代碼,有利于開(kāi)發(fā)者更方便地實(shí)現(xiàn) MVVM 模式。
那么怎么使用呢?
第一步:在 Module:app的build.gradle文件添加如下代碼:
android {
...
// 添加DataBinding依賴
dataBinding{
enabled = true
}
}
第二步:創(chuàng)建一個(gè)Java bean
public class UserInfo {
// 被觀察的屬性(切記:必須是public修飾符,因?yàn)槭荄ataBinding的規(guī)范)
public ObservableField<String> name = new ObservableField<>();
public ObservableField<String> pwd = new ObservableField<>();
}
第三步:修改布局文件和添加ViewModel文件
使用DataBinding的布局文件和普通的布局文件有點(diǎn)不同,DataBinding 布局文件的根標(biāo)簽是layout標(biāo)簽,layout里面有一個(gè)data元素和View元素,這個(gè)View元素就是我們沒(méi)使用DataBinding時(shí)候的布局文件。data元素里面配置variable信息。
使用格式:@{標(biāo)識(shí).成員屬性}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 定義該布局需要綁定的數(shù)據(jù)名稱(chēng)和類(lèi)型 name可以理解key type可以理解為value -->
<data>
<variable
name="loginViewModel"
type="com.migill.mvvm.vm.LoginViewModel" />
</data>
<!-- 下部分內(nèi)容和平時(shí)布局文件一樣 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--在loginViewModel中實(shí)現(xiàn)賬號(hào)名改變的監(jiān)聽(tīng)-->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:addTextChangedListener="@{loginViewModel.nameInputListener}"
android:hint="請(qǐng)輸入賬戶"
android:text="@{loginViewModel.userInfo.name}" />
<!--在loginViewModel中實(shí)現(xiàn)密碼改變的監(jiān)聽(tīng)-->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:addTextChangedListener="@{loginViewModel.pwdInputListener}"
android:hint="請(qǐng)輸入密碼"
android:text="@{loginViewModel.userInfo.pwd}" />
<!--在loginViewModel中實(shí)現(xiàn)點(diǎn)擊登錄的監(jiān)聽(tīng)-->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="@{loginViewModel.loginClickListener}"
android:text="登錄" />
</LinearLayout>
</layout>
根據(jù)上面的布局文件,在LoginViewModel中添加屬性和監(jiān)聽(tīng)方法,這個(gè)是個(gè)未完成的文件,還沒(méi)有具體的實(shí)現(xiàn),等在Activity完成綁定后在添加具體的功能代碼
public class LoginViewModel {
public UserInfo userInfo;
public TextWatcher nameInputListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
}
};
public TextWatcher pwdInputListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
}
};
public View.OnClickListener loginClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
}
};
}
第四步:ReBuilder和Activity中書(shū)寫(xiě)代碼綁定
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1、必須先ReBuilder,2、書(shū)寫(xiě)代碼綁定
ActivityMvvmLoginBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm_login);
new LoginViewModel(binding);
}
}
接下來(lái)就是要完成LoginViewModel文件了,將ViewModel和View進(jìn)行綁定
public class LoginViewModel {
public UserInfo userInfo;
public LoginViewModel(ActivityMvvmLoginBinding binding) {
userInfo = new UserInfo();
// 將ViewModel和View進(jìn)行綁定,通過(guò)DataBinding工具
binding.setLoginViewModel(this);
}
public TextWatcher nameInputListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// View層接收到用戶的輸入,改變Model層的javabean屬性
userInfo.name.set(String.valueOf(s));
}
@Override
public void afterTextChanged(Editable s) {
}
};
public TextWatcher pwdInputListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// View層接收到用戶的輸入,改變Model層的javabean屬性
userInfo.pwd.set(String.valueOf(s));
}
@Override
public void afterTextChanged(Editable s) {
}
};
public View.OnClickListener loginClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// 模擬網(wǎng)絡(luò)請(qǐng)求
new Thread(new Runnable() {
@Override
public void run() {
// Model層屬性的變更,改變View層的顯示
//userInfo.name.set("yesterday");
SystemClock.sleep(2000);
if ("migill".equals(userInfo.name.get()) && "123".equals(userInfo.pwd.get())) {
Log.e("TAG", "登錄成功!");
} else {
Log.e("TAG", "登錄失敗!");
}
}
}).start();
}
};
}
MVVM的思想是可以雙向綁定,我們看運(yùn)行結(jié)果
打開(kāi)注釋 userInfo.name.set("yesterday");
其實(shí)這就是個(gè)雙向綁定,View層接收到用戶的輸入,該變了model層的javabean屬性的值。model層javabean屬性的值的變更,改變了View層的顯示。在判斷用戶名和密碼的時(shí)候發(fā)現(xiàn)不一樣,打印登錄失敗。
注釋//userInfo.name.set("yesterday");






