1. 默认setter
对于设置了DataBinding表达式的XML属性,DataBinding会根据属性表达式的返回值查找该属性的setter方法,比如android:text="@{"str"}"
属性是去找setText(String)方法,所以表达式的返回值类型是十分重要的,影响着DataBinding查找的具体方法。
如果View不提供某个属性的XML属性,但是其实包含了该属性的setter,这时候可以直接使用属性名,DataBinding会自动帮我们调用它的setter,比如DrawerLayout的xml没有scrimColor和drawerListener属性,但是存在其setter方法,则可以如下声明:
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
2. 重命名setter
如果属性名存在且有设置该属性的方法,但是方法名不符合传统setter规范,例如tint属性存在setImageTintList()
方法,则可以通过@BindingMethods注解来绑定属性名及其设置方法:
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
3. 自定义setter
假如不存在刚好设置该属性的方法,可以通过@BindingAdapter注解自定义属性所要调用的静态方法,例如在XML中自定leftPadding属性,命名空间在这里是忽略掉的,Activity中添加以下代码:
@BindingAdapter("android:leftPadding")
public static void setLeftPadding(View view, int leftPadding){
view.setPadding(leftPadding,view.getPaddingTop(),view.getPaddingRight(),view.getPaddingBottom());
}
通过这种方式可以把在XML不方便进行的操作转移到Java代码中,或者复用Java代码中已有的方法。BindingAdapter方法可以放置到Activity当中或者另外定义一个类用来存放BindingAdapter方法。
BindingAdapter方法可以接收多个参数,比如
@BindingAdapter({"bind:imageUrl", "bind:error"})
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
<!-- XML -->
<ImageView app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}"/>
但是只有当ImageView中使用了imageUrl和error属性,并且属性类型与方法类型相对应才会调用定义的BindingAdapter方法。
3.1 获取原值
BindingAdapter的方法可以获取到原先的值,此时方法的声明应该是列出所有的属性原值,然后才是新的值:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
if (oldPadding != newPadding) {
view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
}
}
3.2 事件自定义处理
事件处理只能用在带有单个方法的接口或者抽象类上,比如说:
@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
View.OnLayoutChangeListener newValue) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (oldValue != null) {
view.removeOnLayoutChangeListener(oldValue);
}
if (newValue != null) {
view.addOnLayoutChangeListener(newValue);
}
}
}
如果一个监听有多个方法,此时必须要分成多个监听才行。比如 View.OnAttachStateChangeListener有两个回调方法,onViewAttachedToWindow()和onViewDetachedFromWindow(),那我们就必须手动创建两个接口来区分开:
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
void onViewAttachedToWindow(View v);
}
因为改变一个监听器会影响到另外一个,所以必须要有三个Binding Adapter,两个是单独一个方法回调的,还有一个是所有方法回调的。
@BindingAdapter("android:onViewAttachedToWindow")
public static void setListener(View view, OnViewAttachedToWindow attached) {
setListener(view, null, attached);
}
@BindingAdapter("android:onViewDetachedFromWindow")
public static void setListener(View view, OnViewDetachedFromWindow detached) {
setListener(view, detached, null);
}
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
public static void setListener(View view, final OnViewDetachedFromWindow detach,
final OnViewAttachedToWindow attach) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
final OnAttachStateChangeListener newListener;
if (detach == null && attach == null) {
newListener = null;
} else {
newListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
if (attach != null) {
attach.onViewAttachedToWindow(v);
}
}
@Override
public void onViewDetachedFromWindow(View v) {
if (detach != null) {
detach.onViewDetachedFromWindow(v);
}
}
};
}
final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
newListener, R.id.onAttachStateChangeListener);
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener);
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener);
}
}
}
上述的例子比普通的更复杂一点,因为View使用add和remove来操作监听器,而不是单个的set方法。android.databinding.adapters.ListenerUtil这个类帮忙记录设置的监听器,所以我们才有办法去移除掉。
上述代码的@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
注释表明从该api开始才会生成监听器,这个是根据添加监听器方法addOnAttachStateChangeListener(View.OnAttachStateChangeListener)
所要求的。