Android Data Binding Library 官方文档(译)

卧疾丰暇豫,翰墨时间作。这篇文章主要讲述Android Data Binding Library 官方文档(译)相关的知识,希望能为你提供帮助。
地址: https://developer.android.google.cn/topic/libraries/data-binding/index.html
本文地址: http://blog.csdn.net/jjwwmlp456/article/details/54915981
Data Binding Library ( 数据绑定库) , 旨在减少绑定应用程序逻辑和布局所需的一些耦合性代码
最低支持android 2.1 (API Level 7)
构建环境 使用gradle插件1.5-alpha1以上
在build.gradle添加如下声明

android { .... dataBinding { enabled = true } }

【Android Data Binding Library 官方文档(译)】注: 如果子Moudle使用了数据绑定, 那么最好也在app Moudle中配置一下
数据绑定的布局文件 书写第一组数据绑定表达式
< ?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>

如上, 在< data> 标签中 < variable> 描述了一个变量user, 它对应的类型为”com.example.User”
布局中使用“@ { }”语法来书写表达式。如为TextView的文本设置成user. firstName:
< TextView android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:text= " @ {user.firstName}" />

数据对象 a plain-old java object (POJO) for User:
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; } }

在数据绑定中, 访问@ {user.firstName}, 那么默认就会访问同名的属性firstName, 或对应的getFirstName方法
绑定数据 默认情况下, 绑定类将基于布局文件的名称来产生
如布局文件为main_activity.xml, 这样生成的类是 MainActivityBinding
如下设置Activity的contentView:
@ 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); }

上文说, 在xml中用表达式可以获取数据, 那么同样的, 在代码中也可以设置数据, 如这里的: binding.setUser(user);
还可以使用inflate的方式来获取生成数据绑定类:
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

如果是在一个adapter中来使用, 那么可以如下:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); //or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

事件处理 可以在xml中编写表达式处理事件。比如 View.OnLongClickListener有一个方法为onLongClick(), 那么它对应的事件就是android:onLongClick。当绑定事件后, 会自动将该listener绑定在View上。处理事件有两种方法,
1. 方法引用: 可以引用符合listener 任意方法签名规则的方法。
2. 监听器绑定: 使用lambda表达式
方法引用
比如定义一个符合OnClickListener#onClick(View view)方法参数签名规则的方法:
public class MyHandlers { public void onClickFriend(View view) { ... } }

如下在xml中绑定并使用:
< ?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>

监听器绑定
表达式使用的方法就可以定义的比较随意了, 不用像”方法引用”那样遵循特定规则, 如一个方法
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>

注: 只允许由lambda表达式作为根元素的表达式
像上面, 我们并没有定义onClick(android.view.View) 参数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)}"

您可以使用一个lambda表达式, 它操作多个参数:
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)}"

如果表达式对于null对象无法评估, 那最好对应方法返回一个基本数据类型的默认值。 比如 null for reference types, 0 for int, false for boolean.
如下使用一个三元表达式:
android:onClick= " @ {(v) -> v.isVisible() ? doSomething() : void}"

避免复合监听
存在一些专门的单击事件处理程序, 他们需要一个属性(除了android:onClick)以避免冲突:
Android Data Binding Library 官方文档(译)

文章图片

本文地址: http://blog.csdn.net/jjwwmlp456/article/details/54915981
布局细节 import 在< data> 标签中, import 一些class, 就像在java 代码中一样
< data> < import type= " android.view.View" /> < /data>

在layout布局中使用:
< TextView android:text= " @ {user.lastName}" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:visibility= " @ {user.isAdult ? View.VISIBLE : View.GONE}" />

当有类名称冲突, 其中一个类可以重命名一个别名:
< import type= " android.view.View" /> < import type= " com.example.real.estate.View" alias= " Vista" />

在< variable> 标签来使用import中的类型:
< data> < import type= " com.example.User" /> < import type= " java.util.List" /> < variable name= " user" type= " User" /> < variable name= " userList" type= " List& lt; User& gt; " /> < /data>

< TextView android:text= " @ {((User)(user.connection)).lastName}" android:layout_width= " wrap_content" android:layout_height= " wrap_content" />

也可以在表达式中运用静态属性或方法, 使用MyStringUtils.capitalize():
< 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" />

像java代码中一样, 也会自动引入java.lang.*
Variable 可以定义多个< variable> 标签
< 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>

这些variable变量在编译时会进行类型检查, 如果一个variable实现了Observable接口或是一个Observable的collection, 那就需要在type中声明; 如果不是一个Observable的实现, 那么该variable就不会被观察到
不同的配置有不同的布局文件(如横屏或竖屏), 在这些布局文件之间, 不能定义相互冲突的变量。
自动生成的绑定类, 对每个variable, 都会生成 setter和getter方法。在setter方法被调用前, 它会有一个默认值, 比如 空引用类型null、int类型0、boolean类型false
一个特定的变量context在表达式需要时, 会被生成。这个context值为根view的getContext()值。
自定义绑定 class names 默认情况下,基于布局文件的名称而生成绑定类, 开头大写, 然后移除下划线, 再加上后缀”Binding”。这个类将被放置在一个数据绑定包中, 该包位于moudle包下。例如, 布局文件contact_item.xml将生成ContactItemBinding。如果moudle的包名为com.example.my.app, 那么它将被放置在com.example.my.app.databinding下
通过调整data元素的class属性, 绑定类可以重命名或放置在不同的包。例如:
< data class= " ContactItem" > ... < /data>

