DataBinding使用指南(一):布局和binding表达式
DataBinding使用指南(四):BindingAdapter
DataBinding使用指南(五):绑定布局视图到架构组件
BindingAdapter 负责对适当的框架调用设置值。例如设置一个属性值,调用 setText() 方法,另一个例子是设置一个事件监听器,比如调用 setOnClickListener() 方法。
DataBinding库允许我们指定调用的方法来设置值,提供我们自己的绑定逻辑,并通过使用适配器指定返回的对象的类型。
设置属性值
每当绑定值发生变化时,生成的绑定类必须使用绑定表达式在视图上调用 setter 方法,我们可以允许DataBinding库自动确定方法,显式声明方法或提供自定义逻辑来选择方法。
自动选择方法
对于名为example
的属性,DataBinding库会自动尝试查找接受兼容类型作为参数的setExample(arg)
方法,不考虑属性的命名空间,只有在搜索方法时使用属性的名称和类型。
例如,给定android:text="@{user.name}"
表达式,DataBinding库将查找接受user.getName()
返回的类型的setText(arg)
方法;如果user.getName()
的返回类型是String,则库将查找接受String参数的setText()
方法;如果该表达式返回一个int
,则库将搜索接受int
参数的setText()
方法,表达式必须返回正确的类型,我们也可以根据需要强制返回值。
即使给定名称不存在任何属性,DataBinding也可以工作。我们可以使用DataBinding为任何setter创建属性。例如,DrawerLayout没有任何属性,但有很多setter,以下布局将自动使用setScrimColor(int)和setDrawerListener(DrawerListener)方法作为app:scrimColor
和app:drawerListener
属性,分别为:
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
指定自定义方法名称
一些属性具有不匹配名称的setter,在这些情况下,可以使用BindingMethods注解将一个属性与setter相关联,注解可以被应用到类上,并且可以包含多个BindingMethods注解,每个重命名的方法一个注解,在我们应用中的任何类都可以添加BindingMethods注解。在下面示例中,android:tint
属性与setImageTintList(ColorStateList)方法关联,而不与setTint()
方法关联:
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
大多数情况下,我们不需要重命名Android框架类中的setter,这些属性已经实现了使用名称约定来自动查找匹配方法。
提供自定义逻辑
一些属性需要自定义绑定逻辑,例如,android:paddingLeft
属性没有关联的setter,但是提供了setPadding(left, top, right, bottom)
方法。使用BindingAdapter注解的静态绑定适配器方法允许我们自定义如何调用属性的setter。
Android框架类的属性已经创建了BindingAdapter
注解,例如,以下示例显示paddingLeft
属性的绑定适配器:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
绑定适配器方法的参数类型很重要,第一个参数确定与属性关联的View类型,第二个参数确定给定属性的绑定表达式中接受的参数类型。
绑定适配器对自定义其他类型很有用,例如,可以从工作线程调用自定义加载器来加载图像。
我们定义的绑定适配器会在发生冲突时覆盖Android框架提供的默认适配器。
我们还可以让适配器接收多个属性,如下所示:
@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
我们还可以在布局中使用适配器,如下例所示:
注意,@drawable/venueError
是指应用中的资源,用@{}
括住资源使其成为有效的绑定表达式。
<ImageView
app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}" />
注意:DataBinding库会忽略用于匹配目的的自定义命名空间。
如果 imageUrl
和 error
都用于 ImageView 对象并且imageUrl
是字符串并且 error
是 Drawable,则调用适配器。
如果我们希望在设置任何属性时调用适配器,则可以将适配器的可选标志 requireAll 设置为 false
,如下例所示:
@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false)
public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) {
if (url == null) {
imageView.setImageDrawable(placeholder);
} else {
MyImageLoader.loadInto(imageView, url, placeholder);
}
}
注意:当发生冲突时,绑定适配器会覆盖默认的数据绑定适配器。
绑定适配器方法可以在其处理程序中选择使用旧值。一个方法采用旧值和新值应首先声明属性的所有旧值,然后再声明新值,如下例所示:
@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());
}
}
事件处理程序只能用于一个抽象类和接口的抽象方法,如下例所示:
@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 android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
当一个监听器有多个方法时,它必须被拆分成多个监听器。例如,View.OnAttachStateChangeListener 有两个方法: onViewAttachedToWindow(View) 和 onViewDetachedFromWindow(View),我们必须创建两个接口来区分它们的属性和处理程序,如下所示:
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
void onViewAttachedToWindow(View v);
}
因为更改一个监听器也会影响另一个监听器,所以我们需要一个适用于任一属性或两者都适用的适配器。我们可以在注解中将 requireAll 设置为 false
,以指定不是每个属性都必须分配一个绑定表达式,如下例所示:
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"}, requireAll=false)
public static void setListener(View view, OnViewDetachedFromWindow detach, OnViewAttachedToWindow attach) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
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);
}
}
};
}
OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view, newListener,
R.id.onAttachStateChangeListener);
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener);
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener);
}
}
}
上面的例子比正常情况稍微复杂一点,因为 View 类使用 addOnAttachStateChangeListener( )和 removeOnAttachStateChangeListener( )方法,而不是 OnAttachStateChangeListener 的 setter 方法。android.databinding.adapters.ListenerUtil
类有助于跟踪以前的监听器,它们可能会在绑定适配器中被删除。
转换对象
自动转换对象
当从绑定表达式返回 Object 时,DataBinding库选择用于设置属性值的方法,该 Object
被转换为所选方法的参数类型,使用 ObservableMap 类存储数据的应用中,此行为很方便,如下例所示:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
注意:我们也可以使用 object.key 表示法引用 map 中的值。例如,上述示例中的 @{userMap["lastName"]} 可以替换为@{userMap.lastName} 。
表达式中的 userMap
对象返回一个值,该值自动转换为用于设置 android:text
属性值的 setText(CharSequence)
方法中找到的参数类型,如果参数类型不明确,则必须在表达式中转换返回类型。
自定义转换
在某些情况下,特定类型之间需要自定义转换。例如,View 的 android:background
属性需要 Drawable,但指定的颜色值是一个整数。以下示例显示了一个需要 Drawable
的属性,但是提供了一个整数:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
每当需要一个 Drawable
并返回一个整数时,该 int
应该转换为一个 ColorDrawable,转换可以使用带有 BindingConversion 注解的静态方法完成,如下所示:
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
但是,绑定表达式中提供的值类型必须一致,不能在同一个表达式中使用不同的类型,如以下示例所示:
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
本文最近更新日期: 2018年4月26日。
DataBinding使用指南(一):布局和binding表达式