天天看点

Data Binding(数据绑定)用户指南

1)介绍

这篇文章介绍了如何使用Data Binding库来写声明的layouts文件,并且用最少的代码来绑定你的app逻辑和layouts文件。

Data Binding库不仅灵活而且广泛兼容- 它是一个support库,因此你可以在所有的Android平台最低能到Android 2.1(API等级7+)上使用它。

需求:Android Studio 1.3.0-beta1 或 更高版本。

a)测试版本
请注意:Data Binding库当前是测试版本。在Data Binding处于测试阶段时,开发者应该了解以下注意事项:
  • 这是旨在生成开发者的反馈功能的测试版本。它可能包含bugs,或者不适合你的使用案例,所以需要您在使用它时自担风险。即便如此,我们非常希望得到您的反馈!使用issue tracker来让我们知道对于你的使用案例什么可以工作或者不能工作。
  • Data Binding库的测试版本倾向于显著的改变,包括那些不是与您的应用程序兼容的源代码。即,在未来可能会进行大量的返工来更新此库。
  • 虽然伴有标准Android SDK和Google Play服务条款适用警告,开发人员可以随时发布内置了与Data Binding库beta版本的应用程序。而且采用新的库或工具是一个相当好的主意来彻底测试你的应用程序。
  • 我们在这个时候才刚刚开始与Android Studio的支持。未来会有进一步的Android Studio的支持。
  • 通过使用Data Binding库beta版本,你承认这些警告。

2)构建环境

要开始使用Data Binding,首先需要在Android SDK Manager的支持库里下载该库。

请确保您使用的是Android Studio的兼容版本。Android Studio的Data Binding插件需要Android Studio 1.3.0-beta1 或 更高版本。

a)工作环境

你的app要使用Data Binding,需要添加Data Binding到gradle构建文件里,如下:

  1.    dependencies {
  2.        classpath "com.android.tools.build:gradle:1.2.3"
  3.        classpath "com.android.databinding:dataBinder:1.0-rc0"
  4.    }
  5. }

然后确保jcenter在repositories列表里,如下:

  1. allprojects {
  2.    repositories {
  3.        jcenter()
  4.    }
  5. }

在每一个你想要使用Data Binding的module,添加如下的插件:

  1. apply plugin: ‘com.android.application'
  2. apply plugin: 'com.android.databinding'

Data Binding插件将会在你的项目内添加必需提供的以及编译配置依赖。

3)Data Binding Layout文件

a)Data Binding表达式

Data Binding layout文件有点不同的是:起始根标签是layout,接下来一个data元素以及一个view的根元素。这个view元素就是你没有使用Data Binding的layout文件的根元素。举例说明如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  3.    <data>
  4.        <variable name="user" type="com.example.User"/>
  5.    </data>
  6.    <LinearLayout
  7.        android:orientation="vertical"
  8.        android:layout_width="match_parent"
  9.        android:layout_height="match_parent">
  10.        <TextView android:layout_width="wrap_content"
  11.            android:layout_height="wrap_content"
  12.            android:text="@{user.firstName}"/>
  13.        <TextView android:layout_width="wrap_content"
  14.            android:layout_height="wrap_content"
  15.            android:text="@{user.lastName}"/>
  16.    </LinearLayout>
  17. </layout>

在data内描述了一个名为user的变量属性,使其可以在这个layout中使用:

  1. <variable name="user" type="com.example.User"/>

在layout的属性表达式写作

@{}

,下面是一个TextView的text设置为user的firstName属性:

  1. <TextView android:layout_width="wrap_content"
  2.           android:layout_height="wrap_content"
  3.           android:text="@{user.firstName}"/>
b)Data对象

假设你有一个user的plain-old Java Object(POJO):

  1. public class User {
  2.    public final String firstName;
  3.    public final String lastName;
  4.    public User(String firstName, String lastName) {
  5.        this.firstName = firstName;
  6.        this.lastName = lastName;
  7.    }
  8. }

这个类型的对象拥有从不改变的数据。在app中它是常见的,可以读取一次并且之后从不改变。当然也可以使用JavaBeans对象:

  1. public class User {
  2.    private final String firstName;
  3.    private final String lastName;
  4.    public User(String firstName, String lastName) {
  5.        this.firstName = firstName;
  6.        this.lastName = lastName;
  7.    }
  8.    public String getFirstName() {
  9.        return this.firstName;
  10.    }
  11.    public String getLastName() {
  12.        return this.lastName;
  13.    }
  14. }

