Android-DataBinding使用

一、布局定义

1.相关

使用DataBinding,布局最外层一定是标签,可以没有标签。
如果是使用标签,那么一般用来定义变量,或者导包。

1
2
3
4
5
6
    <data>

        <variable
            name="viewModel"
            type="com.xxx.demo.ui.viewmodel.LoginViewModel" />
    </data>
1
2
3
4
5
6
7
8
9
10
11
12
13
    <data>
        <import type="com.xxx.demo.R" alias="appR" />
        <import type="com.xxx.demo.ext.UtilsExtKt" alias="utilsExtKt" />
        <variable
            name="viewModel"
            type="com.xxx.demo.ui.viewmodel.ProfileViewModel" />
        <variable
            name="user"
            type="com.xxx.demo.model.bean.User" />
        <variable
            name="show_collect"
            type="boolean" />
    </data>

如果是在标签中导包,也是使用标签,通过标签的type属性引入对应的类,可以使用alias属性定义一个别名,也可以不使用alias定义别名,如果是使用了alias定义别名,则使用这个导入的类的时候,就可以直接使用别名,如果没有使用alias,则使用的时候,直接使用类名。

2.在布局中的使用

(1)单向绑定
1
2
3
4
5
6
7
<androidx.appcompat.widget.AppCompatEditText
    android:id="@+id/et_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="true"
    android:text="@={viewModel.username}"
    bindingadapter:afterTextChanged="@{viewModel.verifyInput}" />

单向数据绑定,其实就是刷新对应的实体或者属性,则刷新视图。但是视图的变化并不会更新实体或者对应的变量的值

(2)双向绑定
1
2
3
4
5
6
7
8
<androidx.appcompat.widget.AppCompatEditText
    android:id="@+id/et_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="textPassword"
    android:singleLine="true"
    android:text="@={viewModel.password}"
    bindingadapter:afterTextChanged="@{viewModel.verifyInput}" />

实体或者变量刷新,会更新视图;而视图变化也会更新对应的实体或者变量

(3)设置默认值

因为viewModel.username初始没有值,所以在刚进入页面的时候,会没有任何显示,那么可以使用default显示默认值

1
android:text="@{viewModel.username,default=@string/profile_login_or_register}"

如果是直接写字符串,那么就是default=用户名这样的写法,不需要加眼号
也可以设置默认的visibility,比如:

1
android:visibility="@{shouldBeVisible ? View.VISIBLE : View.GONE, default=View.GONE}"

(4)DataBinding在XML中绑定数据支持的表达式

数学 + - / * %

1
2
3
4
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{article.zan + 1,default = 2}"/>

字符串连接 +

1
2
3
4
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{article.superChapterName+'/'+article.chapterName}" />

字符串拼接的用法,其实就是与正常拼接一样
逻辑 && ||
二进制 & | ^
一元运算 + - ! ~
三元运算 ?:
判断是否为空 ??(例:android:text="@{user.name ?? user.defaultName}",相当于android:text="@{user.name !=null ? user.name : user.defaultName}")
位运算 >> >>> <<

1
2
3
4
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{((article.zan >> 2) >= 10) ? @string/profile_my_collect:@string/profile_personal_center}" />

比较 == > < >= <=

1
2
3
4
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{(article.zan >= 10) ? @string/profile_my_collect:@string/profile_personal_center}" />

instanceof
方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btn_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/dp_20"
            android:layout_marginTop="@dimen/dp_20"
            android:layout_marginEnd="@dimen/dp_20"
            android:background="@drawable/bg_btn"
            android:enabled="@{viewModel.uiState.enableLoginButton}"
            android:onClick="@{()->viewModel.login()}"
            android:text="@string/login"
            android:textColor="@color/white"
            android:textSize="@dimen/sp_16" />

方法的调用,采用@{()->viewModel.login()}的方式,类似于lambda表达式
前面的()表示参数
变量引用
获取数组、集合、Map的值 []

1
2
3
4
5
6
7
8
9
10
11
12
13
<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap<String, Object>"/>
</data>

<TextView
   android:text='@{user["name"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList<Object>"/>
</data>

<TextView
   android:text='@{user["name"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

3.使用@BindingAdapter

首先@BindingAdapter(value = ["flowAdapter"]),value是定义的一个类似于自定义View的属性,用于扩展View的属性
Java的方式:

1
2
3
4
5
6
public class CustomBindingAdapter {
    @BindingAdapter("visibleGone")
    public static void showHide(View view, boolean show) {
        view.setVisibility(show ? View.VISIBLE : View.GONE);
    }
}

Java的方式实现,第一个参数作为View,是定义扩展的View的。使用如下:

1
2
3
4
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:visibleGone="@{isLoading}"/>

kotlin的方式:

1
2
3
4
5
@BindingAdapter(value = ["isCollect"])
fun ImageView.isCollect(collect: Boolean) {
    val vectorDrawableCompat = VectorDrawableCompat.create(BaseApp.context.resources, if (collect) R.drawable.ic_collection_true else R.drawable.ic_collection_false, BaseApp.context.theme)
    this.setImageDrawable(vectorDrawableCompat)
}

kotlin的方式,使用的是函数扩展的方式,给对应的View扩展一个函数,依然使用@BindingAdapter注解定义对应的扩展属性
使用与Java方式一样

二、参考

https://www.jianshu.com/p/bd9016418af2
这篇说的很全面