DataBinding
說(shuō)到DataBinding,大家就會(huì)想到雙向綁定。那究竟什么是雙向綁定,其實(shí)對(duì)于剛接觸的人來(lái)說(shuō)是需要去理解一下的。
在MVVM中,View和Model是互相隔離的。假設(shè)有以下的EditText布局:
android:text="@{echo.text}"
那么顯而易見(jiàn),當(dāng)echo.text發(fā)生變化時(shí),我們希望EditText中的android:text屬性可以自動(dòng)變化。這是Model層->View層的綁定。
反之,當(dāng)用戶通過(guò)View層在EditText中進(jìn)行輸入時(shí),我們希望echo.text字段也可以同步更新到最新的輸入值。這是View到Model層的綁定。這一點(diǎn)很多人都會(huì)忽略。
那么首先我們看是Model層->View層的綁定是怎么實(shí)現(xiàn)的,在DataBinding中大家都知道有以下兩種方式:
-
Model繼承BaseObservable,get帶上@Bindable注解 - 相應(yīng)字段使用
Observablexxx變量,如下text字段(其實(shí)Observablexxx就是繼承BaseObservable)
可以簡(jiǎn)單看下BaseObservable的源碼,后面會(huì)用到:
public class BaseObservable implements Observable {
@Override
public void addOnPropertyChangedCallback(@NonNullOnPropertyChangedCallback callback) {
...
mCallbacks.add(callback);
}
public void notifyPropertyChanged(int fieldId) {
...
mCallbacks.notifyCallbacks(this, fieldId, null);
}
就是一個(gè)回調(diào)模式。
public class Echo {
public ObservableField<String> text = new ObservableField<>();
}
當(dāng)使用ObservableField后,就真正使用了觀察者的模式。也就是說(shuō)當(dāng)調(diào)用setEcho方法后,一個(gè)監(jiān)聽器就被注冊(cè)了,這個(gè)監(jiān)聽器會(huì)在每次text字段被更新后去更新視圖。
先來(lái)一段小小的源碼分析,每個(gè)layout生成的xxxBinding都是關(guān)鍵的類,里面有一個(gè)executeBinding方法。
我們來(lái)看,一個(gè)簡(jiǎn)單的
android:text="@{model.str}"
會(huì)生成什么模板代碼?
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String modelStr = null;
me.lizz0y.myapplication.VM model = mModel;
if ((dirtyFlags & 0x3L) != 0) {
if (model != null) {
// read model.str
modelStr = model.str;
}
}
// batch finished
if ((dirtyFlags & 0x3L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, modelStr);
}
}
關(guān)鍵代碼:
modelStr = model.str;
android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, modelStr);//這個(gè)`xxxAdapter`后面再說(shuō),這里就看出簡(jiǎn)單的賦值就好了
如果變成
public ObservableField<String> str = new ObservableField<String>("sss");
我們會(huì)發(fā)現(xiàn),在executeBinding中多了一句:
updateRegistration(0, modelStr);//localFieldId
將這個(gè)變量的fieldId與model.str這個(gè)Observable綁定在一起,同時(shí)使用前面的addOnPropertyChangedCallback把ViewBinding類作為回調(diào)傳進(jìn)去。
最終,當(dāng)我們對(duì)mode.str進(jìn)行set操作時(shí),一系列回調(diào)最終走到ViewDataBinding的
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeModelStr((android.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
其實(shí)就是對(duì)str這個(gè)變量賦予臟位,讓下次屏幕刷新時(shí)更新這個(gè)變量對(duì)應(yīng)的View。
介紹一些常用的運(yùn)算符:
運(yùn)算符
@BindingConversion
如果在xml里我這么寫:android:background="@{@color/blue}"
會(huì)報(bào)錯(cuò),因?yàn)?code>background應(yīng)該是drawable。所以要進(jìn)行自動(dòng)轉(zhuǎn)化,所以需要進(jìn)行如下定義:
//轉(zhuǎn)化@color/blue為drawable
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
經(jīng)過(guò)前面分析也很簡(jiǎn)單:
android.databinding.adapters.ViewBindingAdapter.setBackground(
this.mboundView0,me.lizz0y.myapplication.VM.convertColorToDrawable(
mboundView0.getResources().getColor(R.color.blue)));
當(dāng)然這里顯然有人會(huì)問(wèn),假設(shè)我定義了多個(gè)怎么破,結(jié)論就是后面的覆蓋前面的。。。
@BindAdapter
舉個(gè)栗子就明白了:
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView view, String u) {
RequestOptions options = new RequestOptions()
.centerCrop()
.placeholder(R.mipmap.ic_launcher_round)
.error(R.mipmap.ic_launcher)
.priority(Priority.HIGH)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(view.getContext()).applyDefaultRequestOptions(options).load(u).transition(new DrawableTransitionOptions().crossFade(1000)).into(view);
}
xml里這么寫:
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:imageUrl="@{user.url}" />
這就每次更新user.url時(shí)就會(huì)自動(dòng)重新設(shè)置圖片。
DataBinding在set屬性attr時(shí)會(huì)先看View有沒(méi)有setXXX方法。如果沒(méi)有就去找有沒(méi)有BindingAdapter注解設(shè)置對(duì)應(yīng)的方法。
前面分析源碼的時(shí)候提到過(guò)
if ((dirtyFlags & 0x3L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, modelStr);
}
我們看這里的BindingAdapter:
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
view.setText(text);
}
可以看到會(huì)比較oldText&newText,以防無(wú)限循環(huán)。
Component
我們可以定義多個(gè)BindingAdapter,但究竟想要哪個(gè)發(fā)揮作用呢? 就可以使用這個(gè)Component
BindingMethod
該注解可以幫助我們重新命名view屬性對(duì)應(yīng)的setter方法名稱。
@BindingMethods({@BindingMethod(type = NestedScrollView.class, attribute = "custom", method = "setMyCustomAttr")})
注解在類上。
個(gè)人覺(jué)得他與BindingAdapter的區(qū)別在于:
- 參數(shù),
BindingAdapter可以拿到view引用 component
監(jiān)聽屬性變更
databinding讓我們省去了各種監(jiān)聽函數(shù),但有的時(shí)候我們需要在屬性變化時(shí)做一些額外的事情:
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();
}
}
});
雙重綁定
下面我們來(lái)說(shuō)說(shuō)如果從View->Model的綁定。我們看在前面已有的基礎(chǔ)上,我們?nèi)绾巫约簩?shí)現(xiàn)雙重綁定
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Text 1"
android:text="@{echo.text}"
android:addTextChangedListener="@{echo.watcher}"/>
假設(shè)有兩個(gè)EditText都使用了android:text,可以看到一個(gè)改變并不能連帶帶動(dòng)另一個(gè)EditText,因?yàn)?code>EditText的輸入并沒(méi)有手動(dòng)去調(diào)用setField方法。所以顯而易見(jiàn)需要再使用DataBinding賦予textChangedListener
public TextWatcher watcher = new TextWatcherAdapter() {
@Override public void afterTextChanged(Editable s) {
if (!Objects.equals(text.get(), s.toString())) { //防止無(wú)限循環(huán)
text.set(s.toString());
}
}
};
customBinding
public class BindableString extends BaseObservable {
private String value;
public String get() {
return value != null ? value : “”;
}
public void set(String value) {
if (!Objects.equals(this.value, value)) {
this.value = value;
notifyChange();
}
}
public boolean isEmpty() {
return value == null || value.isEmpty();
}
}
@BindingConversion
public static String convertBindableToString(
BindableString bindableString) {
return bindableString.get();
}
當(dāng)主動(dòng)調(diào)用BindableString.set時(shí)會(huì)通過(guò)notifyChange去觸發(fā)UI更新,UI更新時(shí)調(diào)用convertBindableToString取出string綁定
或者BindAdapter:
@BindingAdapter({“app:binding”})
public static void bindEditText(EditText view,
final BindableString bindableString) {
Pair<BindableString, TextWatcherAdapter> pair =
(Pair) view.getTag(R.id.bound_observable);
if (pair == null || pair.first != bindableString) {
if (pair != null) {
view.removeTextChangedListener(pair.second);
}
TextWatcherAdapter watcher = new TextWatcherAdapter() {
public void onTextChanged(CharSequence s,
int start, int before, int count) {
bindableString.set(s.toString());
}
};
view.setTag(R.id.bound_observable,
new Pair<>(bindableString, watcher));
view.addTextChangedListener(watcher);
}
String newValue = bindableString.get();
if (!view.getText().toString().equals(newValue)) {
view.setText(newValue);
}
}
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Text 1"
app:binding="@{echo.text}"/>
或者可以使用BindAdapter:
@BindingAdapter({“app:binding”})
public static void bindEditText(EditText view,
final BindableString bindableString) {
Pair<BindableString, TextWatcherAdapter> pair =
(Pair) view.getTag(R.id.bound_observable);
if (pair == null || pair.first != bindableString) {
if (pair != null) {
view.removeTextChangedListener(pair.second);
}
TextWatcherAdapter watcher = new TextWatcherAdapter() {
public void onTextChanged(CharSequence s,
int start, int before, int count) {
bindableString.set(s.toString());
}
};
view.setTag(R.id.bound_observable,
new Pair<>(bindableString, watcher));
view.addTextChangedListener(watcher);
}
String newValue = bindableString.get();
if (!view.getText().toString().equals(newValue)) {
view.setText(newValue);
}
}
當(dāng)然,google不可能真的這么蠢。。讓我們自己去實(shí)現(xiàn)這一套,它在xml里給我們提供了很簡(jiǎn)單的運(yùn)算符@=
@=
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:text="@={model.name}"/>
這么搞完當(dāng)EditText更新時(shí)就自動(dòng)更新model.name字段。當(dāng)然,肯定很好奇背后的實(shí)現(xiàn)(這一part看了我半天。。) 它的實(shí)現(xiàn)是由多個(gè)注解完成的,先看其中兩個(gè):
- @InverseBindingAdapter
- @InverseBindingListener
TextViewBindingAdapter.java:
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
"android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
final OnTextChanged on, final AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (before != null) {
before.beforeTextChanged(s, start, count, after);
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (on != null) {
on.onTextChanged(s, start, before, count);
}
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
@Override
public void afterTextChanged(Editable s) {
if (after != null) {
after.afterTextChanged(s);
}
}
};
}
final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
if (oldValue != null) {
view.removeTextChangedListener(oldValue);
}
if (newValue != null) {
view.addTextChangedListener(newValue);
}
}
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
xxxViewBinding.java
private android.databinding.InverseBindingListener mboundView2androidTextAttrChanged = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of model.str.get()
// is model.str.set((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView2);
// localize variables for thread safety
// model
me.lizz0y.myapplication.VM model = mModel;
// model.str != null
boolean modelStrJavaLangObjectNull = false;
// model != null
boolean modelJavaLangObjectNull = false;
// model.str.get()
java.lang.String modelStrGet = null;
// model.str
android.databinding.ObservableField<java.lang.String> modelStr = null;
modelJavaLangObjectNull = (model) != (null);
if (modelJavaLangObjectNull) {
modelStr = model.str;
modelStrJavaLangObjectNull = (modelStr) != (null);
if (modelStrJavaLangObjectNull) {
modelStr.set(((java.lang.String) (callbackArg_0)));
}
}
}
};
也很簡(jiǎn)單,當(dāng)text發(fā)生變化時(shí),觸發(fā)onTextChange,然后調(diào)用mboundView2androidTextAttrChanged.onChange,里面調(diào)用了由@InverseBindingAdapter注解的getTextString去獲取值賦給model
總結(jié)一下,假設(shè)你要給一個(gè)自定義屬性雙向綁定,寫上@=時(shí):你需要寫以下函數(shù):
@InverseBindingAdapter(attribute = "refreshing", event = "refreshingAttrChanged")
public static boolean getRefreshing(PhilView view) { //賦值時(shí)來(lái)這里取
return isRefreshing;
}
@BindingAdapter(value = {"refreshingAttrChanged"}, requireAll = false)
public static void setRefreshingAttrChanged(PhilView view, final InverseBindingListener inverseBindingListener) {
Log.d(TAG, "setRefreshingAttrChanged");
if (inverseBindingListener == null) {
view.setRefreshingListener(null);
} else {
mInverseBindingListener = inverseBindingListener;
view.setRefreshingListener(mOnRefreshingListener);
}
}
@InverseMethod & @InverseBindingMethod[s]
只是簡(jiǎn)化了一下@InverseBindingAdapter的注解。
與RecyclerView
public class MyViewHolder extends RecyclerView.ViewHolder {
private final ItemBinding binding;
public MyViewHolder(ItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(Item item) {
binding.setItem(item);
binding.executePendingBindings();
}
}
強(qiáng)刷executePendingBinding
強(qiáng)制綁定操作馬上執(zhí)行,而不是推遲到下一幀刷新時(shí)。RecyclerView 會(huì)在 onBindViewHolder 之后立即測(cè)量 View。如果因?yàn)榻壎ㄍ七t到下一幀繪制時(shí)導(dǎo)致錯(cuò)誤的數(shù)據(jù)被綁定到 View 中, View 會(huì)被不正確地測(cè)量,因此這個(gè) executePendingBindings() 方法非常重要!
todoApp結(jié)構(gòu)

