美文网首页
Android DataBinding框架的基本使用

Android DataBinding框架的基本使用

作者: 正在吃饭的晓雨 | 来源:发表于2021-01-28 00:05 被阅读0次

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方法,避免触发死循环

相关文章

网友评论

      本文标题:Android DataBinding框架的基本使用

      本文链接:https://www.haomeiwen.com/subject/mxonzktx.html