关于android:Android-DataBinding-从入门到进阶看这一篇就够

23次阅读

共计 21077 个字符,预计需要花费 53 分钟才能阅读完成。

前言

DataBinding 是谷歌官网公布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于升高布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM 绝对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。DataBinding 可能省去咱们始终以来的 findViewById() 步骤,大量缩小 Activity 内的代码,数据可能单向或双向绑定到 layout 文件中,有助于避免内存透露,而且能主动进行空检测以防止空指针异样

启用 DataBinding 的办法是在对应 Model 的 build.gradle 文件里退出以下代码,同步后就能引入对 DataBinding 的反对

android {
    dataBinding {enabled = true}
}

一、根底入门

启用 DataBinding 后,这里先来看下如何在布局文件中绑定指定的变量关上布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击“Convert to data binding layout”,就能够生成 DataBinding 须要的布局规定

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

    <data>

    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    </android.support.constraint.ConstraintLayout>
</layout>

和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 标签用于申明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就须要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道

这里先来申明一个 Modle

package com.leavesc.databinding_demo.model;

/**
 * 作者:叶应是叶
 * 工夫:2018/5/16 20:20
 * 形容:https://github.com/leavesC
 */
public class User {

    private String name;

    private String password;
    
    ···
}

data 标签里申明要应用到的变量名、类的全门路

    <data>
        <variable
            name="userInfo"
            type="com.leavesc.databinding_demo.model.User" />
    </data>

如果 User 类型要多处用到,也能够间接将之 import 进来,这样就不必每次都指明整个包名门路了,而 java.lang.* 包中的类会被主动导入,所以能够间接应用

    <data>
        <import type="com.leavesc.databinding_demo.model.User"/>
        <variable
            name="userInfo"
            type="User"/>
    </data>

如果存在 import 的类名雷同的状况,能够应用 alias 指定别名

    <data>
        <import type="com.leavesc.databinding_demo.model.User" />
        <import
            alias="TempUser"
            type="com.leavesc.databinding_demo.model2.User" />
        <variable
            name="userInfo"
            type="User" />
        <variable
            name="tempUserInfo"
            type="TempUser" />
    </data>

这里申明了一个 User 类型的变量 userInfo,咱们要做的就是使这个变量与两个 TextView 控件挂钩,通过设置 userInfo 的变量值同时使 TextView 显示相应的文本 残缺的布局代码如下所示

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

    <data>
        <import type="com.leavesc.databinding_demo.model.User" />
        <variable
            name="userInfo"
            type="User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="20dp"
        android:orientation="vertical"
        tools:context="com.leavesc.databinding_demo.Main2Activity">

        <TextView
            android:id="@+id/tv_userName"
            ···
            android:text="@{userInfo.name}" />

        <TextView
            ···
            android:text="@{userInfo.password}" />

    </LinearLayout>

</layout>

通过 @{userInfo.name} 使 TextView 援用到相干的变量,DataBinding 会将之映射到相应的 getter 办法 之后能够在 Activity 中通过 DataBindingUtil 设置布局文件,省略原先 Activity 的 setContentView() 办法,并为变量 userInfo 赋值

    private User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        ActivityMain2Binding activityMain2Binding = DataBindingUtil.setContentView(this, R.layout.activity_main2);
        user = new User("leavesC", "123456");
        activityMain2Binding.setUserInfo(user);
    }

因为 @{userInfo.name}在布局文件中并没有明确的值,所以在预览视图中什么都不会显示,不便于察看文本的大小和字体色彩等属性,此时能够为之设定默认值(文本内容或者是字体大小等属性都实用),默认值将只在预览视图中显示,且默认值不能蕴含引号

    android:text="@{userInfo.name,default=defaultValue}"

此外,也能够通过 ActivityMain2Binding 间接获取到指定 ID 的控件

    activityMain2Binding.tvUserName.setText("leavesC");

每个数据绑定布局文件都会生成一个绑定类,ViewDataBinding 的实例名是依据布局文件名来生成,将之改为首字母大写的驼峰命名法来命名,并省略布局文件名蕴含的下划线。控件的获取形式相似,但首字母小写

也能够通过如下形式自定义 ViewDataBinding 的实例名

    <data class="CustomBinding">

    </data>

