# DataBindingTest
**Repository Path**: giteetest15873/DataBindingTest
## Basic Information
- **Project Name**: DataBindingTest
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-05-19
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 【Android】DataBinding库(MVVM设计模式)
** *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 **
##什么是MVVM
说到DataBinding,就有必要先提起MVVM设计模式。
**Model–View–ViewModel**(**MVVM**) 是一个软件架构设计模式,相比MVVM,大家对MVC或MVP可能会更加熟悉。
- MVC:(VIew-Model-Controller)
早期将VIew、Model、Controller代码块进行划分,使得程序大部分分离,降低耦合。
- MVP:(VIew-Model-Presenter)由于MVC中View和Model之间的依赖太强,导致Activity中的代码过于臃肿。为了他们可以绝对独立的存在,慢慢演化出了MVP。在MVP中View并不直接使用Model,它们之间的通信是通过 Presenter (MVC中的Controller)来进行的。
- MVVM:(Model–View–ViewModel)
MVVM可以算是MVP的升级版,将 Presenter 改名为 ViewModel。关键在于View和Model的双向绑定,当View有用户输入后,ViewModel通知Model更新数据,同理Model数据更新后,ViewModel通知View更新。
##Data Binding
在Google I/O 2015上,伴随着Android M预览版发布的[Data Binding](https://developer.android.com/tools/data-binding/guide.html)兼容函数库。
不知道要扯什么了,还是直接上代码,来看看Data Binding的魅力吧。
- ####环境要求
Data Binding对使用的环境还是有一定要求的(这货有点挑)
*Android Studio版本在1.3以上*
*gradle的版本要在1.5.0-alpha1以上*
*需要在Android SDK manager中下载Android Support repository*
然后在对应的Module的build.gradle中添加
```java
android {
....
dataBinding {
enabled =true
}
}
```
> Gradle需要升级版本的可以参考[升级Gradle版本](http://www.jianshu.com/p/00beddbe3dbc)
- ####创建对象
创建一个User类
```java
public class User {
private String firstName;
private 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;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
```
- ####布局
在activity_main.xml中布局
```xml
```
这里跟平时的布局有点不同,最外层是layout,里面分别是是data以及我们的布局。
**data**:声明了需要用到的user对象,type用于是定路径。
可以在TextView中的看到android:text="@{user.firstName}", 这是什么鬼,没见过这么写的!!!
(不急,继续往下看)
- ####绑定数据
看看下面的MainActivity
```java
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("Micheal", "Jack");
binding.setUser(user);
}
}
```
问我ActivityMainBinding哪来的?我怎么知道...
ActivityMainBinding是根据布局文件的名字生成的,在后面加了Binding。
***运行下看看效果吧***

有点懵逼了,就绑定了下而已,这些数据是怎么显示到界面上的。

> **他是怎么工作的?**
原来Data Binding 在程序代码正在编译的时候,找到所有它需要的信息。然后通过语法来解析这些表达式,最后生成一个类。
通过反编译我们可以看到,Data Binding为我们生成了databinding包,以及ActivityMainBinding类([反编译可以参考这里](http://blog.csdn.net/vipzjyno1/article/details/21039349))

看看我们在onCreate中最后调用的binding.setUser(user),在ActivityMainBinding中可以看到这个方法。

我想就是这个 super.requestRebind()对数据进行了绑定,至于里面怎么实现的,有待进一步研究。
##更多用法
上面只是用一个简单的例子,展示了Data Binding的用法,如果想在实际项目中使用,可不是上面这例子可以搞定的。下面就来说说Data Bindig的更多用法。
- ####首先消除下大家对空指针的顾虑
自动生成的 **DataBinding** 代码会检查null,避免出现NullPointerException。
例如在表达式中@{user.phone}如果user == null 那么会为user.phone设置默认值null而不会导致程序崩溃(基本类型将赋予默认值如int为0,引用类型都会赋值null)
- ####自定义DataBinding名
如果不喜欢自动生成的Data Binding名,我们可以自己来定义
```xml
....
```
class对应的就是生成的Data Binding名
- ####导包
跟Java中的用法相似,布局文件中支持import的使用,原来的代码是这样
```xml
```
使用import后可以写成这样:
```xml
```
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
遇到相同的类名的时候:
```xml
```
使用alias设置别名,这样user对应的就是com.example.gavin.databindingtest.User,mcUser就对应com.example.gavin.mc.User,然后
```xml
```
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
当需要用到一些包时,在Java中可以自动导包,不过在布局文件中就没有这么方便了。需要使用import导入这些包,才能使用。如,需要用到View的时候
```xml
...
```
**注意**:*只要是在Java中需要导入包的类,这边都需要导入,如:Map、ArrayList等,不过java.lang包里的类是可以不用导包的*
- ####表达式
在布局中,不仅可以使用
```xml
android:text="@{user.lastName}"
```
还可以使用表达式如:
#####三元运算
在User中添加boolean类型的isStudent属性,用来判断是否为学生。
```xml
```
**注意**:*需要用到双引号的时候,外层的双引号改成单引号。*
还可以这样用
```xml
```
这里用到的View需要在data中声明
```xml
```
**注意**:android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}",*可能会被标记成红色,不用管它编译会通过的*
#####??
除了常用的操作法,另外还提供了一个 null 的合并运算符号 ??,这是一个三目运算符的简便写法。
```java
contact.lastName ?? contact.name
```
相当于
```java
contact.lastName != null ? contact.lastName : contact.name
```
>**所支持的操作符如下:**
数学运算符 + - / * %
字符串拼接 +
逻辑运算 && ||
二进制运算 & | ^
一元运算符 + - ! ~
位运算符 >> >>> <<
比较运算符 == > < >= <=
instanceof
Grouping ()
文字 - character, String, numeric, null
类型转换 cast
方法调用 methods call
字段使用 field access
数组使用 [] Arrary access
三元运算符 ? :
- #####显示图片
除了文字的设置,网络图片的显示也是我们常用的。来看看Data Binding是怎么实现图片的加载的。
首先要提到BindingAdapter注解,这里创建了一个类,里面有显示图片的方法。
```java
public class ImageUtil {
/**
* 使用ImageLoader显示图片
* @param imageView
* @param url
*/
@BindingAdapter({"bind:image"})
public static void imageLoader(ImageView imageView, String url) {
ImageLoader.getInstance().displayImage(url, imageView);
}
}
```
*(这方法必须是public static的,否则会报错)*
这里只用了bind声明了一个image自定义属性,等下在布局中会用到。
这个类中只有一个静态方法imageLoader,里面有两参数,一个是需要设置图片的view,另一个是对应的Url,这里使用了ImageLoader库加载图片。
看看吧它的布局是什么样的吧
```xml
```
最后在MainActivity中绑定下数据就可以了
```java
binding.setImageUrl(
"http://115.159.198.162:3000/posts/57355a92d9ca741017a28375/1467250338739.jpg");
```
*哇靠!!!就这样?我都没看出来它是怎么设置这些图片的。*
不管了,先看看效果。(其中的原理以后慢慢唠,这里就负责说明怎么使用,这篇已经够长了,不想再写了)

>使用BindingAdapter的时候,我这还出现了这样的提示,不过不影响运行。不知道你们会不会...

**【已解决】**
感谢[颜路](http://www.jianshu.com/users/296d6ebc6cd2)同学指出*@BindingAdapter({"bind:image"}) 改成
@BindingAdapter({"image"}) 就不会有警告了*
- #####点击事件
在MainActivity中声明方法:
```java
//参数View必须有,必须是public,参数View不能改成对应的控件,只能是View,否则编译不通过
public void onClick(View view) {
Toast.makeText(this,"点击事件", Toast.LENGTH_LONG).show();
}
```
布局中:
```xml
...
....
```
最后记得在MainActivity中调用
```java
binding.setMainActivity(this);
```
*(发现:布局文件中,variable中的name,在binding中都会生成一个对应的set方法,如:setMainActivity。有set方法,那就应该有get方法,试试getMainActivity,还真有)*
**运行下看看效果**

当然如果你不想吧点击事件写在MainActivity中,你把它单独写在一个类里面:
```java
public class MyHandler {
public void onClick(View view) {
Toast.makeText(view.getContext(), "点击事件", Toast.LENGTH_LONG).show();
}
}
```
```xml
...
....
```
在MainActivity调用
```java
binding.setHandle(new MyHandler());
```
- ####调用Activity中的变量
上面看到它调用MainActivity中的onClick方法,那么可以调用MainActivity中的属性吗?
在MainActivity中定义mName,
```java
public static String mName = "MM";
```
布局中
```xml
...
```
**注意**:*这个变量必须是public static*
- ####数据改变时更新UI
当数据发生变化时,我们可以这样更新UI
```java
private ActivityMainBinding binding;
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
user = new User("Micheal", "Jack");
binding.setUser(user);
binding.setHandle(new MyHandler());
delay();
}
/**
* 两秒后改变firstName
*/
private void delay() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
user.setFirstName("Com");
binding.setUser(user);
}
}, 2000);
}
```
看看调用的这个setUser是什么:

从反编译的代码中可以看出,setUser方法中重新绑定了数据。
看下效果

- ####**BaseObservable**
使用上面的代码实现了UI的更新你就满足了?其实官方为我们提供了更加简便的方式,使User继承BaseObservable,代码如下
```java
public class User extends BaseObservable {
private String firstName;
private String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
}
```
只要user发生变化,就能达到改变UI的效果。在MainActivity中只要调用以下代码
```java
user.setFirstName("Com");
```
有了BaseObservable就够了?不不不,我比较懒,不想写那么多@Bindable和notifyPropertyChanged。万一里面有几十个属性,那不写哭起来?而且还有可能写丢了。
Data Binding的开发者贴心得为我们准备了一系列的[ObservableField](https://developer.android.com/reference/android/databinding/ObservableField.html),包括: [ObservableBoolean](https://developer.android.com/reference/android/databinding/ObservableBoolean.html), [ObservableByte](https://developer.android.com/reference/android/databinding/ObservableByte.html), [ObservableChar](https://developer.android.com/reference/android/databinding/ObservableChar.html), [ObservableShort](https://developer.android.com/reference/android/databinding/ObservableShort.html), [ObservableInt](https://developer.android.com/reference/android/databinding/ObservableInt.html), [ObservableLong](https://developer.android.com/reference/android/databinding/ObservableLong.html), [ObservableFloat](https://developer.android.com/reference/android/databinding/ObservableFloat.html),[ObservableDouble](https://developer.android.com/reference/android/databinding/ObservableDouble.html), 以及 [ObservableParcelable](https://developer.android.com/reference/android/databinding/ObservableParcelable.html)看看它们的用法
**ObservableField的使用**
1、创建User2
```java
public class User2 {
public final ObservableField firstName = new ObservableField<>();
public final ObservableField lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
public final ObservableBoolean isStudent = new ObservableBoolean();
}
```
这类里面没有Get/Set。
2、布局文件
```xml
```
3、MainActivity中
```java
mUser2 = new User2();
binding.setUser2(mUser2);
mUser2.firstName.set("Mr");
mUser2.lastName.set("Bean");
mUser2.age.set(20);
mUser2.isStudent.set(false);
```
这里new了一个User2对象后,直接就绑定了。之后只要mUser2中的数据发生变化,UI也会随之更新。
除了这几个Map跟List也是必不可少的,Data Binding为我们提供了 [ObservableArrayMap](https://developer.android.com/reference/android/databinding/ObservableArrayMap.html)和[ObservableArrayList](https://developer.android.com/reference/android/databinding/ObservableArrayList.html)。
**ObservableArrayMap的使用**
```java
ObservableArrayMap user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
```
```java
…
```
**ObservableArrayList的使用**
```java
ObservableArrayList