在AndroID Studio 2.1 PrevIEw 3之后,官方开始支持双向绑定了。
可惜目前Google并没有在Data Binding指南里面加入这个教程,并且在整个互联网之中只有这篇文章介绍了如何使用反向绑定。
在阅读一下文章之前,我假设你已经知道如何正向绑定。
回顾一下Data Binding
在正向绑定中,我们在Layout里面的绑定表达式是这样的:
<layout ...> <data> <variable type="com.example.myapp.User" name="user"/> </data> <relativeLayout ...> <TextVIEw androID:text="@{user.name}" .../> </relativeLayout></layout>
当user.name的数据改动时,我们的TextVIEw都会同步改变文字。
双向绑定
现在假设一种情况,当你更换成EditText时,如果你的用户名User.name已经绑定到EditText中,当用户输入文字的时候,你原来的user.name数据并没有同步改动,因此我们需要修改成:
<layout ...> <data> <variable type="com.example.myapp.User" name="user"/> </data> <relativeLayout ...> <EditText androID:text="@={user.name}" .../> </relativeLayout></layout>
看出微小的差别了吗?对,就是"@{}"改成了"@={}",是不是很简单?
隐式引用属性
同样你也可以在别的VIEw上引用属性:
<layout ...> <data> <import type="androID.vIEw.VIEw"/> </data> <relativeLayout ...> <CheckBox androID:ID="@+ID/seeAds" .../> <ImageVIEw androID:visibility="@{seeAds.checked ? VIEw.VISIBLE : VIEw.GONE}" .../> </relativeLayout></layout>
当CheckBox的状态发生改变的时候,ImageVIEw也会同时发生改变。在复杂情况下,这个特性没什么卵用,因为逻辑部分我们是不建议写在XML中。
如何开启双向绑定
开启双向绑定,需要在项目的build.gradle中设置:
classpath 'com.androID.tools.build:gradle:2.1.0-Alpha3'
同样,你需要在你Module的build.gradle中设置:
androID { ... dataBinding.enabled = true}
貌似还有点问题
我们刚才的例子里面只显示了系统自带的应用,那么如果是自定义控件,或者是我们更细颗粒度的Observable呢?等下就揭晓如何自定义自己的双向绑定,我们来看看目前AndroID支持的控件:
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自定义双向绑定
设想一下我们使用了下拉刷新SwipeRefreshLayout
控件,这个时候我们希望在加载数据的时候能控制refreshing的状态,所以我们加入了ObservableBoolean的变量swipeRefreshVIEwRefreshing
来正向绑定数据,并且能够在用户手动下拉刷新的时候同步更新swipeRefreshVIEwRefreshing数据:
// SwipeRefreshLayout.javapublic class SwipeRefreshLayout extends VIEw { private boolean isRefreshing; public voID setRefreshing() {/* ... */} public boolean isRefreshing() {/* ... */} public voID setonRefreshListener(OnRefreshListener Listener) { /* ... */ } public interface OnRefreshListener { voID onRefresh(); }}
接下来我们需要告诉框架,我们需要将SwipeRefreshLayout
的isRefreshing的值反向绑定到swipeRefreshVIEwRefreshing
:
@InverseBindingMethods({ @InverseBindingMethod( type = androID.support.v4.Widget.SwipeRefreshLayout.class,attribute = "refreshing",event = "refreshingAttrChanged",method = "isRefreshing")})
这是一种简单的定义,其中event和method都不是必须的,因为系统会自动生成,写出来是为了更好地了解如何绑定的,可以参考官方文档InverseBindingMethod。
当然你也可以使用另外一种写法,并且如果你的值并不是直接对应Observable的值的时候,就可以在这里进行转换:
@InverseBindingAdapter(attribute = "refreshing",event = "refreshingAttrChanged")public static boolean isRefreshing(SwipeRefreshLayout vIEw) { return vIEw.isRefreshing();}
上面的event同样也不是必须的。以上的定义都是为了让我们能够在布局文件中使用"@={}"这个双向绑定的特性。接下来你需要告诉框架如何处理refreshingAttrChanged事件,就像处理一般的监听事件一样:
@BindingAdapter("refreshingAttrChanged")public static voID setonRefreshListener(final SwipeRefreshLayout vIEw,final InverseBindingListener refreshingAttrChanged) { if (refreshingAttrChanged == null) { vIEw.setonRefreshListener(null); } else { vIEw.setonRefreshListener(new OnRefreshListener() { @OverrIDe public voID onRefresh() { colorChange.onChange(); } }); }}
一般情况下,我们都需要设置正常的OnRefreshListener,所以我们可以合并写成:
@BindingAdapter(value = {"onRefreshListener","refreshingAttrChanged"},requireAll = false)public static voID setonRefreshListener(final SwipeRefreshLayout vIEw,final OnRefreshListener Listener,final InverseBindingListener refreshingAttrChanged) { OnRefreshListener newValue = new OnRefreshListener() { @OverrIDe public voID onRefresh() { if (Listener != null) { Listener.onRefresh(); } if (refreshingAttrChanged != null) { refreshingAttrChanged.onChange(); } } }; OnRefreshListener oldValue = ListenerUtil.trackListener(vIEw,newValue,R.ID.onRefreshListener); if (oldValue != null) { vIEw.setonRefreshListener(null); } vIEw.setonRefreshListener(newValue);}
现在我们终于可以使用双向绑定的技术啦。但是要注意,需要设置requireAll = false,否则系统将识别不了refreshingAttrChanged属性,前文提到的文章例子里并没有设置这个。
在viewmodel中,我们的数据是这样的:
// Myviewmodel.javapublic final ObservableBoolean swipeRefreshVIEwRefreshing = new ObservableBoolean(false);public voID load() { swipeRefreshVIEwRefreshing.set(true); // 网络请求 .... swipeRefreshVIEwRefreshing.set(false);}public SwipeRefreshLayout.OnRefreshListener onRefreshListener() { return new SwipeRefreshLayout.OnRefreshListener() { @OverrIDe public voID onRefresh() { // Do something you need } };}
在布局文件中是这样设置的:
<androID.support.v4.Widget.SwipeRefreshLayout androID:ID="@+ID/swipe_refresh_vIEw" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" app:onRefreshListener="@{viewmodel.onRefreshListener}" app:refreshing="@={viewmodel.swipeRefreshVIEwRefreshing}"> ...</androID.support.v4.Widget.SwipeRefreshLayout>
最后我们还有一个小问题,就是双向绑定有可能会出现死循环,因为当你通过Listener反向设置数据时,数据也会再次发送事件给VIEw。所以我们需要在设置一下避免死循环:
@BindingAdapter("refreshing")public static voID setRefreshing(SwipeRefreshLayout vIEw,boolean refreshing) { if (refreshing != vIEw.isRefreshing()) { vIEw.setRefreshing(refreshing); }}
这样就没问题啦。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的Android DataBinding的官方双向绑定示例全部内容,希望文章能够帮你解决Android DataBinding的官方双向绑定示例所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)