DataBinding全解析3-绑定适配器

·  阅读 547

前言

我们在使用数据绑定库时,只要按规则写好了XML布局,库会自动为我们生成一个Binding类,这个类就包含了我们的布局文件,以及数据,我们只需要把数据(一般是ViewModel)传递给这个Binding类即可,我们无法像在Activity/Fragment中通过observe函数来传递观察者来进行数据观察,我们只需要在Binding类传递一个lifecycleOwner(Activity/Fragment)给它即可,数据观察就可以自动完成。

了解了上面原理后,我们就更需要熟悉这个绑定适配器,只有绑定适配器了解了,才可以更好的处理数据。

正文

根据官方文档,我们有大概有3种方式来处理数据,下面分别介绍。

自动选择法

这个很好理解,就是特征值在视图代码中是通过setXXX来设置其值的。比如有个特性值是example,那么库自动尝试查找接受兼容类型作为参数的方法setExample(arg),搜索方法主要就看特性名和类型。

再举个官方文档的例子,比如:

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

表达式,库会查找接受user.getName()所返回类型的setText(arg)方法,假如user.getName()的返回类型没有,则无法找到。

总结来说,自动选择法,就根据特性值的setter方法来找到对应的函数。

指定自定义方法名称

前面说的特性值自动选择法,是要有名称符合的setter方法,才可以调用成功,但是很多属性根本就没有,这时可以把一个特性值和本来存在的方法给关联起来。

比如android:tint属性是和setImageTintList关联的,而不是和setTint方法关联,所以这里使用@BindingMethods来为特性值指定方法名称:

@BindingMethods(value = [
        BindingMethod(
            type = android.widget.ImageView::class,
            attribute = "android:tint",
            method = "setImageTintList")])
复制代码

在这个例子中,在ImageView视图中,使用tint,就能自动得调用setImageTintList方法来设置其值了。

关于这个,其实在数据绑定库中,已经为我们做了很多这种转换了,方便我们在日常开发中使用,比如TextView,系统已经为我们提供了如下BindingMethod了:

@BindingMethods({
        @BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"),
        @BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),
        @BindingMethod(type = TextView.class, attribute = "android:editorExtras", method = "setInputExtras"),
        @BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"),
        @BindingMethod(type = TextView.class, attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"),
        @BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps"),
        @BindingMethod(type = TextView.class, attribute = "android:textColorHighlight", method = "setHighlightColor"),
        @BindingMethod(type = TextView.class, attribute = "android:textColorHint", method = "setHintTextColor"),
        @BindingMethod(type = TextView.class, attribute = "android:textColorLink", method = "setLinkTextColor"),
        @BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener"),
})
复制代码

提供自定义逻辑,BindingAdapter

对上面逻辑再进一步扩展,上面通过BindingMethod只能把属性和视图里有的方法给关联起来,那现在我想自定义特性值以及自定义方法,我还要定义好几个,那咋办,就是绑定适配器BindingAdapter使用的时候了。

先看官方例子,现在我想给视图设置paddingLeft,但是又没有现成的setPaddingLeft方法,只有setPadding方法设置4个方向的padding,那直接使用BindingAdapter把paddingLeft属性和一个自定义的方法给绑定起来:

@BindingAdapter("android:paddingLeft")
    fun setPaddingLeft(view: View, padding: Int) {
        view.setPadding(padding,
                    view.getPaddingTop(),
                    view.getPaddingRight(),
                    view.getPaddingBottom())
    }
复制代码

官方还有个经典例子,就是给图片设置url,这个我们经常使用,同时这里还是涉及2个特性值,我们直接在xml中给ImageView设置imageUrl和error 2个特性值,然后在代码中来获取这2个特性值对应的值,再对ImageView做处理,代码就是:

 @BindingAdapter("imageUrl", "error")
    fun loadImage(view: ImageView, url: String, error: Drawable) {
        Picasso.get().load(url).error(error).into(view)
    }
复制代码

好了,到这里官方例子就说完了,注意这里的参数,第一个就是你需要设置的View,后面就是值。

还有一个问题,就是多个特性值,而且在方法里我已经把特性值的参数类型在方法里规定了,比如上面的url是String,error是Drawable,且当xml中条件都符合时,才会调用,那有没有办法让其在参数不全的情况下也调用呢,就是requireAll标志位设为false,也就是不全要求的意思,直接看官方例子代码:

  @BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)
    fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) {
        if (url == null) {
            imageView.setImageDrawable(placeholder);
        } else {
            MyImageLoader.loadInto(imageView, url, placeholder);
        }
    }
复制代码

扩展部分

上面基本已经说完了最简单最常用的一些了,在这节可以说一下一些扩展和优化。首先就是Kotlin语言的拓展函数真是太香了,可以大大滴提高代码简洁性,比如我是对所有View都可以设置的特性值,那就对View进行扩展,对EditText的特性值,就对EditText进行扩展,即方便又便于查找,比如本项目中的一个例子:

@BindingAdapter(value = ["afterTextChanged"])
fun EditText.afterTextChanged(action: () -> Unit){
  this.addTextChangedListener(object : TextWatcher{
      override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
      }

      override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
      }

      override fun afterTextChanged(s: Editable?) {
          action()
      }
  })
}
复制代码

这个代码就可以直接写在EditTextKt文件中。

还有一点扩展就是高阶函数的使用,这个在使用中非常的不太熟练,后面还需要加强,比如登录框我需要在EditText内容变更后进行判断是否为空或者是否符合正则等,这时就在afterTextChanged中添加回调,所以这里可以定义一个action的高阶函数,就和上面的例子代码中一样。

总结

为了代码的简洁易读,数据绑定必须要会使用,而且对于复杂的逻辑使用绑定适配器,可以简化代码。

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改