使用前提
- 添加Data Binding 到gradle构建文件里
android {
....
dataBinding {
enabled = true
}
}
Data Binding Layout文件
使用 Data Binding 之后,xml的布局文件就不再单纯地展示 UI 元素,还需要定义 UI 元素用到的变量。所以,它的根节点不再是一个ViewGroup
,而是变成了 layout
(起始根标签),并且新增了一个节点 data
。
Data Binding表达式
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
在layout的属性表达式写做@{xxx.xxx}
Data对象
常用JavaBeans对象:
public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
}
用于TextView中的android:text
属性的表达式@{user.firstName}
将访问JavaBeans对象中的getFirstName
Binding数据
默认情况下,一个Binding类会基于layout文件的名称而产生,将其转换为Pascal case(译注:首字母大写的命名规范)并且添加“Binding
”后缀。将我们布局文件的首字母大写,并且去掉下划线,将下划线后面的字母大写,加上Binding组成。上述的layout文件是activity_main.xml
,因此生成的类名是ActivityMainBinding
。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}
如果你在ListView或者RecyclerView adapter使用Data Binding时,你可能会使用:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
自定义Binding名称
<data class=".Custom">
...
</data>
那么DataBindingUtils.setContentView返回的binding类就是:你的应用包名.Custom
深入Layout文件
- import
- 零个或多个import元素可能在data元素中使用。这些只用在你的layout文件中添加引用,就像在Java中:
<data>
<import type="android.view.View"/>
</data>
- 零个或多个import元素可能在data元素中使用。这些只用在你的layout文件中添加引用,就像在Java中:
- 当类名有冲突时,其中一个类名可以重命名为
alias:
:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
java.lang包里的类,可以不用导包的
<data>
<import type="org.loader.app2.Student" />
<variable
name="stu"
type="Student" />
<variable
name="str"
type="String"/>
<variable
name="error"
type="boolean"/>
<variable
name="num"
type="int" />
</data>
Acitivity
中
binding.setStu(new Student("loader"));
binding.setStr("string");
binding.setError(false);
如果给TextView
\ ImageView
等布局设置Id后,在Java代码中直接可以这么使用binding.所设置的id
表达式
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{error ? "error" : "ok"}'/>
- NULL 合并操作运算符
android:text="@{user.displayName ?? user.lastName}"
等价于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
数据对象
我们可以通过Observable的方式去通知UI数据已经改变,官方为我们提供了更加简便的方式BaseObservable
- 实体类要继承BaseObservale类
- 在Getter上使用注解
@Bindable
- 在Setter里调用方法
notifyPropertyChanged
public class Student extends BaseObservable {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//notifyPropertyChanged(org.loader.app4.BR.name);
notifyPropertyChanged(BR.lastName);
}
}
BR
是编译阶段生成的一个类,功能与 R.java
类似,用 @Bindable
标记过 getter
方法会在 BR 中生成一个 entry。
通过代码可以看出,当数据发生变化时还是需要手动发出通知。 通过调用 notifyPropertyChanged(BR.firstName)
可以通知系统 BR.firstName
这个 entry
的数据已经发生变化,需要更新 UI。
除此之外,还有一种更细粒度的绑定方式,可以具体到成员变量,这种方式无需继承 BaseObservable,一个简单的 POJO(plain-old Java Object) 就可以实现。
public class PlainUser {
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableField<String> lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
系统为我们提供了所有的 primitive type 所对应的 Observable类,例如 ObservableInt
、ObservableFloat
、ObservableBoolean
等等,还有一个 ObservableField
对应着 reference type。
BindingAdapter(自定义setter )
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data class=".Custom">
<variable
name="imageUrl"
type="String" />
</data>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:image="@{imageUrl}"/>
</layout>
以bind:开头,接着书写你在控件中使用的自定义属性名称。
public class Utils {
@BindingAdapter({"bind:image"})
public static void imageLoader(ImageView imageView, String url) {
ImageLoaderUtils.getInstance().displayImage(url, imageView);
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
org.loader.app8.Custom binding = DataBindingUtil.setContentView(this,
R.layout.activity_main);
binding.setImageUrl("http://images.csdn.net/20150810/Blog-Image%E5%89%AF%E6%9C%AC.jpg");
}
}
MainActivity中传入的URL到xml文件中的<ImageView app:image"@{imageUrl}"
然后进入Utils 绑定BindingAdapter的imageLoader的(…,String url)中
Converters
====================待更新
学习来源
MasteringAndroidDataBinding
Android官方数据绑定框架DataBinding(一)
Android官方数据绑定框架DataBinding(二)