新玩具:Android里的DataBinding

为什么我们需要Data Binding

传统的MVC模式如下图所示:

mvc这种模式的初衷是让业务逻辑和View分开,让我们在修改界面而不改变业务逻辑的时候更简单,但是实习操作的时候往往很难完全对View和Controller或Model很好的分离。并且三种组件也增加了一定的复杂度。而当我们有了Data Binding技术我们就可以使用另外一种模式(这种模式常用于Windows软件开发)叫MVVM 全称是Model-View-ViewModel,Model代表的是你的业务逻辑,View是展示的视图,ViewModel是把二者绑定起来的接口。这样说还是有点抽象,在一般Android开发里,如果某个View展示的数据出现了变化,我们需要findViewById找到这个View,并且设置新更新的数值。而在MVVC里,因为Model和View是绑定的,如果model层的数据发送了变化,会自动通知更新view应该如何重新变化展示。听起来是不是非常棒哈。

 如何在Android里使用

  • 准备环境

在整个工程里的build.gradle里添加如下依赖:

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

在你需要的使用data binding的module对应的build.gradle里添加

apply plugin: 'com.android.databinding'

ps:这里需要Android Studio是1.3以上,gradle版本在1.3以上,我之前即使是这样设置了还是有问题,我把build tool还有兼容包全部升级到最新版本就木有问题了。

下面我将已一个例子来介绍Android的DataBinding,这个小程序叫MagicNumber,app里显示一个数从1开始,有一个按钮,按一次按钮数字加1,若数字是奇数数字将显示黄色,若是偶数显示蓝色。

 

 

evenodd

先写布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="num" type="com.lvable.databindingtest.MagicNumber"/>
    </data>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="30sp"
            android:text="The magic number"
            android:id="@+id/textView"
            android:layout_marginTop="43dp"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"/>

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="67dp"
            android:textSize="60sp"
            android:layout_below="@+id/textView"
            android:layout_centerHorizontal="true"
            android:text="@{num.num}"
            android:textColor="@{num.odd ? @color/light_yellow : @color/light_blue}"
            />

        <Button
            android:text="Add"
            android:id="@+id/btn"
            android:onClick="addCount"
            android:layout_below="@id/textView2"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>

</layout>

注意如下变化:布局文件里的根元素不再是ViewGroup而是一个叫layout的标签,这个标签里包含一个data标签还有一个View,data标签里面包含了要绑定的数据元素类型申明等信息。在本例子里,我们要绑定的数据类型叫MagicNumber,然后在布局里它的名字叫num,type是这个类的完整路径名(若是Java自带的类型直接写就好,比如String,int)。在其他地方若要用到这个绑定数据的值只需要用@ {obj.xxxx}就行。这里要注意一个问题,就是在访问obj的成员变量的时候不一定总是它的全名,我试了一下我的MagicNumber里有一个成员变量叫isOdd,但是要访问它的话我要这样写 @{num.odd}。在这里还可以使用一些语句来实现简单的逻辑操作,比如我在布局文件里写入了奇偶不同颜色不同的逻辑。在textView里

android:textColor="@{num.odd ? @color/light_yellow : @color/light_blue}"

带Observable模式的MagicNumber类

Observable模式就是,当绑定的数据发送变化的时候,这个绑定的View会自己更新显示信息。这里可以使用MagicNumber继承一个叫BaseObservable的类来实现。(也可以使用ObservableField来实现对某一个field的Observe而不用继承)。

package com.lvable.databindingtest;

import android.databinding.BaseObservable;
import android.databinding.Bindable;

import com.lvable.databindingtest.BR;


/**
 * Created by Jiaqi Ning on 21/6/2015.
 */
public class MagicNumber extends BaseObservable {
    private String num;
    private boolean isOdd;
    
    public MagicNumber(int num){
        this.num = Integer.toString(num);
    }
   
    @Bindable
    public String getNum() {
        return num;
    }
   
    @Bindable
    public boolean isOdd() {
        return isOdd;
    }
    
    public void addOne(){
        int c = Integer.parseInt(num);
        num = Integer.toString(++c);
        updateEven();
        notifyPropertyChanged(BR.num);
        notifyPropertyChanged(BR.odd);
    }

    private void updateEven(){
        int c = Integer.parseInt(num);
        isOdd = ((c % 2) != 0) ? true : false;
    }
}

这里要注意,需要有更新通知功能的成员变量的get方法里,添加@Bindable这个注解,然后在起更新数据的地方调用对应的notifyPropertyChanged(BR.xxx)。在本程序里是如果MagicNumber调用了addOne就通知数据更新。

(注:BR 是编译阶段生成的一个类,功能与 R.java 类似,用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry,当我们通过代码可以看出,当数据发生变化时还是需要手动发出通知。)

好了,终于到了最后真正实现绑定的一步了!

public class MainActivity extends AppCompatActivity {
    MagicNumber magicNumber = new MagicNumber(1);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setNum(magicNumber);
    }

    public void addCount(View view){
        magicNumber.addOne();
    }

}

注意,这里不再使用setContentView(),使用的DataBindingUtil.setContentView()返回一个binding,用它来绑定数据。(这里的set方法是更具之前data里写入的type动态生成的)。

还有一个更好用的特性,若你在布局文件里给了某个View一个ID,在Activity里你想要使用这个View你不再需要些findViewById,你只需要从前面绑定产生的那个binding.xxxx (xxx是view的id名)就可以直接对这个View进行操作了!Awesome!

参考:    Google官方guide

很好的binding demo教程

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>