使用数据绑定必须得会使用绑定适配器,使用绑定适配器和kotlin代码一结合才能达到数据绑定库的效果,那就是代码简洁。如果一些逻辑我能在xml里就定义好,使用一行代码就可以,这样不仅复用性高而且代码整体简洁性大大提高,当然这部分也是比较难以理解,尤其是出问题了不太好查找。
前言
其实前面说的事件处理以及我们很常用的一些特性值比如TextView的text都是这个来实现的,所以了解原理以及使用非常必要。那么现在数据绑定库提供了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的高阶函数,就和上面的例子代码中一样。
总结
为了代码的简洁易读,数据绑定必须要会使用,而且对于复杂的逻辑使用绑定适配器,可以简化代码。