Android DataBinding框架的基本使用

Demo地址

怎么配置DataBinding

在Module的gradle 文件下,AndroidStudio版本不通开启的方式也不同
AndroidStudio 3.x 版本

android {
   ...  
   dataBinding{
       enabled = true
   }
}

AndroidStudio 4.x 版本(也可以使用3.0的方式开启,但是4.0推荐使用下面的方法)

android {
   ...  
   buildFeatures{
       dataBinding = true
   }
}

这里有一个坑,就是如果子Module配置了DataBinding,必须在主Module也配置

怎么让一个 View 可以绑定数据

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </androidx.appcompat.widget.LinearLayoutCompat>
</layout>

比原来的layout文件多了layout标签和data标签,这样格式的xml布局文件就是一个支持DataBinding的布局文件,可以通过快捷键Alt+Enter键来快速生成

DataBinding 中的被观察者 BaseObservable

如果想要你的UI随着数据的更新而更新,还需要实现BaseObservable

BaseObservable 是一个被观察者,UI组件会观察BaseObservable,如果数据发生了变化,则UI会更新。这里可以把BaseObservable 理解为数据的包装类。

看一个简单的实现

public class UserEntity extends BaseObservable {
    @Bindable
    private String nickname;
    @Bindable
    private String headUrl;

    public UserEntity(String nickname, String headUrl) {
        this.nickname = nickname;
        this.headUrl = headUrl;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
        notifyPropertyChanged(BR.nickname);
    }

    public String getHeadUrl() {
        return headUrl;
    }

    public void setHeadUrl(String headUrl) {
        this.headUrl = headUrl;
        notifyPropertyChanged(BR.headUrl);
    }
}

首先数据类实现BaseObservable,然后对需要被观察的数据用@Bindable注解
重写set/get 方法,然后在set 方法(数据更新的地方)调用notifyPropertyChanged(BR.id),UI就会被通知刷新UI

BR.id是DataBinding生成的id,用来区分数据的id,如果BR下面没有你的id,先检查一个有没有注解,然后再Rebuild 一下

单向绑定

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="userInfo"
            type="com.xiaoyu.mvvmdemo.UserEntity" />
    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{userInfo.nickname}" />

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/nickname_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text='@{"昵称:"+userInfo.nickname}' />
    </androidx.appcompat.widget.LinearLayoutCompat>
</layout>

单向绑定的话比较简单,通过@{}的方式就可以把数据绑定到UI上了
这里写了两个绑定,一个是"",一个是'',如果有做字符串操作的(比如例子中的拼接字符串),是不能使用""进行绑定的

双向绑定

双向绑定相对于单向绑定的语法上来说,只多了一个=

<EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入账号"
        android:text="@={accountNumber}" />

双向绑定在使用的过程中要注意到一点,就是绑定的这个属性,是否支持双向绑定,如果不支持,编译肯定是不会通过的。

下面是谷歌实现的双向绑定

  • AbsListView
    1. android:selectedItemPosition
  • CalendarView
    1. android:date
  • CompoundButton
    1. android:checked
  • DatePicker
    1. android:year
    2. android:month
    3. android:day
  • NumberPicker
    1. android:value
  • RadioGroup
    1.android:checkedButton
  • RatingBar
    1. android:rating
  • SeekBar
    1. android:progress
  • TabHost
    1. android:currentTab
  • TextView
    1. android:text
  • TimePicker
    1. android:hour
    2. android:minute

那么如果说系统提供的绑定方法不能够实现现有的需求怎么办?这里就需要到下面的自定义绑定方法了

自定义绑定 - 单向绑定

自定义单向绑定比较简单。只需要一个注解,一个方法就可以了
这里写一个用Glide加载网图,并且设置占位图的绑定方法

@BindingAdapter(value = {"bindUrl", "bindPlaceholder"}, requireAll = false)
public static void bindUrlAndPlaceholder(ImageView view, String url, int placeholder) {
    Glide.with(view).load(url).apply(new RequestOptions().placeholder(placeholder)).into(view);
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="com.xiaoyu.mvvmdemo.R" />

    <variable
            name="userInfo"
            type="com.xiaoyu.mvvmdemo.UserEntity" />
    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <androidx.appcompat.widget.AppCompatImageView
            bindPlaceholder="@{R.drawable.ic_launcher_background}"
            bindUrl="@{userInfo.headUrl}"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginTop="50dp" />

    </androidx.appcompat.widget.LinearLayoutCompat>
</layout>

@BindingAdapter这个注解是用来说明,我这个方法是一个绑定方法注解里需要两个参数valuerequireAll
value的类型是一个数组,数组的长度为方法参数长度-1(去掉View参数),然后顺序一一对应,bindUrlurl对应,bindPlaceholderplaceholder对应,view即被绑定的View
requireAll这个参数默认为true,它的意思是,value里面的数据是否全部都要绑定

自定义绑定 - 双向绑定

单向绑定可以只是set方法,双向绑定需要做的是,当被观察的View属性发生变化的时候,需要把自身的值给更新。用SwipeRefreshLayout举一个栗子

    @BindingAdapter("bindIsRefreshing")
    public static void setRefreshing(SwipeRefreshLayout refreshLayout, boolean isRefreshing) {
        if (refreshLayout.isRefreshing() != isRefreshing) {
            refreshLayout.setRefreshing(isRefreshing);
        }
    }

    @InverseBindingAdapter(attribute = "bindIsRefreshing", event = "onRefreshChange")
    public static boolean isRefreshing(SwipeRefreshLayout refreshLayout) {
        return refreshLayout.isRefreshing();
    }

    @BindingAdapter("onRefreshChange")
    public static void setOnRefreshListener(SwipeRefreshLayout refreshLayout, InverseBindingListener inverseBindingListener) {
        refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                inverseBindingListener.onChange();
            }
        });
    }

setRefreshing这个方法和 单向绑定一样,不需要多解释。
isRefreshingsetOnRefreshListener 这两个方法需要对照着看,
先看isRefreshing这个方法,返回了SwipeRefreshLayout.isRefreshing(),然后看上面的@InverseBindingAdapter注解,有两个参数,attribute表示被绑定的属性,我们上面因为是定义的bindIsRefreshing,所以这里和上面一样bindIsRefreshing

event这个参数,可以理解为isRefreshing的调用时机。这里转到底三个方法setOnRefreshListener,这个方法中的InverseBindingListener是一个DataBinding的接口,需要在属性发生变化时回调onChange方法,这样双向绑定的观察者就会知道属性发生了变化,就会更新自身的属性值

当栗子上的SwipeRefreshLayout触发OnRefreshListener.onRefresh时,会回调到InverseBindingListener.onChange方法,然后观察者收到回调后,就会调用isRefreshing方法来更新属性

需要注意的是,双向绑定中的set方法一定要加属性值得判断,就是当属性相同时,不调用View的set方法,避免触发死循环

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容