Android进阶六:Databinding的双向绑定 |
您所在的位置:网站首页 › 安卓radiogroup双向绑定 › Android进阶六:Databinding的双向绑定 |
在Android Studio 2.1 Preview 3之后,官方开始支持双向绑定了。 什么是双向绑定呢? 下面是Data Binding的基本使用: 上面的EditText的text属性和对象user的name属性进行了绑定,但是代码如果只像上面这样这样的话,那EditView的text只会在第一次设置binding.setUser(user)的时候被赋予user.name值,之后如果user.name值变化的时候,EditView的text值不会变化。 我们理想的情况是user.name值变化的时候,EditText的text值也同时自动变化,同样,EditText的text值变化的时候,user.name值也同时自动变化。 那所谓双向绑定,包括正向绑定和反向绑定, 正向绑定:当user的name值变化的时候,EidtView的text也同时变化; 反向绑定:当EidtView的text变化的时候,user对象的name值也同时变化。 怎么实现正向绑定呢? 正向绑定方法一:继承Observable的方式 具体如下: 1.我们的实体类(User类)继承BaseObservale类 2.Getter上使用注解@Bindable 3.在Setter里调用方法notifyPropertyChanged 让User类继承BaseObservable: public class User extends BaseObservable {private String name;@Bindable public String getName() { return username; } public void setName(String name) { this.name = name; notifyPropertyChanged( BR.name); } }注意:当Getter方法getName()上加@Bindable以后,会自动在com.android.databinding.library.baseAdapters包的BR类中加了name属性 原理:当代码中设置user的name属性的时候user.setName(name),调用notifyPropertyChanged(BR.name),这个方法在BaseObservable类中定义,它会根据BR.name这个ID去通知相应的UI进行更新。以上就在user的name属性变化以后,EditView的text会自动的发送变化,就实现了正向绑定。 方法二:定义ObservableField方式 如果所有要绑定的都需要创建Observable类,那也太麻烦了。所以Data Binding还提供了一系列Observable,包括 ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, 和ObservableParcelable。我们还能通过ObservableField泛型来申明其他类型,如: private static class User {public final ObservableField firstName =new ObservableField();public final ObservableField lastName =new ObservableField();public final ObservableInt age = new ObservableInt(); }而在xml中,使用方法和普通的String,int一样,只是会自动刷新,但在java中访问则会相对麻烦: user.firstName.set("Google"); int age = user.age.get();那怎么实现反向绑定呢? 对于一部分属性,在XML中的引用中把“@{}”改成“@={}”,比如: 这样当EditText的text发生改变的时候user.name属性也会同步变化,只有少部分控件的属性都支持“@={}”,如下: AbsListView android:selectedItemPosition CalendarView android:date CompoundButton android:checked DatePicker android:year, android:month, android:day NumberPicker android:value RadioGroup android:checkedButton RatingBar android:rating SeekBar android:progress TabHost android:currentTab (估计没人用) TextView android:text TimePicker android:hour, android:minute 反向绑定现在在来看下自定义属性如何实现反向绑定, 自定义一个SeedBar控件,可以调整最大最小值,调整HMIN,MHAX更新RangeBean的min和max属性: Data Bean类如下: public class RangeBean {int max;int min;public int getMax(){return max;}public void setMax(int max){this.max = max;}public int getMin(){return min;}public void setMin(int min){this.min = min;} }自定义控件RangeSeekbar如下: public class RangeSeekbar extends View {……private int max,min;public void setMax(int max){this.max = max;invalidate();}public void setMin(int min){this.min = min;invalidate();}public int getMax(){return this.max;}public int getMin(){return this.min;}…… }布局中添加RangeSeekbar: 添加反向绑定: 1.添加@InverseBindingMethods 接下来我们要修改RangeSeekbar类,在类的顶部添加: @InverseBindingMethods({@InverseBindingMethod(type = com.biyou.RangeSeekbar.class,attribute = "min",event = "minAttrChanged",method = "getMin"),@InverseBindingMethod(type = com.biyou.RangeSeekbar.class,attribute = "max",event = "maxAttrChanged",method = "getMax")}) public class RangeSeekbar extends View {}其中event和method不是必须的,因为系统会自动根据attribute值生成, type 是需要反向绑定的类,一般是View或者ViewGroup, attribute 是需要反向绑定的属性,本例是min和max, event和method 的结构分别是:属性+AttrChanged,get+属性,一般不需要设置,系统自动生成。 method的定义还可以直接在方法上,比如min属性对应的getter: @InverseBindingAdapter(attribute = "min", event = "minAttrChanged")public int getMin(){return this.min;}2.添加@BindingAdapter,设置监听 private static InverseBindingListener minInverseBindingListener;private static InverseBindingListener maxInverseBindingListener;@BindingAdapter("minAttrChanged")public static void setMinChangedListener(RangeSeekbar rangeSeekbar,final InverseBindingListener bindingListener) {minInverseBindingListener = bindingListener;}@BindingAdapter("maxAttrChanged")public static void setMaxChangedListener(RangeSeekbar rangeSeekbar,final InverseBindingListener bindingListener) {maxInverseBindingListener = bindingListener;}注意方法名为:set+属性+ChangedListener,这个方法会在生成的databinding类中被调用 3.触发监听 当HMIN和HMAX进度条被拖动时,调用minInverseBindingListener和maxInverseBindingListener的onChange()方法, onChange()方法会去改变rangeBean对象的min和max属性值: public boolean onTouchEvent(MotionEvent event) {int action = event.getActionMasked();switch (action){case MotionEvent.ACTION_DOWN:LogManager.LogEWithTag("GOODLUCK","ACTION_DOWN");mStartX = (int) event.getX();mStartY = (int) event.getY();if(mLeftCursorRect.contains(mStartX,mStartY)){if(mLeftHited)break;mLeftHited = true;}else if(mRightCursorRect.contains(mStartX,mStartY)){if(mRightHited)break;mRightHited = true;}break;case MotionEvent.ACTION_MOVE:int moveX = (int) event.getX();int moveY = (int) event.getY();if(mLeftHited){if(mLeftCursorRect.left + moveX - mStartX < 0 ||mLeftCursorRect.left + moveX - mStartX > (mSeekbarRect.right - mSeekbarRect.left))return true;mLeftCursorRect.left = mLeftCursorRect.left + moveX - mStartX;mLeftCursorRect.right = mLeftCursorRect.left + mLeftCursor.getIntrinsicWidth();this.min = (int) ((((float)mLeftCursorRect.left)/(mSeekbarRect.right - mSeekbarRect.left)) *255);this.max = (int) ((((float)mRightCursorRect.left)/(mSeekbarRect.right - mSeekbarRect.left)) *255);if(this.onSeekBarChangeListener != null) {this.onSeekBarChangeListener.OnSeekBarChange(min,max);}else{minInverseBindingListener.onChange();}}else if(mRightHited){if(mRightCursorRect.left + moveX - mStartX < 0 ||mRightCursorRect.left + moveX - mStartX > (mSeekbarRect.right - mSeekbarRect.left))return true;mRightCursorRect.left = mRightCursorRect.left + moveX - mStartX;mRightCursorRect.right = mRightCursorRect.left + mRightCursor.getIntrinsicWidth();this.min = (int) (((float)mLeftCursorRect.left)/(mSeekbarRect.right - mSeekbarRect.left) *255);this.max = (int) (((float)mRightCursorRect.left)/(mSeekbarRect.right - mSeekbarRect.left) *255);if(this.onSeekBarChangeListener != null) {this.onSeekBarChangeListener.OnSeekBarChange(min,max);}else{maxInverseBindingListener.onChange();}}if(mLeftCursorRect.left < mRightCursorRect.left) {mSeekbarFgRect.left = mLeftCursorRect.left + mLeftCursor.getIntrinsicWidth() / 2;mSeekbarFgRect.right = mRightCursorRect.left + mRightCursor.getIntrinsicWidth() / 2;}else{mSeekbarFgRect.left = mRightCursorRect.left + mLeftCursor.getIntrinsicWidth() / 2;mSeekbarFgRect.right = mLeftCursorRect.left + mRightCursor.getIntrinsicWidth() / 2;}mStartX = moveX;mStartY = moveY;invalidate();break;case MotionEvent.ACTION_UP:mLeftHited = false;mRightHited = false;break;}return true;}别忘了布局中把min和max的属性值,加上”@={}“: 避免死循环双向绑定有可能会出现死循环,因为当你通过Listener反向设置数据时,数据也会再次发送事件给View。所以我们需要在设置一下避免死循环: public void setMax(int max){if(this.max == max)return;this.max = max;invalidate();}public void setMin(int min){if(this.min == min)return;this.min = min;invalidate();}当值相等的时候返回,不更新UI。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |