Google开源的数据绑定框架, 实现了MVVM架构, 增强了xml的功能, 大幅度精简了java代码量, 并且代码可读性更高, 对性能的影响基本为零.
DataBinding会自动在build目录下生成类. 因为被集成进AndroidStudio所以不需要你手动编译会实时编译, 并且支持大部分代码补全.
启用DataBinding
android{
dataBinding {
enabled = true;
}
}
因为怕你们没注意到我写在文章开头
DataBinding的强大是毋庸置疑, 只会更方便(抛弃MVP吧);
鉴于文章篇幅, 后面我将会出一篇文章以及开源库告诉大家如何实现DataBinding是如何让RecyclerView一行代码写通用适配器(无需写实现类)
一行代码实现多类型/添加头布局脚布局/点击事件;
布局文件
<layout>
<data>
<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只能包含一个View标签. 不能直接包含
标签的内容即DataBinding的数据. data标签只能存在一个.
通过
标签可以指定类, 然后在控件的属性值中就可以使用
<data>
<variable name="user" type="com.liangfeizc.databindingsamples.basic.User" />
data>
通过DataBinding的setxx()
方法可以给Variable设置数据. name值不能包含_
下划线
第二种写法(导入), 默认导入了java/lang
包下的类(String/Integer). 可以直接使用被导入的类的静态方法.
<data>
<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
, 否则会报错无法绑定.
标签有个属性
可以自定义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这个类根据布局文件名生成(id+Binding)
ActivityMainBinding viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserBean userBean = new UserBean();
userBean.setUserName("姜涛");
// setUser这个方法根据Variable标签的name属性自动生成
viewDataBinding.setUser(userBean);
}
}
标签如果需要导入(import)两个同名的类时可以使用alias
属性(别名属性)
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" alias="DetailUser" />
<variable name="user" type="DetailUser" />
在include其他布局的时候可能需要传递变量(variable)值过去
<variable
name="userName"
type="String"/>
....
<include
layout="@layout/include_demo"
bind:userName="@{userName}"/>
include_demo
<data>
<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
public class ObservableUser extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return firstName;
}
// 注解才会自动在build目录BR类中生成entry, 要求方法名必须以get开头
@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); // 需要手动刷新
}
}
还可以监听属性改变事件
ObservableUser.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
}
});
属性第一次改变时会回调两次, 之后都只回调一次. 如果使用notifyChange()
不会得到id(即i等于0). 使用
notifyPropertyChanged(i)
就可以在回调里面得到id.
BaseObservable和Observable的区别:
databinding默认实现了一系列实现Observable接口的字段类型
BaseObservable,
ObservableBoolean,
ObservableByte,
ObservableChar,
ObservableDouble,
ObservableField,
ObservableFloat,
ObservableInt,
ObservableLong,
ObservableParcelable,
ObservableShort,
ViewDataBinding
示例
public class PlainUser {
public final ObservableField firstName = new ObservableField<>();
public final ObservableField lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
对于集合数据类型ObservableArrayMap/ObservableArrayLis/ObjservableMap
等集合数据类型
ObservableArrayMap user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
使用
<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap" />
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"/>
Tip:
ObservableParcelable
序列化数据类型通过表达式使用@=
表达式就可以视图刷新的时候自动更新数据, 但是要求数据实现以下两种方式修改才会触发刷新
<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;
}
}
Tip:
根据我上面说的, 监听器至少回调两次(数据->视图, 视图-> 数据)
以下这种是无效的, 因为String参数传递属于引用类型变量并不是常量, 需要用equals()
// 本段截取官方源码, 我也不知道这sb为什么这么写
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
/**/
正确
if (text == null || text.equals(oldText) || oldText.length() == 0) {
return;
}
总结就是如果没有默认实行的控件属性使用双向数据绑定 就需要你自己实现BindingAdapter注解
用于数据更新自动刷新视图. 后面提.
用于标记方法. 前面提到了DataBinding自定义属性自动识别setter.
如果我们需要自定义xml, 就需要修改View的源码 ,但是DataBinding还有第二种方法相当于可以将setter方法抽取出来, 并且同时支持多个属性.
图片加载框架可以方便使用此方法.
@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
使用:
<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(成员默认值). 所以我们必须要首先进行判空处理.
如果你想自定义一个属性并且将他和这个View内部的函数关联就必须使用这个特性;
该注解属于一个容器. 内部参数是一个@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)中寻找方法(method), 然后通过你创建的布局属性(Attribute)来回调方法
如果属性名和@BindingAdapter冲突会报错
Tip: 可以注意到该注解只是单纯地关联已有的方法, 并不能新增方法. 所以全都是注解的空类.
属性值自动进行类型转换
public static
方法. @{}
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);
}
}
设置布局中TextView的背景,
android:background="@{`吴彦祖`}"
可以看到我给背景随意设置一个字符串. 这样就不会匹配Background
的int参数类型. 然后DataBinding就会检索匹配该类型的@BindingConversion
方法. 然后转换.
注意android:text如果想用int自动转String是不可以的, 因为int值会被识别为resource id. @BindingConversion无法工作.
在android studio3.0提供inverse系列的新注解, 全部都是针对数据双向绑定.
在数据和视图的数据不统一时可以使用该注解@InverseMethod
解决数据转换的问题
例如数据模型存储用户的id但是视图不显示id而是显示用户名(数据和视图的类型不一致), 我们就需要在两者之间转换.
需要创建public static
两个方法, 我们简称为”转换方法(convertion method)”和”反转方法(inverse method)”
转换函数: 是刷新视图的时候使用 (决定视图显示数据) 会回调两次(文章后面详细解释双向绑定的时候可以知道为何), 可以理解为getter
函数;
反转函数: 是刷新数据的时候使用 (决定实体存储数据), 可以理解为setter
函数;
简单示例:
在用户id和用户名之间转换. 存储id但是显示的时候显示用户名
@InverseMethod("toID") public static String toName(TextView view, int id) {
if (id == 1) {
return "吴彦祖";
}
return "";
}
public static int toID(TextView view, String name) {
if (name.equals("吴彦祖")) {
return 1;
}
return 0;
}
使用
<TextView
android:id="@+id/iv"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="@={MyInverseMethod.toName( iv, data.id)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
注意和BindingAdapter不同, 参数有View表达式就必须加上View的id
Tip:
在这个注解之前其实都是通过修改实体的setter和getter方法达到类型的转换. 但是这样会侵入整个实体类
我使用的gson类都是自动生成的我并不想去手动修改任何方法.
参数:
AttrChanged
后缀介绍
创建一个函数使用@InverseBindingAdapter
修饰; 该函数会在UI变化的时候回调, 返回的数据会
@InverseBindingAdapter(attribute = "good", event = "goodAttrChanged")
public static String getTextString(TextView view) {
Log.i("日志", "(DataBinding.java:20) ___ 反转" + view.getText());
return "吴彦祖" + view.getText();
}
event
: 该属性需要你创建一个@BindingAdapter
@BindingAdapter("goodAttrChanged")
public static void setGoodChanged(TextView textView, InverseBindingListener inverseBindingListener) {
Log.d("日志", "(DataBinding.java:24) ___ 属性监听事件 = ");
}
在你绑定DataBinding时候回自动调用这个数据变更方法
, (这个数据变更方法创建的属性并不能在xml中使用)
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
给android:text
属性使用@={}
双向绑定表达式. 数据变化触发视图刷新是回调setter方法
数据变更方法(官方源码简化版):
@BindingAdapter(value = {"android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final InverseBindingListener textAttrChanged) {
// 创建一个文字变化监听器
final TextWatcher newValue;
// 如果全部为null不要监听器
if (textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
if (textAttrChanged != null) {
// 通知刷新
textAttrChanged.onChange();
}
}
@Override public void afterTextChanged(Editable s) {
}
};
}
// 如果视图已经有一个监听器就先删除
final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
if (oldValue != null) {
view.removeTextChangedListener(oldValue);
}
// 给视图添加监听器
if (newValue != null) {
view.addTextChangedListener(newValue);
}
}
这里用到一个InverseBindingListener
public interface InverseBindingListener {
/**
* Notifies the data binding system that the attribute value has changed.
*/
void onChange();
}
总结就是你只要通知
和BindingMethods相似, 但是注解参数是@InverseBindingMethod
如果说BindingMethods是关联setter方法和自定义属性, 那么InverseBindingMethods就是关联getter方法和自定义属性;
setter
是更新视图的时候使用, 而getter
方法是更新数据时候使用的
必须与@BindingAdapter配合使用
示例:
@InverseBindingMethods({
@InverseBindingMethod(type = RadioGroup.class, attribute = "android:checkedButton", method = "getCheckedRadioButtonId"),
})
public class RadioGroupBindingAdapter {
@BindingAdapter("android:checkedButton")
public static void setCheckedButton(RadioGroup view, int id) {
if (id != view.getCheckedRadioButtonId()) {
view.check(id);
}
}
@InverseBindingMethod
参数:
Class type 控件的字节码
String attribute 属性
String event 默认值是属性加AttrChanged
后缀作为默认值
String method 默认值 attribute
的getter (例: getMethod
在自动生成DataBinding代码中可以看到
private android.databinding.InverseBindingListener ivandroidTextAttr = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of data.name
// is data.setName((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = com.liangjingkanji.databinding.MyInverseBindingAdapter.getTextString(iv); // 拿到变化的属性
// localize variables for thread safety
// data != null
boolean dataJavaLangObjectNull = false;
// data.name
java.lang.String dataName = null;
// data
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) {
// read data.name
dataName = data.getName();
}
}
// batch finished
if ((dirtyFlags & 0x1aL) != 0) {
// api target 1
com.liangjingkanji.databinding.MyInverseBindingAdapter.setText(this.iv, dataName);
}
if ((dirtyFlags & 0x10L) != 0) {
// api target 1
// 重点是这段代码, 将上面创建的监听器传入setTextWatcher方法
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方法可用的情况下)
建议参考官方实现源码:
DataBindingAdapter
@{}
里面除了可以执行方法以外还可以写表达式, 并且支持一些特有表达式
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" />
上面这种写法会报错
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 name="string_format">名字: %s 性别: %sstring>
然后就可以使用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不能直接通过静态导入来使用. 需要作为字段常量来使用
事件绑定分为两种:
对于默认的事件需要书写同样的参数的方法才能接受到, 否则报错. 例如onClick()方法必须有View参数.
public class MyHandlers {
// 注意必须要传View参数
public void onClickFriend(View view) { ... }
}
直接通过View的属性来调用类方法
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="activity"
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="@{user.firstName}"
android:onClick="@{activit::click}"/>
LinearLayout>
layout>
Tip: activity.click
和activity::click
都属于方法调用, 但是如果是activity.click()
就会报错. 因为对于默认事件需要统一参数. 必须加上activity.click(View v)
上面提到的都不能向回调里面传递自定义参数. 而如果使用
android:onClick="@{()->activity.click(text)}"
就可以自定义回调参数了
ActivityMainBinding dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
dataBinding.setActivity(this);
dataBinding.setText("吴彦祖"); // 顺序无所谓
然后在布局文件中使用Lambda
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="text"
type="String"/>
<variable
name="activity"
type="com.liangjingkanji.databinding.MainActivity"/>
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="@{()->activity.click(text)}" />
LinearLayout>
layout>
自动生成的DataBinding类都继承自该类. 所以都拥有该类的方法
void addOnRebindCallback(OnRebindCallback listener)
// 添加绑定监听器, 可以在Variable被设置的时候回调
void removeOnRebindCallback(OnRebindCallback listener)
// 删除绑定监听器
View getRoot()
// 返回被绑定的视图对象
abstract void invalidateAll()
// 使所有的表达式无效并且立刻重新设置表达式. 会重新触发OnRebindCallback回调(可以看做重置)
abstract boolean setVariable(int variableId, Object value)
// 可以根据字段id来设置变量
void unbind()
// 解绑布局, ui不会根据数据来变化, 但是监听器还是会触发的
这里有三个方法需要重点讲解:
abstract boolean hasPendingBindings()
// 当ui需要根据当前数据变化时就会返回true(数据变化后有一瞬间)
void executePendingBindings()
// 强制ui立刻刷新数据,
当你改变了数据以后(在你设置了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);
}
});
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;
}
}
});
DataBinding不仅可以绑定Activity还可以绑定视图内容(View)
// 视图
static T bind(View root)
static T bind(View root,
DataBindingComponent bindingComponent)
// 布局
static T inflate(LayoutInflater inflater,
int layoutId,
ViewGroup parent,
boolean attachToParent, DataBindingComponent bindingComponent) // 组件
static T inflate(LayoutInflater inflater,
int layoutId,
ViewGroup parent,
boolean attachToParent)
// activity
static T setContentView(Activity activity,
int layoutId)
static T setContentView(Activity activity,
int layoutId, DataBindingComponent bindingComponent)
还有两个不常用的方法, 检索视图是否被绑定, 如果没有绑定返回nul
static T getBinding(View view)
// 和getBinding不同的是如果视图没有绑定会去检查父容器是否被绑定
static T findBinding(View view)
其他的方法
// 根据传的BR的id来返回字符串类型. 可能用于日志输出
static String convertBrIdToString(int id)
例如BR.name这个字段对应的是4, 就可以使用该方法将4转成”name”
每个DataBinding都可以拥有一个组件或者说设置一个默认的全局组件
创建一个Component的步骤:
public class MyDefaultComponent implements DataBindingComponent {
public MyBindingAdapter mAdapter = new MyBindingAdapter();
public MyBindingAdapter getMyBindingAdapter() {
return mAdapter;
}
class MyBindingAdapter {
@BindingAdapter("android:text") public void setText(TextView textView, String text) {
/*省略*/
textView.setText(text);
}
}
}
设置默认组件都是由DataBindingUtils设置, 但是方法也有所不同
static DataBindingComponent getDefaultComponent()
static void setDefaultComponent(DataBindingComponent bindingComponent)
以上这种设置必须在绑定视图之前设置, 并且是默认全局的, 只需要设置一次.
static T setContentView(Activity activity,
int layoutId, DataBindingComponent bindingComponent)
类似于上面这种在绑定视图的同时来设置组件需要每次绑定视图都设置, 否则就会报错.
或者你可以将@BindingAdapter注解的方法变为Static修饰.
另外不仅仅是@BindingAdapter可以设置成组件, @InverseBindingAdapter同样可以
BindingAdapter
)赋值一个函数, 空指针的情况会返回false;关于DataBinding我推荐使用插件生成, 方便快捷很多;
DataBindingModelFormatter
快捷生成实现Observable
的数据模型
DataBindingSupport
自动生成DataBinding所需的XML格式