45. Databinding原理

使用

1.在app的build.gradle文件下做如下配置
android {
    ......
    dataBinding {
        enabled = true
    }
}
2.在布局文件中配置
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="login"
            type="com.rzm.retrofitdemo.LoginBean" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{login.message}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
3.在Activity中调用方法
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBindingImpl binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
}
4.编译之后生成的布局文件中添加了tag

/Users/renzhenming/AndroidStudioProjects/RetrofitDemo/app/build/intermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml
注意,这个tag很有用,源码中通过tag来遍历布局

    <androidx.constraintlayout.widget.ConstraintLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity" android:tag="layout/activity_main_0">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="binding_1"        
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

原理

public class User extends BaseObservable {
    private String name;
    private String pwd;
}


ActivityMainBinding binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
user=new User("a","123");
binding.setUser(user);

setContentView

在Activity中使用的时候,先绑定布局文件,那么这个操作都做了什么?我们从源码中找答案

ActivityMainBinding binding= DataBindingUtil.setContentView(this,R.layout.activity_main)

在这个方法中,注意contentView是系统布局DecorView中的content节点,layoutId是当前Activity的布局id

    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);
        return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
    }

这里childrenAdded正常情况下必须是1,因为parent是系统的content节点,而使用databing,我们自己写的布局的根结点需要是一个<layout>

    private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
            ViewGroup parent, int startChildren, int layoutId) {
        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);
        }
    }

继续往下就进入到了com.项目路径.DataBinderMapperImpl类中,在getDataBinder中可以看到,返回了ActivityMainBindingImpl

  @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_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;
  }

而在构造ActivityMainBindingImpl的时候可以看出,在mapBindings方法中,将布局中的子节点保存在了数组中,于是通过binding对象就可以取到这些节点了如:binding.tv1

    public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
    }

setUser()

进入ActivityMainBindingImpl中

    public void setUser(@Nullable com.example.databindingdemo_20210117.User User) {
        //1.给User设置一个监听器(OnPropertyChangedCallback)监听数据的变化,当数据变化时,回调onPropertyChanged()方法
        updateRegistration(0, User);
        this.mUser = User;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容