data binding——Android中的MVVM模式

2015年谷歌 IO大会上提出了 data binding框架,通过data binding的技术,我们可以在Android上实现MVVM模式,通过数据绑定的方式能实现视图的自动更新。

1.关于设计模式:

我们常见的设计模式有MVC,MVP,MVVM。

MVC:Model数据层    View视图层    Controller控制层

用户通过视图层将指令发送到控制层,控制层处理逻辑后更新数据层,数据层把更新的数据显示到视图层,完成一个完整流程,这个过程中,视图层和数据层是有互相依赖关系的。

MVP:Model数据层    View视图层    Presenter

MVP模式通过Presenter进行控制,它隔断了视图层和数据层的联系,视图层和控制层通过接口的方式进行交互。

MVVM:Model数据层     View视图层     ViewModel

MVVM模式可以认为是MVP模式是延生,不同之处是视图层和控制层之间的交互方式由接口变为了数据绑定,通过数据的双向绑定技术,实现视图的自动更新。

2.data binding特点

(1)通过使用data binding,可以把一些业务逻辑抽离,将一部分逻辑放到ViewModel中处理,减少Activity、Fragment中的代码。

(2)代码结构更加清晰,视图层和数据层实现了解耦,更有利于单元测试。

(3)数据绑定的工作在xml文件中进行,所以可能不利于调试。

3.使用环境

(1)开发环境:目前data binding只在Android studio 1.3版本以上使用

(2)编译环境:

在项目根目录的gradle文件中添加dependencies

  dependencies {
       classpath "com.android.tools.build:gradle:1.2.3"
       classpath “com.android.databinding:dataBinder:1.0-rc0"
}

在所有项目的gradle文件中添加data binding支持

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'


4.使用方法

(1)View绑定的表达式

<p class="p1"><span class="s1"><</span><span class="s2">layout</span><span class="s1"> xmlns:android="http://schemas.android.com/apk/res/android"></span></p><p class="p2"><span class="s3">   </span><span class="s1"><data></span></p><p class="p2"><span class="s1">       <variable name="user" type="com.example.User"/></span></p>   <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="<span style="color:#ff0000;">@{user.firstName}</span>"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="<span style="color:#ff0000;">@{user.lastName}</span>"/>
   </LinearLayout>
</layout>
在layout文件中,起始跟标签是layout,它包含一个data元素和一个View根元素,data中的variable指定了需要绑定的类,name是该类对象的名称,type是该类的包名路径,在TextView通过@{ }表达式绑定类的属性。

(2)Data对象

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;
   }
}
Data对象,即普通的javabean。

(3)Binding数据

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    User user = new User("Test", "User");
    binding.setUser(user);
}
Android Studio会根据Layout的名称自动生成ViewModel类,比如activity_main.xml会自动生成一个ActivityMainBinding类,ActivityMainBinding中的方法,会根据layout中的属性自动生成。比如在layout中指定了user变量,ActivityMainBinding类中会自动生成get/setUser方法。

也可以通过以下方法来获取binding对象:

//使用Binding类型创建 
ActivityMainBinding.inflate binding = ActivityMainBinding.inflate(layoutInflater);
//绑定根布局
ActivityMainBinding binding = ActivityMainBinding.bind(viewRoot);
在ListView的Adapter中获取binding的方式:

ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
通过以上三步,就可以完成一个最简单的数据绑定。


(4)数据绑定的方法

a. import语法

<data>
    <import type="android.view.View"/>
</data>
类似java语法,可以把所需类通过imoprt引入

b. 类名相同

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
             <span style="color:#ff0000;">alias=“MyView"</span>/>
…
<TextView
   android:text=“@{MyView.width}"
…
当类名相同时,可以使用alias属性来区分。

c. import集合类型

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List<User>"/>
 </data>

<TextView
   android:text="@{userList[0].userName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
/>

d. 使用类的静态方法

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
在类中定义的静态方法也可以直接绑定使用。

e. 自定义binding类名

   默认情况下,自动生成的Binding类会放置在databinding包下,如果模块包是com.example.app,那么它会放置在com.example.app.databinding。可以通过data元素的class属性来重命名或者放置在不同的包中。

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

<data class="com.example.ContactItem">
    ...
</data>