此外,在绑定表达式中会依据须要生成一个名为 context 的非凡变量,context的值是根 ViewgetContext() 办法返回的 Context 对象,context变量会被具备该名称的显式变量申明所笼罩

Databinding 同样是反对在 FragmentRecyclerView 中应用。例如,能够看 Databinding 在 Fragment 中的应用

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false);
        fragmentBlankBinding.setHint("Hello");
        return fragmentBlankBinding.getRoot();}

以上实现数据绑定的形式,每当绑定的变量发生变化的时候,都须要从新向 ViewDataBinding 传递新的变量值能力刷新 UI。接下来看如何实现主动刷新 UI

二、单向数据绑定

实现数据变动主动驱动 UI 刷新的形式有三种:BaseObservableObservableFieldObservableCollection

BaseObservable

一个污浊的 ViewModel 类被更新后,并不会让 UI 自动更新。而数据绑定后,咱们天然会心愿数据变更后 UI 会即时刷新,Observable 就是为此而生的概念

BaseObservable 提供了 notifyChange()notifyPropertyChanged() 两个办法,前者会刷新所有的值域,后者则只更新对应 BRflag,该 BR 的生成通过正文 @Bindable 生成,能够通过 BR notify 特定属性关联的视图

/**
 * 作者:叶应是叶
 * 工夫:2018/5/16 20:54
 * 形容:*/
public class Goods extends BaseObservable {

    // 如果是 public 修饰符,则能够间接在成员变量上方加上 @Bindable 注解
    @Bindable
    public String name;

    // 如果是 private 修饰符,则在成员变量的 get 办法上增加 @Bindable 注解
    private String details;

    private float price;

    public Goods(String name, String details, float price) {
        this.name = name;
        this.details = details;
        this.price = price;
    }

    public void setName(String name) {
        this.name = name;
        // 只更新本字段
        notifyPropertyChanged(com.leavesc.databinding_demo.BR.name);
    }

    @Bindable
    public String getDetails() {return details;}

    public void setDetails(String details) {
        this.details = details;
        // 更新所有字段
        notifyChange();}

    public float getPrice() {return price;}

    public void setPrice(float price) {this.price = price;}

}

setName() 办法中更新的只是本字段,而 setDetails() 办法中更新的是所有字段

增加两个按钮用于扭转 goods 变量的三个属性值,由此能够看出两个 notify 办法的区别。当中波及的按钮点击事件绑定,在上面也会讲到

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

    <data>
        <import type="com.leavesc.databinding_demo.model.Goods" />
        <import type="com.leavesc.databinding_demo.Main3Activity.GoodsHandler" />
        <variable
            name="goods"
            type="Goods" />
        <variable
            name="goodsHandler"
            type="GoodsHandler" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="20dp"
        tools:context=".Main3Activity">

        <TextView
            ···
            android:text="@{goods.name}" />

        <TextView
            ···
            android:text="@{goods.details}" />

        <TextView
            ···
            android:text="@{String.valueOf(goods.price)}" />

        <Button
            ···
            android:onClick="@{()->goodsHandler.changeGoodsName()}"
            android:text="扭转属性 name 和 price"
            android:textAllCaps="false" />

        <Button
            ···
            android:onClick="@{()->goodsHandler.changeGoodsDetails()}"
            android:text="扭转属性 details 和 price"
            android:textAllCaps="false" />

    </LinearLayout>
</layout>
/**
 * 作者:叶应是叶
 * 工夫:2018/5/16 21:07
 * 形容:*/
public class Main3Activity extends AppCompatActivity {

    private Goods goods;

    private ActivityMain3Binding activityMain3Binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        activityMain3Binding = DataBindingUtil.setContentView(this, R.layout.activity_main3);
        goods = new Goods("code", "hi", 24);
        activityMain3Binding.setGoods(goods);
        activityMain3Binding.setGoodsHandler(new GoodsHandler());
    }

    public class GoodsHandler {public void changeGoodsName() {goods.setName("code" + new Random().nextInt(100));
            goods.setPrice(new Random().nextInt(100));
        }

        public void changeGoodsDetails() {goods.setDetails("hi" + new Random().nextInt(100));
            goods.setPrice(new Random().nextInt(100));
        }

    }

}

能够看到,name 视图的刷新没有同时刷新 price 视图,而 details 视图刷新的同时也刷新了 price 视图

