天天看點

資料綁定(data binding)3

勘誤:原文中使用modelview一詞,但實際上mvvm是model-view-viewmodel,故應為viewmodel。

在之前的文章中,我們使用data binding與布局中的<code>textview</code>配合搭建了簡單的app,并說到接下來将會加入圖檔的加載。這會比綁定文字更加麻煩,因為我們從twitter伺服器上拿下來的是url而不是直接的圖檔。現在是時候介紹在mvvm模式中model和viewmodel的差別了。在這個例子中,model擷取的是url,但是view需要的是一個bitmap,那麼model就需要轉換成viewmodel來适應view的需求。要将他們進行如此細緻的區分,是因為從url到bitmap是一個比較并不簡單的過程,需要根據url網絡上去擷取這個圖檔的具體内容。這部分的邏輯已經被我們将要使用的第三方圖像加載庫glide封裝了,不過我們還需要添加一些轉換邏輯。

在這了使用data binding + recyclerview還有一個原因。加載一幅網絡圖檔的代價是很大的,如果你将一個list裡面所有的圖檔都進行加載,而不論它是否要顯示到螢幕上,這是非常浪費性能的。是以應該隻加載要顯示到螢幕上的圖檔,資料綁定也應該隻在這種情況下進行。

我在上一篇中說了,我将使用glide來進行圖像加載。通過glide,你可以使用以下這行簡單的代碼來實作圖像的下載下傳、轉碼、加載過程:

1

2

glide.with(context).load(url).into(imageview);

它會自動異步下載下傳圖檔,并将它顯示到<code>imageview</code>上。glide還能夠幫你進行圖檔緩存等操作,不過我們在此處僅使用它最簡單的圖檔加載功能。

現在看起來我們可以将data binding和imageview結合起來了,這是一個很自然的想法,你可能會這麼做:

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

&lt;?xmlversion="1.0"encoding="utf-8"?&gt;

&lt;layoutxmlns:android="http://schemas.android.com/apk/res/android"

  xmlns:app="http://schemas.android.com/apk/res-auto"&gt;

  &lt;data&gt;

    &lt;variable

      name="status"

      type="com.stylingandroid.databinding.data.status"/&gt;

      name="glide"

      type="com.bumptech.glide.glide"/&gt;

  &lt;/data&gt;

  &lt;relativelayout

    android:id="@+id/status_container"

    android:layout_width="match_parent"

    android:layout_height="match_parent"&gt;

    &lt;!--

      注意:這個裡的databinding将不起作用

    --&gt;

    &lt;imageview

      android:id="@+id/status_avatar"

      android:layout_width="64dp"

      android:layout_height="64dp"

      android:layout_alignparentleft="true"

      android:layout_alignparentstart="true"

      android:layout_alignparenttop="true"

      android:layout_margin="8dip"

      android:contentdescription="@null"

      app:imageurl="@{glide.load(status.imageurl).into(this)}"/&gt;

    .

  &lt;/relativelayout&gt;

&lt;/layout&gt;

這裡實際上有一個問題,我們可以檢視一下data binding的表達式文檔:

a few operations are missing from the expression syntax that you can use in java.

this

super

new

explicit generic invocation

你會發現:

在data binding語句中,<code>this</code>符号是不起作用的!

雖然<code>this</code>沒法使用了,但是我們可以用自定義setter來替代它。當你希望為一個view定義特殊的setter時可以這麼幹。你可以在自定義setter裡面利用glide将擷取的url轉化成bitmap顯示到imageview中。

這是一個自定義setter的例子:

publicfinalclassdatabinder{

    privatedatabinder(){

        //no-op

    }

    @bindingadapter("imageurl")

    publicstaticvoidsetimageurl(imageviewimageview,stringurl){

        contextcontext=imageview.getcontext();

        glide.with(context).load(url).into(imageview);

}

以上是一個很簡單的工具類,它裡面有個函數名為<code>setimageview()</code>,以<code>imageview</code>和<code>string</code>作為輸入參數。不過你應該也注意到了,在這個函數上有一個注解:<code>@bindingadapter("imageurl")</code>,這個注解的作用是向data

binding庫聲明了一個名為imageurl的自定義setter。我們不需要再做其他任何配置,這個注解會在編譯時就幫我們打理好了一切。在加入這條注解之後,我們就可以在xml中使用它:

      app:imageurl="@{status.imageurl}"/&gt;

這個data binding的自定義屬性<code>imageurl是</code>在app命名空間下的(也就是res-auto)。它僅僅需要一個參數——url字元串,它所處的<code>imageview</code>會自動加到自定義setter中。在中也不需要再做其他改動,甚至也不需要加入glide執行個體變量。

這樣就完成了我們的圖像綁定和加載過程,如下圖所示:

資料綁定(data binding)3

雖然解釋了很多,實際上寫的代碼卻很少——幾行自定義setter,再在layout檔案中添加兩行代碼,就足夠了!在下一篇文章中我們再看看怎麼将data binding用在更加有意思的地方。

從我寫這篇文章開始到現在已經有很多人利用自定義setter做了很多有意思的事情。在droidcon nyc會議上,roman nurik向我展示了與本文類似的代碼。我的代碼靈感來源于官方的data binding手冊,特别是使用picasso結合自定義setter進行圖像加載的那部分。看起來使用glide比picasso更容易去了解和使用自定義setter,我相信除了我和roman還有很多人會這麼想,并且都會創作出類似的代碼。

lisa wray利用自定義setter做了一件很有意思的事情:她利用data binding為textview綁定了字型屬性!她的例子完美地展示了自定義setter可以實作多麼靈活的功能!

繼續閱讀