Android开发Android开发经验谈Kotlin编程

Data Binding入门(Kotlin)

2018-08-15  本文已影响10人  喂_balabala
官方文档
https://developer.android.google.cn/topic/libraries/data-binding/
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
    <data>
    <variable name="user" type="com.example.admin.myapplication.User"/>
    <variable name="presenter" type="com.example.admin.myapplication.DataActivity.Presenter"/>
    </data>


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

dataBinding.user=User("aaaa","bbb")
dataBinding.setVariable(BR.user,user)
problem
在xml中写的语法如果有错误的话没有明确提示,只会编译不过
在xml中设置了引用,在activity中再setText无效
 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 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);
}

然后会回到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);
    }
}

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


apt代码生成,里面的方法基本都是static
标记位:executeBindings方法中的dirtyFlags。每个操作都会有一个标记位
批量操作:修改了user的数据不会立即刷新到view中,只有调用了setVariable才会改变
缓存表达式:相同的表达式不会重复运算,直接拿值




Data Binding会自动检测对象是否为空。源码中也能看到,在DataBindingUtil的executeBindings方法中有对user进行非空判断。
Data Binding不会对数组索引进行检测,要自己排除越界问题



还可以bind多个,只要你include里面也写了对应的data和layout标签
 <include layout="@layout/include_demo" bind:user="@{user}" bind:presenter="@{presenter}"/>
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<String,String> 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");
}

    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text='@{user.map["dfga"]}'
              />
想在xml中使用view的属性要导包
<data>
    <import type="android.view.View"/>
</data>

android:visibility="@{user.isShow?View.GONE:View.VISIBLE}"
上一篇下一篇

猜你喜欢

热点阅读