实现了 Observable 接口的类容许注册一个监听器,当可察看对象的属性更改时就会告诉这个监听器,此时就须要用到 OnPropertyChangedCallback

当中 propertyId 就用于标识特定的字段

        goods.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable sender, int propertyId) {if (propertyId == com.leavesc.databinding_demo.BR.name) {Log.e(TAG, "BR.name");
                } else if (propertyId == com.leavesc.databinding_demo.BR.details) {Log.e(TAG, "BR.details");
                } else if (propertyId == com.leavesc.databinding_demo.BR._all) {Log.e(TAG, "BR._all");
                } else {Log.e(TAG, "未知");
                }
            }
        });

ObservableField

继承于 Observable 类相对来说限度有点高,且也须要进行 notify 操作,因而为了简略起见能够抉择应用 ObservableField。ObservableField 能够了解为官网对 BaseObservable 中字段的注解和刷新等操作的封装,官网原生提供了对根本数据类型的封装,例如 ObservableBoolean、ObservableByte、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble 以及 ObservableParcelable,也可通过 ObservableField 泛型来申明其余类型

/**
 * 作者:叶应是叶
 * 工夫:2018/5/13 21:33
 * 形容:*/
public class ObservableGoods {

    private ObservableField<String> name;

    private ObservableFloat price;

    private ObservableField<String> details;

    public ObservableGoods(String name, float price, String details) {this.name = new ObservableField<>(name);
        this.price = new ObservableFloat(price);
        this.details = new ObservableField<>(details);
    }

}


对 ObservableGoods 属性值的扭转都会立刻触发 UI 刷新,概念上与 Observable 区别不大,具体成果可看上面提供的源代码,这里不再赘述

###ObservableCollection

dataBinding 也提供了包装类用于代替原生的 `List` 和 `Map`,别离是 `ObservableList` 和 `ObservableMap`, 当其蕴含的数据发生变化时,绑定的视图也会随之进行刷新

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

xmlns:tools="http://schemas.android.com/tools">

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="android.databinding.ObservableMap"/>
    <variable
        name="list"
        type="ObservableList&lt;String&gt;"/>
    <variable
        name="map"
        type="ObservableMap&lt;String,String&gt;"/>
    <variable
        name="index"
        type="int"/>
    <variable
        name="key"
        type="String"/>
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.leavesc.databinding_demo.Main12Activity">

    <TextView
        ···
        android:padding="20dp"
        android:text="@{list[index],default=xx}"/>

    <TextView
        ···
        android:layout_marginTop="20dp"
        android:padding="20dp"
        android:text="@{map[key],default=yy}"/>

    <Button
        ···
        android:onClick="onClick"
        android:text="扭转数据"/>

</LinearLayout>

</layout>

private ObservableMap<String, String> map;

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
    ActivityMain12Binding activityMain12Binding = DataBindingUtil.setContentView(this, R.layout.activity_main12);
    map = new ObservableArrayMap<>();
    map.put("name", "leavesC");
    map.put("age", "24");
    activityMain12Binding.setMap(map);
    ObservableList<String> list = new ObservableArrayList<>();
    list.add("Ye");
    list.add("leavesC");
    activityMain12Binding.setList(list);
    activityMain12Binding.setIndex(0);
    activityMain12Binding.setKey("name");
}

public void onClick(View view) {map.put("name", "leavesC,hi" + new Random().nextInt(100));
}

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/82ad16b2c9ac47d7a601832da96f0b2e~tplv-k3u1fbpfcp-watermark.image)

## 三、双向数据绑定

双向绑定的意思即为当数据扭转时同时使视图刷新,而视图扭转时也能够同时扭转数据

看以下例子,当 EditText 的输出内容扭转时,会同时同步到变量 `goods`, 绑定变量的形式比单向绑定多了一个等号:`android:text="@={goods.name}"`

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

xmlns:tools="http://schemas.android.com/tools">

<data>
    <import type="com.leavesc.databinding_demo.model.ObservableGoods"/>
    <variable
        name="goods"
        type="ObservableGoods" />
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".Main10Activity">

    <TextView
        ···
        android:text="@{goods.name}" />

    <EditText
        ···
        android:text="@={goods.name}" />

</LinearLayout>

</layout>

