一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。
接下来看一下KeyboardLayoutBindingImpl.java这个类:
d.KeyboardLayoutBindingImpl.java
public class KeyboardLayoutBindingImpl extends KeyboardLayoutBinding {
private KeyboardLayoutBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 5
, (android.widget.TextView) bindings[1]
);
this.mboundView0 = (android.widget.RelativeLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView2 = (android.widget.ImageView) bindings[2];
this.mboundView2.setTag(null);
this.mboundView3 = (android.widget.TextView) bindings[3];
this.mboundView3.setTag(null);
this.mboundView4 = (android.widget.TextView) bindings[4];
this.mboundView4.setTag(null);
this.mboundView5 = (android.widget.TextView) bindings[5];
this.mboundView5.setTag(null);
this.mboundView6 = (android.widget.TextView) bindings[6];
this.mboundView6.setTag(null);
this.name.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x40L;
}
requestRebind();
}
public void setGuide(@Nullable com.xxx.learn.viewmodel.ViewModel Guide) {
this.mGuide = Guide;
synchronized(this) {
mDirtyFlags |= 0x20L;
}
notifyPropertyChanged(BR.guide);
super.requestRebind();
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
return onChangeGuideRightImageDescription((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
......
}
@Override
protected void executeBindings() {
......
//注册观察者
updateRegistration(0, guideRightImageDescription);
......
//回调后进行UI更新
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView3, guideRightImageDescriptionGet);
......
}
}
该类继承了KeyboardLayoutBinding类,在构造方法内,会调用父类的构造方法,将root view[即对应R.layout.keyboard_layout进行inflate生成的view]传给父类,在Fragment内部的onCreateView()中通过getRoot()返回对应layoutId创建的View,其他方法的逻辑执行会在接下来的数据与UI绑定时进行介绍。
e.KeyboardLayoutBinding.java
public abstract class KeyboardLayoutBinding extends ViewDataBinding {
@NonNull
public final TextView name;
@Bindable
protected ViewModel mGuide;
protected KeyboardLayoutBinding(Object _bindingComponent, View _root, int _localFieldCount,
TextView name) {
super(_bindingComponent, _root, _localFieldCount);
this.name = name;
}
public abstract void setGuide(@Nullable ViewModel guide);
@Nullable
public ViewModel getGuide() {
return mGuide;
}
.......
.......
}
KeyboardLayoutBinding是个抽象类,继承了ViewDataBinding。
f.ViewDataBinding.java
public abstract class ViewDataBinding extends BaseObservable implements ViewBinding {
.......
.......
private final View mRoot;
.......
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
mBindingComponent = bindingComponent;
mLocalFieldObservers = new WeakListener[localFieldCount];
this.mRoot = root;
if (Looper.myLooper() == null) {
throw new IllegalStateException("DataBinding must be created in view's UI Thread");
}
.......
}
public View getRoot() {
return mRoot;
}
.......
.......
在创建KeyboardLayoutBindingImpl后,构造方法会一步一步的向上传,最终将root view保存在ViewDataBinding中,然后通过getRoot()获取到view。
四.数据与UI绑定分析
通过上述分析可以看到了DataBindingUtil.inflate创建KeyboardLayoutBinding的整个过程,那数据与UI任何绑定的呢?
在KeyboardLayoutBindingImpl的构造方法内,会调用 invalidateAll(),接下来看一下绑定流程:
a.ObservableField进行observe()
DataBinding使用的是观察者模式,ObservableField数据注册观察者是在创建DataBinding的时候在构造方法中就执行了,先创建了对应ObservableField数量的WeakListener数组,然后执行流程如下:
------>invalidateAll()
------>requestRebind()
------>executePendingBindings()
------>executeBindingsInternal()
------> executeBindings()[Impl]
------>updateRegistration(localFieldId, Observable observable)
------>updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER)[创建WeakPropertyListener]
------>registerTo()[将上步中创建的WeakPropertyListener赋值给执行创建的WeakListener对应的数组值]
------>listener.setTarget(observable)
------>WeakPropertyListener.addListener(Observable)
------>Observable.addOnPropertyChangedCallback(this);
经过以上逻辑执行,Observable[ObservableField]注册了OnPropertyChanged callback,如果数据变化后,会回调OnPropertyChanged()方法,流程图如下:
以上就是数据与UI绑定过程,那数据变化后,是如何反馈到UI上呢?接下来看一下数据变化后UI更新流程:
b.ObservableField数据变化后UI更新
ObservableFiled数据变化后,最终UI更新执行流程如下:
------>set(value)
------>notifyChange()
------>mCallbacks.notifyCallbacks(this, 0, null)[mCallbacks是PropertyChangeRegistry,通过上述addOnPropertyChangedCallback()加入mCallbacks列表]
------>mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2)
------>callback.onPropertyChanged(sender, arg)
------>WeakPropertyListener.onPropertyChanged()
------>handleFieldChange()
------>onFieldChange()------>requestRebind()----->......
------>executeBindings()[Impl]
在ObservableField数据变化后,最终会调用到Impl类里面的executeBindings()来更新UI,流程图如下:
以上就是数据变化后UI更新的整个流程。
五.BindingAdapter
DataBinding提供了BindingAdapter这个注解用于支持自定义属性,或者是修改原有属性。注解值可以是已有的 xml 属性,例如 android:src、android:text等,也可以自定义属性然后在 xml 中使用。
例如,对于一个TextView ,希望在某个变量值发生变化时,可以动态改变显示的文字,此时就可以通过 BindingAdapter来实现。
需要先定义一个静态方法,为之添加 BindingAdapter 注解,注解值是为TextView控件自定义的属性名,而该静态方法的两个参数可以这样来理解:当TextView控件的 step属性值发生变化时,DataBinding 就会将TextView实例以及新的step值传递给setProperty() 方法,从而可以根据此动态来改变TextView的相关属性。
<TextView
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_below="@+id/name"
android:layout_centerHorizontal="true"
android:layout_marginTop="400dp"
android:gravity="center"
android:textStyle="bold"
app:step="@{guide.step}"/>
BindingAdapter实现如下:
import android.util.Log;
import android.widget.TextView;
import androidx.databinding.BindingAdapter;
//可以单独写一个类,统一处理所有使用BindingAdapter注解的控件
public class ViewBinding {
@BindingAdapter(value = {"app:step"})
public static void setProperty(TextView textView, int step) {
Log.e("Seven", "step is: " + step);
//可以根据step来设置textView的属性,例如改变文字,设置宽高等...
textView.setText(xxx);
}
@BindingAdapter(value = {"app:url"})
public static void updateImg(ImageView imageView, String url) {
Glide.with(imageView.getContext()).load(url).into(imageView);
}
}