Data Binding(Kotlin)

https://www.jianshu.com/p/3e3d5520917a

官方文档
Demo传送门
在gradle中添加databinding 为了适配kotlin还要加插件
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

android {
    dataBinding {
    enabled = true
    }
}
onCreate中绑定布局
        // The layout for this activity is a Data Binding layout so it needs to be inflated using
    // DataBindingUtil.
    val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main)

    // The returned binding has references to all the Views with an ID.
    binding.observableFieldsActivityButton.setOnClickListener {
        startActivity(Intent(this, ObservableFieldActivity::class.java))
    }
    binding.viewmodelActivityButton.setOnClickListener {
        startActivity(Intent(this, ViewModelActivity::class.java))
    }
设置数据
//.user是在xml布局中设置的data
    
    
    
    


val user = User("aaaa", "bbb")

dataBinding.user=User("aaaa","bbb")
dataBinding.setVariable(BR.user,user)
problem
在xml中写的语法如果有错误的话没有明确提示,只会编译不过
 android:text="@{user.name}"
 
dataBinding.aaaa.text = User("aaa","bbb").firstName
dataBinding.aaaa.setText(User("aaa","bbb").firstName)
BR
binding resourse(R文件)
源码分析

调用DataBindingUtil.setContentView之后会调用调用DataBindingUtil的setContentView方法。在这里面getDecorView获取顶层视图,再获取整个viewGroup,把它绑定在一起。

    // @Nullable don't annotate with Nullable. It is unlikely to be null and makes using it from
// kotlin really ugly. We cannot make it NonNull w/o breaking backward compatibility.
public static  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);
}

然后会回到ActivityDataBinding的bind方法中,返回ActivityDataBinding的实例。

    @NonNull
public static ActivityDataBinding bind(@NonNull android.view.View view, @Nullable android.databinding.DataBindingComponent bindingComponent) {
    if (!"layout/activity_data_0".equals(view.getTag())) {
        throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
    }
    return new ActivityDataBinding(bindingComponent, view);
}

在ActivityDataBinding的构造方法中mapBindings里面会去遍历里面所有的view,并添加到一个集合里面返回回来,然后就拿到了所有的view,可以看到this.edittext等设置

    public ActivityDataBinding(@NonNull android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
    super(bindingComponent, root, 0);
    final Object[] bindings = mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds);
    this.aaaa = (android.widget.TextView) bindings[1];
    this.aaaa.setTag(null);
    this.bbbb = (android.widget.TextView) bindings[2];
    this.bbbb.setTag(null);
    this.edittext = (android.widget.EditText) bindings[3];
    this.edittext.setTag(null);
    this.followEdittext = (android.widget.TextView) bindings[4];
    this.mboundView0 = (android.widget.LinearLayout) bindings[0];
    this.mboundView0.setTag(null);
    setRootTag(root);
    // listeners
    mCallback1 = new android.databinding.generated.callback.OnClickListener(this, 1);
    invalidateAll();
}

这个时候已经拿到了所有view的对象,现在就可以对他们赋值了。赋值在setVariable方法里面。

    //这两个方法其实是一样的,  .user也会调用.setVariable方法
    dataBinding.user=User("aaaa","bbb")
    dataBinding.setVariable(BR.user,user)

在setVariable里面有在xml中设置的所有data

    @Override
public boolean setVariable(int variableId, @Nullable Object variable)  {
    boolean variableSet = true;
    if (BR.presenter == variableId) {
        setPresenter((com.example.admin.myapplication.DataActivity.Presenter) variable);
    }
    else if (BR.user == variableId) {
        setUser((com.example.admin.myapplication.User) variable);
    }
    else {
        variableSet = false;
    }
        return variableSet;
}

在setUser中会有一个通知notifyPropertyChanged

    public void setUser(@Nullable com.example.admin.myapplication.User User) {
    this.mUser = User;
    synchronized(this) {
        mDirtyFlags |= 0x2L;
    }
    notifyPropertyChanged(BR.user);
    super.requestRebind();
}

最后执行的操作是在executeBindings中进行的,可以看到里面有setText的操作,那么基础操作流程就结束了

    @Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    android.view.View.OnClickListener presenterOnClickAndroidViewViewOnClickListener = null;
    com.example.admin.myapplication.DataActivity.Presenter presenter = mPresenter;
    java.lang.String userFirstName = null;
    android.databinding.adapters.TextViewBindingAdapter.OnTextChanged presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged = null;
    com.example.admin.myapplication.User user = mUser;
    java.lang.String userLastName = null;

    if ((dirtyFlags & 0x5L) != 0) {



            if (presenter != null) {
                // read presenter::onClick
                presenterOnClickAndroidViewViewOnClickListener = (((mPresenterOnClickAndroidViewViewOnClickListener == null) ? (mPresenterOnClickAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mPresenterOnClickAndroidViewViewOnClickListener).setValue(presenter));
                // read presenter::onTextChanged
                presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged = (((mPresenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged == null) ? (mPresenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged = new OnTextChangedImpl()) : mPresenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged).setValue(presenter));
            }
    }
    if ((dirtyFlags & 0x6L) != 0) {



            if (user != null) {
                // read user.firstName
                userFirstName = user.getFirstName();
                // read user.lastName
                userLastName = user.getLastName();
            }
    }
    // batch finished
    if ((dirtyFlags & 0x5L) != 0) {
        // api target 1

        this.aaaa.setOnClickListener(presenterOnClickAndroidViewViewOnClickListener);
        android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.edittext, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, (android.databinding.InverseBindingListener)null);
    }
    if ((dirtyFlags & 0x6L) != 0) {
        // api target 1

        android.databinding.adapters.TextViewBindingAdapter.setText(this.aaaa, userFirstName);
        android.databinding.adapters.TextViewBindingAdapter.setText(this.bbbb, userLastName);
    }
    if ((dirtyFlags & 0x4L) != 0) {
        // api target 1

        this.bbbb.setOnClickListener(mCallback1);
    }
}