f. Includes

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        <span style="color:#ff0000;">xmlns:bind="http://schemas.android.com/apk/res-auto"</span>>
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       …>
       <span style="color:#ff0000;"><include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/></span>
   </LinearLayout>
     …
在layout文件中有其他includes的xml文件要使用绑定数据时,需要添加一个命名空间,然后使用bind属性绑定,前提是在name.xml和contact.xml文件中也要添加。

(5)绑定表达式的语法

a. @{ }表达式支持的运算

数学 + - / * %               字符串连接 +             逻辑 && ||

二进制 & | ^                 一元运算 + - ! ~         移位 >> >>> <<

比较 == > < >= <=       instanceof                 分组 ()

null                              Cast                          方法调用

数据访问 []                  三元运算 ? :

示例:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

b. Null的合并操作

android:text="@{user.displayName ?? user.lastName}"
?? 表示如果左边不为null,使用左边,如果左边为null,使用右边。

c. 避免空指针

在表达式@{user.name}中,如果user是null,user.name会赋予它的默认值(null)。如果你引用user.age,age是int类型,那么它的默认值是0。


d. 集合

android:text="@{list[index]}"
android:text="@{map[key]}"

e. 访问Resources资源

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

f. 表达式

android:text=‘@{map["firstName"]}'
android:text="@{map[`firstName`]}"
android:text="@{map["firstName"]}"
以上三种表达式都可以使用

(6)Data对象

(1)-(3)步骤完成了一个最简单是数据绑定,但是这样数据源仅仅只能做到绑定,并不能实现动态更新,要实现数据的自动更新,就必须要用Observable的方式存储数据,Observable是一个接口,接口内提供了一个监听,并且有BaseObservable类供我们实现这个监听,data binding给我们提供了三种不同的数据变化通知机制,把这些对象绑定到view,当observable对象更新后,UI会自动更新。这三种机制是:Observable,ObservableFields,Observable Collections。

a. Observable

private static class User extends <span style="color:#ff0000;">BaseObservable</span> {
   private String firstName;
   <span style="color:#ff0000;">@Bindable</span>
   public String getFirstName() {
       return this.firstName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       <span style="color:#ff0000;">notifyPropertyChanged(BR.firstName);</span>
   }
}
Data数据需要继承BaseObservable类,并且在get方法上添加@Bindable注解,在set方法中添加notifyPropertyChanged(BR.firstName),BR类是通过注解生成的,通过BR类对需要更新的数据进行设置。

b. ObservableFields

private static class User extends BaseObservable {
<span style="white-space:pre">	</span>public final ObservableField<String> firstName = new ObservableField<>();
<span style="white-space:pre">	</span>public final ObservableInt age = new ObservableInt();
}
ObservableFields为Data对象提供了一种更为简化的写法,可以通过ObservableFiled的泛型指定数据类型,也可以直接使用data binding提供的ObservableInt、ObservableString、ObservableFloat等常用几个数据类型的封装类。访问data对象的时候,直接使用set/get方法:

<span style="white-space:pre">	</span>user.firstName.set("Google");
<span style="white-space:pre">	</span>int age = user.age.get();

c. O bservable Collections

data binding 也向我们提供了常用集合的封装:ObservableArrayList,ObservableArrayMap,使用方法和Map集合、List集合方式一样。

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
在layout文件中进行绑定:

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap<String, Object>"/>
</data>
…
<TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

(7)设置View的id
虽然data binding帮我们避免了findViewById等繁琐的获取View的过程,但是有时候我们也会碰到需要制定View的id的情况,这时候通过data binding也可以直接获取到View

<data>
       <variable name="user" type=“com.example.User"/>
</data>
…
<TextView  android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.userName}"
           android:<span style="color:#ff0000;">id="@+id/userName"</span>/>
<TextView  android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.userPassword}"
           android:<span style="color:#ff0000;">id="@+id/userPassword"</span>/>
指定View的id后,Binding类会自动生成相应的控件对象,我们可以通过binding对象直接获取控件:

TextView mTvUserName = binding.userName;
TextView mTvPassword = binding.userPassword;

有错误之处请批评指正,感激不尽。


参考资料:

http://segmentfault.com/a/1190000003056818

http://www.cnblogs.com/dxy1982/p/3793895.html

http://www.zhihu.com/question/30976423








  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值