####简介
Databinding 是一个实现数据和UI绑定的框架,是一个实现 MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的实现MVVM开发模式。
####解决什么问题
减少编写大量的毫无营养的代码, 如 findViewById()、setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等。 通过 Data Binding , 我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的毫无营养的代码了。
####构建环境
在模块build.gradle中加上如下代码
``` html
dataBinding {
enabled = true
}
```
####用法
#####Data Binding 布局文件 - (View)
Data binding 的布局文件与传统布局文件有一点不同。它以一个 layout 标签作为根节点,里面是 data 标签与 view 标签。view 标签的内容就是不使用 Data Binding 时的普通布局文件内容。以下是一个例子:
``` html
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!-- 变量名,变量类型 描述了一个布局中会用到的属性 -->
<variable name="user" type="com.connorlin.databinding.model.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>
```
每个 variable 标签描述了会在 binding 表达式中使用的属性。生成的Binding类会有标签的getter/setter方法。
``` java
<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.connorlin.databinding.model.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>
```
在 Binding 类中生成对应的 getters 和 setters:
``` java
public com.connorlin.databinding.model.User getUser();
public void setUser(com.connorlin.databinding.model.User user);
public Drawable getImage();
public void setImage(Drawable image);
public String getNote();
public void setNote(String note);
```
布局中每一个带有 ID 的 View,都会生成一个 public final 字段。binding 过程会做一个简单的赋值,在 binding 类中保存对应 ID 的 View。这种机制相比调用 findViewById 效率更高。
#####数据对象 - (Model)
和原来Model写法一样,用于 TextView 的 android:text 属性的表达式@{user.firstName},当User没有getter/setter方法时,会读取 User 对象的 firstName 字段,反之读取对象的 getFirstName()方法。
#####绑定数据 - (ViewModel)
在默认情况下,每一个布局文件经过编译后会生成一个继承于 ViewDataBinding 的 Binding 类,将它转换成帕斯卡命名并在名字后面接上Binding。例如,布局文件叫 main_activity.xml,所以会生成一个 MainActivityBinding 类。这个类包含了布局文件中所有的绑定关系,会根据绑定方法setData()给布局文件赋值。在 inflate 的时候创建 binding 的方法如下:
``` java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ActivityBaseBinding 类是自动生成的
ActivityBaseBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_base);
User user = new User("Connor", "Lin");
// 所有的 set 方法也是根据布局中 variable 名称生成的
binding.setUser(user);
}
```
获取一个View:
``` java
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
```
在ListView、RecyclerView Adapter中使用:
``` java
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
```
Binding类放在模块包名+databinding目录下,更改data标签的class属性可以更改Binding类命令和位置。
``` java
<data class="CustomBinding">
...
</data>
```
以上会在 databinding 包中生成名为 CustomBinding 的 binding 类。如果需要放置在不同的包下,可以在前面加 “.”:
``` java
<data class=".CustomBinding">
...
</data>
```
这样的话, CustomBinding 会直接生成在 module 包下。如果提供完整的包名,binding 类可以放置在任何包名中:
``` java
<data class="com.example.CustomBinding">
...
</data>
```
#####事件处理
布局中声明要处理事件的方法,类似于 android:onClick 可以指定 Activity 中的函数。
方法调用
监听绑定
区别:
创建监听的时机不同;
方法引用是必须表达式正确,在编译时才用这个方法创建监听,否则不创建监听;而监听绑定无论如何会先创建监听,运行时候调用lambda 表达式指定的方法。二者主要区别在于前者在data binding时就实现了监听。
监听绑定允许表达式中更灵活;
只需要返回值相同,当表达式不能匹配回调时,Data Binding return default值, null for reference types, 0 for int, false for boolean, etc.。
方法引用
以下是个例子:
``` java
public class EventHandler {
private Context mContext;
public EventHandler(Context context) {
mContext = context;
}
public void onClickFriend(View view) {
Toast.makeText(mContext, "onClickFriend", Toast.LENGTH_LONG).show();
}
}
```
表达式如下:
``` java
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler"
type="com.connorlin.databinding.handler.EventHandler"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{handler::onClickFriend}"/>
<!-- 注意:表达式里的方法签名必须和监听器里的方法签名吻合 -->
</LinearLayout>
</layout>
```
监听绑定
以下是个例子:
``` java
public void onTaskClick(Task task) {
task.run();
}
```
表达式如下:
``` java
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler" type="com.connorlin.databinding.handler.EventHandler"/>
<variable
name="task" type="com.connorlin.databinding.task.Task"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> handler.onTaskClick(task)}"/>
</LinearLayout>
</layout>
```
关于参数
Listener方法的参数名有两种选择:要么不写,要么就要写全。
``` java
public void onTaskClick(Task task) {
task.run();
}
```
表达式如下:
``` java
android:onClick="@{(theView) -> presenter.onSaveClick(task)}
```
也可以这样
``` java
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
```
导入(Imports)
data 标签内可以有多个 import 标签,作用类似Java
``` java
<data>
<import type="android.view.View"/>
</data>
```
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
当类名发生冲突时,可以使用 alias
``` java
<import type="android.view.View"/>
<import type="com.connorlin.databinding.ui.View" alias="AliasView"/>
```
导入的类型也可以用于变量的类型引用和表达式中
``` java
<data>
<import type="com.connorlin.databinding.model.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
```
注意:Android Studio 还没有对导入提供自动补全的支持。你的应用还是可以被正常编译,要解决这个问题,你可以在变量定义中使用完整的包名。
* java.lang.* 包中的类会被自动导入,可以直接使用,例如, 要定义一个 String 类型的变量
binding 类会生成一个命名为 context 的特殊变量(其实就是 rootView 的 getContext() ) 的返回值), **这个变量可用于表达式中**。 如果有名为 context 的变量存在,那么生成的这个 context 特殊变量将被覆盖。
``` java
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{handler.loadString(context)}"/>
```
``` java
public String loadString(Context context) {
// 使用生成的context变量
return context.getResources().getString(R.string.string_from_context);
}
```
* 变量可以传递给include布局,但是需要在各个布局中声明
* view根标签不可是merge
####表达式语言
表达式语言与 Java 表达式有很多相似之处。下面是相同之处:
数学计算 + - / * %
字符串连接 +
逻辑 && ||
二进制 & | ^
一元 + - ! ~
位移 >> >>> <<
比较 == > < >= <=
instanceof
组 ()
字面量 - 字符,字符串,数字, null
类型转换
函数调用
字段存取
数组存取 []
三元运算符 ?:
在xml中转义是不可避免的
附:常用的转义字符
显示结果 描述 转义字符 十进制
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| 空格| | |
|< 小于号 |<| <|
|> 大于号| >| >|
|& 与号| & |&|
|" 引号 |"| "|
|‘ 撇号 |'| '|
|× 乘号 |× |×|
|÷ 除号 |÷ |÷|
不支持的操作符
一些 Java 中的操作符在表达式语法中不能使用。
this
super
new
显式泛型调用 <T>
Null合并运算符
Null合并运算符 ?? 会在非 null 的时候选择左边的操作,反之选择右边。
``` java
android:text="@{user.lastName ?? `Default LastName`}"
```
等同于
``` java
android:text="@{user.lastName != null ? user.lastName : `Default LastName`}"
```
容器类
通用的容器类:数组,lists,sparse lists,和 maps,可以用 [] 操作符来存取
``` java
<data>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<import type="java.util.List"/>
<variable name="list" type="List<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"
```
字符串常量
使用单引号把属性包起来,就可以很简单地在表达式中使用双引号:
``` java
android:text='@{map["firstName"]}'
也可以用双引号将属性包起来。这样的话,字符串常量就可以用 " 或者反引号 ( ` ) 来调用
```
``` java
android:text="@{map[`firstName`}"
android:text="@{map["firstName"]}"
```
资源
也可以在表达式中使用普通的语法来引用资源:
``` java
android:text="@{@string/fullname(user.fullName)"
```
字符串格式化和复数形式可以这样实现:
``` java
android:text="@{@plurals/sample_plurals(num)}"
```
当复数形式有多个参数时,应该这样写:
``` java
android:text="@{@plurals/numbers(num, num)}"
```
一些资源需要显示类型调用。
Type Normal Reference Expression Reference
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList
数据对象 (Data Objects)
####ViewStubs
ViewStub 相比普通 View 有一些不同。ViewStub 一开始是不可见的,当它们被设置为可见,或者调用 inflate 方法时,ViewStub 会被替换成另外一个布局。
因为 ViewStub 实际上不存在于 View 结构中,binding 类中的类也得移除掉,以便系统回收。因为 binding 类中的 View 都是 final 的,所以Android 提供了一个叫 **ViewStubProxy **的类来代替 ViewStub 。开发者可以使用它来操作 ViewStub,获取 ViewStub inflate 时得到的视图。
**但 inflate 一个新的布局时,必须为新的布局创建一个 binding。**因此, ViewStubProxy 必须监听 ViewStub 的 ViewStub.OnInflateListener,并及时建立 binding。由于 ViewStub 只能有一个 OnInflateListener,你可以将你自己的 listener 设置在 ViewStubProxy 上,在 binding 建立之后, listener 就会被触发。
``` java
mActivityViewStubBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
mActivityViewStubBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
IncludeBinding viewStubBinding = DataBindingUtil.bind(inflated);
User user = new User("Connor", "Lin", 28);
viewStubBinding.setUser(user);
}
});
```
通过 ViewStubProxy 来 inflate ViewStub :
``` java
public void inflate(View view) {
if (!mActivityViewStubBinding.viewStub.isInflated()) {
mActivityViewStubBinding.viewStub.getViewStub().inflate();
}
}
```
此处 isInflated() 和 getViewStub() 会标红,请不要担心,这并不是错误,是 ViewStubProxy 中的方法。
####优缺点
#####缺点
表达式不能自动编译
#####优点
####对比MVP
MVP:
Presenter-->View loadData-->callback-->View.method()
优点
你可以很容易对这种视觉行为进行单元测试。
缺点
XML不被编译,所以经常会在运行时期发现错误,而不是编译期间。
Databinding 是一个实现数据和UI绑定的框架,是一个实现 MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的实现MVVM开发模式。
####解决什么问题
减少编写大量的毫无营养的代码, 如 findViewById()、setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等。 通过 Data Binding , 我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的毫无营养的代码了。
####构建环境
在模块build.gradle中加上如下代码
``` html
dataBinding {
enabled = true
}
```
####用法
#####Data Binding 布局文件 - (View)
Data binding 的布局文件与传统布局文件有一点不同。它以一个 layout 标签作为根节点,里面是 data 标签与 view 标签。view 标签的内容就是不使用 Data Binding 时的普通布局文件内容。以下是一个例子:
``` html
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!-- 变量名,变量类型 描述了一个布局中会用到的属性 -->
<variable name="user" type="com.connorlin.databinding.model.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>
```
每个 variable 标签描述了会在 binding 表达式中使用的属性。生成的Binding类会有标签的getter/setter方法。
``` java
<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.connorlin.databinding.model.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>
```
在 Binding 类中生成对应的 getters 和 setters:
``` java
public com.connorlin.databinding.model.User getUser();
public void setUser(com.connorlin.databinding.model.User user);
public Drawable getImage();
public void setImage(Drawable image);
public String getNote();
public void setNote(String note);
```
布局中每一个带有 ID 的 View,都会生成一个 public final 字段。binding 过程会做一个简单的赋值,在 binding 类中保存对应 ID 的 View。这种机制相比调用 findViewById 效率更高。
#####数据对象 - (Model)
和原来Model写法一样,用于 TextView 的 android:text 属性的表达式@{user.firstName},当User没有getter/setter方法时,会读取 User 对象的 firstName 字段,反之读取对象的 getFirstName()方法。
#####绑定数据 - (ViewModel)
在默认情况下,每一个布局文件经过编译后会生成一个继承于 ViewDataBinding 的 Binding 类,将它转换成帕斯卡命名并在名字后面接上Binding。例如,布局文件叫 main_activity.xml,所以会生成一个 MainActivityBinding 类。这个类包含了布局文件中所有的绑定关系,会根据绑定方法setData()给布局文件赋值。在 inflate 的时候创建 binding 的方法如下:
``` java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ActivityBaseBinding 类是自动生成的
ActivityBaseBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_base);
User user = new User("Connor", "Lin");
// 所有的 set 方法也是根据布局中 variable 名称生成的
binding.setUser(user);
}
```
获取一个View:
``` java
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
```
在ListView、RecyclerView Adapter中使用:
``` java
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
```
Binding类放在模块包名+databinding目录下,更改data标签的class属性可以更改Binding类命令和位置。
``` java
<data class="CustomBinding">
...
</data>
```
以上会在 databinding 包中生成名为 CustomBinding 的 binding 类。如果需要放置在不同的包下,可以在前面加 “.”:
``` java
<data class=".CustomBinding">
...
</data>
```
这样的话, CustomBinding 会直接生成在 module 包下。如果提供完整的包名,binding 类可以放置在任何包名中:
``` java
<data class="com.example.CustomBinding">
...
</data>
```
#####事件处理
布局中声明要处理事件的方法,类似于 android:onClick 可以指定 Activity 中的函数。
方法调用
监听绑定
区别:
创建监听的时机不同;
方法引用是必须表达式正确,在编译时才用这个方法创建监听,否则不创建监听;而监听绑定无论如何会先创建监听,运行时候调用lambda 表达式指定的方法。二者主要区别在于前者在data binding时就实现了监听。
监听绑定允许表达式中更灵活;
只需要返回值相同,当表达式不能匹配回调时,Data Binding return default值, null for reference types, 0 for int, false for boolean, etc.。
方法引用
以下是个例子:
``` java
public class EventHandler {
private Context mContext;
public EventHandler(Context context) {
mContext = context;
}
public void onClickFriend(View view) {
Toast.makeText(mContext, "onClickFriend", Toast.LENGTH_LONG).show();
}
}
```
表达式如下:
``` java
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler"
type="com.connorlin.databinding.handler.EventHandler"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{handler::onClickFriend}"/>
<!-- 注意:表达式里的方法签名必须和监听器里的方法签名吻合 -->
</LinearLayout>
</layout>
```
监听绑定
以下是个例子:
``` java
public void onTaskClick(Task task) {
task.run();
}
```
表达式如下:
``` java
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler" type="com.connorlin.databinding.handler.EventHandler"/>
<variable
name="task" type="com.connorlin.databinding.task.Task"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> handler.onTaskClick(task)}"/>
</LinearLayout>
</layout>
```
关于参数
Listener方法的参数名有两种选择:要么不写,要么就要写全。
``` java
public void onTaskClick(Task task) {
task.run();
}
```
表达式如下:
``` java
android:onClick="@{(theView) -> presenter.onSaveClick(task)}
```
也可以这样
``` java
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
```
导入(Imports)
data 标签内可以有多个 import 标签,作用类似Java
``` java
<data>
<import type="android.view.View"/>
</data>
```
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
当类名发生冲突时,可以使用 alias
``` java
<import type="android.view.View"/>
<import type="com.connorlin.databinding.ui.View" alias="AliasView"/>
```
导入的类型也可以用于变量的类型引用和表达式中
``` java
<data>
<import type="com.connorlin.databinding.model.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
```
注意:Android Studio 还没有对导入提供自动补全的支持。你的应用还是可以被正常编译,要解决这个问题,你可以在变量定义中使用完整的包名。
* java.lang.* 包中的类会被自动导入,可以直接使用,例如, 要定义一个 String 类型的变量
binding 类会生成一个命名为 context 的特殊变量(其实就是 rootView 的 getContext() ) 的返回值), **这个变量可用于表达式中**。 如果有名为 context 的变量存在,那么生成的这个 context 特殊变量将被覆盖。
``` java
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{handler.loadString(context)}"/>
```
``` java
public String loadString(Context context) {
// 使用生成的context变量
return context.getResources().getString(R.string.string_from_context);
}
```
* 变量可以传递给include布局,但是需要在各个布局中声明
* view根标签不可是merge
####表达式语言
表达式语言与 Java 表达式有很多相似之处。下面是相同之处:
数学计算 + - / * %
字符串连接 +
逻辑 && ||
二进制 & | ^
一元 + - ! ~
位移 >> >>> <<
比较 == > < >= <=
instanceof
组 ()
字面量 - 字符,字符串,数字, null
类型转换
函数调用
字段存取
数组存取 []
三元运算符 ?:
在xml中转义是不可避免的
附:常用的转义字符
显示结果 描述 转义字符 十进制
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| 空格| | |
|< 小于号 |<| <|
|> 大于号| >| >|
|& 与号| & |&|
|" 引号 |"| "|
|‘ 撇号 |'| '|
|× 乘号 |× |×|
|÷ 除号 |÷ |÷|
不支持的操作符
一些 Java 中的操作符在表达式语法中不能使用。
this
super
new
显式泛型调用 <T>
Null合并运算符
Null合并运算符 ?? 会在非 null 的时候选择左边的操作,反之选择右边。
``` java
android:text="@{user.lastName ?? `Default LastName`}"
```
等同于
``` java
android:text="@{user.lastName != null ? user.lastName : `Default LastName`}"
```
容器类
通用的容器类:数组,lists,sparse lists,和 maps,可以用 [] 操作符来存取
``` java
<data>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<import type="java.util.List"/>
<variable name="list" type="List<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"
```
字符串常量
使用单引号把属性包起来,就可以很简单地在表达式中使用双引号:
``` java
android:text='@{map["firstName"]}'
也可以用双引号将属性包起来。这样的话,字符串常量就可以用 " 或者反引号 ( ` ) 来调用
```
``` java
android:text="@{map[`firstName`}"
android:text="@{map["firstName"]}"
```
资源
也可以在表达式中使用普通的语法来引用资源:
``` java
android:text="@{@string/fullname(user.fullName)"
```
字符串格式化和复数形式可以这样实现:
``` java
android:text="@{@plurals/sample_plurals(num)}"
```
当复数形式有多个参数时,应该这样写:
``` java
android:text="@{@plurals/numbers(num, num)}"
```
一些资源需要显示类型调用。
Type Normal Reference Expression Reference
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList
数据对象 (Data Objects)
####ViewStubs
ViewStub 相比普通 View 有一些不同。ViewStub 一开始是不可见的,当它们被设置为可见,或者调用 inflate 方法时,ViewStub 会被替换成另外一个布局。
因为 ViewStub 实际上不存在于 View 结构中,binding 类中的类也得移除掉,以便系统回收。因为 binding 类中的 View 都是 final 的,所以Android 提供了一个叫 **ViewStubProxy **的类来代替 ViewStub 。开发者可以使用它来操作 ViewStub,获取 ViewStub inflate 时得到的视图。
**但 inflate 一个新的布局时,必须为新的布局创建一个 binding。**因此, ViewStubProxy 必须监听 ViewStub 的 ViewStub.OnInflateListener,并及时建立 binding。由于 ViewStub 只能有一个 OnInflateListener,你可以将你自己的 listener 设置在 ViewStubProxy 上,在 binding 建立之后, listener 就会被触发。
``` java
mActivityViewStubBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
mActivityViewStubBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
IncludeBinding viewStubBinding = DataBindingUtil.bind(inflated);
User user = new User("Connor", "Lin", 28);
viewStubBinding.setUser(user);
}
});
```
通过 ViewStubProxy 来 inflate ViewStub :
``` java
public void inflate(View view) {
if (!mActivityViewStubBinding.viewStub.isInflated()) {
mActivityViewStubBinding.viewStub.getViewStub().inflate();
}
}
```
此处 isInflated() 和 getViewStub() 会标红,请不要担心,这并不是错误,是 ViewStubProxy 中的方法。
####优缺点
#####缺点
表达式不能自动编译
#####优点
####对比MVP
MVP:
Presenter-->View loadData-->callback-->View.method()
优点
你可以很容易对这种视觉行为进行单元测试。
缺点
XML不被编译,所以经常会在运行时期发现错误,而不是编译期间。