作者:智慧曜彰_272 | 来源:互联网 | 2023-09-18 16:36
什么是双向绑定?其实在之前有一篇关于MVVM的文章中已经介绍过,比如登录的时候Edittext,当我们输入登录账号和密码时候,不需要通过Edittext.getText()获取内容,而是自动更新到M层,相反,当更新M层相关内容,也会自动更新到EditText尽心展示。所以,双向绑定就是:V层->M层、M层->V层。 在官方的介绍中,有两种方式能够实现自定义双向绑定:
InverseBindingMethods+InverseBindingMethod+BindingAdapter InverseBindingAdapter+BindingAdapter 本篇文章讲解第一种方式,后续会讲解第二种方式。 一、InverseBindingMethods 该注解作用于类,官方的介绍是该注解,用于枚举属性、getter和事件相关联。并且value是InverseBindingMethod数组。
二、InverseBindingMethod 该注解作用于方法之上。
type:需要自定义属性的类。必填项,比如RatingBar.class attribute:String类型。必填项,如android:rating event:用于通知view层属性值变化。可选项,否则使用默认配置:如ratingAttrChanged method:get方法,用于接收view层属性变化,通知Model层。可选项,否则使用默认配置:如getRating 三、BindingAdapter 该注解作用于方法之上。方法的第一个参数的类型必须为View类型,不然报错
value:自定义任意属性,比如android:rating requireAll:boolean类型,可选项,返回true,所有的属性都需要支持,返回false,不需要支持所有属性。 四、自定义的属性是如何被调用的 比如在xml设置如下伪代码:
. . . < data> < variablename&#61; "rt" type&#61; "Float" / > < / data> < RatingBarandroid: layout_width&#61; "match_parent" android: layout_height&#61; "wrap_content" android: numStars&#61; "10" android: rt&#61; "&#64;&#61;{rt}" / > . . .
编译时&#xff0c;编译器会扫描RatingBar使用databinding表达式的属性。这里会找到 android:rt 。然后判断这个属性是不是RatingBar自有属性&#xff0c;如果是的话&#xff0c;会找对应的set方法(这里指setRt()方法)生成代码&#xff1b;如果不是自有属性&#xff0c;先找有没有对应且合适的set方法&#xff0c;有的话&#xff0c;就用set方法生成代码&#xff0c;没有的话&#xff0c;就会从所有定义的BindingAdapter中去找合适的方法。比如setRt()方法。
五、自定义双向绑定 &#64;InverseBindingMethods ( value &#61; { &#64;InverseBindingMethod ( type &#61; RatingBar. class , attribute &#61; "android:rating" ) } ) public class MyInverseBindingMethods { &#64;BindingAdapter ( "android:rating" ) public static void setRating ( RatingBar view, float value) { if ( view &#61;&#61; null) { return ; } float rating &#61; view. getRating ( ) ; if ( rating &#61;&#61; value) { return ; } view. setRating ( value) ; } &#64;BindingAdapter ( value &#61; { "android:onRatingChanged" , "android:ratingAttrChanged" } , requireAll &#61; false ) public static void setListener ( RatingBar view, final RatingBar. OnRatingBarChangeListener listener, final InverseBindingListener attrListener) { if ( attrListener &#61;&#61; null) { view. setOnRatingBarChangeListener ( listener) ; } else { view. setOnRatingBarChangeListener ( new RatingBar. OnRatingBarChangeListener ( ) { &#64;Override public void onRatingChanged ( RatingBar ratingBar, float rating, boolean fromUser) { if ( listener !&#61; null) { listener. onRatingChanged ( ratingBar, rating, fromUser) ; } attrListener. onChange ( ) ; } } ) ; } } }
这段代码定义了RatingBar类的android:rating 属性&#xff0c;由于没有定义event和method属性&#xff0c;所以event的属性默认为android:ratingAttrChanged &#xff0c;method的默认属性对应为getRating 。其实RatingBar类已经提供了android:rating 属性&#xff0c;这里为什么要重新定义&#xff1f;其实是为了防止双向绑定死循环的问题。当我们在布局中使用了databinding表达式&#xff0c;那么就会调用到setRating( )方法&#xff0c;判断当前值和设置的值是否相同&#xff0c;避免循环设置。
下面说一下重点方法setListener()&#xff1a; 这里面的BindingAdapter多了两个属性
android:onRatingChanged android:ratingAttrChanged 另外我们知道&#xff0c;这两个属性和setListener方法的参数是按照顺序对应的&#xff0c;所以android:onRatingChanged 对应OnRatingBarChangeListener &#xff0c;android:ratingAttrChanged 对应InverseBindingListener 。而我们又知道&#xff0c;android:ratingAttrChanged 对应 &#64;InverseBindingMethod 的event 属性&#xff0c;所以最终event 转换为InverseBindingListener 。上面说到event 属性用来双向绑定时对view的变化作出通知的 &#xff0c;所以最终试通过InverseBindingListener 实现通知的&#xff0c;对应即 onChange() 方法
六、使用 布局文件
< ? xml version&#61; "1.0" encoding&#61; "utf-8" ? > < layout xmlns: android&#61; "http://schemas.android.com/apk/res/android" xmlns: app&#61; "http://schemas.android.com/apk/res-auto" xmlns: tools&#61; "http://schemas.android.com/tools" > < data> < variablename&#61; "rating" type&#61; "Float" / > < / data> < LinearLayoutandroid: layout_width&#61; "match_parent" android: layout_height&#61; "match_parent" android: orientation&#61; "vertical" tools: context&#61; ".MainActivity" > < RatingBarandroid: layout_width&#61; "match_parent" android: layout_height&#61; "wrap_content" android: numStars&#61; "10" android: rating&#61; "&#64;&#61;{rating}" / > < / LinearLayout> < / layout>
注意使用 &#64;&#61; 表达式。
activity中绑定
final ActivityInverseBindingMethodsBinding binding &#61; DataBindingUtil. setContentView ( this , R. layout. activity_inverse_binding_methods) ; binding. setRating ( 5f ) ; Log. e ( "m---->view" , binding. getRating ( ) &#43; "" ) ; new Handler ( ) . postDelayed ( new Runnable ( ) { &#64;Override public void run ( ) { Log. e ( "view---->m" , binding. getRating ( ) &#43; "" ) ; } } , 5000 ) ;
初始值为5&#xff0c;然后滑动控件&#xff0c;5s后发现控件变化&#xff1a;