Android DataBinding庫(MVVM設計模式)

說到 DataBinding,就有必要先提起 MVVM設計模式
html

 

Model–View–ViewModel(MVVM) 是一個軟件架構設計模式,相比 MVVM,你們對 MVC MVP 可能會更加熟悉。java

 

  • MVC:(VIew-Model-Controller)android

    早期將 View、Model、Controller 代碼塊進行劃分,使得程序大部分分離,下降耦合。git

     

  • MVP:(VIew-Model-Presenter)github

    因爲 MVC View和Model之間的依賴太強,致使 Activity 中的代碼過於臃腫。爲了他們能夠絕對獨立的存在,慢慢演化出了 MVP。在 MVP View 並不直接使用 Model,它們之間的通訊是經過 Presenter (MVC中的Controller) 來進行的。swift

     

  • MVVM:(Model–View–ViewModel)設計模式

    MVVM 能夠算是 MVP的升級版,將 Presenter 更名爲 ViewModel。關鍵在於 View和Model的雙向綁定,當 View 有用戶輸入後,ViewModel 通知 Model 更新數據,同理 Model 數據更新後,ViewModel 通知 View 更新。數組

 

Data Binding網絡

 

在Google I/O 2015上,伴隨着 Android M 預覽版發佈了Data Binding兼容函數庫:架構

https://developer.android.com/tools/data-binding/guide.html

 

不知道要扯什麼了,仍是直接上代碼,來看看 Data Binding 的魅力吧。

 

環境要求

 

Data Binding 對使用的環境仍是有必定要求的(這貨有點挑):

 

  • Android Studio 版本在 1.3以上

  • Gradle 的版本要在 1.5.0-alpha1 以上

  • 須要在 Android SDK manager 中下載 Android Support repository

 

 

而後在對應的 Module build.gradle 中添加:

 

android {
   ....    dataBinding {        enabled =true    } }

 

Gradle須要升級版本的能夠參考:

 

升級Gradle版本

http://www.jianshu.com/p/00beddbe3dbc

 

建立對象

 

建立一個 User類

 

 

佈局

 

activity_main.xml 中佈局:

 

 

這裏跟平時的佈局有點不一樣,最外層是 layout,裏面分別是 data 以及 咱們的佈局。

 

data:聲明瞭須要用到的 user對象type 用於指定路徑。

 

能夠在 TextView 中的看到 android:text="@{user.firstName}", 這是什麼鬼,沒見過這麼寫的!!!(不急,繼續往下看)

 

綁定數據

 

看看下面的 MainActivity

 

 

問我 ActivityMainBinding 哪來的?我怎麼知道...

 

ActivityMainBinding 是根據佈局文件的名字生成的,在後面加了 Binding

 

運行下看看效果吧:

 

 

有點懵逼了,就綁定了下而已,這些數據是怎麼顯示到界面上的。

 

 

他是怎麼工做的?

 

原來 Data Binding 在程序代碼正在編譯的時候,找到全部它須要的信息。而後經過語法來解析這些表達式,最後生成一個類。

 

經過反編譯咱們能夠看到,反編譯能夠參考這裏:

http://blog.csdn.net/vipzjyno1/article/details/21039349

 

Data Binding 爲咱們生成了 databinding,以及 ActivityMainBinding類

 

 

看看咱們在 onCreate 中最後調用的 binding.setUser(user),在 ActivityMainBinding 中能夠看到這個方法:

 

 

我想就是這個 super.requestRebind() 對數據進行了綁定,至於裏面怎麼實現的,有待進一步研究。

 

更多用法

 

上面只是用一個簡單的例子,展現了 Data Binding 的用法,若是想在實際項目中使用,可不是上面這例子能夠搞定的。下面就來講說 Data Bindig 的更多用法。

 

消除空指針顧慮

 

自動生成的 DataBinding 代碼會檢查 null,避免出現NullPointerException

 

例如在表達式中 @{user.phone}若是user == null 那麼會爲 user.phone 設置 默認值null 而不會致使程序崩潰(基本類型將賦予默認值如 int爲0,引用類型都會賦值null)。

 

自定義DataBinding名

 

若是不喜歡自動生成的 Data Binding名,咱們能夠本身來定義:

 

<data class="MainBinding">    ....
</data>

 

class對應 的就是生成的 Data Binding名

 

導包

 

跟Java中的用法類似,佈局文件中支持 import 的使用,原來的代碼是這樣:

 

<data>    <variable name="user" type="com.example.gavin.databindingtest.User" />
</data>

 

使用 import 後能夠寫成這樣:

 

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

 

遇到 相同的類名 的時候:

 

 

使用 alias 設置別名,這樣 user 對應的就是 com.example.gavin.databindingtest.UsermcUser 就對應com.example.gavin.mc.User,而後:

 

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

 

