简介
dataBinding
是android
支持的一种MVVM框架,使代码逻辑结构更为清晰。
使用
基本数据绑定
第一步 将layout变为dataBinding的结构。打开布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击 “Convert to data binding layout”
<?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.StringUtils" /> <!--引入工具类-->
<variable
name="userInfo"
type="User" /><!--引入普通model-->
<variable
name="list"
type="ObservableList<String">>"/><!--引入list,当有尖括号时要转义-->
<variable
name="map"
type="ObservableMap<String,String">>"/><!--引入map,,当有尖括号时要转义-->
</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>
修改完成后,会多出data标签,data标签负责搭建了 View 和 Model 之间的通道
data需要引入变量和类型,如下所示
<data>
<import type="com.leavesc.databinding_demo.model.User" />
<import type="com.leavesc.databinding_demo.StringUtils" /> <!--引入工具类-->
<variable
name="userInfo"
type="User" /><!--引入普通model-->
<variable
name="list"
type="ObservableList<String">>"/><!--引入list,当有尖括号时要转义-->
<variable
name="map"
type="ObservableMap<String,String">>"/><!--引入map,,当有尖括号时要转义-->
</data>
data
的使用
android:text="@{userInfo.name,default=defaultValue}" <!--默认值只在预览时有效-->
android:text="@{String.valueOf(goods.price)}"<!--支持表达式-->
android:onClick="@{()->goodsHandler.changeGoodsName()}"<!--lambda表达式-->
android:text="@{StringUtils.toUpperCase(userInfo.name)}" <!--引用静态方法-->
android:visibility="@{user.male ? View.VISIBLE : View.GONE}" <!--控制属性-->
android:afterTextChanged="@{userPresenter.afterTextChanged}" <!--要求方法的参数返回值与原函数保持一致-->
android:text="@{list.size() > 0 ? list[0].childrenNick : @string/login_new_child}"<!--list集合的使用-->
xml中通过@{userInfo.name}
这种形式和数据类型的绑定。但具体要绑定哪一个变量,见下面。
第二步 此时,已经完成了数据绑定,但具体是哪个数据,需要显示指定,
在代码中,需要activity
fragment
要用ViewDataBinding
来实现。
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMain2Binding activityMain2Binding = DataBindingUtil.setContentView(this, R.layout.activity_main2); //ViewDataBinding子类,会自动生成
user = new User("leavesC", "123456");
activityMain2Binding.setUserInfo(user); //显示绑定具体的变量
}
这种数据绑定,是一次性
的,即当数据发生改变时,View
并不会发生变化。如果需要View
跟着数据变化,见下面。
单向数据绑定
有两个方式,
- 方式一,继承
BaseObservable
,并添加相应注解,get/set方法,其中set方法要调用通知方法noifyXXX。 - 方式二,使用ObservableField,这种方式直接定义相应变量即可。
方式一:继承BaseObservable
,需要在相应的变量(public)或get
方法加上@Bindable
注解。set
方法,要调用notifyChange()
(会刷新所有的值域) 或 notifyPropertyChanged(BR.name)
只更新对应 BR
的 flag
.
方式二:ObservableField
可以理解为官方对 BaseObservable
中字段的注解和刷新等操作的封装,官方原生提供了对基本数据类型的封装,例如 ObservableBoolean
、ObservableByte
、ObservableChar
、ObservableShort
、ObservableInt
、ObservableLong
、ObservableFloat
、ObservableDouble
以及 ObservableParcelable
,也可通过 ObservableField
泛型来申明其他类型。ObservableCollection
dataBinding
也提供了包装类用于替代原生的 List
和 Map
,分别是 ObservableList
和 ObservableMap
,当其包含的数据发生变化时,绑定的视图也会随之进行刷新
这时,已经实现了当data
发生改变时,view
也会相应的更改,但是如果view
的内容发生改变(如果EditText
等),怎样映射到data
呢?见下面双向绑定。
监听ObservableField
当Observable
变化时,view
内容会发生改变。
有时,我们会灵活监听Observable
的变化,比如,自己创建出来的view
,让他跟Observable
绑定,可以使用
observable.addOnPropertyChangedCallback(callback)
双向绑定
双向绑定只是xml中写法不同,如下,在@
后加一个=
android:text="@={goods.name}"
dataBinding
XML一些语法
android:text="@{user.name ?? user.password}" <!--会取第一个不为null的值,等价于-->
android:text="@{user.name != null ? user.name : user.password}"<!--功能同上-->
dataBinding
会避免空指针异常
在viewStub
和include
中的使用
viewStub
和include
一样支持dataBinding
viewStub
和include
的binding data
通过父布局导入
<?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}" /><!--注意这里,将父布局的data传给子布局-->
</LinearLayout>
</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>
对于viewStub
也可以在加载时,将数据binding data传入
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
BindingAdapter 是一个注解用于支持自定义属性,或者扩展已有的属性。
- 注解名为xml属性值。
- 方法名,可以任意,参数,第一个为View,第二个为属性值。
@BindingAdapter({"url"})
public static void loadImage(ImageView view, String url) {
Log.e(TAG, "loadImage url : " + url);
}
然后,在xml中就可以这样来使用。
<?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}" /><!--xml中对bindingAdapter的使用-->
</android.support.constraint.ConstraintLayout>
</layout>
BindingConversion
一种注解,用于类型转化,例如,通过对属性值转换为所需要的类型。
@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"/>
<!--注意text即为bindingConversion在xml中的使用-->
引用资源文件
<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" />