DataBinding是MVVM
在Android
上的一种实现,支持双向绑定,自动刷新。是ButterKnife
等APT框架的有效替代方案。
生成实例,会有一定的规则,layout通过文件名生成,View通过id生成,通过dataBinding.setVariable(Variable variable);来实现数据的绑定。
通过自定义类名,这样就可以避开上面的规则
<data class="CustomBinding"></data> //在app_package/databinding下生成CustomBinding;
<data class=".CustomBinding"></data> //在app_package下生成CustomBinding;
<data class="com.example.CustomBinding"></data> // 明确指定包名和类名。
"user" type="com.example.User"/>
android:text="@{user.firstName}" // 变量绑定
android:onClick="@{presenter.onClick}"// 事件绑定,方法引用
android:text='@{"" + user.age}'//age是int值,在java中手抖会索引到int值的resId
android:onClick="@{() -> presenter.onClickListenerBinding(employee)}"//lambda,签名可不一致
<data>
<import type="android.view.View"/>
<import type="com.example.User"/>
<import type="com.mvvm.model.User" alias="MyUser"/>//类名相同,用alias区分
<variable name="user" type="User">
<variable name="user" type="MyUser">
</data>
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"//依赖了View,需要导入,如java,java.lang.*包不需要导入
type="com.example.User"/>
type="java.util.List"/>
"user" type="User"/>
"userList" type="List<User>"/>// 左尖括号需要转义,
<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>
android:text="@{user.userName ?? user.realName}"//如果userName为null,则显示realName
android:text="@{@string/nameFormat(firstName, lastName)}"
<string name="nameFormat">%s, %sstring>//组合字符串
android:text="@{user.firstName}"//如果user为null,则user.firstName = null
android:marginLeft="@{@dimen/margin + @dimen/avatar_size}"// 资源直接相加
<CheckBox android:id=”@+id/seeAds“/>
<ImageView android:visibility=“@{seeAds.checked ?
View.VISIBLE : View.GONE}”/>//checkbox改变时,ImageView随之改变
算术表达式 : + - / * %
字符串拼接 : +
逻辑表达式 : && ||
二元运算符 : & | ^
一元运算符 : + - ! ~
移位操作符 : >> >>> <<
比较操作符 : == > < >= <=
Instanceof
分组操作符 : Grouping ()
字面量 : character, String, numeric, null
强转Cast,方法调用
Field 访问
Array 访问 []
三元运算符 : ?:
聚合判断 : “??”不支持this, super, new, 以及显示的泛型调用
- BaseObservable: 继承类,字段的
set
方法需要添加注解@Bindable
,并且调用notifyPropertyChanged(BR.field);
方法,通过其继承关系知其继承自Observable
.
ps,如果不想使用BaseObservable
,可以实现Observable
来实现自己的更新,这时候需要借助PropertyChangedRegistry
即可。
public class User extends BaseObservable{
private boolean isFollow;
@Bindable
public boolean isFollow() {
return isFollow;
}
public void setIsFollow(boolean isFollow) {
this.isFollow = isFollow;
notifyPropertyChanged(BR.follow);
//notifyChange:刷新所有的值域
//notifyPropertyChanged: 只更新对应BR的flag
}
}
- ObserableField : 泛型类,除此之外,databinding提供有基本类型
ObservableInt
,ObservableBoolean
…
public static class User {
public final ObservableField realName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
};
//java中的get/set方法
int age = user.age.get();
user.realName.set("Google");
- Collections: 包含
ObservableArrayList/ObservableArrayMap
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("age", 17);
ObservableArrayList<Object> userlist = new ObservableArrayList<>();
user.add(17);
android:text='@{user["age"]}'//从user中找到age
android:text='@{userlist[0]}'//从userlist中根据 `index` 找到 `age`
在不知道具体生成的binding类的时候(Recyclerview的多种ViewHolder)
public class BindingViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
protected final T mBinding;
public BindingViewHolder(T binding) {
super(binding.getRoot());
mBinding = binding;
}
public T getBinding() {
return mBinding;
}
}
// 通过上面的泛型获取相应的ViewDataBinding
public void onBindViewHolder(BindingHolder holder, int position) {
final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
- Setter,通过继承 第三方View,加上自定义的
Set
方法,来支持databinding
// SimpleDraweeView extends ImageView
public void setUrl(String url) {
view.setImageURI(TextUtils.isEmpty(url) ? null : Uri.parse(url));
}
app:url=“@{@string/url}”
- BindingAdapter,没有对应的
Set
或者方法签名不同的时候使用,和 属性动画的Wrapper
方法类似
@BindingAdapter(value ={"bind:imageUrl", "bind:error"},requireAll = false)
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
- BindingMethods,本身支持
Set
,但是xml中的属性名字和set方法名字不对应的时候使用。
@BindingMethods({
@BindingMethod(type = “android.widget.ImageView”,
attribute = “android:tint”,
method = “setImageTintList”),
})
- BindingConversion,当想设置的和需要的不是一个类型时,如:View背景(我们设置int类型的color,而需要的是 一个带颜色的 drawable)
@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);
}
需要注意的是双向绑定的死循环问题,需要在改变之前判断是否相同,ps
public void change(Editable s) {
final String text = s.toString();
if (!text.equals(name.get()) {//不相同才改变
name.set(text);
}
}
addOnPropertyChangedCallback: Model属性改变时回调发生
OnRebindCallback: view发生改变重复绑定时触发
mModel.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
if (i == BR.name) {
Toast.makeText(TwoWayActivity.this, "name changed",
Toast.LENGTH_SHORT).show();
} else if (i == BR.password) {
Toast.makeText(TwoWayActivity.this, "password changed",
Toast.LENGTH_SHORT).show();
}
}
});
通过DataBindingUtil.setDefaultComponent
来设置不同环境下不同的Component
,
设置之后就可以使用该Component
提供的Adapter
方法,默认不设置是全局使用,可以理解为作用域。
public interface TestableAdapter {
@BindingAdapter("android:src")
void setImageUrl(ImageView imageView, String url);
}
public interface DataBindingComponent {
TestableAdapter getTestableAdapter();
}
DataBindingUtil.setDefaultComponent(myComponent);
‐ or ‐
binding = MyLayoutBinding.inflate(layoutInflater, myComponent);
findById
Recyclerview
中使用DataBinding
public void bindTo(User user) {
mBinding.setUser(user);
mBinding.executePendingBindings();//数据绑定刷新所有挂起的更改
}
Recyclerview
的OnRebindCallback
,会监听到数据无效标识。进而改变相应的item holder.getBinding().addOnRebindCallback(new OnRebindCallback() {
//...
public void onCanceled(ViewDataBinding binding) {
if (mRecyclerView == null || mRecyclerView.isComputingLayout()) {
return;
}
int position = holder.getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
notifyItemChanged(position, DATA_INVALIDATION);
}
}
});
使用DataBinding
,支持多种Type
的Recyclerview-Adapter
实践:
DataBindingDemo
相关学习文章:
棉花糖给 Android 带来的 Data Bindings(数据绑定库)
从零开始的Android新项目7 - Data Binding入门篇
从零开始的Android新项目8 - Data Binding高级篇