,同时layout只能包含一个View标签。
标签的内容即DataBinding的数据. data标签只能存在一个
标签可以指定类, 然后在控件的属性值中就可以使用,通过DataBind的setXX()方法可以给variable设置数据。以下2种写法都可以。<variable
name="userInfo"
type="cn.iwcode.androidjetpack.databind1.UserBean" />
<import type="cn.iwcode.androidjetpack.databind1.UserBean" />
<variable
name="userInfo"
type="UserBean" />
可以自定义DataBinding生成的类名以及路径,不常用
标签如果需要导入(import)两个同名的类时可以使用alias属性(别名属性)
<import type="cn.iwcode.androidjetpack.databind1.UserBean" alias="MyUserBean"/>
<variable
name="userInfo"
type="MyUserBean" />
在include其他布局的时候可能需要传递变量(variable)值过去
<include
layout="@layout/include_view"
bind:userName="@{userInfo}"/>
include_view.xml
<variable
name="userInfo"
type="cn.iwcode.androidjetpack.databind1.UserBean"/>
主要是实现数据改变时,视图也随之改变。
实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservable、ObservableField、ObservableCollection
BaseObservable有 notifyChange() 和 notifyPropertyChanged() 两个方法刷新数据,
public class UserBean extends BaseObservable {
//如果是 public 修饰符,则可以直接在成员变量上方加上 @Bindable 注解
//如果是 private 修饰符,则在成员变量的 get 方法上添加 @Bindable 注解
@Bindable
public String name;
public String phoneNUmber;
public int age;
//更新所有的字段
public void setAll(String name, String phoneNUmber, int age) {
this.name = name;
this.phoneNUmber = phoneNUmber;
this.age = age;
notifyChange();
}
// 只更新name字段
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
public void setPhoneNUmber(String phoneNUmber) {
this.phoneNUmber = phoneNUmber;
}
}
在调用setName方法是只更新name字段,而setAll会更新所有的字段。
手动去调用notifyPropertyChanged太麻烦,官方提供了ObservableField包装类。
ObservableField是对 BaseObservable 中字段的注解和刷新等操作的封装,官方原生提供了对基本数据类型的封装,例如 ObservableBoolean、ObservableByte、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble 以及 ObservableParcelable ,也可通过 ObservableField 泛型来申明其他类型。
public class UserBean extends BaseObservable {
public ObservableField<String> name;
public ObservableField<String> phoneNUmber;
public ObservableInt age;
public void setAll(String name, String phoneNUmber,int age) {
this.name.set(name);
this.phoneNUmber.set(phoneNUmber);
this.age.set(age);
}
public void setName(String name) {
this.name.set(name);
}
}
这个主要是提供对list和map的包装,如ObservableArrayMap,ObservableArrayList。
需要注意的是在布局文件中,如果用泛型<>
对应的是<
和>
,如:
ObservableArrayMap<String, String> maps = new ObservableArrayMap<>();
maps.put("firstName", "Google");
maps.put("lastName", "Inc.");
viewDataBinding.setMaps(maps);
<import type="androidx.databinding.ObservableArrayMap" />
<variable
name="maps"
type="ObservableArrayMap<String,String>" />
用于数据自动更新视图,会在BR中生成一个静态字段,用于更新部分数据,如下:
public class UserBean extends BaseObservable {
//如果是 public 修饰符,则可以直接在成员变量上方加上 @Bindable 注解
//如果是 private 修饰符,则在成员变量的 get 方法上添加 @Bindable 注解
@Bindable
public String name;
// 只更新name字段
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
}
创建一个XML属性和函数, 然后在属性中进行设置数据操作会进入该函数.
图片加载时,比较方便使用这个注解。如:
<ImageView
android:id="@+id/imageView"
android:layout_width="80dp"
android:layout_height="80dp"
app:imageUrl="@{userBean.headUrl}"
tools:srcCompat="@tools:sample/avatars[0]" />
public class ImageBindingAdapter {
@BindingAdapter(value={"imageUrl"},requireAll = false)
public static void bindImageUrl(ImageView view, String imageUrl){
RequestOptions options = new RequestOptions()
.centerCrop()
.dontAnimate();
Glide.with(view).load(imageUrl).apply(options).into(view);
}
}
androidx中的view大部分,databinding已经都给我们做了。如果是自定义的view,可能会有需要。
@BindingMethods
是和@BindingMethod
配合使用的,BindingMethod就是引导DataBinding使用控件自身的函数,XML属性和View中的函数关联,而BindingAdapter是提供一个新的类函数给控件使用。
看一下官网上的这个例子:android:tint属性实际对应的方法为setImageTintList()
,而不是setTint()
,如果databinding不做任何处理就会报错,因为在ImageView中找不到对应的setTint方法,android:tint属性对应的应该是setImageTintList(),BindingMethods就是为了能够寻找指定的setXX方法。
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
属性值进行类型转换,拿官网的例子来说:
android:background
接受的属性是一个Drawable,但是给的参数确实一个int的color值,如果不转换的话,肯定是会报错的。所以需要把int的color值转换ColorDrawable。
<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);
}
public static
方法,且只能有1个参数。它的作用就是为某个方法指定一个相反的方法,正方法的最终参数的类型与反方法的返回值必须相同,正方法的返回值类型必须与反方法的最终参数类型相同。
比如,这里写个例子,王者段位和int互转,大家就能明白。
public class Converter {
@InverseMethod("stringToInt")
public static String intToString(int value){
String valueStr;
switch (value){
case 1:
valueStr = "青铜";
break;
case 2:
valueStr = "白银";
break;
case 3:
valueStr = "黄金";
break;
case 4:
valueStr = "铂金";
break;
case 5:
valueStr = "钻石";
break;
case 6:
valueStr = "星耀";
break;
case 7:
valueStr = "王者";
break;
default:
valueStr = "";
break;
}
return valueStr;
}
public static int stringToInt(String value){
if (TextUtils.isEmpty(value)) return 0;
int valueInt ;
switch (value){
case "青铜":
valueInt = 1;
break;
case "白银":
valueInt = 2;
break;
case "黄金":
valueInt = 3;
break;
case "铂金":
valueInt = 4;
break;
case "钻石":
valueInt = 5;
break;
case "星耀":
valueInt = 6;
break;
case "王者":
valueInt = 7;
break;
default:
valueInt = 0;
break;
}
return valueInt;
}
}
xml中使用
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginStart="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="段位"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{String.valueOf(userInfo.level)}"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={Converter.intToString(userInfo.level)}"/>
LinearLayout>
@BindingAdapter
配合使用双向数据绑定需要二个函数
BindingAdapter
)InverseBindingAdapter
)看一下androidx
的部分源码,在androidx.databinding.adapters.TextViewBindingAdapter
:
@BindingAdapter({"android:text"})
public static void setText(TextView view, CharSequence text) {
CharSequence oldText = view.getText();
if (text != oldText) {
if (text != null || oldText.length() != 0) {
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return;
}
} else if (!haveContentsChanged(text, oldText)) {
return;
}
view.setText(text);
}
}
}
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
@BindingAdapter(requireAll = false, value = {"android:beforeTextChanged", "android:onTextChanged", "android:afterTextChanged", "android:textAttrChanged"})
public static void setTextWatcher(TextView view, final BeforeTextChanged before, final OnTextChanged on, final AfterTextChanged after, final InverseBindingListener textAttrChanged) {
TextWatcher newValue;
if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
BeforeTextChanged beforeTextChanged = before;
if (beforeTextChanged != null) {
beforeTextChanged.beforeTextChanged(s, start, count, after);
}
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
OnTextChanged onTextChanged = on;
if (onTextChanged != null) {
onTextChanged.onTextChanged(s, start, before, count);
}
// 设置的双向绑定,textAttrChanged就不是null。
InverseBindingListener inverseBindingListener = textAttrChanged;
if (inverseBindingListener != null) {
inverseBindingListener.onChange();
}
}
public void afterTextChanged(Editable s) {
AfterTextChanged afterTextChanged = after;
if (afterTextChanged != null) {
afterTextChanged.afterTextChanged(s);
}
}
};
}
TextWatcher oldValue = (TextWatcher) ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
if (oldValue != null) {
view.removeTextChangedListener(oldValue);
}
if (newValue != null) {
view.addTextChangedListener(newValue);
}
}
我们再来看一下我们项目生成类中的部分代码,在包名.databinding
中:
...
this.mboundView2androidTextAttrChanged = new InverseBindingListener() {
// 监听回调
public void onChange() {
// 对应@InverseBindingAdapter的get方法
String callbackArg_0 = TextViewBindingAdapter.getTextString(ActivityDataBind3BindingImpl.this.mboundView2);
UserBean userInfo = ActivityDataBind3BindingImpl.this.mUserInfo;
boolean z = true;
if (userInfo != null) {
ObservableField userInfoName = userInfo.name;
if (userInfoName == null) {
z = false;
}
if (z) {
userInfoName.set(callbackArg_0);
}
}
}
};
...
// 设置本文的监听。
TextViewBindingAdapter.setTextWatcher(activityDataBind3BindingImpl.mboundView2, beforeTextChanged, onTextChanged, afterTextChanged, activityDataBind3BindingImpl.mboundView2androidTextAttrChanged);
...
同@BindingMethods
相似,不是很常用。
BindingMethods
是寻找view属性对应的view的set方法。
InverseBindingMethods
是寻找view属性对应的view的get方法。
以上就是DataBinding在我们日常开发中都有可能用到的注解,还有2个是@BindingBuildInfo
与@Untaggable
是hide,自动生成Java类使用的。
是不是看完有点头晕了,其实在我们开发中最常用的也就是Bindable
和BindingAdapter
。我们在回顾一下,所有的注解:
自动生成BR的ID,设置数据刷新视图。
设置自定义属性,与方法绑定。
关联view属性到view原有的setXX方法。
view属性值和数据类型转换。
提供一个正反方法,负责实现视图和数据之间的转换。
与BindingAdapter配合使用,获取视图上的数据
关联view属性到view原有的getXX方法。
DataBinding 支持在布局文件中使用以下运算符、表达式和关键字
不支持
空格  ; ;
< 小于号 <; <;
> 大于号 >; >;
& 与号 &; &;
" 引号 "; ";
‘ 撇号 &apos; ';
× 乘号 ×; ×;
÷ 除号 ÷; ÷;
参考前辈的链接
https://juejin.im/post/5a55ecb6f265da3e4d7298e9#heading-30
https://www.jianshu.com/p/bd9016418af2