这会生成在moudle下的databinding包下, 如果要生成moudle包下, 可以使用”.”前缀:
< data class= " .ContactItem" > ... < /data>

在下面这种情况下, ContactItem直接生成在模块包下。可以使用任何完整的包路径:
< data class= " com.example.ContactItem" > ... < /data>

include variables可以传递给使用了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> < 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>

如上, 在name.xml和contract.xml中都可以使用变量user了
不支持在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>

表达式语言 共同的特征
表达式语言看起来很像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}'

丢失的操作符
this、new、super 如这些, 还是只能在java类中使用
合并null操作符
如下使用 “??” 操作符
android:text= " @ {user.displayName ?? user.lastName}"

等价于:
android:text= " @ {user.displayName != null ? user.displayName : user.lastName}"

属性引用
如下, user有getLastName(), 可以简单引用:
android:text= " @ {user.lastName}"

避免NullPointerException
数据绑定代码会自动检查null值, 避免NullPointerException。像上文说过, 会给定一个默认值
集合
一般的集合都可以使用”[]”操作符: arrays, lists, sparse lists, and maps。例
< data> < import type= " android.util.SparseArray" /> < import type= " java.util.Map" /> < import type= " java.util.List" /> < variable name= " list" type= " List& lt; String& gt; " /> < variable name= " sparse" type= " SparseArray& lt; String& gt; " /> < variable name= " map" type= " Map& lt; String, String& gt; " /> < variable name= " index" type= " int" /> < variable name= " key" type= " String" /> < /data> … android:text= " @ {list[index]}" … android:text= " @ {sparse[index]}" … android:text= " @ {map[key]}"

字符串字面值
如下, 可以使用单引号在属性值上, 用双引号在字符串字面值上:
android:text= ' @ {map[" firstName" ]}'

也可以反过来使用:
android:text= " @ {map[` firstName` }" android:text= " @ {map[' firstName' ]}"

资源
可以使用正常的访问资源的表达式语法:
android:padding= " @ {large? @ dimen/largePadding : @ dimen/smallPadding}"

可以为格式字符串和复数提供参数:
android:text= " @ {@ string/nameFormat(firstName, lastName)}" android:text= " @ {@ plurals/banana(bananaCount)}"

当一个复数多个参数, :
Have an orange Have %d orangesandroid:text= " @ {@ plurals/orange(orangeCount, orangeCount)}"

一些资源需要显式类型的评估:
Android Data Binding Library 官方文档(译)

文章图片

本文地址: http://blog.csdn.net/jjwwmlp456/article/details/54915981
数据对象 任何POJO对象都可以用于数据绑定, 但是更改POJO对象, 并不会引起UI更新。有三种不同的数据更改通知机制: 观察对象, 观察字段和观察集合。当其中一个绑定到用户界面的可观察的数据对象, 观察到数据对象的属性变化, 用户界面将自动更新。
观察对象(Observable Objects) A class implementing the Observable interface , 将被允许附加一个listener, 来监听对象所有属性的改变
Observable interface有一个机制来添加和删除listeners, 但需要通知开发者。为了让开发变得更容易, 提供一个基类, 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 class 。BR类文件将生成在moudle的package下。如果数据类的基类不能被改变, Observable interface的实现类可以使用PropertyChangeRegistry存储和有效地通知listeners。
观察属性(ObservableFields) 如果想做比创建上面的观察对象更少的工作, 那么可以使用ObservableField 和它的同级的一些类型: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable。它们都是一个包含单一属性的可观察的对象。为避免装箱、拆箱操作, 可以在数据类中定义成 public final field …
private static class User { public final ObservableField< String> firstName = new ObservableField< > (); public final ObservableField< String> lastName = new ObservableField< > (); public final ObservableInt age = new ObservableInt(); }

如下, 使用get或set来获取和设置属性值:
user.firstName.set(" Google" ); int age = user.age.get();

观察集合(Observable Collections) 一些应用程序使用更多的动态结构来保存数据。可观察集合允许通过key访问这些数据对象。当key是一个引用类型, 如String, 就可以用ObservableArrayMap。
ObservableArrayMap< String, Object> user = new ObservableArrayMap< > (); user.put(" firstName" , " Google" ); user.put(" lastName" , " Inc." ); user.put(" age" , 17);

在下面的布局中, 就可以通过String类型的key来访问map中的数据:
< data> < import type= " android.databinding.ObservableMap" /> < variable name= " user" type= " ObservableMap& lt; String, Object& gt; " /> < /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" />

当key是一个Integer时, 可用ObservableArrayList:
ObservableArrayList< Object> user = new ObservableArrayList< > (); user.add(" Google" ); user.add(" Inc." ); user.add(17);

