Google Data Binding Library 谷歌官方数据绑定库(七)属性Setter

本文深入探讨了Data Binding中属性Setter的使用方式,包括自动Setter、重命名Setter和自定义Setter等,介绍了如何通过不同方法设置属性值,并给出了具体的实现示例。

本文为菜鸟学习笔记兼翻译练习用,翻译可能会不准确,细节请以原文为准,如有不足之处还请见谅,若能斧正,小弟不胜感激。原文地址:Google DataBinding Library

属性Setter

无论何时当一个绑定值变化,创建的绑定类必须调用有绑定表达式的VIew的setter方法。DataBinding框架有数种方法确定要调用哪个方法来设置值。

自动Setter

对于一个属性,DataBinding尝试各种方法设置属性。属性的命名空间并不重要,只需要属性本身的名称。例如,一个TextView的属性android:text对应的表达式会去查找一个setText(String)的方法。如果表达式返回的是int型,DataBinding会查找setText(int)方法。注意要让表达式返回正确的类型,如有必要就进行强制转换。特别注意DataBinding在没有属性匹配给予的名字的情况下仍然会工作。你可以为任意setter简单的“创造”一个属性。例如,DrawerLayout没有任何特有属性,但是有特有的setter。你可以这样使用自动setter:

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}"/>

重命名Setter

有些属性的setter和名字不匹配。对于这些方法,可以通过BindingMetods注解将属性和setter联系在一起。这必须和一个类联系在一起并且包含BindingMethods注解,每个要重命名的方法都要有。例如,android:tint属性和setImageTintList(ColorStateList)联系在一起,而不是setTint:

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})

这不会让开发者需要去重命名Setter;android框架属性已经实施了相关工作。

自定义Setter

一些属性需要自定义绑定逻辑。比如,android:paddingLeft属性没有相关的setter。取而代之的是存在setPadding(left, top, right, bottom)方法。一个用BindingAdapter注解修饰的静态绑定适配器方法允许开发这自定义一个属性的setter如何被调用。

Android属性已经有BindingAdapters创建了。例如,下边是paddingLeft的Setter的实现方法:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}

Binding adapter对其他的自定义类型也十分有用。例如,一个自定义loader可以切换线程加载图片。

当存在冲突是开发者自定义的binding adapter会覆写默认的数据绑定适配器。

你也可以使用接收多个参数的适配器:

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

<ImageView app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}"/>

这个适配器在imageUrl和error都用到时被使用,iamgeUrl是String型,error是drawable。

  • 在匹配时,自定义命名空间会被忽略。
  • 你也可以为android的命名控件写适配器。

绑定适配器可能会随意的从它们的handler取旧值。一个会取旧值和新值的方法应该有所有的旧值和紧随其后的新值。

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

事件handler可以被接口或抽象类的抽象方法使用。例如:

@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()。我们必须要创建两个接口来区别它们的属性和handler。

@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
    void onViewDetachedFromWindow(View v);
}

@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
    void onViewAttachedToWindow(View v);
}

因为改变一个监听器会影响到其他监听器,我们必须使用三个不同的绑定适配器,一个是同时调用两个属性的,还有两个是分别对应单个属性的:

@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的方法处理监听器,代替了对View.OnAttachStateChangeListener使用set方法。android.databinding.adapters.ListenerUtil类可以帮助我们记录之前的监听器痕迹以便于从绑定adapter中移除。

通过用@TargetApi(VERSION_CODES.HONEYCOMB_MR1)修饰OnViewDetachedFromWindowOnViewAttachedToWindow接口,数据绑定代码产生器可以知道监听器只能在Honeycomb MR1和更新的设备上创建监听器,同版本由addOnAttachStateChangeListener(View.OnAttachStateChangeListener)支持。

下一篇翻译Converters,估计要翻译完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值