Android中的双向绑定是指:将Model设置给View之后,当Model中的数据发生改变时,View显示的内容也会随之改变;当View发生变化时,Model中的数据也会随之改变。双向绑定可以让开发者使用数据驱动视图,并且降低了程序中的耦合度。双向绑定本质是基于观察者模式实现的。在代码层面,主要表现为:当Model中的数据发生变化时,通过回调接口,通知数据绑定器重新设置View中的数据;当View中的属性发生变化时,通过监听器捕获发生的变化,并将此变化传递给Model。这样就实现了双向绑定。整体框架如下图所示:
双向绑定举例:
将ObservableField<String>与EditView进行双向绑定,并使用一个TextView展示。代码如下:
ViewModel代码:
class UserViewModel:ViewModel() {
var name:ObservableField<String> = ObservableField("")
}
XML文件代码:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:MyView="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="userVM"
type="com.example.jetpacklearn.viewModel.UserViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:layout_width="60dp"
android:layout_height="wrap_content"
android:id="@+id/userName"
android:text="@={userVM.name}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/showName"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/showName"
android:text="@{userVM.name}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/userName"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivity文件:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var layoutInflater = LayoutInflater.from(this)
var dataBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(dataBinding.root)
var userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
dataBinding.userVM = userViewModel
}
}
运行结果如下:
接下来,以上述例子介绍双向绑定的实现流程。
双向绑定流程代码分析:
在MainActivity中我们可以使用以下方法获取ViewDataBinding,并设置ViewModel。
var dataBinding = ActivityMainBinding.inflate(layoutInflater)
var userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
dataBinding.userVM = userViewModel
ActivityMainBinding是自动生成的类,其inflate代码如下:
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable Object component) {
return ViewDataBinding.<ActivityMainBinding>inflateInternal(inflater, R.layout.activity_main, null, false, component);
}
接下来,会去调用ViewDataBinding的inflateInternal方法。注意,在这里DataBindingUtil.getDefaultCompinent获取的结果为null,即component对象为null。
protected static <T extends ViewDataBinding> T inflateInternal(
@NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent,
boolean attachToParent, @Nullable Object bindingComponent) {
return DataBindingUtil.inflate(
inflater,
layoutId,
parent,
attachToParent,
checkAndCastToBindingComponent(bindingComponent)
);
}
接下来,调用DataBindingUtil的inflate方法。注意,这里parent为null,attachToParent为false。
public static <T extends ViewDataBinding> T inflate(
@NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent,
boolean attachToParent, @Nullable DataBindingComponent bindingComponent) {
final boolean useChildren = parent != null && attachToParent;//userCHildren为false
final int startChildren = useChildren ? parent.getChildCount() : 0;
//生成Root View
final View view = inflater.inflate(layoutId, parent, attachToParent);
if (useChildren) {
return bindToAddedViews(bindingComponent, parent, startChildren, layoutId);
} else {
//走这里
return bind(bindingComponent, view, layoutId);
}
}
接下来调用bind方法。注意bindingComponent为null
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
sMapper的值如下:
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
private static DataBindingComponent sDefaultComponent = null;
......
}
public class DataBinderMapperImpl extends MergedDataBinderMapper {
DataBinderMapperImpl() {
addMapper(new com.example.jetpacklearn.DataBinderMapperImpl());
}
}
DataBinderMapperImpl继承了MergedDataBinderMapper,MergedDataBinderMapper包含了一组DataBinderMapper对象。DataBinderMapper类是用来获取ViewDataBinding的,它会根据View和layoutId返回一个数据绑定类(xxxxxBinding)的实例。在DataBinderMapperImpl初始化,其会添加一个项目中自动生成的jetpacklearn.DataBinderMapperImpl类的实例。此类的代码为:
public class DataBinderMapperImpl extends DataBinderMapper {
private static final int LAYOUT_ACTIVITYMAIN = 1;
private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);
static {
INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.jetpacklearn.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
}
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
//获取视图的Tag
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
//根据视图的Tag生成不同的ViewDataBinding实例
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYMAIN: {
if ("layout/activity_main_0".equals(tag)) {
return new ActivityMainBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
}
return null;
}
......
}
而sMapper.getDataBinder方法最终会调用上面类中的getDataBinder方法,以生成数据绑定类的实例。在getDataBinder方法,会根据根View的tag来生成不同的ViewDataBinding对象。但是,我们在xml文件中并没有设置tag,那么这里的tag是哪里来的呢?实际上,我们在写完xml文件后,在编译时,数据绑定系统会根据我们的xml文件再生成一个XML文件,并在此XML文件中设置tag。生成的XML文件如下所示:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:tag="layout/activity_main_0"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:MyView="http://schemas.android.com/apk/res-auto">
<EditText
android:layout_width="60dp"
android:layout_height="wrap_content"
android:id="@+id/userName"
android:tag="binding_1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/showName"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/showName"
android:tag="binding_2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/userName"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
在编译器生成的文件中,不但会给根View设置tag,而且还会给里面使用数据绑定的View设置tag。
接下来,看一下在实例化ActivityMainBindingImpl时,都会做些什么。
public class ActivityMainBindingImpl extends ActivityMainBinding {
@Nullable
private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = null;
}
// views
@NonNull
private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;
......
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.TextView) bindings[2]
, (android.widget.EditText) bindings[1]
);
this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
this.mboundView0.setTag(null);
this.showName.setTag(null);
this.userName.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
......
}
在实例化ActivityMainBindingImpl时,其做的工作如下:首先,调用mapBindings方法生成一个视图数组bindings,此视图数组中即含有根View(下标为0),亦有使用数据绑定的视图(下标参考生成的XML中的tag)。然后,调用super方法,去执行父类ViewDataBinding的构造方法。在父类的构造方法中,会构建一个进行数据绑定的Runnable对象,以在收到绑定通知时,进行数据绑定。最后,将ActivityMainBindingIMpl实例放到根View的tag里,并发送一个数据绑定请求。接下来,依次对上面3个工作进行详细分析。
1、mapBindings方法的代码如下:
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
//创建视图数组
Object[] bindings = new Object[numBindings];
//添加视图元素
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
final int indexInIncludes;
//还没将数据绑定实例放到view的Tag中,此时getBinding的返回值为null
final ViewDataBinding existingBinding = getBinding(view);
if (existingBinding != null) {
return;
}
//获取视图的TAG,参考生成的XML文件
Object objTag = view.getTag();
final String tag = (objTag instanceof String) ? (String) objTag : null;
boolean isBound = false;
if (isRoot && tag != null && tag.startsWith("layout")) {
final int underscoreIndex = tag.lastIndexOf('_');
if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
//从生成的XML文件中知,index为0
final int index = parseTagInt(tag, underscoreIndex + 1);
if (bindings[index] == null) {
//添加根视图
bindings[index] = view;
}
//在案例中,未使用include标签,此值为-1
indexInIncludes = includes == null ? -1 : index;
isBound = true;
} else {
indexInIncludes = -1;
}
} else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
//将子View放到bindings数组中
//生成的XML文件中,子View的tag格式为binding_{x},此时tagIndex的值就是x
int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
if (bindings[tagIndex] == null) {
bindings[tagIndex] = view;
}
isBound = true;
indexInIncludes = includes == null ? -1 : tagIndex;
} else {
// Not a bound view
indexInIncludes = -1;
}
......
//将xml文件中的子View放到bindings数组中
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
final int count = viewGroup.getChildCount();
int minInclude = 0;
for (int i = 0; i < count; i++) {
final View child = viewGroup.getChildAt(i);
boolean isInclude = false;
//未使用include标签,此处先略过
if (indexInIncludes >= 0 && child.getTag() instanceof String) {
......
}
if (!isInclude) {
//递归调用当前方法,传入child,且isRoot为false以绑定子视图
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
2、super方法对应的父类ViewDataBinding的构造方法如下:
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
mBindingComponent = bindingComponent;
//此数组用于Observable数据变化时的回调
mLocalFieldObservers = new WeakListener[localFieldCount];
this.mRoot = root;
if (Looper.myLooper() == null) {
throw new IllegalStateException("DataBinding must be created in view's UI Thread");
}
if (USE_CHOREOGRAPHER) {
//SDK>16时走这里
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}
}
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
// Nested so that we don't get a lint warning in IntelliJ
if (!mRoot.isAttachedToWindow()) {
// Don't execute the pending bindings until the View
// is attached again.
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
//调用此方法,进行数据绑定
executePendingBindings();
}
};
3、将绑定类的实例放到根View的Tag,并发送绑定请求
setRootTag(root);
// listeners
invalidateAll();
setRootTag代码如下:
protected void setRootTag(View view) {
view.setTag(R.id.dataBinding, this);
}
invalidateAll方法代码如下:
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
......
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
//SDK>16走这里
//发送数据绑定请求,最终会调用mRebindRunnable的executePendingBindings方法
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
调用的executePendingBindings方法的代码如下:
public void executePendingBindings() {
if (mContainingBinding == null) {
//走这里
executeBindingsInternal();
} else {
//include标签下的View走这里
mContainingBinding.executePendingBindings();
}
}
private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
......
if (!mRebindHalted) {
//调用此方法进行数据绑定,此方法的真正实现在生成的xxxxBindingImpl类中(如ActivityMainBindingImpl)
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}
executeBindings方法的代码如下:
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
com.example.jetpacklearn.viewModel.UserViewModel userVM = mUserVM;
androidx.databinding.ObservableField<java.lang.String> userVMName = null;
java.lang.String userVMNameGet = null;
if ((dirtyFlags & 0x7L) != 0) {
if (userVM != null) {
// read userVM.name
userVMName = userVM.getName();
}
//给userVMName添加一个数据变化的监听器;实现双向绑定的步骤1
updateRegistration(0, userVMName);
if (userVMName != null) {
// read userVM.name.get()
userVMNameGet = userVMName.get();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.showName, userVMNameGet);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.userName, userVMNameGet);
}
if ((dirtyFlags & 0x4L) != 0) {
// api target 1
//给userName(EditView)添加文本监听器;实现双向绑定的步骤2
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.userName, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, userNameandroidTextAttrChanged);
}
}
为了实现双向绑定,既需要为数据设置监听器,监听数据变化;也需要为View设置监听器,以监听View的变化。在此,通过updateRegistration方法对数据设置监听器;通过最后一行的setTextWatcher方法对View设置监听器。因此,主要介绍中updateRegistration方法和setTextWatcher中的参数userNameAndroidTextAttrChanged。这两部分是实现双向绑定的关键。先介绍updateRegistration方法。
3.1 给Modle设置监听器
updateRegistration方法代码如下:
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
CREATE_PROPERTY_LISTENER的作用是 为Observable类添加一个回调接口,此接口会在数据变化时,通知观察者。其代码如下:
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
//创建一个WeakListener对象,此对象在给Observable类设置回调接口时,起到承接作用。
//WeakListener中包含了ViewDataBinding实例,回调接口的引用(当前类)
mListener = new WeakListener<Observable>(binder, localFieldId, this);
}
@Override
public WeakListener<Observable> getListener() {
return mListener;
}
@Override
public void addListener(Observable target) {
//给Observable添加回调接口
target.addOnPropertyChangedCallback(this);
}
......
//Observable类的子类会在其set方法中调用此方法
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
if (binder == null) {
return;
}
Observable obj = mListener.getTarget();
if (obj != sender) {
return; // notification from the wrong object?
}
//数据变化时,会调用此方法,通知绑定器重新绑定数据
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
WeakListener类的代码如下:
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
private final ObservableReference<T> mObservable;
protected final int mLocalFieldId;
private T mTarget;
public WeakListener(ViewDataBinding binder, int localFieldId,
ObservableReference<T> observable) {
//binder为ViewDataBinding实例
super(binder, sReferenceQueue);
//标识ViewDataBinding中的Observable字段的ID
mLocalFieldId = localFieldId;
//实现了回调接口OnPropertyChangedCallback
mObservable = observable;
}
......
}
handleFieldChange代码如下:
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
......
//调用ActivityMainBinding的onFieldChange方法,标注哪些数据发送了变化
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
//请求重新进行数据绑定
requestRebind();
}
}
updateRegistration(localFIeldId,observable,CREATE_PROPERTY_LISTENER)的代码如下:
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//给observable设置回调接口OnPropertyChangedCallback
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
registerTo方法代码如下:
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//生成一个WeakListener对象,此对象包含了OnPropertyChangedCallback的引用
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
//给observable添加回调接口(OnPropertyChangedCallback)
listener.setTarget(observable);
}
setTarget方法的代码如下:
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
//mObservable为WeakPropertyListener类实例,其addListener代码在上面
//mTarget是一个observable对象,此方法作用是给此对象添加一个回调接口
mObservable.addListener(mTarget);
}
}
题外话:在第一次看到此代码时,感觉很绕,回调很多,使人不容易看懂,觉着为什么不直接使用以下这样的写法:
userVMName.addOnPropertyChangedCallback(new XXXXCallBack())
这样的写法,可以让人一看就懂。一眼就能看出此行代码的作用是什么,添加了什么样的回调接口。而官方代码则看的绕来绕去的,不易理解。后来思考了以下,发现官方的写法其实挺有道理的。官方代码的写法使代码的耦合度降低了,符合“开闭原则”,“依赖倒置原则”。而我们的简单写方法则不符合这两个原则。比如,当我们要更换一个新的Callback时,简单写法需要修改addxxxCallback方法的参数,而官方的代码,则采用扩展的方法实现这种需求,不需要对原来代码进行修改。即只需要重新实现一个CreateListener的类,并添加一个新的updateRegistration方法就好。这样虽然多写了一些代码,但是保证了“开闭原则”,即对修改关闭,对扩展开放。
在此,通过下面的类图再回顾一下给Observable添加PropertyChangedCallback的过程。首先,创建一个WeakListener对象。然后,调用此对象的setTarget(Observable)方法,给Observable添加回调接口。此过程的核心是WeakListener类,此类是一个“虚假”的监听器类,其并没有实现监听功能。而是持有了回调接口的引用,并负责给Observable对象设置回调接口。官方将“给Observable对象设置回调接口”这个功能抽象为了一个接口ObservableReference,此举符合“依赖倒置”原则,方便日后对代码进行扩展。
3.2、给View设置监听器
上面介绍了如何给Model设置监听器,接下来介绍如何给View设置监听器。以上面的案例代码为例,绑定系统通过setTextWatcher给EditView设置监听器,以在EditView的文字变化时,通知Model去同步数据,setTextWatcher方法如下:
protected void executeBindings() {
......
if ((dirtyFlags & 0x4L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.userName, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, userNameandroidTextAttrChanged);
}
}
userNameandroidTextAttrChanged对应的类的代码如下:
private androidx.databinding.InverseBindingListener userNameandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
@Override
public void onChange() {
//EditView的文本变化时会调用此方法
java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(userName);
boolean userVMNameJavaLangObjectNull = false;
// userVM
com.example.jetpacklearn.viewModel.UserViewModel userVM = mUserVM;
// userVM != null
boolean userVMJavaLangObjectNull = false;
// userVM.name
androidx.databinding.ObservableField<java.lang.String> userVMName = null;
// userVM.name.get()
java.lang.String userVMNameGet = null;
userVMJavaLangObjectNull = (userVM) != (null);
if (userVMJavaLangObjectNull) {
userVMName = userVM.getName();
userVMNameJavaLangObjectNull = (userVMName) != (null);
if (userVMNameJavaLangObjectNull) {
//将EditView的text赋值给userVMName
userVMName.set(((java.lang.String) (callbackArg_0)));
}
}
}
};
userVMName的set方法代码如下:
public class ObservableField<T> extends BaseObservableField implements Serializable {
static final long serialVersionUID = 1L;
private T mValue;
......
/**
* Set the stored value.
*
* @param value The new value
*/
public void set(T value) {
if (value != mValue) {
//避免无线循环,只有数据真正改变时,才通知数据绑定器进行数据绑定
mValue = value;
notifyChange();
}
}
}
至此,双向绑定原理的介绍基本结束。其本质可以看作分别给数据源Model和视图View设置监听器,以在数据变化时,通知彼此同步变化。