[译]Data Binding

本文为官网文章翻译 原文地址
这篇文档将展示如何使用 Data Binding 库声明布局以及如何最低化应用逻辑和布局之间的耦合。
Data Binding Library 非常灵活并且具有很高的兼容性---对于这个兼容库,你可以使用Android2.1以上的所有版本的Android平台。
为了使用 data binding,Gradle的 android插件需要是1.5.0-alpha1或者更高。

构建环境

为了使用 Data Binding,通过sdk manager下载 support repository 中的依赖库。
为了配置app应用 data binding,在 app module 的 build.gradle 文件中,添加 dataBinding 元素。
使用下面的代码片段进行data binding 的配资:
android{
....
dataBinding{
enabled=true
}
}
如果你的app module依赖于一个使用了 data binding 的库,你的 app module 也必须在build.gradle 文件中配置 data binding。
同样,你需要确认你的android studio 应该不低于1.3

Data Binding 布局文件

第一个data binding 表达式。

data binding布局文件和普通布局文件稍微有些不同。它以layout为根标签(root tag),随后紧跟data元素和一个view的根元素。view根元素中布局的写法于普通非绑定的布局文件相同。示例:



   
       
   
   
       
       
   

data中user变量描述了一个会在布局中用到的属性。

在布局文件中,表达式使用"@{}"语法描述属性特征。下面是一个示例,将TextView的text属性设置为设置用户名。


Data 对象

假设有一个User普通类

public class User {
   public final String firstName;
   public final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}

这个类型中的数据不能够改变。在一个应用中,数据一旦被读取就不再改变是很常见的场景。当然,也可以使用下面的JavaBeans

public class User {
   private final String firstName;
   private final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
   public String getFirstName() {
       return this.firstName;
   }
   public String getLastName() {
       return this.lastName;
   }
}

从数据绑定的观点来看,上面两个类是等价的。表达式 @{user.firstName}被用来设置TextView的text属性。对于前面的类来说,通过直接访问 firstName 属性的方式;对于后面的类来说,通过getFirstName()的方法。当然,也可以通过firstName()来解决这个问题,如果这个方法存在的话。

绑定数据

默认的,绑定类将被创建,它的名字以布局文件名为基础,遵循Pascal原则,以"Binding"为后缀。比如,上面的布局文件名为 main_activity.xml,因此将产生的类就是MainActivityBinding。这个类持有所有从特征(比如user变量)到布局view的绑定,并且知道怎样为绑定表达式赋值。创建绑定的最简单的方式,就是在它被inflating的时候。

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
}

完成。另外,可以通过下面的方式来得到view:

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
View view = binding.getRoot();

如果你在一个ListView或者RecyclerView的adapter中使用数据绑定,推荐使用:

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

事件处理

Data Binding允许写表达式来处理view事件(比如onClick)。事件属性名和监听器(listener)方法一致(也有一些例外)。举个栗子,View.OnLongClickListener 有 onLongClick()方法,所以,该事件的属性名为 android:onLongClick。属性名所对应的,就是该事件的处理方法。下面介绍两种方式来处理事件:

  • 方法引用(Method References):在表达式中,可以引用方法,该方法应该遵守监听器方法签名的特征(译者:比如参数个数、参数类型、返回值类型、访问限制等)。当一个表达式被认为是对一个方法的引用,Data Binding 将把这个引用方法和所有者对象包裹进一个监听器中,并将该监听器设置到目标view上。
  • 监听绑定(Listener Bindings):当事件发生时,兰姆达表达式将被执行。Data Binding会在view上创建一个监听器,当事件被分发下来,监听器将计算兰姆达表达式。

方法引用

事件可以直接和事件处理器关联绑定,就是android:onClick被指派到Activity中的onClick方法一样。和View#onClick属性相比,方法引用的优势在于,表达式将在编译的时候进行处理,因此,如果方法不存在或者签名不正确,将报编译期错误。
方法引用和监听器绑定主要的不同在于,方法引用中,真正的监听的实现是在数据被绑定的时候,而不是在事件被触发的时候。如果你更喜欢在事件发生的时候计算表达式,应该是用监听绑定(listener binding)
为了给事件指定处理器handler,应该使用一般的绑定的表达式,它的值就是处理事件的方法名。举个栗子

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

