Android中MVVM模式
來自: http://my.oschina.net/u/1175007/blog/613889
官方文檔地址:
https://developer.android.com/intl/zh-cn/tools/data-binding/guide.html
Data Binding Library有什么用?
每當有新東西出現我們都喜歡問有什么用?這可不是然并卵的東西,我們來了解下。
1、是官方支持的MVVM模式框架
2、可以直接在布局 xml 文件中綁定數據,無需再 findViewById 然后手工設置數據
3、可以提高解析XML的速度
4、UI與功能的解耦合
一、環境
在開始使用新東西之前,我們需要稍微的配置一下環境,這里要求你的Android Studio版本是1.3+,使用eclipse的同學暫時還沒有辦法使用該框架,請換用Android Studio。還有,在開始之前,請更新你的Support repository到最新的版本。
萬事俱備,那我們就開始搭配環境!
新建一個project,在dependencies中添加以下依賴
dependencies { classpath "com.android.tools.build:gradle:1.3.0" classpath "com.android.databinding:dataBinder:1.0-rc1" }
由于依賴的項目在 jcenter 服務器中,所以在repositories 中需要添加 jcenter如下:
allprojects { repositories { jcenter() } }
在需要使用支持庫的module 的build.gradle文件中添加插件申請:
apply plugin: 'com.android.application' apply plugin: 'com.android.databinding'
例子:現在做一個點擊一下按鈕然后年齡會+1的小功能,這里也模擬了我們是數據更新。
第一步:創建XML布局
創建一個布局xml文件,就像以前一樣。然后這里我們需要做一些少少的修改,在這個框架下我們的思維要稍稍改變一下了,以前的布局XML只描述了布局,他是相對固定的東西,在Data Binding Library下我們的布局XML就像是一個類,他可以有變量也能進行一定的運算。其實Data Binding Library還真的給你生成了一個類似這樣的類。
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="cn.golditfin.bean.User" /> <variable name="buttonname" type="String" /> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" /> <TextView android:id="@+id/age_textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:text="@{String.valueOf(user.age)}" /> <Button android:id="@+id/age_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:text="@{buttonname}" /> </RelativeLayout> </layout>
其中我們布局文件的根節點變成了layout,然后定義了變量。
在此布局定義了:
1、兩個變量,一個是對象User,一個是String類型的buttonname。
2、兩個TextView一個Button
<data> <variable name="user" type="cn.golditfin.bean.User"/> </data>
在 data 元素中使用 variable 來聲明在布局文件中使用的變量。
name聲明了變量的名稱,type聲明了變量的類型,變量可以為基本類型如int,也可以為集合或者對象。
View中使用變量用@{} 格式來調用,下面Textview使用了我們對象user的變量name。
<TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" />
第二步:定義數據對象
public class User { public String name; public int age; public User(String name,int age){ this.name = name; this.age = age; } }
很簡單就兩個成員變量
然后是Java代碼調用。
第三步:綁定數據
public class MainActivity extends AppCompatActivity { private User myUser; private ActivityMainBinding myBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); myBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); myUser = new User("年齡",4); myBinding.setUser(myUser); myBinding.setButtonname("年齡+1"); myBinding.ageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myUser.age++; myBinding.setUser(myUser); } }); } }
在Activity的onCreate方法中進行了基本的數據綁定。一句一句代碼給你們解釋。
1、原來設置布局的setContentView方法我們現在不用了,改成了DataBindingUtil.setContentView
2、對于第一點的返回值是框架給我們生成的類,該類按照單詞首字母大寫的規則,布局的名字加上Binding組成。比如我們的布局是activity_main.xml,所以按照這個規則來轉換的話就變成了ActivityMainBinding。
3、自動生成的類ActivityMainBinding其實就是代表了那個布局,里面包括了布局的View,我們聲明的變量。我們可以通過這個對象獲取到布局的元素。
4、binding.setUser(user);沒錯,這里的setUser方法是框架給我們自動生成的,每一個布局中聲明的變量都會自動生成對應的get,set方法。
好的,現在我們跑起來看看。
現在已經實現我們的想法了,但是看到這里的代碼以及我們實現的過程遇到數據改變不能刷新問題。
當我嘗試單方面的進行myUser對象數據更改的時候界面上的數據并不能自動刷新,然后我通過setUser再次設置就可以刷新了,這簡直是煩惱啊!
帶著這個問題我們來進一步的學習Data Binding Library。
解決問題
數據刷新自動同步如何完成呢?
解決數據同步其實谷歌已經想到了,在這里提供了兩個解決辦法。
1、讓實體類繼承BaseObservable類
public class User extends BaseObservable{ public String name; public int age; public User(String name,int age){ this.name = name; this.age = age; } @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.user); } @Bindable public int getAge() { return age; } public void setAge(int age) { this.age = age; notifyPropertyChanged(BR.user); } }
說明:
1、首先繼承BaseObservable類
2、給成員變量的get方法添加@Bindable注解
3、在成員變量的set方法的最后調用方法notifyPropertyChanged,其中里面的參數是在BR類中記錄的變量,那些變量就是我們在XML中聲明的那些啦,類似于安卓資源管理的R.java文件,不過這里管理的是布局XML的變量,是自動生成的。
在這里我又有問題要問了,之前我的實體類這么簡約,現在變這么長,感覺不爽!
那么有沒有簡約版的呢,因為我很懶不想弄這么多代碼?
2、使用Observable數據類型
這是一個簡約的方法,谷歌為我們提供了基于常用數據類型的Observable類型,就是我們熟悉的數據類型前面加上Observable組成。
例如:
Int類型對應ObservableInt
Boolean類型對應ObservableBoolean
其他基本類型類似以上寫法。
而String類型對應的是ObservableField<String>,ObservableField是一個泛型
我們可以看看他的源碼
然后再看看ObservableInt的源碼
其他類型的源碼基本類似。這樣看來其實ObservableField除了可以存放對象還可以代替其他所有基本類型來使用。
public class User { public ObservableField<String> name = new ObservableField<>(); public ObservableInt age = new ObservableInt(); public User(String name,int age){ this.name.set(name); this.age.set(age); } }
以上就是進過改造的實體類。
使用Observable數據類型之后數據是通過get,set方法來獲取和改變的。
在XML布局中使用時跟原來的數據類似一樣。
public class MainActivity extends AppCompatActivity { private User myUser; private ActivityMainBinding myBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); myUser = new User("年齡",4); myBinding.setUser(myUser); myBinding.setButtonname("年齡+1"); myBinding.ageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myUser.age.set(myUser.age.get()+1); } }); } }