public class Main10Activity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
    ActivityMain10Binding activityMain10Binding = DataBindingUtil.setContentView(this, R.layout.activity_main10);
    ObservableGoods goods = new ObservableGoods("code", "hi", 23);
    activityMain10Binding.setGoods(goods);
}

}


![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e3b139c6a9b744058ee56e249fafaca2~tplv-k3u1fbpfcp-watermark.image)

## 四、事件绑定

严格意义上来说,事件绑定也是一种变量绑定,只不过设置的变量是回调接口而已 事件绑定可用于以下多种回调事件

*   android:onClick
*   android:onLongClick
*   android:afterTextChanged
*   android:onTextChanged
*   ...

在 Activity 外部新建一个 **UserPresenter** 类来申明 **onClick()** 和 **afterTextChanged()** 事件相应的回调办法

public class UserPresenter {

    public void onUserNameClick(User user) {Toast.makeText(Main5Activity.this, "用户名:" + user.getName(), Toast.LENGTH_SHORT).show();}

    public void afterTextChanged(Editable s) {user.setName(s.toString());
        activityMain5Binding.setUserInfo(user);
    }

    public void afterUserPasswordChanged(Editable s) {user.setPassword(s.toString());
        activityMain5Binding.setUserInfo(user);
    }

}

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

xmlns:tools="http://schemas.android.com/tools">

<data>
    <import type="com.leavesc.databinding_demo.model.User" />
    <import type="com.leavesc.databinding_demo.MainActivity.UserPresenter" />
    <variable
        name="userInfo"
        type="User" />
    <variable
        name="userPresenter"
        type="UserPresenter" />
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="20dp"
    android:orientation="vertical"
    tools:context="com.leavesc.databinding_demo.MainActivity">

    <TextView
        ···
        android:onClick="@{()->userPresenter.onUserNameClick(userInfo)}"
        android:text="@{userInfo.name}" />

    <TextView
        ···
        android:text="@{userInfo.password}" />

    <EditText
        ···
        android:afterTextChanged="@{userPresenter.afterTextChanged}"
        android:hint="用户名" />

    <EditText
        ···
        android:afterTextChanged="@{userPresenter.afterUserPasswordChanged}"
        android:hint="明码" />

</LinearLayout>

</layout>


办法援用的形式与调用函数的形式相似,既能够抉择放弃事件回调办法的签名统一:**@{userPresenter.afterTextChanged}**,此时办法名能够不一样,但办法参数和返回值必须和原始的回调函数保持一致。也能够援用不遵循默认签名的函数:**@{()->userPresenter.onUserNameClick(userInfo)}**,这里用到了 Lambda 表达式,这样就能够不遵循默认的办法签名,将 `userInfo` 对象间接传回点击办法中。此外,也能够应用办法援用 **::** 的模式来进行事件绑定 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/18d28b35a91747a0b8ea1aa8e42483c6~tplv-k3u1fbpfcp-watermark.image)

## 五、应用类办法

首先定义一个静态方法

public class StringUtils {

public static String toUpperCase(String str) {return str.toUpperCase();
}

}


在 data 标签中导入该工具类

<import type=”com.leavesc.databinding_demo.StringUtils” />


而后就能够像看待个别的函数一样来调用了

<TextView

 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:onClick="@{()->userPresenter.onUserNameClick(userInfo)}"
 android:text="@{StringUtils.toUpperCase(userInfo.name)}" />

## 六、运算符

### 根底运算符

DataBinding 反对在布局文件中应用以下运算符、表达式和关键字

*   算术 + - / \* %
*   字符串合并 +
*   逻辑 && ||
*   二元 & | ^
*   一元 + - ! ~
*   移位 >> >>> <<
*   比拟 == > < >= <=
*   Instanceof
*   Grouping ()
*   character, String, numeric, null
*   Cast
*   办法调用
*   Field 拜访
*   Array 拜访 \[\]
*   三元 ?:

目前不反对以下操作

*   this
*   super
*   new
*   显示泛型调用

此外,DataBinding 还反对以下几种模式的调用

###Null Coalescing

空合并运算符 **??** 会取第一个不为 null 的值作为返回值

<TextView

 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:text="@{user.name ?? user.password}" />

等价于
android:text="@{user.name != null ? user.name : user.password}"

### 属性管制

能够通过变量值来管制 View 的属性

