DataBinding会自动在build目录下生成类. 因为被集成进AndroidStudio所以不需要你手动编译会实时编译, 并且支持大部分代码补全.
apply plugin: "kotlin-kapt" // Kotlin 使用 Databinding必须添加
android{
/.../
dataBinding {
enabled = true;
Databinding不是替代ButterKnife之类的 findById只是他的一个小小的辅助功能而已, 我推荐使用Kotlin来解决这个需求;
Databinding的大部分情况下错误提示很完善, 个别XML书写错误也易于排查
我想强调的是Xml中的@{} 只做赋值或者简单的三元运算或者判空等不要做复杂运算, 否则违背解耦原则.
业务逻辑应该尽量在Model中
ViewModel属于DataBinding自动生成的类
MVP对比MVVM的劣势
MVP通过接口回调实现导致代码可读性差, 阅读顺序不连贯
MVP无法实现双向数据绑定
MVP的实现因人而异, 差异性导致阅读性差
MVP的代码量比MVC还要多, 属于通过提升代码量来解耦, 代码量比MVVM几何倍增
前端任何平台都开始趋向于MVVM, Web领域MVVM属于最成熟的应用
我开源一个基于Kotlin和Databinding特性的RecyclerView库: BRV, 具备无与伦比的简洁和MVVM特性;
我平时项目开发必备框架
Android上最强网络请求 Net
Android上最强列表(包含StateLayout) BRV
Android最强缺省页 StateLayout
JSON和长文本日志打印工具 LogCat
支持异步和全局自定义的吐司工具 Tooltip
开发调试窗口工具 DebugKit
一行代码创建透明状态栏 StatusBar
<layout>
<variable
name="user"
type="com.liangjingkanji.databinding.pojo.UserBean"/>
</data>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.liangjingkanji.databinding.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.userName}"
</RelativeLayout>
</layout>
layout
布局根节点必须是<layout> . 同时layout只能包含一个View标签. 不能直接包含<merge>
<data>标签的内容即DataBinding的数据. data标签只能存在一个.
variable
通过<variable>标签可以指定类, 然后在控件的属性值中就可以使用
<variable name="user" type="com.liangfeizc.databindingsamples.basic.User" />
</data>
通过DataBinding的setxx()方法可以给Variable设置数据. name值不能包含_下划线
import
第二种写法(导入), 默认导入了java/lang包下的类(String/Integer). 可以直接使用被导入的类的静态方法.
<import type="com.liangfeizc.databindingsamples.basic.User" />
<variable name="user" type="User" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.userName}"
Tip: user代表UserBean这个类, 可以使用UserBean中的方法以及成员变量. 如果是getxx()会自动识别为xx. 注意不能使用字符串android, 否则会报错无法绑定.
class
<data>标签有个属性<class>可以自定义DataBinding生成的类名以及路径
<data class="CustomDataBinding"></data>
<data class=".CustomDataBinding"></data>
Tip:注意没有代码自动补全. 自定义路径Module/build/generated/source/apt/debug/databinding/目录下, 基本上不需要自定义路径
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityMainBinding viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserBean userBean = new UserBean();
userBean.setUserName("drake");
viewDataBinding.setUser(userBean);
alias
<variable>标签如果需要导入(import)两个同名的类时可以使用alias属性(别名属性)
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" alias="DetailUser" />
<variable name="user" type="DetailUser" />
include
在include其他布局的时候可能需要传递变量(variable)值过去
<variable
name="userName"
type="String"/>
<include
layout="@layout/include_demo"
bind:userName="@{userName}"/>
include_demo
<variable
name="userName"
type="String"/>
</data>
android:text="@{userName}"
两个布局通过include的bind:<变量名>值来传递. 而且两者必须有同一个变量
DataBinding不支持merge标签传递变量
自动布局属性
DataBinding对于自定义属性支持非常好, 只要View中包含setter方法就可以直接在布局中使用该属性(这是因为DataBinding的库中官方已经帮你写好了很多自定义属性)
public void setCustomName(@NonNull final String customName) {
mLastName.setText("吴彦祖");
然后直接使用(但是IDE没有代码补全)
app:customName="@{@string/wuyanzu}"
但是setter方法只支持单个参数. app:这个命名空间可以随意
数据双向绑定
数据刷新视图
BaseObservable
如果需要数据变化是视图也跟着变化则需要使用到以下两种方法
有两种方式:
继承BaseObservable
public class ObservableUser extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return firstName;
@Bindable
public String getLastName() {
return lastName;
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
简化用法只需要数据模型继承BaseObservable即可, 然后每次变更数据后调用notify()函数既可以刷新视图. 不需要注解.
observableUser.name
observableUser.notifyChange()
如果你无法继承可以通过实现接口方式也可以. 查看BaseObservable实现的接口自己实现即可, 也可以复制代码示例.
还可以监听属性改变事件
ObservableUser.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
属性第一次改变时会回调两次, 之后都只回调一次. 如果使用notifyChange()不会得到id(即i等于0). 使用
notifyPropertyChanged(i)就可以在回调里面得到id.
BaseObservable和Observable的区别
BaseObservable是实现了Observable的类, 帮我们实现了监听器的线程安全问题.
BaseObservable使用了PropertyChangeRegistry来执行OnPropertyChangedCallback
所以我不推荐你直接实现Observable.
ObservableField
这属于第二种方式, databinding默认实现了一系列实现Observable接口的字段类型
BaseObservable,
ObservableBoolean,
ObservableByte,
ObservableChar,
ObservableDouble,
ObservableField<T>,
ObservableFloat,
ObservableInt,
ObservableLong,
ObservableParcelable<T extends Parcelable>,
ObservableShort,
ViewDataBinding
public class PlainUser {
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableField<String> lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
对于集合数据类型ObservableArrayMap/ObservableArrayLis/ObjservableMap等集合数据类型
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
<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"/>
还支持ObservableParcelable<Object>序列化数据类型
上面说的这两种只会视图跟随数据更新, 数据并不会跟随视图刷新.
ObservableField同样支持addOnPropertyChangedCallback监听属性改变
如果数据为LiveData同样支持, 并且ViewDataBinding可以设置生命周期.
视图刷新数据
通过表达式使用@=表达式就可以视图刷新的时候自动更新数据, 但是要求数据实现以下两种方式修改才会触发刷新
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:text="@={model.name}"/>
这种双向绑定存在一个很大的问题就是会死循环. 数据变化(回调监听器)触发视图变化, 然后视图又会触发数据变化(再次回调监听器), 然后一直循环, 设置相同的数据也视为数据变化.
所以我们需要判断当前变化的数据是否等同于旧数据
public class CustomBindingAdapter {
@BindingAdapter("android:text") public static void setText(TextView view, CharSequence text) {
CharSequence oldText = view.getText();
if (!haveContentsChanged(text, oldText)) {
return;
view.setText(text);
private static boolean haveContentsChanged(CharSequence str1, CharSequence str2) {
if ((str1 == null) != (str2 == null)) {
return true;
} else if (str1 == null) {
return false;
final int length = str1.length();
if (length != str2.length()) {
return true;
for (int i = 0; i < length; i++) {
if (str1.charAt(i) != str2.charAt(i)) {
return true;
return false;
根据我上面说的, 监听器至少回调两次(数据->视图, 视图-> 数据)
以下这种是无效的, 因为String参数传递属于引用类型变量并不是常量, 需要用equals()
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
if (text == null || text.equals(oldText) || oldText.length() == 0) {
return;
总结就是如果没有默认实行的控件属性使用双向数据绑定 就需要你自己实现BindingAdapter注解
DataBinding通过注解来控制ViewModel的类生成
@Bindable
用于数据更新自动刷新视图. 后面的数据绑定提到.
@BindingAdapter
创建一个XML属性和函数, 然后在属性中进行设置数据操作会进入该函数.
图片加载框架可以方便使用此方法.
@BindingAdapter(value = { "imageUrl", "error" }, requireAll = false)
public static void loadImage(ImageView view, String url, Drawable error) {
Glide.with(view.getContext()).load(url).into(view);
修饰方法, 要求方法必须public static
第一个参数必须是控件或其父类
方法名随意
最后这个boolean类型是可选参数. 可以要求是否所有参数都需要填写. 默认true.
如果requireAll为false, 你没有填写的属性值将为null. 所以需要做非空判断.
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
app:error="@{@drawable/error}"
wuyanzu:imageUrl="@{imageUrl}"
app:onClickListener="@{activity.avatarClickListener}"
可以看到命名空间可以随意, 但是如果在BindingAdapter的数组内你定义了命名空间就必须完全遵守
@BindingAdapter({ "android:imageUrl", "error" })
public static void loadImage(ImageView view, String url, Drawable error) {
if(url == null) return;
Glide.with(view.getContext()).load(url).into(view);
Tip: 如果你的数据初始化是在异步的. 会回调方法但是数据为null(成员默认值). 所以我们必须要首先进行判空处理.
Kotlin实现有两种方法
单例类+@JvmStatic注解
object ProgressAdapter {
@JvmStatic
@BindingAdapter("android:bindName")
fun setBindName(view: View, name:String){
@BindingAdapter("android:bindName")
fun setBindName(view: View, name:String){
@BindingAdapter("android:bindName")
fun View.setBindName( name:String){
@BindingMethods
如果你想创建一个XML属性并且和View中的函数关联(即会自动使用属性值作为参数调用该函数). 就应该使用@BindingMethods注解一个类(该类无限制甚至可以是一个接口).
如果说@BindingAdapter是创建一个新的函数功能给控件使用, 那么BindingMethod就是引导DataBinding使用控件自身的函数.
该注解属于一个容器. 内部参数是一个@BindingMethod数组, 只能用于修饰类;
任意类或接口, 不需要覆写任何函数
官方示例:
@BindingMethods({
@BindingMethod(type = android.widget.ProgressBar.class, attribute = "android:indeterminateTint", method = "setIndeterminateTintList"),
@BindingMethod(type = android.widget.ProgressBar.class, attribute = "android:progressTint", method = "setProgressTintList"),
@BindingMethod(type = android.widget.ProgressBar.class, attribute = "android:secondaryProgressTint", method = "setSecondaryProgressTintList"),
public class ProgressBarBindingAdapter {
@BindingMethod
注解参数(必选)
type: 字节码 即你的控件类
attribute: XML属性
method: 函数名 即控件中的函数名称
如果属性名和@BindingAdapter定义的XML属性相同会冲突报错
如果控件类中已经存在一个和你定义的属性相关联的函数(例setName函数和android:name属性就相关联)则会优先执行该函数
@BindingConversion
属性值自动进行类型转换
只能修饰public static 方法.
任意位置任意方法名都不限制
DataBinding自动匹配被该注解修饰的方法和匹配参数类型
返回值类型必须和属性setter方法匹配, 且参数只能有一个
要求属性值必须是@{}DataBinding表达式
官方示例:
public class Converters {
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
@BindingConversion
public static ColorStateList convertColorToColorStateList(int color) {
return ColorStateList.valueOf(color);
我写的Kotlin示例
@BindingConversion
fun int2string(integer:Int):String{
Log.d("日志", "(CusView.kt:92) int2string ___ integer = [$integer]")
return integer.toString()
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<variable
name="m"
type="com.example.architecture.Model" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.architecture.CusView
android:bindName="@={m.age}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</layout>
我这代码实际上会报错, 因为涉及到双向数据绑定, @BindingConversion只会在数据设置视图的时候生效. 但是如果是视图设置数据则会走其他函数(get), 如果该函数返回的类型和Model中的类型不匹配则会报异常, 除非你将那个函数改为类型匹配的.
或者去掉=符号不使用双向数据绑定
android:text不能使用int转为string, 因为他本身能正常接收int(作为resourceID). 然后会报
android.content.res.Resources$NotFoundException: String resource ID
@InverseMethod
该注解属于AndroidStudio3之后提供的inverse系列的新注解, 全部都是针对数据双向绑定.
在数据和视图的数据不统一时可以使用该注解@InverseMethod解决数据转换的问题
例如数据模型存储用户的id但是视图不显示id而是显示用户名(数据和视图的类型不一致), 我们就需要在两者之间转换.
我们需要两个函数: 设置数据到视图的函数 称为set / 设置视图变更到数据的函数 称为get
set和get都至少要有一个参数
自身参数必须和另一个函数的返回值对应(不然怎么叫转换)
简单示例:
在用户id和用户名之间转换. 存储id但是显示的时候显示用户名
class Model {
var name = "设计师"
@InverseMethod("ui2data")
fun data2ui():String{
return "设计师金城武"
fun ui2data():String{
return "设计师吴彦祖"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<variable
name="m"
type="com.example.architecture.Model" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.architecture.CusView
android:text="@{m.data2ui(m.name)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</layout>
@InverseBindingAdapter
String attribute 属性值(必填)
String event 非必填, 默认值等于 <attribute>AttrChanged
他和@BindingAdapter配合实现双向数据绑定
完全的双向数据绑定需要三个函数
set (数据到视图)
get (视图到数据)
notify (通知Databinding视图已经刷新可以更新数据(Model)了)
set函数, 之前已经写过了
@BindingAdapter("android:bindName")
fun TextView.setBindName(name:String?){
if (name.isNullOrEmpty() && name != text) {
text = name
get函数
@InverseBindingAdapter(attribute = "android:bindName", event = "cus_event")
fun TextView.getBindName():String{
return text.toString()
不允许存在更多参数
返回值类型必须是绑定的数据类型
notify函数 视图变化后要通知Databinding开始设置Model层, 同样要用到@BindingAdapter, 不同的是参数要求只能为InverseBindingListener.
@BindingAdapter("cus_event")
fun TextView.notifyBindName( inverseBindingListener: InverseBindingListener){
doAfterTextChanged {
inverseBindingListener.onChange()
InverseBindingListener 是个接口只有一个函数, 他是notify函数必要的参数.
public interface InverseBindingListener {
* Notifies the data binding system that the attribute value has changed.
void onChange();
@InverseBindingMethods
同@BindingMethods相似
但是@InverseBindingMethods是视图变更数据(get函数), 而BindingMethods是数据到视图(set函数)
public @interface InverseBindingMethod {
* 控件的类字节码
Class type
();
* 自定义的属性
String attribute();
* nitify函数的名称 即用于通知数据更新的函数
String event() default "";
* 控件自身的函数名称, 如果省略即自动生成为 {attribute}AttrChange
String method() default "";
如果说BindingMethods是关联setter方法和自定义属性, 那么InverseBindingMethods就是关联getter方法和自定义属性;
setter是更新视图的时候使用, 而getter方法是更新数据时候使用的
比@BindingMethods要多一个函数即notify函数用于通知更新
@BindingAdapter("cus_event")
fun TextView.notifyBindName( inverseBindingListener: InverseBindingListener){
doAfterTextChanged {
inverseBindingListener.onChange()
@InverseBindingMethods(
InverseBindingMethod(
type = CusView::class,
attribute = "android:bindName",
method = "getName", event = "cus_event"
object Adapter {
如果attribute属性值属于不存在的属性, 则需要再创建一个BindingAdapter自定义属性来处理.
查看下生成类中的视图更新数据的实现源码
private android.databinding.InverseBindingListener ivandroidTextAttr = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
java.lang.String callbackArg_0 = com.liangjingkanji.databinding.MyInverseBindingAdapter.getTextString(iv);
boolean dataJavaLangObjectNull = false;
java.lang.String dataName = null;
com.liangjingkanji.databinding.Bean data = mData;
dataJavaLangObjectNull = (data) != (null);
if (dataJavaLangObjectNull) {
data.setName(((java.lang.String) (callbackArg_0)));
所以如果你没用重写Inverse的数据变更方法将无法让视图通知数据刷新.
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
java.lang.String dataName = null;
com.liangjingkanji.databinding.Bean data = mData;
if ((dirtyFlags & 0x1aL) != 0) {
if (data != null) {
dataName = data.getName();
if ((dirtyFlags & 0x1aL) != 0) {
com.liangjingkanji.databinding.MyInverseBindingAdapter.setText(this.iv, dataName);
if ((dirtyFlags & 0x10L) != 0) {
com.liangjingkanji.databinding.MyInverseBindingAdapter.setTextWatcher(this.iv, (com.liangjingkanji.databinding.MyInverseBindingAdapter.BeforeTextChanged)null, (com.liangjingkanji.databinding.MyInverseBindingAdapter.OnTextChanged)null, (com.liangjingkanji.databinding.MyInverseBindingAdapter.AfterTextChanged)null, ivandroidTextAttr);
@BindingBuildInfo和@Untaggable这两个注解是DataBinding自动生成Java类时使用的.
Bindable
设置数据刷新视图. 自动生成BR的ID
BindingAdapter
设置自定义属性. 可以覆盖系统原有属性
BindingMethod/BindingMethods
关联自定义属性到控件原有的setter方法
BindingConversion
如果属性不能匹配类型参数将自动根据类型参数匹配到该注解修饰的方法来转换
InverseMethod
负责实现视图和数据之间的转换
InverseBindingAdapter
视图通知数据刷新的
InverseBindingMethod/InverseBindingMethods
视图通知数据刷新的(如果存在已有getter方法可用的情况下)
BindingMethods系优先级高于BindingAdapter系列
所有注解的功能都是基于XML属性值为Databinding表达式才生效(即@{})
建议参考官方实现源码:
DataBindingAdapter
这里指的是XML文件中使用的表达式(用于赋值变量), @{}里面除了可以执行方法以外还可以写表达式, 并且支持一些特有表达式
算术 + - / * %
字符串合并 +
逻辑 && ||
二元 & | ^
一元 + - ! ~
比较 == > < >= <=
Instanceof
Grouping ()
文字 - character, String, numeric, null
Field 访问
Array 访问 []
三元 ?:
避免空指针
variable的值即使设置null或者没有设置也不会出现空指针异常.
这是因为官方已经用DataBinding的@BindingAdapter注解重写了很多属性. 并且里面进行了判空处理.
<variable
name="userName"
type="String"/>
.....
android:text="@{userName}"
不会出现空指针异常.
dataBinding.setUserName(null);
并且还支持特有的非空多元表达式
android:text="@{user.displayName ?? user.lastName}"
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
还是需要注意数组越界的
集合不属于java.lang*下, 需要导入全路径.
<variable
name="list"
type="java.util.List<String>"/>
<variable
name="map"
type="java.util.Map<String, String>"/>
上面这种写法会报错
Error:与元素类型 "variable" 相关联的 "type" 属性值不能包含 '<' 字符。
因为<符号需要转义.
常用转义字符
空格  ;  ;
< 小于号 <; <;
> 大于号 >; >;
& 与号 &; &;
" 引号 "; ";
‘ 撇号 &apos; ';
× 乘号 ×; ×;
÷ 除号 ÷; ÷;
<variable
name="list"
type="java.util.List<String>"/>
<variable
name="map"
type="java.util.Map<String, String>"/>
集合和数组都可以用[]来得到元素
android:text="@{map["firstName"]}"
如果想要在@{}中使用字符串, 可以使用三种方式
android:text='@{"吴彦祖"}'
android:text="@{`吴彦祖`}"
android:text="@{@string/user_name}"
同样支持@color或@drawable
格式化字符串
首先在strings中定义<string>
<string name="string_format">名字: %s 性别: %s</string>
然后就可以使用DataBinding表达式
android:text="@{@string/string_format(`吴彦祖`, `男`)}"
输出内容:
名字: 吴彦祖 性别: 男
如果Variable还没有复制就会使用默认值显示.
android:text="@{user.integral, default=`30`}"
DataBinding本身提供了一个名为context的Variable. 可以直接使用. 等同于View的getContext().
android:text="@{context.getApplicationInfo().toString()}"
引用其他控件
<TextView
android:id="@+id/datingName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_toRightOf="@id/iv_dating"
android:text="活动"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_toRightOf="@id/iv_order"
android:text="@{datingName.text}"
引用包含_的控件id是可以直接忽略该符号. 例如tv_name直接写tvName.
谢谢 lambda 指出错误
不论顺序都可以引用
使用Class
如果想用Class作为参数传递, 那么该Class不能直接通过静态导入来使用. 需要作为字段常量来使用
DataBinding还支持在XML中绑定函数参数类型, 并且还是Lambda和高阶函数类型, 这点比Java还先进.
即直接将对象作为和属性等同的方式在XML使用. 这就必须先手动创建一个对象. 稍显麻烦.
创建自定义属性
object EventDataBindingComponent {
* 在绑定视图时可以用于Model来处理UI, 由于破坏视图和逻辑解耦的规则不是很建议使用
* 这会导致不方便业务逻辑进行单元测试
* @see OnBindViewListener 该接口支持泛型定义具体视图
* @receiver View
* @param block OnBindViewListener<View>
@JvmStatic
@BindingAdapter("view")
fun View.setView(listener: OnBindViewListener) {
listener.onBind(this)
上面使用到的接口
interface OnBindViewListener {
fun onBind(v: View)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<variable
name="v"
type="com.liangjingkanji.databinding.MainActivity"/>
</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="设计师吴彦祖"
android:onClick="@{v::click}"/>
</LinearLayout>
</layout>
在XML中使用高阶函数需要匹配如下规则
BindingAdapter的函数参数要求是一个接口, 不支持Kotlin的函数类型参数
接口只允许一个函数
接口的方法签名(返回值|参数)和传递的高阶函数匹配
Lambda
高阶函数不允许自定义传递参数(否则需要修改接口). 所以可以使用Lambda来进行控制.
创建一个多参数的函数
fun onBinding(v:View, name:String){
Log.d("日志", "(MainActivity.kt:45) this = [$v] name = [$name]")
XML使用
view="@{(view) -> v.onBinding(view, `吴彦祖`)}"
如果不使用参数
view="@{() -> v.onBinding(`吴彦祖`)}
ViewDataBinding
自动生成的DataBinding类都继承自该类. 所以都拥有该类的方法
void addOnRebindCallback(OnRebindCallback listener)
void removeOnRebindCallback(OnRebindCallback listener)
View getRoot()
abstract void invalidateAll()
abstract boolean setVariable(int variableId, Object value)
void unbind()
这里有三个方法需要重点讲解:
abstract boolean hasPendingBindings()
void executePendingBindings()
当你改变了数据以后(在你设置了Observable观察器的情况下)会马上刷新ui, 但是会在下一帧才会刷新UI, 存在一定的延迟时间. 在这段时间内hasPendingBindings()会返回true. 如果想要同步(或者说立刻)刷新UI可以马上调用executePendingBindings().
OnRebindCallback
该监听器可以监听到布局绑定的生命周期
mDataBinding.addOnRebindCallback(new OnRebindCallback() {
* 绑定之前
* @param binding
* @return 如果返回true就会绑定布局, 返回false则取消绑定
@Override public boolean onPreBind(ViewDataBinding binding) {
return false;
* 如果取消绑定则回调该方法(取决于onPreBind的返回值)
* @param binding
@Override public void onCanceled(ViewDataBinding binding) {
super.onCanceled(binding);
* 绑定完成
* @param binding
@Override public void onBound(ViewDataBinding binding) {
super.onBound(binding);
OnPropertyChangedCallback
DataBinding也有个数据变更监听器, 可以监听Variable的设置事件
mDataBinding.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
* 会在DataBinding设置数据的时候回调
* @param sender DataBinding生成的类
* @param propertyId Variable的id
@Override public void onPropertyChanged(Observable sender, int propertyId) {
ActivityMainBinding databinding = (ActivityMainBinding) sender;
switch (propertyId) {
case BR.data:
Log.d("日志", "(MainActivity.java:54) ___ Result = " + databinding.getData().getName());
break;
case BR.dataSecond:
break;
DataBindingUtil
DataBinding不仅可以绑定Activity还可以绑定视图内容(View)
static <T extends ViewDataBinding> T bind(View root)
static <T extends ViewDataBinding> T bind(View root,
DataBindingComponent bindingComponent)
static <T extends ViewDataBinding> T inflate(LayoutInflater inflater,
int layoutId,
ViewGroup parent,
boolean attachToParent, DataBindingComponent bindingComponent)
static <T extends ViewDataBinding> T inflate(LayoutInflater inflater,
int layoutId,
ViewGroup parent,
boolean attachToParent)
static <T extends ViewDataBinding> T setContentView(Activity activity,
int layoutId)
static <T extends ViewDataBinding> T setContentView(Activity activity,
int layoutId, DataBindingComponent bindingComponent)
还有两个不常用的方法, 检索视图是否被绑定, 如果没有绑定返回nul
static <T extends ViewDataBinding> T getBinding(View view)
static <T extends ViewDataBinding> T findBinding(View view)
其他的方法
static String convertBrIdToString(int id)
例如BR.name这个字段对应的是4, 就可以使用该方法将4转成"name"
DataBindingComponent
默认情况下BindingAdapter注解针对所有的XML属性都可以使用. 而通过制定不同的DatabindingComponent可以切换这些自定义属性.
创建DatabindingComponent的步骤:
创建自定义类, 类中存在包含使用@BindingAdapter的函数, 无需静态函数.
这个时候AndroidStudio会自动生成DatabindingComponnent接口
创建DatabindingComponent派生类, 这个时候会提示有方法要求覆写. 如果你省略第一步骤则不会有.
通过DataBindingUtils工具将你自定义的派生类设置到Databinding中, 这里包含全局默认和单例.
class PinkComponent {
@BindingAdapter("android:bindName")
fun TextView.setBindName(name:String?){
if (!name.isNullOrEmpty() && name != text) {
text = "数据体"
@BindingAdapter("android:bindNameAttrChanged")
fun TextView.notifyBindName(inverseBindingListener: InverseBindingListener){
doAfterTextChanged {
inverseBindingListener.onChange()
@InverseBindingAdapter(attribute = "android:bindName")
fun TextView.getBindName():String{
return text.toString()
class CusComponent : DataBindingComponent {
override fun getPinkComponent(): PinkComponent {
return PinkComponent()
设置默认组件都是由DataBindingUtils设置, 但是方法也有所不同
static void setDefaultComponent(DataBindingComponent bindingComponent)
static DataBindingComponent getDefaultComponent()
以上这种设置必须在绑定视图之前设置, 并且是默认全局的, 只需要设置一次.
static <T extends ViewDataBinding> T setContentView(Activity activity,
int layoutId, DataBindingComponent bindingComponent)
如果你没有执行setDefaultComponent则选择通过函数单独传入, 则每次都要传入否则报错.
DatabindingComponent只能使用@BindingAdapter注解
可以使用include不过不能作为root布局. merge不能使用
如果没有自动生成DataBinding类可以先写个variable(或者make module下)
即使你没有绑定数据(你可能会在网络请求成功里面绑定数据), 但是只要视图创建完成就会自定绑定数据. 这个时候数据是空对象. 空对象的字段也会有默认值(String的默认值是NULL, TextView就会显示NULL); 并且如果你用了三元表达式, 空对象的三元表达式都为false; 所以建议不要考虑空对象的情况;
如果你给一个要求值是布尔类型值的自定义属性(BindingAdapter)赋值一个函数, 空指针的情况会返回false;
DataBindingSupport
通过快捷键(alt + enter)在XML布局中自动创建表达式和节点 , AS4失效
DataBindingConvert
使用快捷键快速将包裹布局为layout, AS4可用