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