<TextView

 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:text="可见性变动"
 android:visibility="@{user.male  ? View.VISIBLE : View.GONE}" />

### 防止空指针异样

DataBinding 也会主动帮忙咱们防止空指针异样 例如,如果 **"@{userInfo.password}"** 中 **userInfo** 为 **null** 的话,**userInfo.password** 会被赋值为默认值 **null**,而不会抛出空指针异样

## 七、include 和 viewStub

###include


对于 include 的布局文件,一样是反对通过 dataBinding 来进行数据绑定,此时一样须要在待 include 的布局中仍然应用 layout 标签,申明须要应用到的变量

`view_include.xml`

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

<data>
    <import type="com.leavesc.databinding_demo.model.User" />
    <variable
        name="userInfo"
        type="User" />
</data>

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#acc">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="20dp"
        android:text="@{userInfo.name}" />

</android.support.constraint.ConstraintLayout>

</layout>


在主布局文件中将相应的变量传递给 include 布局,从而使两个布局文件之间共享同一个变量

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

xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>
    <import type="com.leavesc.databinding_demo.model.User" />
    <variable
        name="userInfo"
        type="User" />
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".Main6Activity">
    
    <include
        layout="@layout/view_include"
        bind:userInfo="@{userInfo}" />
    
</LinearLayout>

</layout>


##viewStub

dataBinding 一样反对 ViewStub 布局

在布局文件中援用 viewStub 布局

<ViewStub

    android:id="@+id/view_stub"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout="@layout/view_stub"/>

获取到 ViewStub 对象,由此就能够来管制 ViewStub 的可见性
ActivityMain6Binding activityMain6Binding = DataBindingUtil.setContentView(this, R.layout.activity_main6);
View view = activityMain6Binding.viewStub.getViewStub().inflate();

如果须要为 ViewStub 绑定变量值,则 ViewStub 文件一样要应用 layout 标签进行布局,主布局文件应用自定义的 bind 命名空间将变量传递给 ViewStub
<ViewStub
    android:id="@+id/view_stub"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout="@layout/view_stub"
    bind:userInfo="@{userInfo}" />

如果在 xml 中没有应用 `bind:userInfo="@{userInf}"` 对 ViewStub 进行数据绑定,则能够等到当 ViewStub **Inflate** 时再绑定变量,此时须要为 ViewStub 设置 `setOnInflateListener` 回调函数,在回调函数中进行数据绑定
    activityMain6Binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
        @Override
        public void onInflate(ViewStub stub, View inflated) {// 如果在 xml 中没有应用 bind:userInfo="@{userInf}" 对 viewStub 进行数据绑定
            // 那么能够在此处进行手动绑定
            ViewStubBinding viewStubBinding = DataBindingUtil.bind(inflated);
            viewStubBinding.setUserInfo(user);
            Log.e(TAG, "onInflate");
        }
    });

## 八、BindingAdapter

dataBinding 提供了 **BindingAdapter** 这个注解用于反对自定义属性,或者是批改原有属性。注解值能够是已有的 xml 属性,例如 `android:src`、`android:text` 等,也能够自定义属性而后在 xml 中应用

例如,对于一个 ImageView,咱们心愿在某个变量值发生变化时,能够动静扭转显示的图片,此时就能够通过 BindingAdapter 来实现

须要先定义一个静态方法,为之增加 BindingAdapter 注解,注解值是为 ImageView 控件自定义的属性名,而该静态方法的两个参数能够这样来了解:当 ImageView 控件的 url 属性值发生变化时,dataBinding 就会将 ImageView 实例以及新的 url 值传递给 loadImage() 办法,从而能够在此动静扭转 ImageView 的相干属性
@BindingAdapter({"url"})
public static void loadImage(ImageView view, String url) {Log.e(TAG, "loadImage url :" + url);
}


在 xml 文件中关联变量值,当中,bind 这个名称能够自定义

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

    <data>
        <import type="com.leavesc.databinding_demo.model.Image" />
        <variable
            name="image"
            type="Image" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".Main8Activity">

        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher_background"
            bind:url="@{image.url}" />
        
    </android.support.constraint.ConstraintLayout>
</layout>

