Android DataBinding库(MVVM设计模式)

说到 DataBinding,就有必要先提起 MVVM设计模式
html

 

Model–View–ViewModel(MVVM) 是一个软件架构设计模式,相比 MVVM,你们对 MVC MVP 可能会更加熟悉。java

 

  • MVC:(VIew-Model-Controller)android

    早期将 View、Model、Controller 代码块进行划分,使得程序大部分分离,下降耦合。git

     

  • MVP:(VIew-Model-Presenter)github

    因为 MVC View和Model之间的依赖太强,致使 Activity 中的代码过于臃肿。为了他们能够绝对独立的存在,慢慢演化出了 MVP。在 MVP View 并不直接使用 Model,它们之间的通讯是经过 Presenter (MVC中的Controller) 来进行的。swift

     

  • 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 中添加:

 

android {
   ....    dataBinding {        enabled =true    } }

 

Gradle须要升级版本的能够参考:

 

升级Gradle版本

http://www.jianshu.com/p/00beddbe3dbc

 

建立对象

 

建立一个 User类

 

 

布局

 

activity_main.xml 中布局:

 

 

这里跟平时的布局有点不一样,最外层是 layout,里面分别是 data 以及 咱们的布局。

 

data:声明了须要用到的 user对象type 用于指定路径。

 

能够在 TextView 中的看到 android:text="@{user.firstName}", 这是什么鬼,没见过这么写的!!!(不急,继续往下看)

 

绑定数据

 

看看下面的 MainActivity

 

 

问我 ActivityMainBinding 哪来的?我怎么知道...

 

ActivityMainBinding 是根据布局文件的名字生成的,在后面加了 Binding

 

运行下看看效果吧:

 

 

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

 

 

他是怎么工做的?

 

原来 Data Binding 在程序代码正在编译的时候,找到全部它须要的信息。而后经过语法来解析这些表达式,最后生成一个类。

 

经过反编译咱们能够看到,反编译能够参考这里:

http://blog.csdn.net/vipzjyno1/article/details/21039349

 

Data Binding 为咱们生成了 databinding,以及 ActivityMainBinding类

 

 

看看咱们在 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名,咱们能够本身来定义:

 

<data class="MainBinding">    ....
</data>

 

class对应 的就是生成的 Data Binding名

 

导包

 

跟Java中的用法类似,布局文件中支持 import 的使用,原来的代码是这样:

 

<data>    <variable name="user" type="com.example.gavin.databindingtest.User" />
</data>

 

使用 import 后能够写成这样:

 

<data>    <import type="com.example.gavin.databindingtest.User"/>    <variable        name="user"        type="User" />
</data>

 

遇到 相同的类名 的时候:

 

 

使用 alias 设置别名,这样 user 对应的就是 com.example.gavin.databindingtest.UsermcUser 就对应com.example.gavin.mc.User,而后:

 

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{user.firstName}"/>

 

当须要用到一些包时,在Java中能够自动导包,不过在布局文件中就没有这么方便了。须要使用 import 导入这些包,才能使用。如须要用到 View 的时候:

 

<data>    <import type="android.view.View"/></data>    ...    <TextView    ...    android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}"
/>

 

注意:只要是在Java中须要导入包的类,这边都须要导入,如:Map、ArrayList 等,不过 java.lang 包里的类是能够不用导包的。

 

表达式

 

在布局中,不只可使用:

 

android:text="@{user.lastName}"

 

还可使用表达式如:

 

三元运算:

 

User 中添加 boolean类型 isStudent属性,用来判断是否为学生:

 

 

注意:须要用到双引号的时候,外层的双引号改为单引号。

 

还能够这样用:

 

 

这里用到的 View 须要在 data 中声明:

 

<data>    <import type="android.view.View"/>
</data>

 

注意:android:visibility="@{user.isStudent ? View.VISIBLE : View.GONE}",可能会被标记成红色,不用管它编译会经过的。

 

