设置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. iOS集合视图单元格高亮和选中的区别

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交 ...

  2. Java中循环声明变量方法

    Java循环声明变量 之前想这样做,但是网上一直搜索不到,下面是我的方式 项目中 // 得到需要查询外表的数量,然后分别创建缓存,插入数据多的时候如果编码在缓存里面,就不需要再去查询数据库了.key: ...

  3. 应付模块的R12 TRACE 和 FND Debug 文件 / FND 日志 调试

     取得R12 TRACE: 1. 导航职责: 系统管理员> 配置文件> 系统> 查找 用户: 用户提交报表 配置: 初始化 SQL 语句 - 自定义 2. 点击用户栏位-编辑区域 ...

  4. 最简单的基于librtmp的示例:发布H.264(H.264通过RTMP发布)

    ===================================================== 最简单的基于libRTMP的示例系列文章列表: 最简单的基于librtmp的示例:接收(RT ...

  5. android开发之调试技巧

    我们都知道,android的调试打了断点之后运行时要使用debug as->android application 但是这样的运行效率非常低,那么我们有没有快速的方法呢? 当然有. 我们打完断点 ...

  6. Android简易实战教程--第二话《两种进度条》

    点击按钮模拟进度条下载进度,"下载"完成进度条消失. 代码如下: xml: <?xml version="1.0" encoding="utf- ...

  7. Android数据库框架——GreenDao轻量级的对象关系映射框架,永久告别sqlite

    Android数据库框架--GreenDao轻量级的对象关系映射框架,永久告别sqlite 前不久,我在写了ORMLite这个框架的博文 Android数据库框架--ORMLite轻量级的对象关系映射 ...

  8. ASP.NET实现网页版小优盘

    今天看到了一篇不错的文章,就拿来一起分享一下吧. 实现的是文件的上传与下载功能. 关于文件上传: 谈及文件上传到网站上,首先我们想到的就是通过什么上传呢?在ASP.NET中,只需要用FileUploa ...

  9. Spark1.4从HDFS读取文件运行Java语言WordCounts

    Hadoop:2.4.0 Spark:1.4.0 Ubuntu 14.0 1.首先启动Hadoop的HDFS系统.     HADOOP_HOME/sbin/start-dfs.sh 2.在Linux ...

  10. Spark Streaming + Flume整合官网文档阅读及运行示例

    1,基于Flume的Push模式(Flume-style Push-based Approach)      Flume被用于在Flume agents之间推送数据.在这种方式下,Spark Stre ...