Android DataBinding的官方双向绑定示例
作者:SoloHo 发布时间:2023-09-15 04:39:27
在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.java
public 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.java
public 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);
}
}
这样就没问题啦。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
来源:http://www.jianshu.com/p/c481d1f4e0b6


猜你喜欢
- SQLite分析 SQLite是轻量级的、嵌入式的、关系型数据库,
- Toast一般用来显示一行文字,用法比较固定:Toast.makeText(Context context,String message,i
- //activity的xml<?xml version="1.0" encoding="utf-8&qu
- 本文实例讲述了Android开发实现跟随手指的小球效果。分享给大家供大家参考,具体如下:配置DrawView类用于绘制小球public cl
- Java 静态绑定与动态绑定 最
- Android 监听手机GPS打开状态实现代码GPS_Presenterpackage com.yiba.core;import andro
- 谜题在C#中,用virtual关键字修饰的方法(属性、事件)称为虚方法(属性、事件),表示该方法可以由派生类重写(override)。虚方法
- 封面图下个季度的目标是把前端监控相关的内容梳理出来,梳理出来之后可能会在公司内部做个分享~Flutter应用程序既括代码也包括一些其他的资产
- 1 前言Stream 是 java8 中处理集合的抽象概念,可以执行非常复杂的查询、过滤和映射数据等操作。Stream API 提供了一种高
- // 1. 实现一个函数,在一个有序整型数组中二分查找出指定的值,找到则返回该值的位置,找不到返回 -1。package demo;publ
- 前言今天记录一个前段时间遇到的一个小问题的解决方法, 跨域!!!相信跨域这个问题, 做开发的或多或少都遇到过, 而且已经有很多博主已经分享了
- 主流有几种方法,一种是用下面的函数publicstaticStringgetProperty(Stringkey)键相关值的描述java.v
- Android环境布置完毕,直接就是一个Helloworld程序,详情请看《利用adt-bundle轻松搭建Android开发环境与Hell
- 目录一、背景二、推荐方式2.1 自定义的枚举2.2 外部枚举三、总结一、背景平时工作开发过程中,难免会用到状态机(状态的流转)。如奖学金审批
- 本文实例为大家分享了android实现圆环倒计时控件的具体代码,供大家参考,具体内容如下1.自定义属性<?xml version=&q
- 最近ui同学使用了一种新字体(锐字锐线怒放黑简) 发现全部切成图片字体 吓死我了unity3d和fairygui搭配使用字体过程1.第一步肯
- 一、采用Object.toString()toString方法是java.lang.Object对象的一个public方法。在java中任何
- 前言笔者上次用C#写.Net代码差不多还是10多年以前,由于当时Java已经颇具王者风范,Net几乎被打得溃不成军。因此当时笔者对于这个.N
- 前言对于信息的加密方式多种多样,之前为大家介绍了一种自己设计的加密方式,有兴趣的朋友可以欣赏一下,欢迎给予指点。今天为大家介绍一下对称加密方
- 补充使用Spring Cloud Config加密功能需要下载JCE扩展,用于生成无限长度的密文。链接:http://www.oracle.