绑定适配器

绑定适配器负责对 设置值。例如,设置属性值,就像调用 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:scrimColorapp: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() 方法:

KotlinJava
@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 属性:

KotlinJava
@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());
}

参数类型非常重要。第一个参数决定 与属性关联的视图。第二个参数决定了 指定属性的绑定表达式中接受的类型。

绑定适配器也可用于其他类型的自定义。例如: 可以从工作器线程调用自定义加载器来加载图片。

您还可以使用可接收多个属性的适配器,如 示例:

KotlinJava
@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}" />

如果将 imageUrlerror 用于 ImageView 对象,imageUrl 是一个 字符串,error 是一个 Drawable。如果您想 设置任意属性时要调用的适配器,将可选属性 requireAll 标志设置为 false,如以下示例所示:

KotlinJava
@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);
 
}
}

绑定适配器方法可以在其处理程序中使用旧值。方法 采用旧值和新值时,必须先声明属性的所有旧值, 后跟新值,如以下示例所示:

KotlinJava
@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());
   
}
}

事件处理脚本只能与接口或抽象类(具有 抽象方法,如以下示例所示:

KotlinJava
@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)。 该库提供了两个接口,用于区分属性和处理程序 :

KotlinJava
// 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: 注解,以指定并非必须为每个属性分配绑定 表达式,如以下示例所示:

KotlinJava
@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 方法, OnAttachStateChangeListenerandroid.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 注解,如下所示:

KotlinJava
@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

博文

目前没有任何推荐文档页面。

请尝试您的 Google 账号。