设置View的id

虽然说Data Binding这种分层模式使得我们对数据的传递简单明了,一般情况下我们可以不设置View的id,不使用findViewById即可对View进行数据上一系列的操作,不过有时候根据情况我们需要对某些View设置id,但是还是可以不findViewById即可得到该控件的对象,因为设置id后ViewDataBinding类会自动生成对应的控件对象,如:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.sunzxyong.User"/>
   </data>
   <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="@{user.userName}"
           android:id="@+id/userName"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.userPassword}"
          android:id="@+id/userPassword"/>
   </LinearLayout>
</layout>

那么在ViewDataBinding类中会自动生成相应的控件对象:

public final TextView userName;
public final TextView userPassword;

这些对象名和id名是一样的,然后我们可以通过:

ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
TextView mTvUserName = mBinding.userName;
TextView mTvPassword = mBinding.userPassword;

即可得到TextView的对象了,再进行后续操作。。。

Observable观察者

我们知道,Data Binding中如果我们直接修改Model实体对象(也就是POJO)中的数据,这些数据并不能直接更新到UI,所以Data Binding给了我们一套很好的通知机制,分别有三类: Observable objects, observable fields, and observable collections,分别表示观察对象、观察字段、观察集合,若相应的对象、字段、集合中数据变化时候,那么UI将会自动更新数据。下面我们一一来介绍它们的用法:

Observable objects

因为Observable是个接口,Google为我们提供了一个BaseObservable类,我们只要把Model类继承自它就获得了通知UI更新数据的能力了,然后再getter方法上添加Bindable注解,在setter方法中使用notifying提醒UI更新数据。如:

private static class User extends BaseObservable {
   private String userName;
   private String userPassword;
   public User(String userName, String userPassword) {
        this.userName = userName;
        this.userPassword = userPassword;
    }

    @Bindable
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
        notifyPropertyChanged(BR.userName);
    }

    @Bindable
    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
        notifyPropertyChanged(BR.userPassword);
    }
}

首先我们需要在getter方法上添加Bindable注解后,Bindable注解会自动生成一个BR类,该类位于app module包下,通过BR类我们设置更新的数据,当Model中的数据发生变化时,setter方法中的notifyPropertyChanged()就会通知UI更新数据了。

下面我们通过一个Demo来看看效果吧:

原先的User类:

我们没有继承BaseObservable

public class User  {
    private String userName;
    private String userPassword;

    public User(String userName, String userPassword) {
        this.userName = userName;
        this.userPassword = userPassword;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

}

然后onCreate()方法中代码为:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        final User user = new User("Sunzxyong", "12345678");
        mBinding.setUser(user);
        //点击按钮改变User的数据
        mBinding.btn.setOnClickListener(new android.view.View.OnClickListener() {
            @Override
            public void onClick(android.view.View v) {
                user.setUserName("Hello");
                user.setUserPassword("87654321");
            }
        });
    }

效果如下:



我们无论怎么点击UI界面上都没有更新数据。。。

那么我们把User类继承自BaseObservable:

public class User extends BaseObservable {
    private String userName;
    private String userPassword;

    public User(String userName, String userPassword) {
        this.userName = userName;
        this.userPassword = userPassword;
    }

    @Bindable
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
        notifyPropertyChanged(BR.userName);
    }

    @Bindable
    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
        notifyPropertyChanged(BR.userPassword);
    }

onCreate()的代码还是一样:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        com.sunzxyong.binding.databinding.ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        final User user = new User("Sunzxyong", "12345678");
        mBinding.setUser(user);
        mBinding.btn.setOnClickListener(new android.view.View.OnClickListener() {
            @Override
            public void onClick(android.view.View v) {
                user.setUserName("Hello");
                user.setUserPassword("87654321");
            }
        });
    }

效果如下:

可以看到Model中的数据更新时UI界面上的数据也同时更新了。

ObservableFields

我们刚刚介绍的通知UI更新的方法是用User类继承自BaseObservable,然后在getter上添加注解、在setter中添加notify方法,这感觉总是有点麻烦,步骤繁琐,于是,Google推出ObservableFields类,使用它我们可以简化我们的Model类,如:

public class User{
    public final ObservableField<String> userName = new ObservableField<>();
    public final ObservableField<String> userPassword = new ObservableField<>();
}

没错刚刚那一堆代码就变成了两行,然后通过:

User user = new User();
user.userName.set("sunzxyong");
user.userPassword.set("12345678");
String userName = user.userName.get();
String userPassword = user.userPassword.get();

来设置和获取数据,这样就简便多了。于是onCreate()方法中的代码就变成了这样:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        final User user = new User();
        user.userName.set("sunzxyong");
        user.userPassword.set("12345678");
        mBinding.setUser(user);
        mBinding.btn.setOnClickListener(new android.view.View.OnClickListener() {
            @Override
            public void onClick(android.view.View v) {
                user.userName.set("hello");
                user.userPassword.set("87654321");
            }
        });
    }