??

 

除了经常使用的操做法,另外还提供了一个 null 的合并运算符号 ??,这是一个三目运算符的简便写法。

 

 

contact.lastName ?? contact.name

 

至关于:

 

contact.lastName != null ? contact.lastName : contact.name

 

所支持的操做符以下:

 

数学运算符 + - / * %
字符串拼接 +
逻辑运算 && ||
二进制运算 & | ^
一元运算符 + - ! ~
位运算符 >> >>> <<
比较运算符 == > < >= <=
instanceof
Grouping ()
文字 - character, String, numeric, null
类型转换 cast
方法调用 methods call
字段使用 field access
数组使用 [] Arrary access
三元运算符 ? :

 

显示图片

 

除了文字的设置,网络图片的显示也是咱们经常使用的。来看看 Data Binding 是怎么实现图片的加载的。

 

首先要提到 BindingAdapter注解,这里建立了一个类,里面有显示图片的方法:

 


(这方法必须是public static的,不然会报错)

 

这里只用了 bind 声明了一个 image 自定义属性,等下在布局中会用到。

 

这个类中只有一个静态方法 imageLoader,里面有两参数,一个是须要设置图片的 view,另外一个是对应的 Url,这里使用了 ImageLoader 库加载图片。

 

看看它的布局是什么样的吧:

 

 

最后在 MainActivity 中绑定下数据就能够了:

 

binding.setImageUrl("http://115.159.198.162:3000/posts/57355a92d9ca741017a28375/1467250338739.jpg");

 

哇靠!!!就这样?我都没看出来它是怎么设置这些图片的。

 

无论了,先看看效果。(其中的原理之后慢慢唠,这里就负责说明怎么使用,这篇已经够长了,不想再写了)

 


看个美女压压惊

 

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

 

 

已解决

感谢 颜路 同窗指出 @BindingAdapter({"bind:image"})  改为 @BindingAdapter({"image"}) 就不会有警告了。

 

点击事件

 

MainActivity 中声明方法:

 

//参数View必须有,必须是public
//参数View不能改为对应的控件,只能是View,不然编译不经过
public void onClick(View view) {
   Toast.makeText(this,"点击事件", Toast.LENGTH_LONG).show(); }

 

布局中:

 

 

最后记得在 MainActivity 中调用:

 

binding.setMainActivity(this);

 

发现:布局文件中,variable 中的 name,在 binding 中都会生成一个对应的 set方法,如:setMainActivity。有 set方法,那就应该有 get方法,试试 getMainActivity,还真有。

 

运行下看看效果:

 

 

固然若是你不想把点击事件写在 MainActivity 中,你把它单独写在一个类里面:

 

public class MyHandler {
   public void onClick(View view) {
       Toast.makeText(view.getContext(), "点击事件", Toast.LENGTH_LONG).show();    } }

 

 

MainActivity 调用:

 

binding.setHandle(new MyHandler());

 

调用Activity中变量

 

上面看到它调用 MainActivity中的onClick方法,那么能够调用 MainActivity 中的属性吗?

 

MainActivity 中定义 mName

 

public static String mName = "MM";

 

布局中:

 

 

注意:这个变量必须是 public static。

 

数据改变时更新UI

 

当数据发生变化时,咱们能够这样更新UI:

 

 

看看调用的这个 setUser 是什么:

 

 

从反编译的代码中能够看出,setUser 方法中从新绑定了数据。

 

看下效果:

 

 

BaseObservable

 

使用上面的代码实现了UI的更新你就知足了?其实官方为咱们提供了更加简便的方式,使 User继承BaseObservable,代码以下:

 

 

只要 user 发生变化,就能达到改变UI的效果。在 MainActivity 中只要调用如下代码:

 

user.setFirstName("Com");

 

有了 BaseObservable 就够了?不不不,我比较懒,不想写那么多 @Bindable notifyPropertyChanged。万一里面有几十个属性,那不写哭起来?并且还有可能写丢了。

 