Data Binding(Kotlin)_第1张图片

处理layout文件:要移除掉layout和data标签,还原成之前
解析表达式: android:text="@{user.name}"
依赖解析:解析表达式里面的是参数还是方法等等
找到setter:依赖解析完之后运算里面的表达式
Data Binding(Kotlin)_第2张图片
Data Binding(Kotlin)_第3张图片

apt代码生成,里面的方法基本都是static
标记位:executeBindings方法中的dirtyFlags。每个操作都会有一个标记位
批量操作:修改了user的数据不会立即刷新到view中,只有调用了setVariable才会改变
缓存表达式:相同的表达式不会重复运算,直接拿值
Data Binding(Kotlin)_第4张图片
Data Binding(Kotlin)_第5张图片
Data Binding会自动检测对象是否为空。源码中也能看到,在DataBindingUtil的executeBindings方法中有对user进行非空判断。
Data Binding不会对数组索引进行检测,要自己排除越界问题
Data Binding(Kotlin)_第6张图片
还可以bind多个,只要你include里面也写了对应的data和layout标签

 

Data Binding(Kotlin)_第7张图片

BaseObservable

数据类继承BaseObservable,get方法设置@Bindable注解,然后在set方法里面通知内容改变notifyPropertyChanged,这样就可以在不调用setVariable的时候也能刷新数据了。

public class User extends BaseObservable{

    @Bindable
public String getFirstName() {
    return this.firstName;
}
public void setFirstName(String name){
    firstName=name;
    notifyPropertyChanged(BR.firstName);
}

如果不用Bindable注解,可以在set方法里面调用notifyChange();这样会全体刷新

    public void setLastName(String name){
    lastName=name;
    notifyChange();
}
Observable Fields

观察成员变量

public ObservableBoolean isShow;
Observable Collection

设置集合,在xml布局中可以直接使用

public ObservableArrayMap map = new ObservableArrayMap<>();
public User(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    map.put("aaa","asdf");
    map.put("vd","sdf");
    map.put("dfga","fg");
}

    
想在xml中使用view的属性要导包

    


android:visibility="@{user.isShow?View.GONE:View.VISIBLE}"
RecycleView高级绑定Demo
  • recycleview中有多种布局的时候,xml中的variable取同一个name,不然无法区分

  • 变量或者observable改变后,会在下一帧进行绑定的改变,如果要立刻执行,要调用executePendingBindings方法

  • data binding 会本地化变量/值域,以解决同步问题(对collection不行)

  • 默认生成的binding是类名+Binding,想自定义binging类名可以在XML中的data中增加class属性

    
      ...
    
    

Data Binding(Kotlin)_第8张图片

Data Binding(Kotlin)_第9张图片

Data Binding(Kotlin)_第10张图片

  • 高版本中不用写 bind:

        
    
    @BindingAdapter({"source", "placeholder", "isCircle"})
    public static void bindPlaceHolderImageCircle(ImageView image, String source, Drawable placeholder,   boolean isCircle) {
        loadImage(image, source, placeholder, null, isCircle);
    }
    

Data Binding(Kotlin)_第11张图片

双向绑定 @={viewModule.smsCodeObservable}
  • 不用再写edittext的监听器

                
    

Data Binding(Kotlin)_第12张图片
Data Binding(Kotlin)_第13张图片
Data Binding(Kotlin)_第14张图片
Data Binding(Kotlin)_第15张图片
Data Binding(Kotlin)_第16张图片

  • 双向绑定并不是支持所有属性,只支持带有额外事件的,例如text、checked、year、hour、rating、progress等
addOnPropertyChangedCallback监听属性变更
 loginViewModel.loginModelState.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
        override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
            changeModel(loginViewModel.loginModelState.get()!!)
        }
    } )

Data Binding(Kotlin)_第17张图片
Data Binding(Kotlin)_第18张图片
Data Binding(Kotlin)_第19张图片
Data Binding(Kotlin)_第20张图片

Data Binding(Kotlin)_第21张图片

Data Binding(Kotlin)_第22张图片

你可能感兴趣的:(other)