BindingAdapter 更为弱小的一点是能够笼罩 Android 原先的控件属性。例如,能够设定每一个 Button 的文本都要加上后缀:“-Button”

    @BindingAdapter("android:text")
    public static void setText(Button view, String text) {view.setText(text + "-Button");
    }
    <Button
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:onClick="@{()->handler.onClick(image)}"
       android:text='@{" 扭转图片 Url"}'/>

这样,整个工程中应用到了 “android:text” 这个属性的控件,其显示的文本就会多出一个后缀

九、BindingConversion

dataBinding 还反对对数据进行转换,或者进行类型转换

与 BindingAdapter 相似,以下办法会将布局文件中所有以 @{String} 形式援用到的 String 类型变量加上后缀-conversionString

    @BindingConversion
    public static String conversionString(String text) {return text + "-conversionString";}

xml 文件

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{"xxx"}'
            android:textAllCaps="false"/>

能够看到,对于 Button 来说,BindingAdapter 和 BindingConversion 同时失效了,而 BindingConversion 的优先级要高些

此外,BindingConversion 也能够用于转换属性值的类型

看以下布局,此处在向 backgroundtextColor 两个属性赋值时,间接就应用了字符串,按失常状况来说这天然是会报错的,但有了 BindingConversion 后就能够主动将字符串类型的值转为须要的 DrawableColor

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background='@{" 红色 "}'
            android:padding="20dp"
            android:text="红色背景蓝色字"
            android:textColor='@{" 蓝色 "}'/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:background='@{" 蓝色 "}'
            android:padding="20dp"
            android:text="蓝色背景红色字"
            android:textColor='@{" 红色 "}'/>
    @BindingConversion
    public static Drawable convertStringToDrawable(String str) {if (str.equals("红色")) {return new ColorDrawable(Color.parseColor("#FF4081"));
        }
        if (str.equals("蓝色")) {return new ColorDrawable(Color.parseColor("#3F51B5"));
        }
        return new ColorDrawable(Color.parseColor("#344567"));
    }

    @BindingConversion
    public static int convertStringToColor(String str) {if (str.equals("红色")) {return Color.parseColor("#FF4081");
        }
        if (str.equals("蓝色")) {return Color.parseColor("#3F51B5");
        }
        return Color.parseColor("#344567");
    }

十、Array、List、Set、Map …

dataBinding 也反对在布局文件中应用 数组、Lsit、Set 和 Map,且在布局文件中都能够通过 list[index] 的模式来获取元素

而为了和 variable 标签的尖括号辨别开,在申明 Lsit< String > 之类的数据类型时,须要应用尖括号的转义字符

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

    <data>
        <import type="java.util.List" />
        <import type="java.util.Map" />
        <import type="java.util.Set" />
        <import type="android.util.SparseArray" />
        <variable
            name="array"
            type="String[]" />
        <variable
            name="list"
            type="List&lt;String&gt;" />
        <variable
            name="map"
            type="Map&lt;String, String&gt;" />
        <variable
            name="set"
            type="Set&lt;String&gt;" />
        <variable
            name="sparse"
            type="SparseArray&lt;String&gt;" />
        <variable
            name="index"
            type="int" />
        <variable
            name="key"
            type="String" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".Main7Activity">

        <TextView
            ···
            android:text="@{array[1]}" />
        <TextView
            ···
            android:text="@{sparse[index]}" />
        <TextView
            ···
            android:text="@{list[index]}" />
        <TextView
            ···
            android:text="@{map[key]}" />
        <TextView
            ···
            android:text='@{map["leavesC"]}' />
        <TextView
            ···
            android:text='@{set.contains("xxx")?"xxx":key}' />
    </LinearLayout>
</layout>

十一、资源援用

dataBinding 反对对尺寸和字符串这类资源的拜访

dimens.xml

    <dimen name="paddingBig">190dp</dimen>
    <dimen name="paddingSmall">150dp</dimen>

strings.xml

    <string name="format">%s is %s</string>
    <data>
        <variable
            name="flag"
            type="boolean" />
    </data>       
    <Button
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingLeft="@{flag ? @dimen/paddingBig:@dimen/paddingSmall}"
         android:text='@{@string/format("leavesC","Ye")}'
         android:textAllCaps="false" />



对 DataBinding 的介绍到这里也就完结,当然,必定还有些遗落的知识点,不过大体上我自认也曾经讲得很分明了,剩下的就留待日后补充了。

正文完
 0