Data Binding的开发者贴心得为咱们准备了一系列的 ObservableField,包括: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat,ObservableDouble 以及 ObservableParcelable (原文蓝字部分都是超连接,感兴趣的朋友能够经过原文查看,我这里就不贴出来了,下文如有蓝色字体视为同等状况)看看它们的用法。

 

ObservableField 的使用

 

1. 建立User2

 

 

这类里面 没有Get/Set

 

2. 布局文件

 

<TextView    ...    android:text="@{user2.firstName}" />
<TextView    ...    android:text="@{user2.lastName}" />
<TextView    ...    android:text="@{String.valueOf(user2.age)}" />

 

3. MainActivity中:

 

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 ObservableArrayList

 

ObservableArrayMap 的使用

 

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>(); user.put("firstName", "Google"); user.put("lastName", "Inc."); user.put("age", 17);

 

 

ObservableArrayList 的使用

 

 

ObservableArrayList<Object> user = new ObservableArrayList<>(); user.add("Google"); user.add("Inc."); user.add(17);



 

在布局中使用到 ObservableBoolean 类型时,编译没法经过:

 

android:text='@{user2.isStudent?"学生":"非学生"}'

 

【目前已知】

将中文改为英文是能够经过编译的,像下面这样:

 

android:text='@{user2.isStudent?"Student":"Not Student"}'

 

为什么使用中文不能够?缘由未明。(感谢指教)

 

在RecyclerView或ListView中使用

 

前面说了那么多基础的用法,可仍是不能达到咱们的需求。几乎在每一个app中都有列表的存在,RecyclerView ListView,从上面所说的彷佛还看不出 Data Binding RecyclerView ListView 中是否也能起做用。(用屁股想也知道,Google的开发团对怎么可能会犯这么低级的错误)。下面以 RecyclerView 为例子:

 

1. 直接看 Item 的布局(user_item.xml):

 

 

2. RecyclerView 的数据绑定是在 Adapter 中完成的,下面看看 Adapter,这里使用了一个 Adapter,若是你在使用的时候发现 RecyclerView 的动画没了,去这里寻找答案:

https://realm.io/cn/news/data-binding-android-boyar-mount/

 

 

3. 最后在布局和 MainActivity 中的使用跟平时的用法同样。

 

布局中加入 RecyclerView

 

<android.support.v7.widget.RecyclerView    android:id="@+id/recycler_view"    android:layout_width="match_parent"    android:layout_height="match_parent"/>

 

MainActivity 中:

 

 

这样就能够了。

 

不过,在自动生成的 ActivityMainBinding 中,咱们能够看到根据 RecyclerView的id,会自动生成一个 recyclerView

 

因此在 MainActivity 中,咱们能够不用 findViewById,直接使用 binding.recyclerView

 

 

来看看效果吧:

 

 

Tips

 

tip1

 

若须要显示int类型,须要加上"",例如:

 

user.age 为 int类型,须要这样用:

 

<TextView      android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text='@{""+user.age}'/>

 

或者

 

<TextView      android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="@{String.valueOf(user.age)}"/>

 

tip2

 

不建议新手使用,出现错误的时候根据提示,不容易找到出错位置。(是根本找不到...)

 

 

参考

 

Google官方(权威,不过全英文。点击事件写的好像不对,后来去其余地方查的)

https://developer.android.com/topic/libraries/data-binding/index.html#data_binding_layout_files

 

Realm(十分全面)

https://realm.io/cn/news/data-binding-android-boyar-mount

 

CSDN-亓斌(有点像google文档的翻译版,总体结果类似)

http://blog.csdn.net/qibin0506/article/details/47393725

 

阳春面的博客(好奇怪的名字)

https://www.aswifter.com/2015/07/04/android-data-binding-1

 

源码地址:

https://github.com/Gavin-ZYX/DataBindingTest

相关文章
相关标签/搜索