This document explains how to use the Data Binding Library to write declarative layouts and minimize the glue code necessary to bind your application logic and layoutshtml
本文介绍了如何使用数据绑定库去写声明布局文件和减小绑定你的应用程序的逻辑和布局所必需的粘合代码。java
The Data Binding Library offers both flexibility and broad compatibility — it's a support library, so you can use it with all Android platform versions back to Android 2.1 (API level 7+).android
数据绑定库是一个支持库,它提供了灵活性和广阔的兼容性,因此你能够在版本Android2.1以后的全部android平台中使用它。express
To use data binding, Android Plugin for Gradle 1.5.0-alpha1 or higher is required. See how to update the Android Plugin for Gradle.api
使用数据绑定要求Android中的Gradle插件在1.5.0-alpha1 或者更高的版本。并发
To get started with Data Binding, download the library from the Support repository in the Android SDK manager.app
开始使用数据绑定库前先要在Android SDK 管理者中从支持库中下载该库。框架
To configure your app to use data binding, add the dataBinding element to your build.gradle file in the app module.less
在你的应用程序的模块中的build.gradle文件中添加dataBinding元素去配置你的应用程序去使用数据绑定库的功能。ide
Use the following code snippet to configure data binding:
使用下面的代码片断去配置使用数据绑定
android { .... dataBinding { enabled = true } }
If you have an app module that depends on a library which uses data binding, your app module must configure data binding in its build.gradle file as well.
若是你有一个应用程序模块依赖了一个使用数据绑定的库,你的应用程序模块一样须要在build.gradle文件中配置数据绑定。
Also, make sure you are using a compatible version of Android Studio. Android Studio 1.3 and later provides support for data binding as described in Android Studio Support for Data Binding.
此外,请确保你使用了一个兼容版本的Android Studio。Android Studio 1.3 以后的都为数据绑定提供了支持。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}"/> </LinearLayout> </layout>
<variable name="user" type="com.example.User"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/>
public class User { public final String firstName; public final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
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; } }
By default, a Binding class will be generated based on the name of the layout file, converting it to Pascal case and suffixing "Binding" to it. The above layout file was main_activity.xml so the generate class was MainActivityBinding. This class holds all the bindings from the layout properties (e.g. the user variable) to the layout's Views and knows how to assign values for the binding expressions.The easiest means for creating the bindings is to do it while inflating:
默认的,将会根据布局文件的名字生成一个绑定类,将其转换为Pascal的实例并在名字后面添加Binding做为后缀.上述的布局文件是main_activity.xml,因此生成类叫作MainActivityBinding.该类控制来自布局的视图中的布局属性中的全部绑定而且知道如何去为绑定表达式分配值。在渲染布局时建立绑定是最简单的方式。
@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); }
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
If you are using data binding items inside a ListView or RecyclerView adapter, you may prefer to use:
若是你正在使用数据绑定一个ListView或者RecyclerView适配器中的项目,你可能更喜欢使用:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); //or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
Data Binding allows you to write expressions handling events that are dispatched from the views (e.g. onClick). Event attribute names are governed by the name of the listener method with a few exceptions. For example, View.OnLongClickListener has a method onLongClick(), so the attribute for this event is android:onLongClick. There are two ways to handle an event.
数据绑定容许你写表达式去处理来自视图(点击)的转发的事件。经过监听方法宁子管理事件属性名单,有些例外。例如,View.onLongClickListener有一个onLongCkick()方法,因此对于该事件的属性是android:onLongClick.如下是两个方式去处理事件。
Events can be bound to handler methods directly, similar to the way android: onClick can be assigned to a method in an Activity. One major advantage compared to the View#onClick attribute is that the expression is processed at compile time, so if the method does not exist or its signature is not correct, you receive a compile time error.
事件能够被直接绑定处处理方法上,与在一个Actuvity中使用android:onClick能够被分配到一个方法上的方式相似。与设置View#onClick属性相比一个主要的优点是该表达式在编译时被处理,若是该方法不存在或者它的签名不正确,你将收到一个编译时错误。
The major difference between Method References and Listener Bindings is that the actual listener implementation is created when the data is bound, not when the event is triggered. If you prefer to evaluate the expression when the event happens, you should use listener binding.
方法的引用和监听者的绑定这两种方式主要的不一样是当数据绑定时被监听者的是被真实的建立,而不是在事件触发时。若是你更倾向与在事件发生时去评估该表达式,你应该使用监听者绑定。
To assign an event to its handler, use a normal binding expression, with the value being the method name to call. For example, if your data object has two methods:
使用值是做为方法名的正常的绑定表达式去分配一个事件给处理程序。例如,若是你的数据对象有两个方法。
public class MyHandlers { public void onClickFriend(View view) { ... } }
The binding expression can assign the click listener for a View:
绑定表达式能够分配给视图的点击监听者:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="handlers" type="com.example.Handlers"/> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" android:onClick="@{handlers::onClickFriend}"/> </LinearLayout> </layout>
Listener Bindings are binding expressions that run when an event happens. They are similar to method references, but they let you run arbitrary data binding expressions. This feature is available with Android Gradle Plugin for Gradle version 2.0 and later.
监听者的绑定是在一个事件发生时运行绑定的绑定表达式。他们相似方法的引用,但它们容许你运行任意的数据绑定表达式,该特性在使用Android Gradle插件且Gradle版本为2.0以后是可用的。
In method references, the parameters of the method must match the parameters of the event listener. In Listener Bindings, only your return value must match the expected return value of the listener (unless it is expecting void). For example, you can have a presenter class that has the following method:
在方法的绑定中,方法的参数必须匹配事件监听者的参数。在监听者绑定中,只有你的返回值必须匹配监听者预期返回的值(除非它的愈切返回值是空)。例如,你能够有一个持久化的类,该类有如下方法:
public class Presenter { public void onSaveClick(Task task){} }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="task" type="com.android.example.Task" /> <variable name="presenter" type="com.android.example.Presenter" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{() -> presenter.onSaveClick(task)}" /> </LinearLayout> </layout>
Listeners are represented by lambda expressions that are allowed only as root elements of your expressions. When a callback is used in an expression, Data Binding automatically creates the necessary listener and registers for the event. When the view fires the event, Data Binding evaluates the given expression. As in regular binding expressions, you still get the null and thread safety of Data Binding while these listener expressions are being evaluated.
对于你的表达式,只有做为一个根元素,监听者才容许用lambda表达式表示。
Note that in the example above, we haven't defined the view parameter that is passed into onClick(android.view.View). Listener bindings provide two choices for listener parameters: you can either ignore all parameters to the method or name all of them. If you prefer to name the parameters, you can use them in your expression. For example, the expression above could be written as:
注意在上面的例子中,咱们尚未定义传递到onClick(View view)中的视图参数。监听者绑定队友监听参入提供两种选择:你能够忽略全部方法的参数或者他们的名字。若是你更倾向于命名参数,你能够在你的表达式中使用他们。例如,以上的表达式能够被写做:
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
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){} }
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
public class Presenter { public boolean onLongClick(View view, Task task){} }
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
If the expression cannot be evaluated due to null objects, Data Binding returns the default Java value for that type. For example, null for reference types, 0 for int, false for boolean, etc.
若是表达式由于空对象不能被评估,数不绑定返回一个默认类型的Java值。例如,Null对应引用类型,0对应整型,false对应布尔类型。
If you need to use an expression with a predicate (e.g. ternary), you can use void as a symbol.
若是你须要在表达式中使用断言,你可使用void做为一个标志。
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
Listener expressions are very powerful and can make your code very easy to read. On the other hand, listeners containing complex expressions make your layouts hard to read and unmaintainable. These expressions should be as simple as passing available data from your UI to your callback method. You should implement any business logic inside the callback method that you invoked from the listener expression.
监听表达式是很是强大的,它能够提升你的代码的可读性。另外一方面,监听者包含的复合表达式增长你的布局的阅读难度和不可维护。对于你的回调方法的表达式应该尽量简单就像经过你的UI的传递可用的数据给你的回调方法同样。你应该在从监听表达式调用的回调方法中事件全部的业务逻辑。
Some specialized click event handlers exist and they need an attribute other than android: onClick to avoid a conflict. The following attributes have been created to avoid such conflicts:
存在一些专门的单击事件处理程序须要比android: onClick多一个其余的属性以免冲突。如下的属性被建立去避免冲突:
Class Listener Setter Attribute SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut
Zero or more import elements may be used inside the data element. These allow easy reference to classes inside your layout file, just like in Java.
零个或者更多被导入的元素能够在数据元素里面使用。在你的布局文件里能够就像在Java里 面同样很容易的引用类。
<data> <import type="android.view.View"/> </data>
Now, View may be used within your binding expression:
如今,能够在你的绑定表达式里面使用View
<TextView android:text="@{user.lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
When there are class name conflicts, one of the classes may be renamed to an "alias:"
当类名冲突时,一个类可使用别名。
<import type="android.view.View"/> <import type="com.example.real.estate.View" alias="Vista"/>
<data> <import type="com.example.User"/> <import type="java.util.List"/> <variable name="user" type="User"/> <variable name="userList" type="List<User>"/> </data>
<TextView android:text="@{((User)(user.connection)).lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
Imported types may also be used when referencing static fields and methods in expressions:
当用引用静态字段和方法时,也能够在表达式中使用导入的类型。
<data> <import type="com.example.MyStringUtils"/> <variable name="user" type="com.example.User"/> </data> … <TextView android:text="@{MyStringUtils.capitalize(user.lastName)}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
Just as in Java, java.lang.* is imported automatically.
就像在Java中同样,java.lang.*是自动导入的
<data> <import type="android.graphics.drawable.Drawable"/> <variable name="user" type="com.example.User"/> <variable name="image" type="Drawable"/> <variable name="note" type="String"/> </data>
The variable types are inspected at compile time, so if a variable implements Observable or is an observable collection, that should be reflected in the type. If the variable is a base class or interface that does not implement the Observable* interface, the variables will not be observed!
变量类型在编译时检查,因此若是一个变量实现了Observable或者是一个obsevable集合,在类型中该变量应该被反射。若是该变凉是一个基本类型或者没有实现Observable及其子类接口的接口,该变量就不能被观察!
When there are different layout files for various configurations (e.g. landscape or portrait), the variables will be combined. There must not be conflicting variable definitions between these layout files.
当在不一样的布局文件中且各个布局文件的配置也不一样时,该变凉将被整合。在那些布局文件之间变量的定义绝对不能有冲突。
The generated binding class will have a setter and getter for each of the described variables. The variables will take the default Java values until the setter is called — null for reference types, 0 for int, false for boolean, etc.
生成的绑定类对于每个描述的变量将会生成一个setter和getter构造器。在setter方法被调用以前该变量将得到一个默认的Java值-引用是null,整型是0,布尔类型是false
A special variable named context is generated for use in binding expressions as needed. The value for context is the Context from the root View's getContext(). The context variable will be overridden by an explicit variable declaration with that name.
为了使用绑定表达式中,生成一个特殊的被成为上下文的变量是必须的。该上下文的值就是来自于根视图中经过getContext()方法得到的上下文。该上下文变量将被一个使用名字显示声明的变量覆盖。
By default, a Binding class is generated based on the name of the layout file, starting it with upper-case, removing underscores ( _ ) and capitalizing the following letter and then suffixing "Binding". This class will be placed in a databinding package under the module package. For example, the layout file contact_item.xml will generate ContactItemBinding. If the module package is com.example.my.app, then it will be placed in com.example.my.app.databinding.
默认的,一个绑定类是基于布局文件的名字进行生成,使用大写开始,移除下划线(_)而且后面跟着的字大写而后添加Binding后缀。该类奖杯放置在模块包下的databinding包中。例如,布局文件contact_item.xml将生成ContactItemBinding.若是模块包是com.example.myapp,那么该类将被放置在com.example.myapp.databinding包中。
Binding classes may be renamed or placed in different packages by adjusting the class attribute of the data element. For example:
绑定类的名字能够经过调整数据元素中类的属性能够重命名。例如:
<data class="ContactItem"> ... </data>
<data class=".ContactItem"> ... </data>
<data class="com.example.ContactItem"> ... </data>
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </LinearLayout> </layout>
Here, there must be a user variable in both the name.xml and contact.xml layout files.
这里的name.xml和contact.xml布局文件中都必须有一个user变量
Data binding does not support include as a direct child of a merge element. For example, the following layout is not supported:
数据绑定不支持在做为一个merge元素的直接子元素进行include操做。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <merge> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </merge> </layout>
The expression language looks a lot like a Java expression. These are the same:
表达式语言看上去很像java表达式。下面是一些相同的部分
Mathematical + - / * % 数学运算
String concatenation + 字符串链接
Logical && || 逻辑运算符
Binary & | ^ 位运算符
Unary + - ! ~ 一元运算符
Shift >> >>> << 位移运算
Comparison == > < >= <= 比较运算
instanceof 判断实例类型
Grouping () 分组
Literals - character, String, numeric, null
Cast
Method calls
Field access
Array access []
Ternary operator ?:
android:text="@{String.valueOf(index + 1)}" android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}'
A few operations are missing from the expression syntax that you can use in Java.
一些能够在Java中使用的操做在表达式语法中是没有的
this
super
new
Explicit generic invocation
The null coalescing operator (??) chooses the left operand if it is not null or the right if it is null. null聚合操做(??)若是不为null选择左边的操做数,若是为null,选择右边的操做数
android:text="@{user.displayName ?? user.lastName}"
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
#Property Reference 属性引用
android:text="@{user.lastName}"
#Avoiding NullPointerException 避免空指针异常
Generated data binding code automatically checks for nulls and avoid null pointer exceptions. For example, in the expression @{user.name}, if user is null, user.name will be assigned its default value (null). If you were referencing user.age, where age is an int, then it would default to 0.
生成的数据绑定代码会自动检查空引用和空指针异常。例如,在表达式@{user.name}
,若是user是null,user.name将被配置一个默认的null值。若是你引用user.age,age是一个int类型字段,那么他将默认是0.
#Collections 集合
<data> <import type="android.util.SparseArray"/> <import type="java.util.Map"/> <import type="java.util.List"/> <variable name="list" type="List<String>"/> <variable name="sparse" type="SparseArray<String>"/> <variable name="map" type="Map<String, String>"/> <variable name="index" type="int"/> <variable name="key" type="String"/> </data> … android:text="@{list[index]}" … android:text="@{sparse[index]}" … android:text="@{map[key]}"
#String Literals
android:text='@{map["firstName"]}'
android:text="@{map[`firstName`}" android:text="@{map['firstName']}"
#Resources
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
android:text="@{@string/nameFormat(firstName, lastName)}" android:text="@{@plurals/banana(bananaCount)}"
Have an orange Have %d oranges android:text="@{@plurals/orange(orangeCount, orangeCount)}"
Type Normal Reference Expression Reference String[] @array @stringArray int[] @array @intArray TypedArray @array @typedArray Animator @animator @animator StateListAnimator @animator @stateListAnimator color int @color @color ColorStateList @color @colorStateList
Any plain old Java object (POJO) may be used for data binding, but modifying a POJO will not cause the UI to update. The real power of data binding can be used by giving your data objects the ability to notify when data changes. There are three different data change notification mechanisms, Observable objects, observable fields, and observable collections.
数据绑定可使用全部的Java对象(POJO),可是修改一个POJO不会引发一个UI的更新。经过给予你的数据对象一个在数据发生变化时能够通知的能力来使用数据绑定真正强大的能力。这是三个不一样的数据改变通知机制,Observable对象,observable字段,observable集合
When one of these observable data object is bound to the UI and a property of the data object changes, the UI will be updated automatically.
当被绑定到UI中的有一个是observable数据对象时,若是数据对象的属性值改变了,UI也会自动更新。
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); } }
The Bindable annotation generates an entry in the BR class file during compilation. The BR class file will be generated in the module package. If the base class for data classes cannot be changed, the Observable interface may be implemented using the convenient PropertyChangeRegistry to store and notify listeners efficiently.
在编译的时候,Bindable注解在BR类文件中生成一个条目。在模块包下生成一个BR文件。若是数据类是一个不能改变的类,Observable接口会经过使用适当的PropertyChangeRegister去有效的存储和通知监听者来实现。
#ObservableFields
private static class User { public final ObservableField<String> firstName = new ObservableField<>(); public final ObservableField<String> lastName = new ObservableField<>(); public final ObservableInt age = new ObservableInt(); }
user.firstName.set("Google"); int age = user.age.get();
Some applications use more dynamic structures to hold data. Observable collections allow keyed access to these data objects. ObservableArrayMap is useful when the key is a reference type, such as String.
一些应用更多地使用动态结构去控制数据。Observable collections 容许键值对的形势去获取数据对象。ObservableArrayMap是很经常使用的一个,它的key是一个引用类型,好比String。
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>(); user.put("firstName", "Google"); user.put("lastName", "Inc."); user.put("age", 17);
In the layout, the map may be accessed through the String keys:
在布局中,能够经过String类型的key访问map。
<data> <import type="android.databinding.ObservableMap"/> <variable name="user" type="ObservableMap<String, Object>"/> </data> … <TextView android:text='@{user["lastName"]}' android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:text='@{String.valueOf(1 + (Integer)user["age"])}' android:layout_width="wrap_content" android:layout_height="wrap_content"/>
ObservableArrayList is useful when the key is an integer:
ObservableArrayList 也是很经常使用的,它的key是一个integer:
ObservableArrayList<Object> user = new ObservableArrayList<>(); user.add("Google"); user.add("Inc."); user.add(17);
In the layout, the list may be accessed through the indices:
在布局中能够经过 下标访问list
<data> <import type="android.databinding.ObservableList"/> <import type="com.example.my.app.Fields"/> <variable name="user" type="ObservableList<Object>"/> </data> … <TextView android:text='@{user[Fields.LAST_NAME]}' android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}' android:layout_width="wrap_content" android:layout_height="wrap_content"/>
The generated binding class links the layout variables with the Views within the layout. As discussed earlier, the name and package of the Binding may be customized. The Generated binding classes all extend ViewDataBinding.
使用Views内部的layout能够将生成的绑定类于布局变量连接起来。就像前面讨论的同样,绑定的包名可疑被自定义。全部生成的绑定类都继承ViewDataBinding这个类
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater); MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
If the layout was inflated using a different mechanism, it may be bound separately:
若是layout是经过不一样的机制进行inflated,那么它能够被单独地绑定。
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
Sometimes the binding cannot be known in advance. In such cases, the binding can be created using the DataBindingUtil class:
有时,咱们并不能预先知道绑定的东西。在这种状况下,可使用DataBindingUtils类去建立绑定。
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId, parent, attachToParent); ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
A public final field will be generated for each View with an ID in the layout. The binding does a single pass on the View hierarchy, extracting the Views with IDs. This mechanism can be faster than calling findViewById for several Views. For example:
在layout中使用View的一个ID为每一个View生成一个public final类型的字段。在View的层级中,绑定根据IDs提取View并简单的传入View的层级中。这个机制比经过调用各类Views的findViewById方法获取View更加的快速。例如:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" android:id="@+id/firstName"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}" android:id="@+id/lastName"/> </LinearLayout> </layout>
Will generate a binding class with:
将会使用以下代码生成一个绑定类:
public final TextView firstName; public final TextView lastName;
IDs are not nearly as necessary as without data binding, but there are still some instances where access to Views are still necessary from code.
若是没有数据绑定,IDs并非必须的,可是仍然有些状况下须要从代码里面访问Views时,IDs仍然是须要的。
<data> <import type="android.graphics.drawable.Drawable"/> <variable name="user" type="com.example.User"/> <variable name="image" type="Drawable"/> <variable name="note" type="String"/> </data>
public abstract com.example.User getUser(); public abstract void setUser(com.example.User user); public abstract Drawable getImage(); public abstract void setImage(Drawable image); public abstract String getNote(); public abstract void setNote(String note);
ViewStubs are a little different from normal Views. They start off invisible and when they either are made visible or are explicitly told to inflate, they replace themselves in the layout by inflating another layout.
ViewStubs 与通常的Views有些许不一样。ViewStubs开始时通常时不可见的,当ViewStubs被置为可见的活着明确调用inflate,ViewStubs将会使用其余的layout替换自己。
Because the ViewStub essentially disappears from the View hierarchy, the View in the binding object must also disappear to allow collection. Because the Views are final, a ViewStubProxy object takes the place of the ViewStub, giving the developer access to the ViewStub when it exists and also access to the inflated View hierarchy when the ViewStub has been inflated.
由于从View的层级中ViewStub自己并不会被渲染出来,绑定对象中的View对于collection也必须容许其消失。由于这个Views是final的,当ViewStub已经被inflated且在ViewStubProxy对象取代ViewStub的位置时,若是得到了View的层级的ViewStub是存的,开发者便能得到该ViewStub。
When inflating another layout, a binding must be established for the new layout. Therefore, the ViewStubProxy must listen to the ViewStub's ViewStub.OnInflateListener and establish the binding at that time. Since only one can exist, the ViewStubProxy allows the developer to set an OnInflateListener on it that it will call after establishing the binding.
在inflating其余的layout时,对于一个新的layout,一个绑定必须是明确的。所以,ViewStubProxy必须监听ViewStubs的ViewStub.OnInflateListener 而且明确这绑定的时间。ViewStubProxy只容许开发者去设置一个OnInflateListener,在明确地绑定时,该监听者将被调用。
At times, the specific binding class won't be known. For example, a RecyclerView.Adapter operating against arbitrary layouts won't know the specific binding class. It still must assign the binding value during the onBindViewHolder(VH, int).
有时,特殊的绑定类不能被了解。例如,一个RecyclerView.Adapter 的操做违反了任意布局不能知道指定的绑定类。当onBindViewHolder(VH,int)回调时它仍然可以被分配到绑定的值。
In this example, all layouts that the RecyclerView binds to have an "item" variable. The BindingHolder has a getBinding method returning the ViewDataBinding base.
好比如下例子,RecyclerView的全部布局绑定一个item变量。BindinngHolder又一个getBinding方法,该方法返回基本的ViewDataBinding。
public void onBindViewHolder(BindingHolder holder, int position) { final T item = mItems.get(position); holder.getBinding().setVariable(BR.item, item); holder.getBinding().executePendingBindings(); }
###Immediate Binding 当即绑定
For an attribute, data binding tries to find the method setAttribute. The namespace for the attribute does not matter, only the attribute name itself.
对于一个属性,数据绑定尝试着去查找setAttribute的方法。于属性的命名空间没有关系,只与属性自己的名字有关。
For example, an expression associated with TextView's attribute android:text will look for a setText(String). If the expression returns an int, data binding will search for a setText(int) method. Be careful to have the expression return the correct type, casting if necessary. Note that data binding will work even if no attribute exists with the given name. You can then easily "create" attributes for any setter by using data binding. For example, support DrawerLayout doesn't have any attributes, but plenty of setters. You can use the automatic setters to use one of these.
例如,一个与TextView的android : text关联的表达式就会查找一个setText(String) 的方法。若是该表达式返回一个int值,数据绑定会继续搜索一个setText(int)的方法。若是有须要,要特别注意表达式返回正确类型。
<android.support.v4.widget.DrawerLayout android:layout_width="wrap_content" android:layout_height="wrap_content" app:scrimColor="@{@color/scrim}" app:drawerListener="@{fragment.drawerListener}"/>
###Renamed Setters 重命名Setters
@BindingMethods({ @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"), })
Some attributes need custom binding logic. For example, there is no associated setter for the android:paddingLeft attribute. Instead, setPadding(left, top, right, bottom) exists. A static binding adapter method with the BindingAdapter annotation allows the developer to customize how a setter for an attribute is called.
一些属性须要自定义绑定逻辑。例如,对于android:paddingLeft属性是与setter没有联系的。而是与setPadding(left,top,right,bottom)方法相关联。使用BindingAdapter注解的一个静态绑定适配器的方法容许开发者去定义该属性的setter方法被调用时作些什么。
The android attributes have already had BindingAdapters created. For example, here is the one for paddingLeft:
android的属性早已被BindingAdaoters建立。例如,这是一个paddingLeft的例子:
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int padding) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); }
Binding adapters are useful for other types of customization. For example, a custom loader can be called off-thread to load an image.
绑定适配器对于自定义的类型是颇有用的。例如,一个自定的一加载器能够调用离线的线程去加载一个图像。
Developer-created binding adapters will override the data binding default adapters when there is a conflict.
当有冲突的时候,被开发者建立的绑定适配器将被默认的绑数据绑定适配器覆盖。
You can also have adapters that receive multiple parameters.
你也能够有一个接收多个参数的适配器
@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}"/>
This adapter will be called if both imageUrl and error are used for an ImageView and imageUrl is a string and error is a drawable.
若是被一个ImageView使用的imageUrl错误或者imageUrl是一个错误的drawable字符串,该适配器都会被调用
Custom namespaces are ignored during matching.
自定义命名空间在匹配的过程当中被忽略
You can also write adapters for android namespace.
你也能够对android命名空间写一个适配器
Binding adapter methods may optionally take the old values in their handlers. A method taking old and new values should have all old values for the attributes come first, followed by the new values:
在他们的处理程序中,绑定适配器的方法能够随意的得到旧的值。一个方法得到旧的值和新的值的时候,首先得到的是该属性的全部旧的值,而后才是新的值。
@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()); } }
@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); } } }
@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); } } }
The above example is slightly more complicated than normal because View uses add and remove for the listener instead of a set method for View.OnAttachStateChangeListener. The android.databinding.adapters.ListenerUtil class helps keep track of the previous listeners so that they may be removed in the Binding Adaper.
以上的例子比通常的例子要稍微复杂。由于View对监听器使用add和remove去替换一个View。OnAttachStateChangeListener的set方法。android.databinding.adapters.ListenerUtil类帮助跟踪以前的简体器一遍监听器能够在Binding Adapter中移除。
By annotating the interfaces OnViewDetachedFromWindow and OnViewAttachedToWindow with @TargetApi(VERSION_CODES.HONEYCOMB_MR1), the data binding code generator knows that the listener should only be generated when running on Honeycomb MR1 and new devices, the same version supported by addOnAttachStateChangeListener(View.OnAttachStateChangeListener).
OnViewDetachedFromWindow和OnViewAttachedToWindsw接口经过使用注解@TaegetApi(VERSION_CODES.HONEYCOMEB_MR1),只有当运行在Honeycomb MR1盒心得设备时,数据绑定代码生产者才会生成监听器。
#Converters 转换器
###Object Conversions 对象转换器
When an Object is returned from a binding expression, a setter will be chosen from the automatic, renamed, and custom setters. The Object will be cast to a parameter type of the chosen setter.
当从一个绑定表达式中返回一个对象,该对象的一个自动的、从命名的河自定义的setters将被选择。该对象的类型将被转换成被选择的setter的参数类型。
This is a convenience for those using ObservableMaps to hold data. for example:
使用ObservableMaps去控制数据的这是很方便的,例如:
<TextView android:text='@{userMap["lastName"]}' android:layout_width="wrap_content" android:layout_height="wrap_content"/>
###Custom Conversions 自定义转换器
<View android:background="@{isError ? @color/red : @color/white}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
<View android:background="@{isError ? @drawable/error : @color/white}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
###Android Studio Support for Data Binding Android Studio对于数据绑定的支持。
Android Studio supports many of the code editing features for data binding code. For example, it supports the following features for data binding expressions:
Android Studio支持不少语句数据绑定代码的代码编辑特性。例如,它支持对于数据绑定表达式的一下特性:
Note: Arrays and a generic type, such as the Observable class, might display errors when there are no errors. 注意:Arrays和通常的类型,例如Observable类没有错误的时候可能会显示错误。
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName, default=PLACEHOLDER}"/>