从Data Binding的角度来看,这两个类是等价的。用于TextView中的

android:text

属性的表达式

@{user.firstName}

将访问前者POJO对象中的

firstName

和后者JavaBeans对象中的

getFirstName()

方法。

c)Binding数据

默认情况下,一个Binding类会基于layout文件的名称而产生,将其转换为Pascal case(译注:首字母大写的命名规范)并且添加“Binding”后缀。上述的layout文件是

activity_main.xml

,因此生成的类名是

ActivityMainBinding

。此类包含从layout属性到layout的Views中所有的bindings(例如

user

变量),并且它还知道如何给Binding表达式分配数值。创建bindings的最简单的方式是在inflating(译注:layout文件与Activity/Fragment的“链接”)期间如下:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3.    super.onCreate(savedInstanceState);
  4.    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
  5.    User user = new User("Test", "User");
  6.    binding.setUser(user);
  7. }

就是这样,运行app后,你将会看到Test User。或者你可以通过如下获取View:

  1. MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

如果你在ListView或者RecyclerView adapter使用Data Binding时,你可能会使用:

  1. ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
  2. false);
  3. //or
  4. ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

4)深入Layout文件

a)Import

零个或多个

import

元素可能在

data

元素中使用。这些只用在你的layout文件中添加引用,就像在Java中:

  1. <data>
  2.     <import type="android.view.View"/>
  3. </data>

现在,View可以使用你的Binding表达式:

  1. <TextView
  2.    android:text="@{user.lastName}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"
  5.    android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

当类名有冲突时,其中一个类名可以重命名为

alias:

  1. <import type="android.view.View"/>
  2. <import type="com.example.real.estate.View"
  3.         alias="Vista"/>

这样,在该layout文件中

Vista

对应

com.example.real.estate.View

,而

View

对应

android.view.View

。导入的类型可以在Variable和表达式中使用作为引用来使用:

  1. <data>
  2.     <import type="com.example.User"/>
  3.     <import type="java.util.List"/>
  4.     <variable name="user" type="User"/>
  5.     <variable name="userList" type="List<User>"/>
  6.  </data>
注意:Android Studio还没有处理imports,所以自动导入Variable在你的IDE不能使用。您的app仍会正常编译,你可以在您的Variable定义中使用完全符合规定的名称来解决该IDE问题。
  1. <TextView
  2.    android:text="@{((User)(user.connection)).lastName}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

导入的类型还可以在表达式中使用static属性和方法:

  1. <data>
  2.     <import type="com.example.MyStringUtils"/>
  3.     <variable name="user" type="com.example.User"/>
  4. </data>
  5. <TextView
  6.    android:text="@{MyStringUtils.capitalize(user.lastName)}"
  7.    android:layout_width="wrap_content"
  8.    android:layout_height="wrap_content"/>

就像在Java中,

java.lang。*

是自动导入的。

b)Variables

data

中可以使用任意数量的

variable

元素。每一个

variable

元素描述了一个用于layout文件中Binding表达式的属性。

  1. <data>
  2.     <import type="android.graphics.drawable.Drawable"/>
  3.     <variable name="user"  type="com.example.User"/>
  4.     <variable name="image" type="Drawable"/>
  5.     <variable name="note"  type="String"/>
  6. </data>

Variable

类型在编译时检查,因此如果一个Variable实现了Observable或observable collection,这应该反映在类型中。(译注:需要查找资料来理解)如果variable是一个没有实现Observable接口的基本类或者接口,Variables不会被observed!

当对于多种配置有不同的layout文件时(如,横向或纵向),Variables会被合并。这些layout文件之间必须不能有冲突的Variable定义。

产生的Binding类对于每一个描述的Variables都会有setter和getter。这些Variables会使用默认的Java值 - null(引用类型)、0(int)、false(boolean)等等,直到调用setter时。

c)自定义Binding类名称

默认情况下,Binding类的命名是基于所述layout文件的名称,用大写开头,除去下划线()以及()后的第一个字母大写,然后添加“Binding”后缀。这个类将被放置在一个模块封装包里的

databinding

封装包下。例如,所述layout文件

contact_item.xml

将生成

ContactItemBinding

。如果模块包是

com.example.my.app

,那么它将被放置在

com.example.my.app.databinding

Binding类可通过调整

data