布局中使用ObservableArrayList:
< data> < import type= " android.databinding.ObservableList" /> < import type= " com.example.my.app.Fields" /> < variable name= " user" type= " ObservableList& lt; Object& gt; " /> < /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" />

本文地址: http://blog.csdn.net/jjwwmlp456/article/details/54915981
生成Binding 生成的绑定类, 会链接布局的variables。正如前面所讨论的, 绑定的名称和包可能是自定义的的。生成的所有绑定类都 extends ViewDataBinding。
创建 如前文所述, 由相应的布局文件, 而生成了相应的binding class。代码中使用它, 需要LayoutInflater, 如:
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater); MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

如果布局已经inflate了, 在某些场景时, 可以单独绑定:
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有时binding class 不能提前知道。在这种情况下,可以使用DataBindingUtil类创建绑定:
ViewDataBinding binding = DataBindingUtil.inflate( LayoutInflater, layoutId, parent, attachToParent); ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

View中使用id 在布局中的每个使用了id的view, 都会在binding class中创建出一个同名的public属性。这种绑定机制, 比findViewById更快速。例:
< 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>

在binding class中将会生成:
public final TextView firstName; public final TextView lastName;

不使用data binding的view, 可以没有id, 但有些view还是需要在代码中来访问
Variable 将为每个variable生成getter和setter方法。如
< 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);

ViewStub
  • ViewStub 这种view与普通的view有一点不同。它的特点是, 在一开始, 它是不可见的; 在需要可见时, 会由配置的另一个布局来替换自身
  • 简单的说, 如果要使用数据绑定, 那么在ViewStub所配置的布局inflate之前, 该布局就已建立好了data binding
高级绑定 动态Variable
有时,特定的绑定类不会为人所知。例如,一个RecyclerView.Adapter操作的任意布局, 不知其特定的绑定类。仍需要通过onBindViewHolder(VH, int)来绑定值。
在下面的例子中, RecyclerView中的所有布局, 都绑定了一个”item”, 有一个BindingHolder#getBinding() 返回ViewDataBinding 类型:
public void onBindViewHolder(BindingHolder holder, int position) { final T item = mItems.get(position); holder.getBinding().setVariable(BR.item, item); holder.getBinding().executePendingBindings(); }

直接绑定
当一个variable or observable发生了改变, 绑定框架会安排在下一帧进行视图的改变。然而, 有时希望立即发生改变。可以使用executePendingBindings()来强制执行
后台线程
你可以改变你的数据模型在一个后台线程, 只要它不是一个集合。数据绑定框架将本地化每个变量和属性, 以避免任何并发问题。
Attribute Setters 当view使用了绑定表达式, 只要绑定值发生变化, 生成的绑定类必须调用相应的setter方法。定制数据绑定框架的方式方法调用设置值。数据绑定框架允许自定义setter方法。
自动Setters 对于一个attribute, 数据绑定框架将试图查找对应的setAttribute()。它的namespace并不重要, 只关注attribute的name。如, TextView中的属性android:text使用了表达式, 那么框架就会查找setText(String)。如果表达式返回的是int, 那么将会查找setText(int)。所以, 表达式需要返回正确的类型。数据绑定框架, 支持创建一个布局元素(View|ViewGroup)中, 并不存在的属性。
如下, 生成的binding class中, 将生成一个setDrawerListener(listener):
< android.support.v4.widget.DrawerLayout android:layout_width= " wrap_content" android:layout_height= " wrap_content" app:scrimColor= " @ {@ color/scrim}" app:drawerListener= " @ {fragment.drawerListener}" />

重命名 Setters 有一些属性的setter方法, 与它的属性名不匹配。这些属性的setter可能会通过使用@ BindingMethods, 来进行重命名。要求内问包含@ BindingMethod。例如ImageView有一个属性android:tint, 它对应setter方法为setImageTintList(ColorStateList tint):
@ BindingMethods({ @ BindingMethod(type = " android.widget.ImageView" , attribute = " android:tint" , method = " setImageTintList" ), })

开发人员将不太可能需要重命名setter, 因android属性框架已经实现。
自定义Setters 一些属性需要自定义绑定逻辑。例如, 属性android:paddingLeft没有对应的setter方法, 而在view中有一个方法为setPadding(left, top, right, bottom)。可以使用@ BindingAdapter来自定义一个关于属性android:paddingLeft的setter。例:
@ BindingAdapter(" android:paddingLeft" ) public static void setPaddingLeft(View view, int padding) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); }

@ BindingAdapter也可以运用在自定义类型上。 当数据绑定框架创建的Binding adapter
与开发人员创建的有冲突时, 会采用开发人员所创建的来替代。
还可以用Binding adapter来接收多个参数:
@ BindingAdapter({" bind:imageUrl" , " bind:error" }) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.with(view.getContext()).load(url).error(error).into(view); }

对应的xml绑定:
< ImageView app:imageUrl= " @ {venue.imageUrl}" app:error= " @ {@ drawable/venueError}" />

当ImageView中同时使用了属性String imageUrl和Drawlable error, 通过Binding adapter, 这时的setter就是 loadImage()
注意: 在匹配时, 自定义nam

    推荐阅读