绑定适配器负责对
设置值。例如,设置属性值,就像调用
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()
方法:
Kotlin
@BindingMethods(value = [ BindingMethod( type = android.widget.ImageView::class, attribute = "android:tint", method = "setImageTintList")])
Java
@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
属性:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, padding: Int) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) }
Java
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int padding) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); }
参数类型非常重要。第一个参数决定 与属性关联的视图。第二个参数决定了 指定属性的绑定表达式中接受的类型。
绑定适配器也可用于其他类型的自定义。例如: 可以从工作器线程调用自定义加载器来加载图片。
您还可以使用可接收多个属性的适配器,如 示例:
Kotlin
@BindingAdapter("imageUrl", "error") fun loadImage(view: ImageView, url: String, error: Drawable) { Picasso.get().load(url).error(error).into(view) }
Java
@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
,如以下示例所示:
Kotlin
@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); } }
Java
@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); } }
绑定适配器方法可以在其处理程序中使用旧值。方法 采用旧值和新值时,必须先声明属性的所有旧值, 后跟新值,如以下示例所示:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) { if (oldPadding != newPadding) { view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) } }
Java
@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()); } }
事件处理脚本只能与接口或抽象类(具有 抽象方法,如以下示例所示:
Kotlin
@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) } } }
Java
@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)
。
该库提供了两个接口,用于区分属性和处理程序
:
Kotlin
// 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) }
Java
@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
:
注解,以指定并非必须为每个属性分配绑定
表达式,如以下示例所示:
Kotlin
@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) } } }
Java
@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
注解,如下所示:
Kotlin
@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)
Java
@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"/>
其他资源
如需详细了解数据绑定,请参阅以下资源。