一、最官方的使用方法,不多BB直接上链接
DataBinding使用简介: 戳我👉
二、源码分析
1、我们先说一下最简单使用
- 先看一下我们databinding的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">
<data>
<variable
name="testViewModel"
type="com.sun.kotlindemo.dataBinding.TestViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{testViewModel.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{testViewModel.desc}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name" />
<EditText
android:id="@+id/editText"
android:layout_width="300dp"
android:layout_height="100dp"
android:background="#00acff"
android:gravity="center"
android:text="@={testViewModel.name}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView"
android:layout_width="300dp"
android:layout_height="100dp"
android:background="#00acff"
android:layout_marginTop="10dp"
android:text="点我修改名字"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@+id/editText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- 我们来看下activity中的使用
- 我们来看下TestViewModel
以上就是DataBinding的基本使用,当我们点击@+id/textView时,@+id/name 和 @+id/editText都会随之改变,当我们在@+id/editText 中输入文字时,@+id/name也会更新为editView输入的文字,这就是DataBinding+ViewModel+LiveData实现的双向绑定
2、我们先说一下最简单使用
- 先看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">
<data>
//将要进行绑定的数据data
<variable
name="testViewModel"
type="com.sun.kotlindemo.dataBinding.TestViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
//布局文件
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
来看一下经过编译后生成的文件
这个文件就是我们的xml生成的用于DataBinding使用的xml文件,在这里对我们的view进行了包装,对每一view都添加了一个tag,并且生成一个属性指向我们viewModel中的data
- 接下来我们看activity中的DataBindingUtil.setContentView()做了什么
DataBindingUtil.setContentView<ActivityDataBindingTextViewModelBinding>(this, R.layout.activity_data_binding_text_view_model)
点进去
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
继续点setContentView()
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
//获取我们的根布局
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
//将我们的xml绑定在根布局上
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
继续点bindToAddedViews()
private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
ViewGroup parent, int startChildren, int layoutId) {
//获取根布局中的子view个数,也就是我们的xml中view的个数
final int endChildren = parent.getChildCount();
final int childrenAdded = endChildren - startChildren;
if (childrenAdded == 1) {
final View childView = parent.getChildAt(endChildren - 1);
return bind(component, childView, layoutId);
} else {
final View[] children = new View[childrenAdded];
for (int i = 0; i < childrenAdded; i++) {
children[i] = parent.getChildAt(i + startChildren);
}
return bind(component, children, layoutId);
}
}
继续点bind()
//绑定多个view
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
//绑定单个view
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
继续点sMapper.getDataBinder(),在DataBinderMapper中.getDataBinder()是抽象的,看我们生成的类【这里强调一下,这个类是生成的类】DataBinderMapperImpl中这个方法
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYDATABINDINGBEAN: {
if ("layout/activity_data_binding_bean_0".equals(tag)) {
return new ActivityDataBindingBeanBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_data_binding_bean is invalid. Received: " + tag);
}
case LAYOUT_ACTIVITYDATABINDINGTEXTVIEWMODEL: {
//这个就是我们写的xml中的ConstraintLayout生成的
if ("layout/activity_data_binding_text_view_model_0".equals(tag)) {
//继续看这个
return new ActivityDataBindingTextViewModelBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_data_binding_text_view_model is invalid. Received: " + tag);
}
}
}
继续点 ActivityDataBindingTextViewModelBindingImpl()
public ActivityDataBindingTextViewModelBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
//这里这个5就是我们xml中view的数量,
this(bindingComponent, root, mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds));
}
继续点 mapBindings()
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
//创建一个大小为5的数组
Object[] bindings = new Object[numBindings];
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
继续点 mapBindings()
- 接下来我们来看数据的绑定
dataBinding.testViewModel = viewModel
点击textViewModel是跳转不到生成的代码中的,我们自己打开
打开这个文件我们可以看到
public void setTestViewModel(@Nullable com.sun.kotlindemo.dataBinding.TestViewModel TestViewModel) {
this.mTestViewModel = TestViewModel;
synchronized(this) {
mDirtyFlags |= 0x4L;
}
notifyPropertyChanged(BR.testViewModel);
super.requestRebind();
}
点击 super.requestRebind();
点击 mRebindRunnable
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();
}
};
点击executePendingBindings()
public void executePendingBindings() {
if (mContainingBinding == null) {
//会走这个方法
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
点击executeBindingsInternal()
点击 executeBindings(),你会发现这个是一个抽象方法,它的实现在我们生成的 ActivityDataBindingTextViewModelBindingImpl 中,进入看这个方法
点击updateLiveDataRegistration()
protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
mInLiveDataRegisterObserver = true;
try {
//会走这里
return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
} finally {
mInLiveDataRegisterObserver = false;
}
}
点击updateRegistration()
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
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) {
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
//会走这里
listener.setTarget(observable);
}
点击 setTarger()
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
//会走这里
mObservable.addListener(mTarget);
}
}
点击 addListener(),你会发现这是一个抽象方法,我们看LiveDataListener中的addListener()
到这里,将我们的MutableLiveData类型的name进行observe绑定,绑定的观察者就是这个LiveDataListener,它实现类 Observe 接口
private static class LiveDataListener implements Observer,
绑定监听结束,那么只要这个name发生变化,就会执行LiveDataListener的onChanged()
重头戏:数据变化后,更新UI
- 当我们点击@+id/textView去改变ViewModel中的name值,也就是数据发生变化,那么它是怎么更新布局的呢,我们来看 LiveDataListener的onChanged()
@Override
public void onChanged(@Nullable Object o) {
ViewDataBinding binder = mListener.getBinder();
if (binder != null) {
binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
}
}
点击 handleFieldChange()
点击 requestRebind()会把之前的流程走一遍,最终会走到 我们生成的类 ActivityDataBindingTextViewModelBindingImpl 中的 executeBindings()
重头戏:UI变化后,更新数据 5. 在ActivityDataBindingTextViewModelBindingImpl 中的 executeBindings()中,为editview添加了变化的监听,我们看到最后一个参数是 editTextandroidTextAttrChanged,记住它
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.editText, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, editTextandroidTextAttrChanged);
点击 setTextWatcher()
我们先看一下 editTextandroidTextAttrChanged 是什么?它是一个接口的实现
private androidx.databinding.InverseBindingListener editTextandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
@Override
public void onChange() {
//获取editText的值
java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(editText);
androidx.lifecycle.MutableLiveData<java.lang.String> testViewModelName = null;
com.sun.kotlindemo.dataBinding.TestViewModel testViewModel = mTestViewModel;
java.lang.String testViewModelNameGetValue = null;
boolean testViewModelJavaLangObjectNull = false;
boolean testViewModelNameJavaLangObjectNull = false;
testViewModelJavaLangObjectNull = (testViewModel) != (null);
if (testViewModelJavaLangObjectNull) {
testViewModelName = testViewModel.getName();
testViewModelNameJavaLangObjectNull = (testViewModelName) != (null);
if (testViewModelNameJavaLangObjectNull)
//在这里,将EditText的text赋值给了TestViewModel中的name
testViewModelName.setValue(((java.lang.String) (callbackArg_0)));
}
}
}
};
到这里,已经将editText输入的值,更新给了TestViewModel中的name了,实现了UI变化 ---> 数据更新