天天看点

数据绑定(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可以实现多么灵活的功能!

继续阅读