绑定表达式给View指定了一个点击监听器:



   
       
       
   
   
       
   

注意,表达式中方法的签名应该和监听器对象中的方法签名保持匹配。

监听绑定

监听表达式将在事件发生的时候绑定事件。这种绑定方式和方法引用类似,但是,这种绑定允许你运行任意的绑定表达式。这个属性需要gradle版本2.0以上。
在方法引用中,方法的参数必须匹配事件监听器方法。在监听器绑定中,仅仅只需要返回值和监听器方法的返回值匹配即可。举个栗子

public class Presenter {
    public void onSaveClick(Task task){}
}

绑定点击事件如下:

 
  
      
          
          
      
      
          

监听器被表现为兰姆达表达式,兰姆达表达式必须作为整个表达式的根元素。当在表达式中使用回调函数,Data Binding将自动创建必要的监听并注册在事件上。当view上的事件发生,Data Binding将计算给定的表达式。
注意,在上面的栗子中,我们并没有定义要传入到onClick(View view)中的view参数。监听器绑定对于监听器参数提供了两种选择:如果不需要监听器(各种listener)参数,你可以选择忽略方法中的所有参数不写,如果需要使用时,应该写出使用所有监听器参数。举个栗子:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"
android:onClick="@{() -> presenter.onSaveClick(task)}"

第一种写了监听器参数,第二种没有写,但是它们是等价的,因为在事实上,处理方法onSavaClick中并没有需要view。
如果你想在表达式中使用监听器参数,可以这样写:

public class Presenter {
    public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

你可以在兰姆达表达式中使用多个参数:

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}
  

如果监听函数没有返回void,你的表达式要注意返回值的一致性.例如,如果你想监听一个long click事件,你的表达式应该返回一个布尔值:

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}
  android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果表达式因为null对象而不能进行计算,Data Binding 会返回该类型下默认的Java值。对于引用类型是null,对于int就是0,对于布尔值就是false。

Data Objects

任何普通对象(POJO)都可以用来进行数据绑定。但是修改一个POJO对象并不能触发UI的更新。数据绑定真正的力量在于当数据更改时UI层能得到更新。这里,将提供三种不同的数据更改通知机制:Observable objects, observable fields 和 ovservable collections.
当这些之中的一个任意一个observable 数据对象被绑定到 UI,当数据对象发生变化时,UI将得到自动更新。

Observable Objects

当一个类实现了Observable接口,这将
Observable接口有一种机制增添和删除listener,但是,nofitying取决于开发者。为了简化开发工作,一个基类,BaseObservable被创建用来实现listener的注册机制。而数据类只需要负责当属性发生的变化的时候进行通知。这个做法,通过对getter方法进行Bindable注解,在setter中进行通知来完成。

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

Bindable注解会在编辑期间产生进入BR类文件的入口。BR类文件被创建在module package中。

ObservableFields

创建一个Observable对象需要做一些工作,因此,如果开发者想要节省时间或者需要更改的属性很少,就可以使用ObservableField。相似的,还有ObserableBoolean,ObserableByte,ObservableChar,ObservableShort,ObservableInt,ObserableLong,ObserableFloat,ObservableDouble,ObservableParcelable等。ObservableFields 是一种自包含的被观察者对象,拥有一个field。

private static class User {
   public final ObservableField firstName =
       new ObservableField<>();
   public final ObservableField lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

为了访问值,使用set和个体方法:

user.firstName.set("Google");
int age = user.age.get();

Observable Collections

有些应用使用更加动态的结构来持有数据。Observable collections允许通过键值对访问这些数据。当键为引用类型时,ObservableArrayMap将是非常有用的。比如,当key为字符串时:

ObservableArrayMap user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

在布局文件中,可以通过key来访问数据:


    
    


当key为Integer时,ObservableArrayList是非常有用的:

ObservableArrayList user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);
 
 

在布局文件中,应该这样通过索引访问:


    
    
    


你可能感兴趣的:([译]Data Binding)