绑定适配器负责对
设置值。例如,设置属性值,就像调用
setText()
方法。另一个
设置事件监听器,比如调用
setOnClickListener()
方法。
借助数据绑定库,您可以指定要调用的用于设置值的方法, 提供自己的绑定逻辑,并通过以下方式指定所返回对象的类型: 使用适配器
设置属性值
每当绑定值发生变化时,生成的绑定类都必须调用 setter 方法。您可以将数据绑定 库会自动确定此方法,您也可以显式声明 方法或提供自定义逻辑来选择方法。
自动选择方法
对于名为 example
的属性,该库会自动查找该方法
接受兼容类型作为参数的 setExample(arg)
。命名空间
属性。仅使用属性名称和类型
。
例如,给定 android:text="@{user.name}"
表达式,该库
查找接受由 返回的类型的 setText(arg)
方法
user.getName()
。如果 user.getName()
的返回类型为 String
,则
库会查找接受 String
参数的 setText()
方法。如果
表达式返回一个 int
,该库会搜索符合以下条件的 setText()
方法:
接受 int
参数。表达式必须返回正确的类型。您可以
必要时对返回值进行类型转换。
即使不存在具有给定名称的特性,数据绑定也会起作用。您可以
使用数据绑定为任何 setter 创建属性。例如,支持
类别
DrawerLayout
没有属性,但有很多 setter。以下布局
系统会自动使用
setScrimColor(int)
和
addDrawerListener(DrawerListener)
方法用作 app:scrimColor
和 app:drawerListener
的 setter
属性:
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
指定自定义方法名称
一些属性具有名称不符的 setter 方法。在这种情况下,
属性可以使用
BindingMethods
注解。注解与类一起使用,并且可以包含多个
BindingMethod
注解,每个重命名的方法对应一个注解。绑定方法是
您可以将其添加到应用中的任何类。
在以下示例中,android:tint
属性与
setImageTintList(ColorStateList)
方法,而不是使用 setTint()
方法:
@BindingMethods(value = [
BindingMethod(
type = android.widget.ImageView::class,
attribute = "android:tint",
method = "setImageTintList")])
@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")
fun setPaddingLeft(view: View, padding: Int) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom())
}
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
参数类型非常重要。第一个参数决定 与属性关联的视图。第二个参数决定了 指定属性的绑定表达式中接受的类型。
绑定适配器也可用于其他类型的自定义。例如: 可以从工作器线程调用自定义加载器来加载图片。
您还可以使用可接收多个属性的适配器,如 示例:
@BindingAdapter("imageUrl", "error")
fun loadImage(view: ImageView, url: String, error: Drawable) {
Picasso.get().load(url).error(error).into(view)
}
@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.get().load(url).error(error).into(view);
}
您可以在布局中使用适配器,如以下示例所示。注意事项
@drawable/venueError
引用应用中的资源。将
设置为 @{}
的资源使其成为有效的绑定表达式。
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
如果将 imageUrl
和 error
用于
ImageView
对象,imageUrl
是一个
字符串,error
是一个
Drawable
。如果您想
设置任意属性时要调用的适配器,将可选属性
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);
}
}
@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")
fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
if (oldPadding != newPadding) {
view.setPadding(newPadding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom())
}
}
@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")
fun setOnLayoutChangeListener(
view: View,
oldValue: View.OnLayoutChangeListener?,
newValue: View.OnLayoutChangeListener?
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (oldValue != null) {
view.removeOnLayoutChangeListener(oldValue)
}
if (newValue != null) {
view.addOnLayoutChangeListener(newValue)
}
}
}
@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)
。
该库提供了两个接口,用于区分属性和处理程序
:
// Translation from provided interfaces in Java:
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewDetachedFromWindow {
fun onViewDetachedFromWindow(v: View)
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewAttachedToWindow {
fun onViewAttachedToWindow(v: 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
)
fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
val newListener: View.OnAttachStateChangeListener?
newListener = if (detach == null && attach == null) {
null
} else {
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
attach.onViewAttachedToWindow(v)
}
override fun onViewDetachedFromWindow(v: View) {
detach.onViewDetachedFromWindow(v)
}
}
}
val oldListener: View.OnAttachStateChangeListener? =
ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener)
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener)
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener)
}
}
}
@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()
而不是 setter 方法,
OnAttachStateChangeListener
。
android.databinding.adapters.ListenerUtil
类可帮助您跟踪这些事件,
监听器,以便在绑定适配器中将其移除。
对象转换
自动转换对象
当从绑定返回 Object
时
表达式,库会选择用于设置
属性。Object
会转换为所选方法的参数类型。这个
在应用中使用
ObservableMap
类
存储数据,如以下示例所示:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
表达式中的 userMap
对象会返回一个值,该值会自动
转换为 setText(CharSequence)
方法中的参数类型,用于
设置 android:text
属性的值。如果参数类型为
不明确,请在表达式中转换返回值类型。
自定义转换
在某些情况下,需要在特定类型之间进行自定义转换。对于
例如,视图的 android:background
属性需要 Drawable
,但
指定的 color
值是一个整数。以下示例展示了
属性需要 Drawable
,但结果提供了一个整数:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
每当需要 Drawable
且返回整数时,请转换 int
ColorDrawable
。
要执行转换,请使用具有
BindingConversion
注解,如下所示:
@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
但是,绑定表达式中提供的值类型必须保持一致。 您不能在同一个表达式中使用不同的类型,如下所示 示例:
// The @drawable and @color represent different value types in the same
// expression, which causes a build error.
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
其他资源
如需详细了解数据绑定,请参阅以下资源。
示例
Codelab
博文
为您推荐
数据绑定库
Discover the latest app development tools, platform updates, training, and documentation for developers across every Android device.
布局和绑定表达式
Discover the latest app development tools, platform updates, training, and documentation for developers across every Android device.
视图绑定
Discover the latest app development tools, platform updates, training, and documentation for developers across every Android device.