元素中的

class

属性来重命名或放置在不同的包中。例如:

  1. <data class="ContactItem">
  2.     ...
  3. </data>

在模块封装包的databinding包中会生成名为

ContactItem

的Binding类。如果要想让该类生成在不同的包种,你需要添加前缀

.

,如下:

  1. <data class=".ContactItem">
  2.     ...
  3. </data>

在这个情况下,

ContactItem

类直接在模块包种生成。或者你可以提供整个包名:

  1. <data class="com.example.ContactItem">
  2.     ...
  3. </data>
d)Includes

通过使用application namespace以及在属性中的Variable名字从容器layout中传递Variables到一个被包含的layout:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android"
  3.         xmlns:bind="http://schemas.android.com/apk/res-auto">
  4.    <data>
  5.        <variable name="user" type="com.example.User"/>
  6.    </data>
  7.    <LinearLayout
  8.        android:orientation="vertical"
  9.        android:layout_width="match_parent"
  10.        android:layout_height="match_parent">
  11.        <include layout="@layout/name"
  12.            bind:user="@{user}"/>
  13.        <include layout="@layout/contact"
  14.            bind:user="@{user}"/>
  15.    </LinearLayout>
  16. </layout>

注意:在

name.xml

以及

contact.xml

两个layout文件中必需要有

user

 variable

e)表达式
  • 常用表达式跟Java表达式很像,以下这些是一样的:
    • 数学 

      +

      -

      /

      *

      %

    • 字符串连接 

      +

    • 逻辑 

      &&

      ||

    • 二进制 

      &

      |

      ^

    • 一元运算 

      +

      -

      !

      ~

    • 移位 

      >>

      >>>

      <<

    • 比较 

      ==

      >

      <

      >=

      <=

    • instanceof

    • 分组 

      ()

    • null

    • Cast

    • 方法调用
    • 数据访问 

      []

    • 三元运算 

      ?:

    • 示例:
      1. android:text="@{String.valueOf(index + 1)}"
      2. android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
      3. android:transitionName='@{"image_" + id}'
  • 缺少的操作:
    • this

    • super

    • new

    • 显式泛型调用
  • Null合并操作
    • ??

       - 左边的对象如果它不是null,选择左边的对象;或者如果它是null,选择右边的对象:
      1. android:text="@{user.displayName ?? user.lastName}"
  • 函数上的写法如下:
    1. android:text="@{user.displayName != null ? user.displayName : user.lastName}"
  • 属性引用

    第一个已经在前边提到了

    a)Data Binding表达式

    :JavaBean引用的简短格式。

    当一个表达式引用一个类的属性,它仍使用同样的格式对于字段、getters以及ObservableFields。

    1. android:text="@{user.lastName}"
  • 避免 NullPointerException

    Data Binding代码生成时自动检查是否为nulls来避免出现null pointer exceptions错误。例如,在表达式

    @{user.name}

    中,如果

    user

    是null,

    user.name

    会赋予它的默认值(null)。如果你引用

    user.age

    ,age是

    int

    类型,那么它的默认值是0。
  • 集合

    常用的集合:arrays、lists、sparse lists以及maps,为了简便都可以使用

    []

    来访问。
    1. <data>
    2.   <import type="android.util.SparseArray"/>
    3.   <import type="java.util.Map"/>
    4.   <import type="java.util.List"/>
    5.   <variable name="list" type="List<String>"/>
    6.   <variable name="sparse" type="SparseArray<String>"/>
    7.   <variable name="map" type="Map<String, String>"/>
    8.   <variable name="index" type="int"/>
    9.   <variable name="key" type="String"/>
    10. </data>
    11. android:text="@{list[index]}"
    12. android:text="@{sparse[index]}"
    13. android:text="@{map[key]}"
  • 字符串

    当使用单引号包含属性值时,在表达式中使用双引号很容易:

    1. android:text='@{map["firstName"]}'
    使用双引号来包含属性值也是可以的。字符串前后需要使用"`":
    1. android:text="@{map[`firstName`]}"
    2. android:text="@{map["firstName"]}"
  • Resources

    使用正常的表达式来访问resources也是可行的:

    1. android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
    格式化字符串和复数可以通过提供参数来判断
    1. android:text="@{@string/nameFormat(firstName, lastName)}"
    2. android:text="@{@plurals/banana(bananaCount)}"
    当复数需要多个参数时,所有的参数都会通过:
    1. Have an orange
    2. Have %d oranges
    3. android:text="@{@plurals/orange(orangeCount, orangeCount)}"
    一些资源需要显式类型判断:
