簡介
DataBinding是MVVM
在Android
上的一種實現,支持雙向綁定,自動刷新。是ButterKnife
等APT框架的有效替代方案。
基本用法
DataBindingUtil
生成實例,會有一定的規則,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> // 明確指定包名和類名。
變量/方法/事件綁定/lambda
<variable name="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,簽名可不一致
導入/alias
<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.*包不需要導入
泛型支持
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>// 左尖括號需要轉義,
include
<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>
避免空指針/ ?? /Resource
android:text="@{user.userName ?? user.realName}"//如果userName爲null,則顯示realName
</br>
android:text="@{@string/nameFormat(firstName, lastName)}"
<string name="nameFormat">%s, %s</string>//組合字符串
</br>
android:text="@{user.firstName}"//如果user爲null,則user.firstName = null
<br/>
android:marginLeft="@{@dimen/margin + @dimen/avatar_size}"// 資源直接相加
重複表達式/隱式更新
<CheckBox android:id=”@+id/seeAds“/>
<ImageView android:visibility=“@{seeAds.checked ?
View.VISIBLE : View.GONE}”/>//checkbox改變時,ImageView隨之改變
EL表達式
算術表達式 : + - / * %
字符串拼接 : +
邏輯表達式 : && ||
二元運算符 : & | ^
一元運算符 : + - ! ~
移位操作符 : >> >>> <<
比較操作符 : == > < >= <=
Instanceof
分組操作符 : Grouping ()
字面量 : character, String, numeric, null
強轉Cast,方法調用
Field 訪問
Array 訪問 []
三元運算符 : ?:
聚合判斷 : “??”不支持this, super, new, 以及顯示的泛型調用
Observable:ViewModel改變與UI自動更新
- 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<String> 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);
<br/>
ObservableArrayList<Object> userlist = new ObservableArrayList<>();
user.add(17);
<br/>
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)
<TextView
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);
}
雙向綁定: @=
需要注意的是雙向綁定的死循環問題,需要在改變之前判斷是否相同,ps
<EditText android:text=“@{user.name}”
android:afterTextChanged=“@{callback.change}”/>
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();
}
}
});
Component
通過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);
性能
- 0反射
- 避免重複計算(如生成公用的本地變量)
- 只遍歷索引一次,而不是每次都
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
相關學習文章: