MVVM框架使用之BaseObservable与双向绑定

 黑色字体重要

(字体放大会促进学习兴趣哦)

其他相关文章:

android之LifeCycle:https://blog.csdn.net/li6472/article/details/119795952?spm=1001.2014.3001.5501

android之LiveData:https://blog.csdn.net/li6472/article/details/119784791?spm=1001.2014.3001.5501

android之ViewModel:https://blog.csdn.net/li6472/article/details/119800423?spm=1001.2014.3001.5501

android之MVVM框架使用之xml详解:https://blog.csdn.net/li6472/article/details/119782825?spm=1001.2014.3001.5501

android之MVVM理解:https://blog.csdn.net/li6472/article/details/119762190?spm=1001.2014.3001.5501

amdroid之MVVM写一个简单的程序:https://blog.csdn.net/li6472/article/details/119761806?spm=1001.2014.3001.5501

一  双向绑定

在TextView中,我们通过dataBinding把实体中的数据放到TextView中展示,这是从实体到view方向上的绑定'

我们在TextView手动输入了一些数据,我们通过dataBinding把view中的数据设置到对应的实体类的字段中,这是从view到实体类方向上的绑定,整合起来就是双向绑定

二 双向绑定的问题

- 死循环绑定:因为数据源改变会通知view刷新,而view改变又会通知数据源刷新,这样一直循环往复,那么对于一些相同数据也要更改,浪费资源,就形成了死循环绑定。

双向绑定问题的解决方式

死循环绑定的解决方式:

在处理双向绑定的业务逻辑时,要对新旧数据进行比较,只处理新旧数据不一样的数据,对于新旧数据一样的数据作return处理,通过这种方式来避免死循环绑定。

例如(不需要看懂)

@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
    final CharSequence oldText = view.getText();
    if (text == oldText || (text == null && oldText.length() == 0)) {
        return;
    }
    if (text instanceof Spanned) {
        if (text.equals(oldText)) {
            return; // No change in the spans, so don't set anything.
        }
    } else if (!haveContentsChanged(text, oldText)) {
        return; // No content changes, so don't set anything.
    }
    view.setText(text);
}

判空否则会出现空指针

二 BaseObservable

一般在写ViewModel层时需要用到BaseObservable + @Bindable + notifyPropertyChanged()
 1 首先BaseObservable

必须要求子类继承BaseObservable
- 继承之后必须要使用@Bindable注解和notifyPropertyChanged()方法

BaseObservable功能的具体表现主要是在数据源发生改变时,自动通知view刷新数据。这句话包含2个意思:
- BaseObservable是双向绑定的基础
- 结合双向绑定表达式@={},也可以做到当view内容变化时,自动通知数据源更新。

监听view内容的变化是BaseObservable做不到的

得依赖指定的注解才能把view内容的变化通知出去(对于android自带的view,DataBinding已经帮我们做了),然后BaseObservable收到这些通知后触发 notifyPropertyChanged(),进而改变数据源以及界面。

有时我们继承了其他类那怎么继承 BaseObservable呢

 实体类已经继承了其他父类,不能再继承BaseObservable:
既然不能继承BaseObservable,那就实现Observable接口呗,反正BaseObservable也是Observable接口的实现类。但问题是如何处理实体类中的get/set方法以及Observable接口中的两个方法:addOnPropertyChangedCallback和removeOnPropertyChangedCallback。
好在DataBinding给出了解决方式,那就是PropertyChangeRegistry,一个简单的示例如下:

public class User implements Observable {
    private PropertyChangeRegistry registry = new PropertyChangeRegistry();

    private String name;
    private int age;
    private String sex;
    private boolean isStudent;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        registry.notifyChange(this, BR.name);
    }

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        registry.notifyChange(this, BR.age);
    }

    @Bindable
    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
        registry.notifyChange(this, BR.sex);
    }

    @Bindable
    public boolean isStudent() {
        return isStudent;
    }

    public void setStudent(boolean student) {
        isStudent = student;
        registry.notifyChange(this, BR.student);
    }

    @Override
    public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        registry.add(callback);
    }

    @Override
    public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        registry.remove(callback);
    }
}


采用实现Observable接口的方式仍然需要为每个get方法添加`@Bindable注解

    实体类的字段很多很多,为每个get/set方法都对应加上@Bindable注解和notifyPropertyChanged()方法太浪费时间
    为解决这个问题,DataBinding提供了响应式对象:

针对8种基本类型的数据结构提供了专门的包装类

    ObservableBoolean
    ObservableByte
    ObservableChar
    ObservableDouble
    ObservableFloat
    ObservableInt
    ObservableLong
    ObservableShort

针对集合提供的包装类

    ObservableArrayList
    ObservableArrayMap

针对实现了Parcelable接口的对象提供的包装类

    ObservableParcelable

针对其他类型提供的包装类

    ObservableField。最典型的:ObservableField

一个响应式对象的使用示例:

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue"); //判断当前线程是否是主线程,不是主线程就抛出异常
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

再看下postValue()方法的具体实现:

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    // 会在主线程中执行  mPostValueRunnable中的内容。
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 在主线程中调用setValue()方法
        setValue((T) newValue);
    }
};

可以看到,通过使用响应式对象,确确实实不用写@Bindable注解和notifyPropertyChanged()方法了,甚至连get/set方法都省了,看起来一切都很不错,但是响应式对象的问题在于赋值和取值操作变得稍显复杂了,就像下面这样:

 User user = new User();
 user.age.set(5);//赋值
 int i = user.age.get();//取值

并且把字段定义成public形式的,破坏了Java的封装性,有些不符合日常编码习惯。  

2 @Bindable

    该注解用于双向绑定,需要与 notifyPropertyChanged()方法结合使用
    该注解用于标记实体类中的get方法或“is”开头的方法,且实体类必须继承BaseObserable.
    用法极其简单

    实体类也可以不用继承BaseObservable,而是实现Observable接口,但是需要自行处理一些接口方法逻辑,BaseObservable是实现Observable接口的类,内部已经做好了相关逻辑处理,所以选择继承BaseObservable相对简单一些。

示例用法

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue"); //判断当前线程是否是主线程,不是主线程就抛出异常
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

再看下postValue()方法的具体实现:

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    // 会在主线程中执行  mPostValueRunnable中的内容。
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 在主线程中调用setValue()方法
        setValue((T) newValue);
    }
};

既然用法如此简单,那就不多说了。
@Bindable注解是用来干什么的?

使用@Bindable注解标记的get方法,在编译时,会在BR类中生成对应的字段,然后与notifyPropertyChanged()方法配合使用,当该字段中的数据被修改时,dataBinding会自动刷新对应view的数据,而不用我们在拿到新数据后重新把数据在setText()一遍,就凭这一点,dataBinding就可以简化大量的代码

3 notifyPropertyChanged()

动态数据更新notifyPropertyChanged

在User的set方法内用notifyPropertyChanged通知数据变化,数据模型变化后由Android系统通知get然后刷新view。

因个人能力及精力有限,文中难免有讲解不到位的地方,还请各位读者谅解;也难免存在疏漏或理解错误的地方,恳请各位开发者拍砖!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值