类型 正常引用 表达式引用
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

5)Data 对象

任何Plain old Java object(POJO)可用于Data Binding,但修改POJO不会导致UI更新。Data Binding的真正能力是当数据变化时,可以通知给你的Data对象。有三种不同的数据变化通知机制:

Observable

对象、

ObservableFields

以及

observable  collections

当这些可观察Data对象绑定到UI,Data对象属性的更改后,UI也将自动更新。

a)Observable 对象

实现

android.databinding.Observable

接口的类可以允许附加一个监听器到Bound对象以便监听对象上的所有属性的变化。

Observable

接口有一个机制来添加和删除监听器,但通知与否由开发人员管理。为了使开发更容易,一个

BaseObservable

的基类为实现监听器注册机制而创建。Data实现类依然负责通知当属性改变时。这是通过指定一个

Bindable

注解给getter以及setter内通知来完成的。

  1. private static class User extends BaseObservable {
  2.    private String firstName;
  3.    private String lastName;
  4.    @Bindable
  5.    public String getFirstName() {
  6.        return this.firstName;
  7.    }
  8.    @Bindable
  9.    public String getFirstName() {
  10.        return this.lastName;
  11.    }
  12.    public void setFirstName(String firstName) {
  13.        this.firstName = firstName;
  14.        notifyPropertyChanged(BR.firstName);
  15.    }
  16.    public void setLastName(String lastName) {
  17.        this.lastName = lastName;
  18.        notifyPropertyChanged(BR.lastName);
  19.    }
  20. }

在编译期间,

Bindable

注解在BR类文件中生成一个Entry。BR类文件会在模块包内生成。如果用于Data类的基类不能改变,

Observable

接口通过方便的

PropertyChangeRegistry

来实现用于储存和有效地通知监听器。

b)Observable 字段

一些小工作会涉及到创建Observable类,因此那些想要节省时间或者几乎没有几个属性的开发者可以使用

ObservableFields

ObservableFields

是自包含具有单个字段的observable对象。它有所有基本类型和一个是引用类型。要使用它需要在data对象中创建public final字段:

  1. private static class User extends BaseObservable {
  2.    public final ObservableField<String> firstName =
  3.        new ObservableField<>();
  4.    public final ObservableField<String> lastName =
  5.        new ObservableField<>();
  6.    public final ObservableInt age = new ObservableInt();
  7. }

就是这样,要访问该值,使用set和get方法:

  1. user.firstName.set("Google");
  2. int age = user.age.get();
c)Observable 集合

一些app使用更多的动态结构来保存数据。Observable集合允许键控访问这些data对象。

ObservableArrayMap

用于键是引用类型,如

String

  1. ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
  2. user.put("firstName", "Google");
  3. user.put("lastName", "Inc.");
  4. user.put("age", 17);

在layout文件中,通过String键可以访问map:

  1. <data>
  2.     <import type="android.databinding.ObservableMap"/>
  3.     <variable name="user" type="ObservableMap<String, Object>"/>
  4. </data>
  5. <TextView
  6.    android:text='@{user["lastName"]}'
  7.    android:layout_width="wrap_content"
  8.    android:layout_height="wrap_content"/>
  9. <TextView
  10.    android:text='@{String.valueOf(1 + (Integer)user["age"])}'
  11.    android:layout_width="wrap_content"
  12.    android:layout_height="wrap_content"/>

ObservableArrayList

用于键是整数:

  1. ObservableArrayList<Object> user = new ObservableArrayList<>();
  2. user.add("Google");
  3. user.add("Inc.");
  4. user.add(17);

在layout文件中,通过索引可以访问list:

  1. <data>
  2.     <import type="android.databinding.ObservableList"/>
  3.     <import type="com.example.my.app.Fields"/>
  4.     <variable name="user" type="ObservableList<Object>"/>
  5. </data>
  6. <TextView
  7.    android:text='@{user[Fields.LAST_NAME]}'
  8.    android:layout_width="wrap_content"
  9.    android:layout_height="wrap_content"/>
  10. <TextView
  11.    android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
  12.    android:layout_width="wrap_content"
  13.    android:layout_height="wrap_content"/>

6)Binding生成

Binding类的生成链接了layout中variables与Views。如前面所讨论的,Binding的名称和包名可以定制。所生成的Binding类都扩展了

android.databinding.ViewDataBinding

a)创建

Binding应在inflation之后就立马创建,以确保View层次结构不在之前打扰layout中的binding到views上的表达式。有几个方法可以绑定到一个layout。最常见的是在Binding类上使用静态方法.

inflate

方法载入View的层次结构并且绑定到它只需这一步。还有一个更简单的版本,只需要

LayoutInflater

还有一个是采用

ViewGroup

  1. MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
  2. MyLayoutBinding binding = MyLayoutBinding.inflate(LayoutInflater, viewGroup, false);

如果使用不同的机制载入layout,他可一分开绑定:

  1. MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有时Binding不能提前知道,对于这种情况,可以使用

DataBindingUtil

类来创建Binding:

  1. ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
  2.     parent, attachToParent);
  3. ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
b)带ID的Views

在layout中对于每个带ID的View会生成一个public final字段。Binding在View层次结构上做单一的传递,提取带ID的Views。这种机制比起某些Views使用

findViewById

还要快。例如:

  1. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  2.    <data>
  3.        <variable name="user" type="com.example.User"/>
  4.    </data>
  5.    <LinearLayout
  6.        android:orientation="vertical"
  7.        android:layout_width="match_parent"
  8.        android:layout_height="match_parent">
  9.        <TextView android:layout_width="wrap_content"
  10.            android:layout_height="wrap_content"
  11.            android:text="@{user.firstName}"
  12.    android:id="@+id/firstName"/>
  13.        <TextView android:layout_width="wrap_content"
  14.            android:layout_height="wrap_content"
  15.            android:text="@{user.lastName}"
  16.   android:id="@+id/lastName"/>
  17.    </LinearLayout>
  18. </layout>

它会生成如下的Binding类:

  1. public final TextView firstName;
  2. public final TextView lastName;

IDs不像没有Data Bindings那样几乎没有必要,但是仍然会有一些实例需要从代码中访问Views。

c)Variables

每个Variable会有访问方法。

  1. <data>
  2.     <import type="android.graphics.drawable.Drawable"/>
  3.     <variable name="user"  type="com.example.User"/>
  4.     <variable name="image" type="Drawable"/>
  5.     <variable name="note"  type="String"/>
  6. </data>

它会在Binding中生成setters和getters:

  1. public abstract com.example.User getUser();
  2. public abstract void setUser(com.example.User user);
  3. public abstract Drawable getImage();
  4. public abstract void setImage(Drawable image);
  5. public abstract String getNote();
  6. public abstract void setNote(String note);
d)ViewStubs

ViewStubs跟正常的Views略有不同。他们开始时是不可见的,当他们要么设置为可见或被明确告知要载入时,它们通过载入另外一个layout取代了自己。

由于ViewStub基本上从View的层次结构上消失,在Binding对象的View也必须消失来允许被收集。因为Views是最后的,一个

ViewStubProxy

对象取带ViewStub,给开发者获得了ViewStub,当它存在以及还可以访问载入的View层次结构时当ViewStub已被载入时。

当载入另一个layout,为新的布局必需创建一个Binding。因此,

ViewStubProxy

必需监听

ViewStub

OnInflateListener

监听器并在那个时候建立Binding。因为只有一个可以存在,

ViewStubProxy

允许开发者在其上设置一个

OnInflateListener

它会在建立Binding后调用。

e)Binding进阶
  • 动态Variables

有时,不知道具体的Binding类,例如,一个

RecyclerView

适配器对layouts任意操作并不知道具体的Binding类。它仍然必需在

onBindViewHolder

期间赋值给Binding。

在这个例子中,该

RecyclerView

绑定的所有layouts有一个“item”的Variable。该

BindingHolder

有一个

getBinding

方法返回

ViewDataBinding

  1. public void onBindViewHolder(BindingHolder holder, int position) {
  2.    final T item = mItems.get(position);
  3.    holder.getBinding().setVariable(BR.item, item);
  4.    holder.getBinding().executePendingBindings();
  5. }
  • 直接Binding

当一个variable或observable变化时,binding会在下一帧之前被计划要改变。有很多次,但是在Binding时必须立即执行。要强制执行,使用

executePendingBindings()

方法。

  • 后台线程

只要它不是一个集合,你可以在后台线程中改变你的数据模型。在判断是否要避免任何并发问题时,Data Binding会对每个Varialbe/field本地化。