現(xiàn)在先讓我們忘記前面說(shuō)的一切有關(guān)雙向綁定的事情。。。來(lái)看看google推出的架構(gòu)LiveData&ViewModel
我們先看平時(shí)開發(fā)時(shí)會(huì)有哪些問(wèn)題:
通常Android系統(tǒng)來(lái)管理UI controllers(如Activity、Fragment)的生命周期,由系統(tǒng)響應(yīng)用戶交互或者重建組件,用戶無(wú)法操控。當(dāng)組件被銷毀并重建后,原來(lái)組件相關(guān)的數(shù)據(jù)也會(huì)丟失,如果數(shù)據(jù)類型比較簡(jiǎn)單,同時(shí)數(shù)據(jù)量也不大,可以通過(guò)onSaveInstanceState()存儲(chǔ)數(shù)據(jù),組件重建之后通過(guò)onCreate(),從中讀取Bundle恢復(fù)數(shù)據(jù)。但如果是大量數(shù)據(jù),不方便序列化及反序列化,則上述方法將不適用。
UI controllers經(jīng)常會(huì)發(fā)送很多異步請(qǐng)求,有可能會(huì)出現(xiàn)UI組件已銷毀,而請(qǐng)求還未返回的情況,因此UI controllers需要做額外的工作以防止內(nèi)存泄露。
當(dāng)Activity因?yàn)榕渲米兓N毀重建時(shí),一般數(shù)據(jù)會(huì)重新請(qǐng)求,其實(shí)這是一種浪費(fèi),最好就是能夠保留上次的數(shù)據(jù)。
解決
fragment和fragment之間通信的問(wèn)題
LiveData
LiveData,顧名思義和生命周期綁定,解決上面的第二個(gè)異步問(wèn)題:
能夠感知組件(Fragment、Activity、Service)的生命周期;
只有在組件出于激活狀態(tài)(STARTED、RESUMED)才會(huì)通知觀察者有數(shù)據(jù)更新;
public class NameViewModel extends ViewModel{
// Create a LiveData with a String
private MutableLiveData<String> mCurrentName;
// Create a LiveData with a String list
private MutableLiveData<List<String>> mNameListData;
public MutableLiveData<String> getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData<>();
}
return mCurrentName;
}
public MutableLiveData<List<String>> getNameList(){
if (mNameListData == null) {
mNameListData = new MutableLiveData<>();
}
return mNameListData;
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNameViewModel = ViewModelProviders.of(this).get(NameViewModel.class);
mNameViewModel.getCurrentName().observe(this,(String name) -> {
mTvName.setText(name);
Log.d(TAG, "currentName: " + name);
}); // 訂閱LiveData中當(dāng)前Name數(shù)據(jù)變化,以lambda形式定義Observer
mNameViewModel.getNameList().observe(this, (List<String> nameList) -> {
for (String item : nameList) {
Log.d(TAG, "name: " + item);
}
}); // 訂閱LiveData中Name列表數(shù)據(jù)變化,以lambda形式定義Observer
}
當(dāng)組件處于激活狀態(tài),并且mCurrentName變量發(fā)生變化時(shí),fragment觀察者就會(huì)收到監(jiān)聽
ViewModel
[站外圖片上傳中...(image-93bfcd-1526989876647)]
說(shuō)實(shí)話一開始看到這個(gè)我總以為跟MVVM的ViewModel有什么異曲同工之妙,事實(shí)證明我想多了。此ViewModel是用來(lái)存儲(chǔ)和管理UI相關(guān)的數(shù)據(jù)。
ViewModel是生存在整個(gè)生命周期內(nèi)的,所以在這個(gè)類中不能存在android.content.Context; 簡(jiǎn)單來(lái)說(shuō)不能有view或context的引用,所以一般都會(huì)傳ApplicationContext進(jìn)去。
用法很簡(jiǎn)單:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
很容易看出它為什么可以解決以上問(wèn)題, ViewModelProviders.of(this).get(MyViewModel.class);調(diào)用這個(gè)時(shí),內(nèi)部代碼會(huì)幫我們做存儲(chǔ)。至于怎么做存儲(chǔ),也很常見(jiàn),加了一個(gè)fragment并setRetain(true)就可以了。這樣重建恢復(fù)后拿的還是同一個(gè)ViewModel,因此顯然數(shù)據(jù)也都還在。同時(shí),一個(gè)Activity對(duì)應(yīng)的兩個(gè)fragemt也可以通信:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
使用getActivity拿到同一份ViewModel,就可以拿到同一份數(shù)據(jù)。也很容易看到,其實(shí)LiveData是需要與ViewModel結(jié)合在一起用的
todoApp-mvvm-live
從todoApp可以看出最明顯的區(qū)別:
@NonNull
public static TaskDetailViewModel obtainViewModel(FragmentActivity activity) {
// Use a Factory to inject dependencies into the ViewModel
ViewModelFactory factory = ViewModelFactory.getInstance(activity.getApplication());
return ViewModelProviders.of(activity, factory).get(TaskDetailViewModel.class);
}
其次,其實(shí)這個(gè)例子還是跟dataBinding搞在一起了,否則拿回?cái)?shù)據(jù)更新UI時(shí)需要用到大量LiveData的observe函數(shù)。
最后所以用了LiveData的作用在哪,我們看一個(gè)例子,點(diǎn)擊某個(gè)按鈕后跳轉(zhuǎn)Activity:
before
public class TasksActivity extends AppCompatActivity implements TaskItemNavigator, TasksNavigator {
....
}
@Nullable
private WeakReference<TaskItemNavigator> mNavigator;
在ViewModel中:
public void taskClicked() {
String taskId = getTaskId();
if (taskId == null) {
// Click happened before task was loaded, no-op.
return;
}
if (mNavigator != null && mNavigator.get() != null) { //煩躁
mNavigator.get().openTaskDetails(taskId);
}
}
after
TasksActivity.java
// Subscribe to "open task" event
mViewModel.getOpenTaskEvent().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String taskId) {
if (taskId != null) {
openTaskDetails(taskId);
}
}
});
// mTasksViewModel.getOpenTaskEvent().setValue(task.getId());
利用liveData的生命周期特性,就不用管activity是否已經(jīng)消失。
調(diào)試
Databinding想調(diào)試自動(dòng)生成的代碼,需要在setting里選擇Reference code generated by the compiler