當須要用到一些包時,在Java中能夠自動導包,不過在佈局文件中就沒有這麼方便了。須要使用 import 導入這些包,才能使用。如須要用到 View 的時候:

 

<data>    <import type="android.view.View"/></data>    ...    <TextView    ...    android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}"
/>

 

注意:只要是在Java中須要導入包的類,這邊都須要導入,如:Map、ArrayList 等,不過 java.lang 包裏的類是能夠不用導包的。

 

表達式

 

在佈局中,不只可使用:

 

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

 

還可使用表達式如:

 

三元運算:

 

User 中添加 boolean類型 isStudent屬性,用來判斷是否爲學生:

 

 

注意:須要用到雙引號的時候,外層的雙引號改爲單引號。

 

還能夠這樣用:

 

 

這裏用到的 View 須要在 data 中聲明:

 

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

 

注意:android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}",可能會被標記成紅色,不用管它編譯會經過的。

 

??

 

除了經常使用的操做法,另外還提供了一個 null 的合併運算符號 ??,這是一個三目運算符的簡便寫法。

 

 

contact.lastName ?? contact.name

 

至關於:

 

contact.lastName != null ? contact.lastName : contact.name

 

所支持的操做符以下:

 

數學運算符 + - / * %
字符串拼接 +
邏輯運算 && ||
二進制運算 & | ^
一元運算符 + - ! ~
位運算符 >> >>> <<
比較運算符 == > < >= <=
instanceof
Grouping ()
文字 - character, String, numeric, null
類型轉換 cast
方法調用 methods call
字段使用 field access
數組使用 [] Arrary access
三元運算符 ? :

 

顯示圖片

 

除了文字的設置,網絡圖片的顯示也是咱們經常使用的。來看看 Data Binding 是怎麼實現圖片的加載的。

 

首先要提到 BindingAdapter註解,這裏建立了一個類,裏面有顯示圖片的方法:

 


(這方法必須是public static的,不然會報錯)

 

這裏只用了 bind 聲明瞭一個 image 自定義屬性,等下在佈局中會用到。

 

這個類中只有一個靜態方法 imageLoader,裏面有兩參數,一個是須要設置圖片的 view,另外一個是對應的 Url,這裏使用了 ImageLoader 庫加載圖片。

 

看看它的佈局是什麼樣的吧:

 

 

最後在 MainActivity 中綁定下數據就能夠了:

 

binding.setImageUrl("http://115.159.198.162:3000/posts/57355a92d9ca741017a28375/1467250338739.jpg");

 

哇靠!!!就這樣?我都沒看出來它是怎麼設置這些圖片的。

 

無論了,先看看效果。(其中的原理之後慢慢嘮,這裏就負責說明怎麼使用,這篇已經夠長了,不想再寫了)

 


看個美女壓壓驚

 

使用 BindingAdapter 的時候,我這還出現了這樣的提示,不過不影響運行。不知道大家會不會...

 

 

已解決

感謝 顏路 同窗指出 @BindingAdapter({"bind:image"})  改爲 @BindingAdapter({"image"}) 就不會有警告了。

 

點擊事件

 

MainActivity 中聲明方法:

 

//參數View必須有,必須是public
//參數View不能改爲對應的控件,只能是View,不然編譯不經過
public void onClick(View view) {
   Toast.makeText(this,"點擊事件", Toast.LENGTH_LONG).show(); }

 

佈局中:

 

 

最後記得在 MainActivity 中調用:

 

binding.setMainActivity(this);

 

發現:佈局文件中,variable 中的 name,在 binding 中都會生成一個對應的 set方法,如:setMainActivity。有 set方法,那就應該有 get方法,試試 getMainActivity,還真有。

 

運行下看看效果:

 

 

固然若是你不想把點擊事件寫在 MainActivity 中,你把它單獨寫在一個類裏面:

 

public class MyHandler {
   public void onClick(View view) {
       Toast.makeText(view.getContext(), "點擊事件", Toast.LENGTH_LONG).show();    } }

 

 

MainActivity 調用:

 

binding.setHandle(new MyHandler());

 

調用Activity中變量

 

上面看到它調用 MainActivity中的onClick方法,那麼能夠調用 MainActivity 中的屬性嗎?

 

MainActivity 中定義 mName

 

public static String mName = "MM";

 

佈局中:

 

 

注意:這個變量必須是 public static。

 

數據改變時更新UI

 

當數據發生變化時,咱們能夠這樣更新UI:

 

 

看看調用的這個 setUser 是什麼:

 

 

從反編譯的代碼中能夠看出,setUser 方法中從新綁定了數據。

 

看下效果:

 

 

BaseObservable

 