7)属性Setters

每当绑定值的变化,生成的Binding类必须调用setter方法。Data Binding框架有可以自定义赋值的方法。

a)自动Setters

对于一个属性,Data Binding试图找到

setAttribute

方法。与该属性的namespace并不什么关系,仅仅与属性本身名称有关。

例如,有关TextView的

android:text

属性的表达式会寻找一个

setText(String)

的方法。如果表达式返回一个

int

,Data Binding会搜索的

setText(int)

方法。注意:要表达式返回正确的类型,如果需要的话使用

casting

。Data Binding仍会工作即使没有给定名称的属性存在。然后,您可以通过Data Binding轻松地为任何setter“创造”属性。例如,

DrawerLayout

没有任何属性,但大量的setters。您可以使用自动setters来使用其中的一个。

  1. <android.support.v4.widget.DrawerLayout
  2.     android:layout_width="wrap_content"
  3.     android:layout_height="wrap_content"
  4.     app:scrimColor="@{@color/scrim}"
  5.     app:drawerListener="@{fragment.drawerListener}"/>
b)重命名的Setters

一些有setters的属性按名称并不匹配。对于这些方法,属性可以通过

BindingMethods

注解相关联。这必须与一个包含

BindingMethod

注解的类相关联,每一个用于一个重命名的方法。例如,

android:tint

属性与

setImageTintList

相关联,而不与

setTint

相关。

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

以上例子,开发者需要重命名setters是不太可能了,android架构属性已经实现了。

c)自定义Setters

有些属性需要自定义绑定逻辑。例如,对于

android:paddingLeft

属性并没有相关setter。相反,

setPadding(left, top, right, bottom)

是存在在。一个带有

BindingAdapter

注解的静态绑定适配器方法允许开发者自定义setter如何对于一个属性的调用。

Android的属性已经创造了

BindingAdapters

。举例来说,对于

paddingLeft

  1. @BindingAdapter("android:paddingLeft")
  2. public static void setPaddingLeft(View view, int padding) {
  3.    view.setPadding(padding,
  4.                    view.getPaddingTop(),
  5.                    view.getPaddingRight(),
  6.                    view.getPaddingBottom());
  7. }

Binding适配器对其他定制类型非常有用。例如,自定义loader可以用来异步载入图像。

当有冲突时,开发人员创建的Binding适配器将覆盖Data Binding默认适配器。

您也可以创建可以接收多个参数的适配器。

  1. @BindingAdapter({"bind:imageUrl", "bind:error"})
  2. public static void loadImage(ImageView view, String url, Drawable error) {
  3.    Picasso.with(view.getContext()).load(url).error(error).into(view);
  4. }
  1. <ImageView app:imageUrl=“@{venue.imageUrl}”
  2. app:error=“@{@drawable/venueError}”/>

如果对于一个

ImageView

imageUrl和error都被使用并且

imageUrl

是一个string类型以及error是一个drawable时,该适配器会被调用。

  • 匹配的过程中自定义namespaces将被忽略。
  • 你也可以为Android namespaces写适配器。

8)转换

a)对象转换

当从Binding表达式返回一个对象,一个setter会从自动、重命名以及自定义的setters中选择。该对象将被转换为所选择的setter的参数类型。

这是为了方便那些使用

ObservableMaps

来保存数据。例如:

  1. <TextView
  2.    android:text='@{userMap["lastName"]}'
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

userMap

返回一个对象并且该对象将自动转换为

setText(CharSequence)

的参数类型。当有关参数类型可能混乱时,开发人员需要在表达式中转换。

b)自定义转换

有时候转换应该是自动的在特定类型之间。例如,设置背景的时候:

  1. <View
  2.    android:background="@{isError ? @color/red : @color/white}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

这里,背景需要

Drawable

对象,但颜色是一个整数。不管何时有

Drawable

并且返回值是一个整数,那么整数类型会被转换为

ColorDrawable

。这个转换是通过使用带有

BindingConversion

注解的静态方法完成的:

  1. @BindingConversion
  2. public static ColorDrawable convertColorToDrawable(int color) {
  3.    return new ColorDrawable(color);
  4. }

注意:转换仅仅发生在setter级别,因此它是不允许以下混合类型:

  1. <View
  2.    android:background="@{isError ? @drawable/error : @color/white}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

---------------------<完>-----------------------