zoukankan      html  css  js  c++  java
  • Android官方数据绑定框架DataBinding*

    数据绑定框架给我们带来了更大的方便性,以前我们可能需要在Activity里写很多的findViewById,烦人的代码也增加了我们代码的耦合性,现在我们马上就可以抛弃那么多的findViewById。说到这里,有人可能会有个疑问:我使用一些注解框架也可以不用findViewById啊,是的,但是注解注定要拖慢我们代码的速度,Data Binding则不会,官网文档说还会提高解析XML的速度,最主要的Data Binding并不是单单减少了我们的findViewById,更多好处请往下看文章。

    一、环境 
    在开始使用新东西之前,我们需要稍微的配置一下环境,这里要求你的Android Studio版本是1.3+,使用eclipse的同学暂时还没有办法使用该框架,请换用Android Studio。还有,在开始之前,请更新你的Support repository到最新的版本。 
    万事俱备,那我们就开始搭配环境!

    新建一个project,在dependencies中添加以下依赖

    新建module,并且在module的build.gradle的android标签中添加

    dataBinding {
        enabled = true
    }

    ok,到现在为止,我们的环境就准备完毕了,下面我们就开始Data Binding的学习啦。

    二、Data Binding尝试 
    在代码开始,我们并不直接进入新东西的讲解,而且以一段代码展现Data Binding的魅力。 
    首先我们需要一个Java bean,很简单,一个学生类。

    [java] view plain copy
     
     
    1. public class Student {  
    2.     private String name;  
    3.     private String addr;  
    4.   
    5.     public Student() {  
    6.     }  
    7.   
    8.     public Student(String name, String addr) {  
    9.         this.name = name;  
    10.         this.addr = addr;  
    11.     }  
    12.   
    13.     public String getName() {  
    14.         return name;  
    15.     }  
    16.   
    17.     public void setName(String name) {  
    18.         this.name = name;  
    19.     }  
    20.   
    21.     public String getAddr() {  
    22.         return this.addr;  
    23.     }  
    24.   
    25.     public void setAddr(String addr) {  
    26.         this.addr = addr;  
    27.     }  
    28. }  

    再来看看我们布局文件怎么写:

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data>  
    3.         <variable  
    4.             name="stu"  
    5.             type="org.loader.androiddatabinding.Student" />  
    6.     </data>  
    7.   
    8.     <LinearLayout  
    9.         android:orientation="vertical"  
    10.         android:layout_width="match_parent"  
    11.         android:layout_height="wrap_content">  
    12.         <TextView  
    13.             android:layout_width="wrap_content"  
    14.             android:layout_height="wrap_content"  
    15.             android:text="@{stu.name}"/>  
    16.   
    17.         <TextView  
    18.             android:layout_width="wrap_content"  
    19.             android:layout_height="wrap_content"  
    20.             android:text="@{stu.addr}"/>  
    21.     </LinearLayout>  
    22. </layout>  


    可以看到我们的xml布局和以前还有有一定的差别的,但是差别也不是很大。 
    最后来看看Activity怎么写。

    [java] view plain copy
     
     
    1. public class MainActivity extends AppCompatActivity {  
    2.   
    3.     @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         super.onCreate(savedInstanceState);  
    6.         ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);  
    7.         binding.setStu(new Student("loader", "山东莱芜"));  
    8.     }  
    9. }  

    Activity的代码非常简单,就添加了两行代码,而且,值得注意的是:我们并没有findViewById然后再去setText。 
    这段小代码运行的结果大家可能已经猜到了,就是在界面上显示loader山东莱芜两句话。

    )

    在看完小实例后,大家是不是感觉棒棒哒? 没有了之前的find控件,没有了setText,Activity代码更加简洁明了! 
    下面开始,我们进入Data Binding的学习!

    三、 初始Data Binding 
    上面的代码算是带领我们进入了Data Binding的世界,那我们先从布局文件开始入手Data Binding吧。再来看看上面的布局文件。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data>  
    3.         <variable  
    4.             name="stu"  
    5.             type="org.loader.androiddatabinding.Student" />  
    6.     </data>  
    7.     ...  
    8. </layout>  


    我们的根节点变成了layout,在layout的子节点中分成两部分,第一部分是data节点,第二部分才是我们之前的根节点,在data节点下我们又定义了一个variable, 
    从名称上看,这应该是一个变量,变量的名称是stu,类型是org.loader.androiddatabinding.Student,这类似我们在java文件中这么定义:

    [html] view plain copy
     
     
    1. org.loader.androiddatabinding.Student stu;  

    ok,这样很好理解了吧,不过这里要写Student完整的包名,一个还好,如果这里我们需要多个Student呢?要累死? NO,NO,NO,我们还可以向写java文件那样导入包。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data>  
    3.         <import type="org.loader.app2.Student" />  
    4.         <variable  
    5.             name="stu"  
    6.             type="Student" />  
    7.     </data>  
    8.     ...  
    9. </layout>  


    这样写,就类似于java的

    import org.loader.app2.Student;...Student stu;...

    既然变量我们定义好了,那该怎么使用呢?还是看上面的xml文件。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.   ...  
    3.   <LinearLayout  
    4.       android:orientation="vertical"  
    5.       android:layout_width="match_parent"  
    6.       android:layout_height="wrap_content">  
    7.       <TextView  
    8.           android:layout_width="wrap_content"  
    9.           android:layout_height="wrap_content"  
    10.           android:text="@{stu.name}"/>  
    11.   
    12.       <TextView  
    13.           android:layout_width="wrap_content"  
    14.           android:layout_height="wrap_content"  
    15.           android:text="@{stu.addr}"/>  
    16.   </LinearLayout>  
    17. </layout>  


    恩,注意看两个TextViewandroid:text,它的值是一个以@开始,以{}包裹的形式出现,而内容呢?是stu.name。stu就是我们上面定义的variable
    name还记得吗?是我们Student类中的一个变量。其实这里就会去调用stu.getName()方法。 
    好了,很快,我们就入门了Data Binding,下面让我们来多定义几个变量试试看。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data>  
    3.         <import type="org.loader.app2.Student" />  
    4.         <variable  
    5.             name="stu"  
    6.             type="Student" />  
    7.         <variable  
    8.             name="str"  
    9.             type="String"/>  
    10.         <variable  
    11.             name="error"  
    12.             type="boolean"/>  
    13.         <variable  
    14.             name="num"  
    15.             type="int" />  
    16.     </data>  
    17.   
    18.     <LinearLayout  
    19.         android:orientation="vertical"  
    20.         android:layout_width="match_parent"  
    21.         android:layout_height="wrap_content">  
    22.         <TextView  
    23.             android:layout_width="wrap_content"  
    24.             android:layout_height="wrap_content"  
    25.             android:text="@{stu.name}"/>  
    26.         <TextView  
    27.             android:layout_width="wrap_content"  
    28.             android:layout_height="wrap_content"  
    29.             android:text="@{str}"/>  
    30.         <TextView  
    31.             android:layout_width="wrap_content"  
    32.             android:layout_height="wrap_content"  
    33.             android:text="@{String.valueOf(num)}"/>  
    34.     </LinearLayout>  
    35. </layout>  


    来看看定义的变量,多了好几个,有一个String类型的变量我们并没有导包,这里说明一下,和在java里一样,java.lang包里的类,我们是可以不用导包的,再往下,一个booleanint类型的变量,都是java基本类型的,所以说嘛,在这里定义变量,你就想成是在java里定义就ok。 
    再来看看这几个TextView,第二个,我们直接使用@{str}来为android:text设置成上面定义个str的值,继续往下要注意了,我们使用了

    android:text="@{String.valueOf(num)}"

    来设置了一个int类型的变量,大家都知道我们在给android:text设置int类型的值时一定要转化为String类型,要不它就认为是资源文件了,这里我们还学到了一点,在xml中,我们不仅可以使用变量,而且还可以调用方法!

    四、 变量定义的高级部分 
    在上面,我们学会了如何去在xml中定义变量,但是不知道你发现没?我们没有定义像ListMap等这样的集合变量。那到底能不能定义呢?答案肯定是可以的,而且定义的方式和我们上面的基本一致,区别就在于我们还需要为它定义key的变量,例如:

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data>  
    3.         <import type="org.loader.app2.Student" />  
    4.         <import type="android.graphics.Bitmap" />  
    5.         <import type="java.util.ArrayList" />  
    6.         <import type="java.util.HashMap" />  
    7.         <variable  
    8.             name="stu"  
    9.             type="Student" />  
    10.         <variable  
    11.             name="str"  
    12.             type="String"/>  
    13.         <variable  
    14.             name="error"  
    15.             type="boolean"/>  
    16.         <variable  
    17.             name="num"  
    18.             type="int" />  
    19.         <variable  
    20.             name="list"  
    21.             type="ArrayList<String>" />  
    22.         <variable  
    23.             name="map"  
    24.             type="HashMap<String, String>" />  
    25.         <variable  
    26.             name="array"  
    27.             type="String[]" />  
    28.   
    29.         <variable  
    30.             name="listKey"  
    31.             type="int" />  
    32.         <variable  
    33.             name="mapKey"  
    34.             type="String" />  
    35.         <variable  
    36.             name="arrayKey"  
    37.             type="int" />  
    38.     </data>  
    39.   
    40.     <LinearLayout  
    41.         android:orientation="vertical"  
    42.         android:layout_width="match_parent"  
    43.         android:layout_height="wrap_content">  
    44.         <TextView  
    45.             android:layout_width="wrap_content"  
    46.             android:layout_height="wrap_content"  
    47.             android:text="@{stu.name}"/>  
    48.         <TextView  
    49.             android:layout_width="wrap_content"  
    50.             android:layout_height="wrap_content"  
    51.             android:text="@{str}"/>  
    52.         <TextView  
    53.             android:layout_width="wrap_content"  
    54.             android:layout_height="wrap_content"  
    55.             android:text="@{String.valueOf(num)}"/>  
    56.         <TextView  
    57.             android:layout_width="wrap_content"  
    58.             android:layout_height="wrap_content"  
    59.             android:text="@{list[listKey]}"/>  
    60.   
    61.         <TextView  
    62.             android:layout_width="wrap_content"  
    63.             android:layout_height="wrap_content"  
    64.             android:text="@{map[`name`]}"/>  
    65.         <TextView  
    66.             android:layout_width="wrap_content"  
    67.             android:layout_height="wrap_content"  
    68.             android:text="@{array[0]}"/>  
    69.     </LinearLayout>  
    70. </layout>  

    这段代码比较长,但是我们仅关心那几个集合和数组,可以看到我们定义集合和定义普通变量一样,只不过这里我们还指定了一些的泛型,例如:ArrayList&lt;String>。 
    下面我们还为下面使用这些集合准备了几个key,也都是变量。 
    继续看看怎么使用,和我们在java中使用不同,这里都是以:集合变量名[key]的形式使用,如果你的key是一个字面字符串可以使用反引号,也可以使用转义后的双引号。恩,这里也没有什么可以说的了,大家多看几遍就掌握了,都是概念性的东西,记住就ok。

    五、在java代码中使用 
    前面定义了这么多变量,但是我们还没有给他们赋值!在哪赋值呢?肯定是在java代码中使用了,大部分情况我们还是在Activity中去使用它,以前我们都是在onCreate方法中通过setContentView去设置布局,但现在不一样了,现在我们是用过DataBindingUtil类的一个静态方法setContentView设置布局,同时该方法会返回一个对象,什么对象?这个对象有点特殊,它是一个自动生成的类的对象,看下面:

    [java] view plain copy
     
     
    1. @Override  
    2.    protected void onCreate(Bundle savedInstanceState) {  
    3.        super.onCreate(savedInstanceState);  
    4.        ActivityMainBinding binding = DataBindingUtil.setContentView(this,  
    5.                R.layout.activity_main);  
    6.     }  


    看到ActivityMainBinding了吗?就是它!那自动生成有什么规则了没?当然有了,记好了:

    将我们布局文件的首字母大写,并且去掉下划线,将下划线后面的字母大写,加上Binding组成。

    看看上面的类,是不是符合这个规则。继续看看这个对象哪来的,是通过

    [java] view plain copy
     
     
    1. DataBindingUtil.setContentView(this, R.layout.activity_main);  


    返回的,DataBindingUtil.setContentView的两个参数分别是当前Activity和布局文件。那接下来,就是我们关心的给变量赋值了。

    [java] view plain copy
     
     
    1. @Override  
    2. protected void onCreate(Bundle savedInstanceState) {  
    3.    ...  
    4.     binding.setStu(new Student("loader"));  
    5.     binding.setStr("string");  
    6.     binding.setError(false);  
    7.   
    8.     ArrayList<String> list = new ArrayList<String>() {  
    9.         {  
    10.             add("arraylist");  
    11.         }  
    12.     };  
    13.     binding.setList(list);  
    14.     binding.setListKey(0);  
    15.   
    16.     HashMap<String, String> map = new HashMap<String, String>() {  
    17.         {  
    18.             put("name", "hashmap");  
    19.         }  
    20.     };  
    21.     binding.setMap(map);  
    22. //        binding.setMapKey("name");  
    23.   
    24.     String[] array = new String[1];  
    25.     array[0] = "array";  
    26.     binding.setArray(array);  
    27.     binding.setArrayKey(0);  
    28. }  

    一连串的binding.setXXX,这个XXX是什么呢?就是我们在xml中定义的那些变量首字母大写了!也没好好说的吧,多看几遍。

    六、 表达式 
    短暂的幸福时光,我们还是要告别java代码了,继续回到xml中,这一块,我们来学习一下表达式,什么?这玩意在xml中还支持表达式!

    [html] view plain copy
     
     
    1. <TextView  
    2.     android:layout_width="wrap_content"  
    3.     android:layout_height="wrap_content"  
    4.     android:text='@{error ? "error" : "ok"}'/>  


    还记得上面我们定义了一个boolean的变量没有用到,这里我们就用到了,看好android:text,这里是一个三元表达式,如果error是true,则text就是error,否则是ok。这里还支持null合并操作,什么是null合并,相信看一眼你就知道了

    [html] view plain copy
     
     
    1. android:text='@{str==null ?? "not null"}'  

    简单解释一下,如果str是null,text的值就是str本身,否则就是”not null”。 
    它还支持一下表达式:

    • Mathematical + - / * %
    • String concatenation +
    • Logical && ||
    • Binary & | ^
    • Unary + - ! ~
    • Shift >> >>> <<
    • Comparison == > < >= <=
    • instanceof
    • Grouping ()
    • Literals - character, String, numeric, null
    • Cast
    • Method calls
    • Field access
    • Array access []
    • Ternary operator ?:

    但是它不支持一下表达式:

    • this
    • super
    • new
    • Explicit generic invocation

    七、 其他遗漏点 
    说到这里,xml中的事情基本算完了,但是还有几个小地方没有说,顺便说一下。 
    1. 设置别名 
    假如我们import了两个相同名称的类咋办?别怕,别名来拯救你!例如:

    [html] view plain copy
     
     
    1. ...  
    2. <data>  
    3.   <import type="xxx.Name" alias="MyName">  
    4.   <import type="xxx.xx.Name">  
    5. </data>  
    6. <TextView xxx:@{MyName.getName()}>  
    7. <TextView xxx:@{Name.getName()}>  
    8. ...  
    1. 自定义Binding名称 
      还记得系统为我们生成好的那个binding类名吗?如果只能使用那样的是不是有点太暴力了?好在google对我们还算友好了,允许我们自定义binding名称,定制名称也很简单,就是给data一个class字段就ok。 
      例如:

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

    那么:DataBindingUtils.setContentView返回的binding类就是:你的应用包名.Custom

    八、事件绑定 
    大家都知道,在xml中我们可以给button设置一个onClick来达到事件的绑定,现在DataBinding也提供了事件绑定,而且不仅仅是button。 
    来看一下:

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data>  
    3.         <import type="org.loader.app3.EventHandlers" />  
    4.         <variable  
    5.             name="handlers"  
    6.             type="EventHandlers" />  
    7.     </data>  
    8.   
    9.     <LinearLayout  
    10.         android:layout_width="wrap_content"  
    11.         android:layout_height="wrap_content"  
    12.         android:orientation="vertical">  
    13.         <TextView  
    14.             android:layout_width="wrap_content"  
    15.             android:layout_height="wrap_content"  
    16.             android:text="CLICK ME"  
    17.             android:onClick="@{handlers.handleClick}"/>  
    18.     </LinearLayout>  
    19. </layout>  


    定义了一个EventHandlers类型的handlers变量,并在onClick的时候执行EventHandlershandleClick方法。 
    继续看看EventHandlers是怎么写的。

    [java] view plain copy
     
     
    1. public class EventHandlers {  
    2.     public void handleClick(View view) {  
    3.         Toast.makeText(view.getContext(), "you clicked the view", Toast.LENGTH_LONG).show();  
    4.     }  
    5. }  

    很简单,就是简单的Toast了一下,这里要注意的是,handlerClick方法需要一个View的参数。

    九、 数据对象 
    我们学会了通过binding为我们的变量设置数据,但是不知道你有没有发现一个问题,当我们数据改变的时候会怎样?数据是跟随着改变呢?还是原来的数据呢?这里告诉你答案:很不幸,显示的还是原来的数据?那有没有办法让数据源发生变化后显示的数据也随之发生变化?先来想想ListView是怎么做的, ListView的数据是通过Adapter提供的,当数据发生改变时,我们通过notifyDatasetChanged通过UI去改变数据,这里面的原理其实就是内容观察者,庆幸的是DataBinding也支持内容观察者,而且使用起来也相当方便!

    BaseObservable 
    我们可以通过Observable的方式去通知UI数据已经改变了,当然了,官方为我们提供了更加简便的方式BaseObservable,我们的实体类只需要继承该类,稍做几个操作,就能轻松实现数据变化的通知。如何使用呢? 首先我们的实体类要继承BaseObservale类,第二步在Getter上使用注解@Bindable,第三步,在Setter里调用方法notifyPropertyChanged,第四步,完成。就是这么简单,下面我们来实际操作一下。 
    首先定义一个实体类,并继承BaseObservable

    [java] view plain copy
     
     
    1. public class Student extends BaseObservable {  
    2.     private String name;  
    3.   
    4.     public Student() {  
    5.     }  
    6.   
    7.     public Student(String name) {  
    8.         this.name = name;  
    9.     }  
    10.   
    11.     @Bindable  
    12.     public String getName() {  
    13.         return name;  
    14.     }  
    15.   
    16.     public void setName(String name) {  
    17.         this.name = name;  
    18.         notifyPropertyChanged(org.loader.app4.BR.name);  
    19.     }  
    20. }  


    观察getName方法,我们使用了@Bindable注解,观察setName,我们调用了notifyPropertyChanged方法,这个方法还需要一个参数,这里参数类似于R.java,保存了我们所有变量的引用地址,这里我们使用了name。 
    再来看看布局文件。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data class=".Custom">  
    3.         <import type="org.loader.app4.Student" />  
    4.         <variable  
    5.             name="stu"  
    6.             type="Student"/>  
    7.         <variable  
    8.             name="click"  
    9.             type="org.loader.app4.MainActivity" />  
    10.     </data>  
    11.     <TextView  
    12.         android:layout_width="wrap_content"  
    13.         android:layout_height="wrap_content"  
    14.         android:onClick="@{click.click}"  
    15.         android:text="@{stu.name}"/>  
    16. </layout>  


    不多说了,我们给TextView设置了文本,还有点击事件。Activity,

    [java] view plain copy
     
     
    1. public class MainActivity extends AppCompatActivity {  
    2.   
    3.     private Student mStu;  
    4.   
    5.     @Override  
    6.     protected void onCreate(Bundle savedInstanceState) {  
    7.         super.onCreate(savedInstanceState);  
    8.         org.loader.app4.Custom binding = DataBindingUtil.setContentView(this,  
    9.                 R.layout.activity_main);  
    10.         mStu = new Student("loader");  
    11.         binding.setStu(mStu);  
    12.         binding.setClick(this);  
    13.     }  
    14.   
    15.     public void click(View view) {  
    16.         mStu.setName("qibin");  
    17.     }  
    18. }  

    这段代码,首先显示的是loader,当我们点击TextView时,界面换成qibin。

    ObservableFields家族 
    上面使用BaseObservable已经非常容易了,但是google工程师还不满足,继续给我们封装了一系列的ObservableFields,这里有ObservableFieldObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableParcelable
    ObservableFields的使用方法就更加简单了,例如下面代码,

    [java] view plain copy
     
     
    1. public class People {  
    2.     public ObservableField<String> name = new ObservableField<>();  
    3.     public ObservableInt age = new ObservableInt();  
    4.     public ObservableBoolean isMan = new ObservableBoolean();  
    5. }  


    很简单,只有三个ObservableField变量,并且没有getter和setter,因为我们不需要getter和setter。 
    在xml中怎么使用呢?

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data class=".Custom">  
    3.   
    4.         <variable  
    5.             name="people"  
    6.             type="org.loader.app4.People" />  
    7.     </data>  
    8.     <LinearLayout  
    9.         android:layout_width="wrap_content"  
    10.         android:layout_height="wrap_content"  
    11.         android:orientation="vertical">  
    12.         <TextView  
    13.             android:layout_width="wrap_content"  
    14.             android:layout_height="wrap_content"  
    15.             android:text="@{people.name}"/>  
    16.   
    17.         <TextView  
    18.             android:layout_width="wrap_content"  
    19.             android:layout_height="wrap_content"  
    20.             android:text="@{String.valueOf(people.age)}"/>  
    21.         <TextView  
    22.             android:layout_width="wrap_content"  
    23.             android:layout_height="wrap_content"  
    24.             android:text='@{people.isMan ? "man" : "women"}'/>  
    25.     </LinearLayout>  
    26. </layout>  


    也很简单,直接使用变量,那怎么赋值和取值呢?这些ObservableField都会有一对getset方法,所以使用起来也很方便了:

    [java] view plain copy
     
     
    1. ...  
    2. mPeople = new People();  
    3. binding.setPeople(mPeople);  
    4. mPeople.name.set("people");  
    5. mPeople.age.set(19);  
    6. mPeople.isMan.set(true);  
    7. ...  

    也不多说了。

    Observable Collections 
    既然普通的变量我们有了ObservableFields的分装,那集合呢?当然也有啦,来看着两个:ObservableArrayMap,ObservableArrayList。使用和普通的Map、List基本相同,直接看代码:

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data class=".Custom">  
    3.         <variable  
    4.             name="map"  
    5.             type="android.databinding.ObservableArrayMap<String,String>" />  
    6.         <variable  
    7.             name="list"  
    8.             type="android.databinding.ObservableArrayList<String>" />  
    9.     </data>  
    10.     <LinearLayout  
    11.         android:layout_width="wrap_content"  
    12.         android:layout_height="wrap_content"  
    13.         android:orientation="vertical">  
    14.         <TextView  
    15.             android:layout_width="wrap_content"  
    16.             android:layout_height="wrap_content"  
    17.             android:text="@{map[`name`]}"/>  
    18.         <TextView  
    19.             android:layout_width="wrap_content"  
    20.             android:layout_height="wrap_content"  
    21.             android:text="@{list[0]}"/>  
    22.     </LinearLayout>  
    23. </layout>  


    在布局中,使用方式和普通的集合一样,如果看不太懂,可以往上翻博客,看上面的集合是怎么使用的。 
    在来看java文件,怎么设置数据,

    [java] view plain copy
     
     
    1. ObservableArrayMap<String, String> map = new ObservableArrayMap<>();  
    2. ObservableArrayList<String> list = new ObservableArrayList<>();  
    3. map.put("name", "loader or qibin");  
    4. list.add("loader!!!");  
    5. binding.setMap(map);  
    6. binding.setList(list);  


    太简单了,简直和ListMap使用方法一模一样!!! 

    十、inflate 
    不知道大家注意没有,上面的代码我们都是在activity中通过DataBindingUtil.setContentView来加载的布局的,现在有个问题了,如果我们是在Fragment中使用呢?Fragment没有setContentView怎么办?不要着急,Data Binding也提供了inflate的支持! 
    使用方法如下,大家肯定会觉得非常眼熟。

    [java] view plain copy
     
     
    1. MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);  
    2. MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);  


    接下来,我们就尝试着在Fragment中使用一下Data Binding吧。 
    首先还是那个学生类,Student

    [java] view plain copy
     
     
    1. public class Student extends BaseObservable {  
    2.     private String name;  
    3.     private int age;  
    4.   
    5.     public Student() {  
    6.     }  
    7.   
    8.     public Student(int age, String name) {  
    9.         this.age = age;  
    10.         this.name = name;  
    11.     }  
    12.   
    13.     @Bindable  
    14.     public int getAge() {  
    15.         return age;  
    16.     }  
    17.   
    18.     public void setAge(int age) {  
    19.         this.age = age;  
    20.         notifyPropertyChanged(org.loader.app5.BR.age);  
    21.     }  
    22.   
    23.     @Bindable  
    24.     public String getName() {  
    25.         return name;  
    26.     }  
    27.   
    28.     public void setName(String name) {  
    29.         this.name = name;  
    30.         notifyPropertyChanged(org.loader.app5.BR.name);  
    31.     }  
    32. }  


    继续,activity的布局

    [html] view plain copy
     
     
    1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.     tools:context=".MainActivity">  
    6.   
    7.     <FrameLayout  
    8.         android:id="@+id/container"  
    9.         android:layout_width="wrap_content"  
    10.         android:layout_height="wrap_content"/>  
    11.   
    12. </RelativeLayout>  


    activity的代码,

    [java] view plain copy
     
     
    1. public class MainActivity extends AppCompatActivity {  
    2.   
    3.     @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         super.onCreate(savedInstanceState);  
    6.         setContentView(R.layout.activity_main);  
    7.         getSupportFragmentManager().beginTransaction()  
    8.                 .replace(R.id.container, new MyFragment()).commit();  
    9.     }  
    10. }  

    重点来了,我们这里data binding的操作都放在了fragment里,那么我们先来看看fragment的布局。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data class=".Custom">  
    3.         <import type="org.loader.app5.Student" />  
    4.         <variable  
    5.             name="stu"  
    6.             type="Student" />  
    7.         <variable  
    8.             name="frag"  
    9.             type="org.loader.app5.MyFragment" />  
    10.     </data>  
    11.   
    12.     <LinearLayout  
    13.         android:orientation="vertical"  
    14.         android:layout_width="match_parent"  
    15.         android:layout_height="wrap_content">  
    16.         <TextView  
    17.             android:layout_width="wrap_content"  
    18.             android:layout_height="wrap_content"  
    19.             android:onClick="@{frag.click}"  
    20.             android:text="@{stu.name}"/>  
    21.   
    22.         <TextView  
    23.             android:layout_width="wrap_content"  
    24.             android:layout_height="wrap_content"  
    25.             android:text="@{String.valueOf(stu.age)}"/>  
    26.     </LinearLayout>  
    27. </layout>  

    两个TextView分别绑定了Student的name和age字段,而且给name添加了一个点击事件,点击后会调用Fragment的click方法。我们来迫不及待的看一下Fragment怎么写:

    [java] view plain copy
     
     
    1. public class MyFragment extends Fragment {  
    2.   
    3.     private Student mStu;  
    4.   
    5.     @Nullable  
    6.     @Override  
    7.     public View onCreateView(LayoutInflater inflater,  
    8.                              ViewGroup container, Bundle savedInstanceState) {  
    9.         org.loader.app5.Custom binding = DataBindingUtil.inflate(inflater,  
    10.                 R.layout.frag_layout, container, false);  
    11.         mStu = new Student(20, "loader");  
    12.         binding.setStu(mStu);  
    13.         binding.setFrag(this);  
    14.         return binding.getRoot();  
    15.     }  
    16.   
    17.     public void click(View view) {  
    18.         mStu.setName("qibin");  
    19.         mStu.setAge(18);  
    20.     }  
    21. }  

    onCreateView中,不同于在Activity中,这里我们使用了DataBindingUtil.inflate方法,接受4个参数,第一个参数是一个LayoutInflater对象,正好,我们这里可以使用onCreateView的第一个参数,第二个参数是我们的布局文件,第三个参数是一个ViewGroup,第四个参数是一个boolean类型的,和在LayoutInflater.inflate一样,后两个参数决定了是否想Container中添加我们加载进来的布局。 
    下面的代码和我们之前写的并无差别,但是有一点,onCreateView方法需要返回一个View对象,我们从哪获取呢?ViewDataBinding有一个方法getRoot可以获取我们加载的布局,是不是很简单? 
    来看一下效果:

    十一、 Data Binding VS RecyclerView 
    有了上面的思路,大家是不是也会在ListView和RecyclerView中使用了?我们仅以一个RecyclerView来学习一下。 
    首先来看看item的布局,

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.   
    3.     <data>  
    4.         <variable  
    5.             name="stu"  
    6.             type="org.loader.app6.Student" />  
    7.     </data>  
    8.   
    9.     <RelativeLayout  
    10.         android:layout_width="match_parent"  
    11.         android:layout_height="match_parent">  
    12.   
    13.         <TextView  
    14.             android:layout_width="wrap_content"  
    15.             android:layout_height="wrap_content"  
    16.             android:text="@{stu.name}"  
    17.             android:layout_alignParentLeft="true"/>  
    18.   
    19.         <TextView  
    20.             android:layout_width="wrap_content"  
    21.             android:layout_height="wrap_content"  
    22.             android:text="@{String.valueOf(stu.age)}"  
    23.             android:layout_alignParentRight="true"/>  
    24.   
    25.     </RelativeLayout>  
    26. </layout>  


    可以看到,还是用了那个Student实体,这样得代码,相信你也已经看烦了吧。 
    那我们来看看activity的。

    [java] view plain copy
     
     
    1. private RecyclerView mRecyclerView;  
    2. private ArrayList<Student> mData = new ArrayList<Student>() {  
    3.     {  
    4.         for (int i=0;i<10;i++) add(new Student("loader" + i, 18 + i));  
    5.     }  
    6. };  
    7.   
    8. @Override  
    9. protected void onCreate(Bundle savedInstanceState) {  
    10.     super.onCreate(savedInstanceState);  
    11.     setContentView(R.layout.activity_main);  
    12.   
    13.     mRecyclerView = (RecyclerView) findViewById(R.id.recycler);  
    14.     mRecyclerView.setLayoutManager(new LinearLayoutManager(this,  
    15.             LinearLayoutManager.VERTICAL, false));  
    16.     mRecyclerView.setAdapter(new MyAdapter(mData));  
    17. }  


    这里给RecyclerView设置了一个Adapter,相信最主要的代码就在这个Adapter里。

    [java] view plain copy
     
     
    1. private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {  
    2.   
    3.     private ArrayList<Student> mData = new ArrayList<>();  
    4.   
    5.     private MyAdapter(ArrayList<Student> data) {  
    6.         mData.addAll(data);  
    7.     }  
    8.   
    9.     @Override  
    10.     public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {  
    11.         ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater  
    12.                 .from(viewGroup.getContext()), R.layout.item, viewGroup, false);  
    13.         ViewHolder holder = new ViewHolder(binding.getRoot());  
    14.         holder.setBinding(binding);  
    15.         return holder;  
    16.     }  
    17.   
    18.     @Override  
    19.     public void onBindViewHolder(ViewHolder viewHolder, int i) {  
    20.         viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));  
    21.         viewHolder.getBinding().executePendingBindings();  
    22.     }  
    23.   
    24.     @Override  
    25.     public int getItemCount() {  
    26.         return mData.size();  
    27.     }  
    28.   
    29.     class ViewHolder extends RecyclerView.ViewHolder {  
    30.   
    31.         private ViewDataBinding binding;  
    32.   
    33.         public ViewHolder(View itemView) {  
    34.             super(itemView);  
    35.         }  
    36.   
    37.         public void setBinding(ViewDataBinding binding) {  
    38.             this.binding = binding;  
    39.         }  
    40.   
    41.         public ViewDataBinding getBinding() {  
    42.             return this.binding;  
    43.         }  
    44.     }  


    果然,这个adapter的写法和我们之前的写法不太一样,首先看看ViewHolder,在这个holder里,我们保存了一个ViewDataBinding对象,并给它提供了GetterSetter方法, 这个ViewDataBinding是干嘛的?我们稍后去讲。继续看看onCreateViewHolder,在这里面,我们首先调用DataBindingUtil.inflate方法返回了一个ViewDataBinding的对象,这个ViewDataBinding是个啥?我们以前没见过啊,这里告诉大家我们之前返回的那些都是ViewDataBinding的子类!继续看代码,我们new了一个holder,参数是肯定是我们的item布局了,继续看,接着我们又把binding设置给了holder,最后返回holder。这时候,我们的holder里就保存了刚刚返回的ViewDataBinding对象,干嘛用呢?继续看onBindViewHolder就知道了。

    [java] view plain copy
     
     
    1. @Override  
    2. public void onBindViewHolder(ViewHolder viewHolder, int i) {  
    3.     viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));  
    4.     viewHolder.getBinding().executePendingBindings();  
    5. }  


    只有两行代码,但是都是我们没有见过的,首先第一行,我们以前都是使用类似binding.setStu这样方法去设置变量,那这个setVariable呢? 为什么没有setStu,这里要记住,ViewDataBinding是我们之前用的那些binding的父类,只有自动生成的那些子类才会有setXXX方法,那现在我们需要在ViewDataBinding中设置变量咋办?这个类为我们提供了setVariable去设置变量,第一个参数是我们的变量名的引用,第二个是我们要设置的值。第二行代码,executePendingBindings的作用是干嘛的?官方的回答是:
    当数据改变时,binding会在下一帧去改变数据,如果我们需要立即改变,就去调用executePendingBindings方法。

    所以这里的作用就是去让数据的改变立即执行。 
    ok,现在看起来,我们的代码更加简洁了,而且不需要保存控件的实例,是不是很爽? 来看看效果:

    十二、 View with ID 
    在使用Data Binding的过程中,我们发现并没有保存View的实例,但是现在我们有需求需要这个View的实例咋办?难道走老路findViewById?当然不是啦,当我们需要某个view的实例时,我们只要给该view一个id,然后Data Binding框架就会给我们自动生成该view的实例,放哪了?当然是ViewDataBinding里面。 
    上代码:

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data class=".Custom">  
    3.         <variable  
    4.             name="str"  
    5.             type="android.databinding.ObservableField<String>" />  
    6.         <variable  
    7.             name="handler"  
    8.             type="org.loader.app7.MainActivity" />  
    9.     </data>  
    10.   
    11.     <TextView  
    12.         android:id="@+id/textView"  
    13.         android:layout_width="match_parent"  
    14.         android:layout_height="wrap_content"  
    15.         android:text="@{str.get}"  
    16.         android:onClick="@{handler.click}"/>  
    17. </layout>  


    xml中代码没有什么好说的,都是之前的代码,如果在这有点迷糊,建议你还是回头看看上篇博客。需要注意的是, 
    我们给TextView了一个id-textView。 
    activity,

    [java] view plain copy
     
     
    1. public class MainActivity extends AppCompatActivity {  
    2.   
    3.     private org.loader.app7.Custom mBinding;  
    4.     private ObservableField<String> mString;  
    5.   
    6.     @Override  
    7.     protected void onCreate(Bundle savedInstanceState) {  
    8.         super.onCreate(savedInstanceState);  
    9.         mBinding = DataBindingUtil.setContentView(this,  
    10.                 R.layout.activity_main);  
    11.         mString = new ObservableField<String>();  
    12.         mString.set("loader");  
    13.         mBinding.setStr(mString);  
    14.         mBinding.setHandler(this);  
    15.     }  
    16.   
    17.     public void click(View view) {  
    18.         mString.set("qibin");  
    19.         mBinding.textView.setTextColor(Color.GREEN);  
    20.     }  
    21. }  

    通过ViewDataBinding类的实例直接去获取的。

    只要我们给了view一个id,那么框架就会在ViewDataBinding中自动帮我们保存这个view的实例,变量名就是我们设置的id。

    十三、 自定义setter 
    想想这样的一种情景,一个ImageView需要通过网络去加载图片,那我们怎么办?看似好像使用DataBinding不行,恩,我们上面所学到东西确实不能够解决这个问题,但是DataBinding框架给我们提供了很好的扩展,允许我们自定义setter,那该怎么做呢?这里就要引出另一个知识点——BindingAdapter,这是一个注解,参数是一个数组,数组中存放的是我们自定义的’属性’。接下来就以一个例子学习一下BindingAdapter的使用。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:app="http://schemas.android.com/apk/res-auto">  
    3.     <data class=".Custom">  
    4.         <variable  
    5.             name="imageUrl"  
    6.             type="String" />  
    7.     </data>  
    8.   
    9.     <ImageView  
    10.         android:layout_width="match_parent"  
    11.         android:layout_height="wrap_content"  
    12.         app:image="@{imageUrl}"/>  
    13. </layout>  


    这里我们增加了一个命名空间app,并且注意ImageView的app:image属性,这里和我们自定义view时自定义的属性一样,但是这里并不需要我们去重写ImageView,这条属性的值是我们上面定义的String类型的imageUrl,从名称中看到这里我们可能会塞给他一个url。 
    activity,

    [java] view plain copy
     
     
    1. public class MainActivity extends AppCompatActivity {  
    2.   
    3.     @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         super.onCreate(savedInstanceState);  
    6.         org.loader.app8.Custom binding = DataBindingUtil.setContentView(this,  
    7.                 R.layout.activity_main);  
    8.         binding.setImageUrl("http://images.csdn.net/20150810/Blog-Image%E5%89%AF%E6%9C%AC.jpg");  
    9.     }  
    10. }  


    果然在这里我们set了一个url,那图片怎么加载呢?这里就要使用到我们刚才说的BindingAdapter注解了。

    [java] view plain copy
     
     
    1. public class Utils {  
    2.     @BindingAdapter({"bind:image"})  
    3.     public static void imageLoader(ImageView imageView, String url) {  
    4.         ImageLoaderUtils.getInstance().displayImage(url, imageView);  
    5.     }  
    6. }  

    我们定义了一个Utils类,这个类你可以随便起名,该类中只有一个静态的方法imageLoader,该方法有两个参数,一个是需要设置数据的view, 
    一个是我们需要的url。值得注意的是那个BindingAdapter注解,看看他的参数,是一个数组,内容只有一个bind:image,仅仅几行代码,我们不需要 
    手工调用Utils.imageLoader,也不需要知道imageLoader方法定义到哪了,一个网络图片加载就搞定了,是不是很神奇,这里面起关键作用的就是BindingAdapter 
    注解了,来看看它的参数怎么定义的吧,难道是乱写?当然不是,这里要遵循一定的规则,

    以bind:开头,接着书写你在控件中使用的自定义属性名称。

    这里就是image了,不信来看。

    [html] view plain copy
     
     
    1. <ImageView  
    2.     android:layout_width="match_parent"  
    3.     android:layout_height="wrap_content"  
    4.     app:image="@{imageUrl}"/>  

    看看运行结果:

    十四、 Converters 
    Converter是什么呢?举个例子吧:假如你的控件需要一个格式化好的时间,但是你只有一个Date类型额变量咋办?肯定有人会说这个简单,转化完成后在设置,恩,这也是一种办法,但是DataBinding还给我们提供了另外一种方式,虽然原理一样,但是这种方式使用的场景更多,那就是——Converter。和上面的BindingAdapter使用方法一样,这也是一个注解。下面还是以一段代码的形式进行学习。

    [html] view plain copy
     
     
    1. <layout xmlns:android="http://schemas.android.com/apk/res/android">  
    2.     <data class=".Custom">  
    3.         <variable  
    4.             name="time"  
    5.             type="java.util.Date" />  
    6.     </data>  
    7.   
    8.     <TextView  
    9.         android:layout_width="match_parent"  
    10.         android:layout_height="wrap_content"  
    11.         android:text="@{time}"/>  
    12. </layout>  


    看TextView的text属性,我们需要一个String类型的值,但是这里确给了一个Date类型的,这就需要我们去定义Converter去转换它, 
    activity,

    [java] view plain copy
     
     
    1. public class MainActivity extends AppCompatActivity {  
    2.   
    3.     @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         super.onCreate(savedInstanceState);  
    6.         org.loader.app9.Custom binding = DataBindingUtil.setContentView(this,  
    7.                 R.layout.activity_main);  
    8.         binding.setTime(new Date());  
    9.     }  
    10. }  


    去给这个Date类型的变量设置值。怎么去定义Converter呢? 看代码:

    [java] view plain copy
     
     
    1. public class Utils {  
    2.   
    3.     @BindingConversion  
    4.     public static String convertDate(Date date) {  
    5.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
    6.         return sdf.format(date);  
    7.     }  
    8. }  

    和上面一样,我们不需要关心这个convertDate在哪个类中,重要的是他的@BindingConversion注解,这个方法接受一个Date类型的变量,正好我们的android:text设置的就是一个Date类型的值,在方法内部我们将这个Date类型的变量转换成String类型的日期并且返回。这样UI上就显示出我们转化好的字符串。 
    看看效果:

    好了,到这里DataBinding的知识我们就算学习完了,在学完之后发现这东西也没什么难度,学会使用就ok了,而且android官网也有非常详细的文档, 
    这两篇博客只是系统的去讲解了DataBinding的使用,大家在以后使用的过程中发现忘记怎么用了,可以再来翻看博客或者直接去官方查看。

  • 相关阅读:
    window 7/8/10 安装nginx
    全面了解 Nginx 到底能做什么
    MySQL优化
    office 2013 破解工具 及 软件下载
    centos6+如何对外开放80,3306端口号或者其他端口号
    CentOS 中查看软件的版本号
    CentOS 中安装 mysql 5.7+
    STL入门大全(待编辑)
    Feign
    微信公众号
  • 原文地址:https://www.cnblogs.com/chenxibobo/p/6076474.html
Copyright © 2011-2022 走看看