黑色字体重要
(字体放大会促进学习兴趣哦)
其他相关文章:
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。
因个人能力及精力有限,文中难免有讲解不到位的地方,还请各位读者谅解;也难免存在疏漏或理解错误的地方,恳请各位开发者拍砖!!!