Android进阶六:Databinding的双向绑定

您所在的位置:网站首页 安卓radiogroup双向绑定 Android进阶六:Databinding的双向绑定

Android进阶六:Databinding的双向绑定

2023-04-15 09:23| 来源: 网络整理| 查看: 265

在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属性:

图111

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