简书链接:双向绑定的实现原理与样板代码 文章字数:789,阅读全文大约需要3分钟 双向绑定的方法有几种这里用到的是借助@BindingAdapter
来实现。无项 就是xml里面有一个model,你设置进去了,它就更新就是了。纯单项 ,所谓单项 ,就是你调用一个值set就可以让view变化,这需要 @Bindable
@ notifyPropertyChanged(BR.title);
双项 是值 可以修改值来改变view的状态 或者通过修改view的状态来实现修改model的值 这就是你中有我我中有你.
model继承 extends BaseObservable
然后set里面设置 这需要@BindingAdapter
@InverseBindingAdapter
纯@BindingAdapter
是方便xml里面设置用的。 比如里面定义 @BindingAdapter("intvalue")
就可以使用xxx:@{xxx.xx}
1 2 3 4 5 6 7 8 9 10 11 12 class BBB extends BaseObservable{ @Bindable//有了注解这个才会生成BR.title public String getTitle() { return title; } public void setTitle(String title) { this.title = title; notifyPropertyChanged(BR.title);//双向绑定通知改变,不通知就不会生效。 } }
单项绑定的我不想再说了,直接上高阶代码
下面代码实现 int类型编辑框的双向生效,修改编辑框内容的时候model值变化,修改model值得时候编辑框内容变化。
任意创建一个类定义下面几个静态方法
第一个方法代表 修改view,
1 2 @BindingAdapter("intvalue") public static void setTextIntValue(TextView view, int value) {
第二个方法代表修改model
1 2 @InverseBindingAdapter(attribute = "intvalue", event = "intvalueAttrChanged") public static int getTextIntValue(TextView view) {
第三个方法代表修改model的触发条件在里面设置view监听然后调用onChange从而触发调用第二个方法。
1 @InverseBindingAdapter(attribute = "intvalue", event = "intvalueAttrChanged")
第二个方法的event对应的是第三个方法的@BindingAdapter("intvalueAttrChanged")
用法: 在xml里面定义 1 2 3 4 <EditText app:intvalue="@={model.age}" />
触发原理: 编译后会生成一个databindimpl,它会根据注解生成viewbinding生成一个类, 里面的代码就是把第三个方法的代码拷贝一份进行设置监听, 当你修改编辑框内容的时候监听触发了,onChange,onChange()这是代表view改变,我之前说过是会通知model改变,model的改变为第二个方法,getxxint ,你可以尝试写死一个值,你会发现这个值在model里面生效了。
当你通过set器修改model的值,通过model里面定义的
1 notifyPropertyChanged(BR.title);/
就会触发view的修改,但是这里是个死循环, 这双向绑定不需要加 notifyPropertyChanged(BR.title);
或者加一个判断, 当然我这里之所以还是加上了,是我监听的不对,我写在了onTextChanged里面。 直接不加上notifyPropertyChanged
也能修改,但是光标位置不对,感觉不太爽,所以解决方法就是调整一下回调通知或者不加判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 /** * 根据model的值来设置 view的值 双向绑定 ,setValue Model 不需要加notifyFieldChanage() 否则死循环 除非文本判断。 **/ @BindingAdapter("intvalue") public static void setTextIntValue(TextView view, int value) { // final TextWatcher oldValue = ListenerUtil.trackListener(view, null, R.id.textWatcher); if(ParseUtil.parseInt(view.getText().toString()) ==value){//0 or "" ==0 找到死循环的原因了,现在不需要加这句话了。 return; } if(value==0){ view.setText("");//可以在失去焦点的时候设置。 }else{ view.setText(value + ""); } // if(oldValue!=null){ // view.addTextChangedListener(oldValue); } /** * 在onChange 里面调用本方法获得返回值, 然后来修改模型的值. * * @param view * @return */ @InverseBindingAdapter(attribute = "intvalue", event = "intvalueAttrChanged") public static int getTextIntValue(TextView view) { return ParseUtil.parseInt(view.getText().toString()); } @BindingAdapter("intvalueAttrChanged") public static void intValueChangeBind(final TextView view, final InverseBindingListener changeBindListener) { TextWatcher watcher =null; if(changeBindListener!=null){ watcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { changeBindListener.onChange(); } @Override public void afterTextChanged(Editable s) { } }; }else{ } // view.addTextChangedListener(watcher);//下面的话没有任何意义 //下面的话加不加都解决不了死循环的问题 只能用判断来解决死循环 addTextChangedListener可能是这句话导致 final TextWatcher oldValue = ListenerUtil.trackListener(view, watcher, R.id.textWatcher); if (oldValue != null) { view.removeTextChangedListener(oldValue); } if (watcher != null) { view.addTextChangedListener(watcher); } }
更多databind请访问我的其它文章。