zoukankan      html  css  js  c++  java
  • Android DataBinding库(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 中添加:

    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

  • 相关阅读:
    python note 30 断点续传
    python note 29 线程创建
    python note 28 socketserver
    python note 27 粘包
    python note 26 socket
    python note 25 约束
    Sed 用法
    python note 24 反射
    python note 23 组合
    python note 22 面向对象成员
  • 原文地址:https://www.cnblogs.com/ldq2016/p/6698181.html
Copyright © 2011-2022 走看看