使用上面的代碼實現了UI的更新你就知足了?其實官方爲咱們提供了更加簡便的方式,使 User繼承BaseObservable,代碼以下:

 

 

只要 user 發生變化,就能達到改變UI的效果。在 MainActivity 中只要調用如下代碼:

 

user.setFirstName("Com");

 

有了 BaseObservable 就夠了?不不不,我比較懶,不想寫那麼多 @Bindable notifyPropertyChanged。萬一裏面有幾十個屬性,那不寫哭起來?並且還有可能寫丟了。

 

Data Binding的開發者貼心得爲咱們準備了一系列的 ObservableField,包括: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat,ObservableDouble 以及 ObservableParcelable (原文藍字部分都是超連接,感興趣的朋友能夠經過原文查看,我這裏就不貼出來了,下文如有藍色字體視爲同等狀況)看看它們的用法。

 

ObservableField 的使用

 

1. 建立User2

 

 

這類裏面 沒有Get/Set

 

2. 佈局文件

 

<TextView    ...    android:text="@{user2.firstName}" />
<TextView    ...    android:text="@{user2.lastName}" />
<TextView    ...    android:text="@{String.valueOf(user2.age)}" />

 

3. MainActivity中:

 

mUser2 = new User2(); binding.setUser2(mUser2); mUser2.firstName.set("Mr"); mUser2.lastName.set("Bean"); mUser2.age.set(20); mUser2.isStudent.set(false);

 

這裏 new 了一個 User2 對象後,直接就綁定了。以後只要 mUser2 中的數據發生變化,UI也會隨之更新。

 

除了這幾個 Map List 也是必不可少的,Data Binding爲咱們提供了ObservableArrayMap ObservableArrayList

 

ObservableArrayMap 的使用

 

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>(); user.put("firstName", "Google"); user.put("lastName", "Inc."); user.put("age", 17);

 

 

ObservableArrayList 的使用

 

 

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



 

在佈局中使用到 ObservableBoolean 類型時,編譯沒法經過:

 

android:text='@{user2.isStudent?"學生":"非學生"}'

 

【目前已知】

將中文改爲英文是能夠經過編譯的,像下面這樣:

 

android:text='@{user2.isStudent?"Student":"Not Student"}'

 

爲什麼使用中文不能夠?緣由未明。(感謝指教)

 

在RecyclerView或ListView中使用

 

前面說了那麼多基礎的用法,可仍是不能達到咱們的需求。幾乎在每一個app中都有列表的存在,RecyclerView ListView,從上面所說的彷佛還看不出 Data Binding RecyclerView ListView 中是否也能起做用。(用屁股想也知道,Google的開發團對怎麼可能會犯這麼低級的錯誤)。下面以 RecyclerView 爲例子:

 

1. 直接看 Item 的佈局(user_item.xml):

 

 

2. RecyclerView 的數據綁定是在 Adapter 中完成的,下面看看 Adapter,這裏使用了一個 Adapter,若是你在使用的時候發現 RecyclerView 的動畫沒了,去這裏尋找答案:

https://realm.io/cn/news/data-binding-android-boyar-mount/

 

 

3. 最後在佈局和 MainActivity 中的使用跟平時的用法同樣。

 

佈局中加入 RecyclerView

 

<android.support.v7.widget.RecyclerView    android:id="@+id/recycler_view"    android:layout_width="match_parent"    android:layout_height="match_parent"/>

 

MainActivity 中:

 

 

這樣就能夠了。

 

不過,在自動生成的 ActivityMainBinding 中,咱們能夠看到根據 RecyclerView的id,會自動生成一個 recyclerView

 

因此在 MainActivity 中,咱們能夠不用 findViewById,直接使用 binding.recyclerView

 

 

來看看效果吧:

 

 

Tips

 

tip1

 

若須要顯示int類型,須要加上"",例如:

 

user.age 爲 int類型,須要這樣用:

 

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

 

或者

 

<TextView      android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{String.valueOf(user.age)}"/>

 

tip2

 

不建議新手使用,出現錯誤的時候根據提示,不容易找到出錯位置。(是根本找不到...)

 

 

參考

 

Google官方(權威,不過全英文。點擊事件寫的好像不對,後來去其餘地方查的)

https://developer.android.com/topic/libraries/data-binding/index.html#data_binding_layout_files

 

Realm(十分全面)

https://realm.io/cn/news/data-binding-android-boyar-mount

 

CSDN-亓斌(有點像google文檔的翻譯版,總體結果類似)

http://blog.csdn.net/qibin0506/article/details/47393725

 

陽春麪的博客(好奇怪的名字)

https://www.aswifter.com/2015/07/04/android-data-binding-1

 

源碼地址:

https://github.com/Gavin-ZYX/DataBindingTest

相關文章
相關標籤/搜索