我们还是来看一下效果:



UI还是正常更新数据。

当然ObservableField<T>中传入的泛型可以是java中的基本类型,当然我们还可以使用 ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, ObservableParcelable等具体的类型,效果也和ObservableField<T>是一样的,如:

public class User{
    public final ObservableField<String> userName = new ObservableField<>();
    public final ObservableField<Integer> userPassword = new ObservableField<>();
    public final ObservableInt userAge = new ObservableInt();
}

Observable Collections

Google也为我们提供了一些通知类型的集合,有这三种:ObservableArrayList<T>、ObservableArrayMap<K,V>、ObservableMap<K,V>,它和平场使用的List、Map用法一样,但是多了通知功能。

我们在layout中的<data>区域导入包后就可以直接用它了,当它内部的数据发生改变时就自动会通知UI界面更新。如:

<data>
        <import type="android.databinding.ObservableMap"/>
        <import type="com.sunzxyong.binding.Keys"/>
        <variable
            name="map"
            type="ObservableMap&lt;String,Object>"/>
</data>
......
<TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map[Keys.name]}" />

<TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(1+(Integer)map[Keys.age])}" />

我来看一个Demo,使用ObservableMap<K,V>,当map中的数据改变时候同时也通知了UI界面更新。

xml布局为:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="android.databinding.ObservableMap"/>
        <import type="com.sunzxyong.binding.Keys"/>
        <variable
            name="map"
            type="ObservableMap&lt;String,Object>"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map[Keys.name]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(1+(Integer)map[Keys.age])}" />
        <Button
            android:id="@+id/btn"
            android:layout_marginTop="30dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="changeData" />
    </LinearLayout>
</layout>

onCreate()方法:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        final ObservableMap<String, Object> map = new ObservableArrayMap<>();
        map.put("name", "sunzxyong");
        map.put("age", 22);
        mBinding.setMap(map);

        mBinding.btn.setOnClickListener(new android.view.View.OnClickListener() {
            @Override
            public void onClick(android.view.View v) {
                map.put("name","hello");
                map.put("age",20);
            }
        });

Keys类:

public class Keys{
    public static final String name = "name";
    public static final String age = "age";
}

效果:

创建Binding类

我们将数据绑定到xml文件后,Binding类是自动根据xml布局文件名生成的(继承自android.databinding.ViewDataBinding),我们创建Binding对象一般有以下几种方法:

  1. 直接使用自动创建的Binding类创建
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
  1. 绑定根布局View
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
  1. 使用DataBindingUtil创建
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

Dynamic Variables动态变量

根据Google官方文档:

At times, the specific binding class won’t be known. For example, a RecyclerView.Adapter operating against arbitrary layouts won’t know the specific binding class. It still must assign the binding value during the onBindViewHolder(VH, int).

说明在使用ListView、GridView、RecyclerView的时候,由于绑定的类不能确定,比如RecyclerView只有在onBindViewHolder()方法中才能确定绑定的Item,所以我们只有在该办法中动态得到Binding Class(ViewModel)、动态绑定数据。方法是:

1、先创建好一个item布局,在布局中绑定数据:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="user"
            type="com.sunzxyong.binding.model.User"/>
    </data>

    <LinearLayout
        ...>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.userName}"
            android:textSize="20sp"
            android:textColor="#ffffff" />
...
    </LinearLayout>
</layout>

2、创建ViewHolder时定义一个和item布局 对应的Binding 对象,通过getter和setter对这个Binding对象操作:

public class BindingHolder extends RecyclerView.ViewHolder {
    private RecyclerItemBinding binding;

    public BindingHolder(View itemView) {
        super(itemView);
    }

    public RecyclerItemBinding getBinding() {
        return binding;
    }

    public void setBinding(RecyclerItemBinding binding) {
        this.binding = binding;
    }
}

3、在Adapter中onCreateViewHolder()方法中使用DataBindingUtil.inflate()创建Binding 对象,然后创建一个ViewHolder对象,通过ViewHolder的set把Binding对象设置进去:

RecyclerItemBinding mItemBinding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.recycler_item, parent, false);
BindingHolder mHolder = new BindingHolder(mItemBinding.getRoot());
mHolder.setBinding(mItemBinding);//把mItemBinding设置给ViewHolder

4、在onBindViewHolder()方法中通过holder的getBinding()方法得到item对应的Binding 对象,再设置数据:

//通过holder.getBinding()得到Binding Class
User user = users.get(position);
holder.getBinding().setVariable(com.sunzxyong.binding.BR.user,user);
holder.getBinding().executePendingBindings();//立即更新UI

好了Data Binding的高级用法就讲完了,下一篇我们通过一个Demo来看看怎么整体使用Data Binding。。。

Android Data Binding高级用法-Observable、动态生成Binding Class(三)的更多相关文章

  1. Autofac高级用法之动态代理

    前言 Autofac的DynamicProxy来自老牌的Castle项目.DynamicProxy(以下称为动态代理)起作用主要是为我们的类生成一个代理类,这个代理类可以在我们调用原本类的方法之前,调 ...

  2. 【转】Autofac高级用法之动态代理

    原文:http://www.cnblogs.com/stulzq/p/8547839.html 前言 Autofac的DynamicProxy来自老牌的Castle项目.DynamicProxy(以下 ...

  3. Android Studio @Bind的用法,自动生成findViewById无需再实例化控件

    第一步:app 的build.gradle文件中添加 如下代码: compile 'com.jakewharton:butterknife:7.0.0' 点击Sync Now 同步下载第二步:安装插件 ...

  4. SQL Server分区动态生成脚本(三)(按年份划分)

    --生成分区脚本DECLARE @DataBaseName NVARCHAR(50)--数据库名称DECLARE @TableName NVARCHAR(50)--表名称DECLARE @Column ...

  5. Android Data Binding代码实践(告别findViewById)(四)

    Data Binding实战(一) Data Binding语法解析(二) Data Binding高级用法(三) 好了,继前三篇学习了Data Binding之后,我们可以发现它的强大之处有这么几点 ...

  6. android data binding jetpack II 动态数据更新

    android data binding jetpack VIII BindingConversion android data binding jetpack VII @BindingAdapter ...

  7. android -------- Data Binding的使用(三)Observable

    解决:databinding 中 ViewModel数据发生改变,View中也要改变(实时更新) BaseObservable 在ViewModel 中可以继承 BaseObservable publ ...

  8. 完全掌握Android Data Binding

    转载:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0603/2992.html 来源 https://github.com/L ...

  9. 精通 Android Data Binding

    转自:https://github.com/LyndonChin/MasteringAndroidDataBinding 官方虽然已经给出了教程 - Data Binding Guide (中文版 - ...

随机推荐

  1. Microsoft Dynamics CRM 2011 当您在 大型数据集上执行 RetrieveMultiple 查询很慢的解决方法

    症状 当您在 Microsoft Dynamics CRM 2011 年大型数据集上执行 RetrieveMultiple 查询时,您会比较慢. 原因 发生此问题是因为大型数据集缓存 Retrieve ...

  2. 2.Cocos2d-x-3.2编写3d打飞机,项目代码总结

    1.AppDelete中applicationDidFinishLaunching代码示范 2.当电话来了时,停止恢复游戏声音的代码(在AppDelegate中加入下面代码) boolAppDel ...

  3. 搜索----Android Demo

    在前面的博客中,小编简单的介绍了,点击发现按钮,自动加载热门的相关数据,成长的脚步从不停歇,完成了发现的功能,今天我们来简单看一下如何在搜索栏中输入关键字,搜索出我们所需要的信息,今天这篇博文小编就简 ...

  4. Ubuntu 12.04: How to enable root login

    1. vi /etc/lightdm/lightdm.conf and add following modifications. greeter-show-manual-login=true allo ...

  5. linux后台运行程序(secureCRT断掉或关闭后继续运行程序)

    secureCRT断掉后想继续运行程序.也就是程序会在后台一直执行到结束. secureCRT通过ssh联接到client端运行程序,如果关掉窗口或者断开连接时,任务会被kill. 为了程序能够后台执 ...

  6. Java-IO之ByteArrayOutputStream

    ByteArrayOutputSTream是字节数组输出流,继承于OutputStream.ByteArrayOutputStream中的数据被写入到一个byte数组中,缓冲区会随着数据的不断写入而自 ...

  7. iOS中发送短信/发送邮件的实现 韩俊强的博客

    需要引入框架: MessageUI.framework 布局如下: 短信和邮件: #import "ViewController.h" #import <MessageUI/ ...

  8. Java:将字符串中的数字转换成整型

    在C语言中,将字符串中的数字转换为整型的方法是是利用atoi这个函数.在Java中,我们可以利用parseInt方法来实现,具体代码如下: public class HelloWorld { publ ...

  9. The Singularity is Near---预测人工智能,科技走向的神书---奇点临近

    比尔盖茨评价本文作者: 雷·库兹韦尔是我所知道的预测人工智能未来最权威的人.他的这本耐人寻味的书预测未来信息技术得到空前发展,将促使人类超越自身的生物极限--以我们无法想象的方式超越我们的生命. 中文 ...

  10. Leetcode_160_Intersection of Two Linked Lists

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/41593747 Intersection of Two Li ...