在上一篇博文中我们给出了一个例子,在layout中添加data 节点,并在里面用variable标签声明一个变量,然后在java文件中声明一个对应的变量,将其绑定到layout中。 那接下来的部分我们就继续了解下,可以绑定哪些类型:
在绑定某个类型之前需要将当前类型导入到layout布局中,要实现这个目标必须依赖于import标签: 
使用DataBinding在布局中可以导入类: 基本的格式如下:
<data >     <import  type="android.os.Bundle"/> </data > 
 
这就和Java中导包一样,没特殊的地方: 在类名有冲突的时候,还可以使用别名来区别二者,具体的别名使用方法如下:
<import  type ="android.os.Bundle"/> <import  type ="com.idealist.testDatabing.Bundle"         alias ="selfBundle"/> 
 
基本上包括自定义类型在内的全部类型都可以在layout中声明:
导入的类型还可以在表达式中使用static属性和方法:
<TextView    android:text ="@{MyUtils.log(user.lastName)}"     android:layout_width ="wrap_content"     android:layout_height ="wrap_content" /> 
 
像JAVA一样,java.lang.*是自动导入的。
导入包后就可以使用这些包声明对应的变量,变量声明方式如下: 
Variables 变量 这个就和变量定义一个样,type指定变量类型,name 指定变量名,对于bean类型必须有get/set方法 
 
<variable name ="name"   type ="String" /> 
 
当属性名需要用到单引号的时候外面引号需要用双引号,反之,当内部用单引号的时候外部需要用双引号,这个很多语言都有这个用法。
android: text =android: text ="@{map[`firstName`]}" 
 
在变量中还可以用到对应的资源
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding} "  android:text="@{@plurals/banana(bananaCount)} "  
 
集合的用法:  集合泛型要特别注意左尖括号需要使用转译:
<data  class="CollectionsBinding" >     <import  type ="java.util.Map"  />     <import  type ="java.util.List"  />     <import  type ="android.util.SparseArray"  />     <variable  name="list"  type ="List<String>"  />     <variable  name="sparse"  type ="SparseArray<String>" />     <variable  name="map"  type ="Map<String, String>" />     <variable  name="index"  type ="int"  />     <variable  name="key"  type ="String"  /> </data > 
 
List 用法:
<TextView     android:text ="@{list[index]}"      android:layout_width ="wrap_content"      android:layout_height ="wrap_content"  /> 
 
SparseArray 用法:
<TextView     android:text ="@{sparse[index]}"      android:layout_width ="wrap_content"      android:layout_height ="wrap_content"  /> 
 
Map 用法:
<TextView     android:text ="@{map[key]}"      android:layout_width ="wrap_content"      android:layout_height ="wrap_content"  /> <TextView          android:text ='@{map["firstName"]}'           android:layout_width ="wrap_content"           android:layout_height ="wrap_content"  /> <TextView     android:text ="@{map[`firstName`]}"      android:layout_width ="wrap_content"      android:layout_height ="wrap_content"  /> 
 
 
Include 用法: 注意下面的例子中在name.xml以及contact.xml两个layout文件中必需要有user variable
<?xml version="1.0"  encoding="utf-8" ?> <layout  xmlns:android ="http://schemas.android.com/apk/res/android"          xmlns:bind ="http://schemas.android.com/apk/res-auto" >    <data >         <variable  name ="user"  type ="com.example.User" />     </data >     <LinearLayout          android:orientation ="vertical"         android:layout_width ="match_parent"         android:layout_height ="match_parent" >        <include  layout ="@layout/name"              bind:user ="@  {user} " />       <include  layout ="@layout/contact"              bind:user ="@  {user} " />   </LinearLayout >  </layout > 
 
除了上述的用法,目前还支持设置默认值给某个属性
android: text="@{String.valueOf(user.age),default=`defaultValue`}" 
 
 
除了直接使用变量外还可以使用通过运算符进行运算后的值 
支持的运算符:
算术运算符 + - / * % 逻辑运算符 && || 二进制运算符 & | ^ 字符串连接 + 一元运算 + - ! ~ 移位运算 >> >>> << 比较运算 == > < >= <= instanceof null 强制转换 方法调用 数据访问 [] 三元运算 ?: 除了上述基本的运算外下面还有一些特殊的操作符:
 
 
 
?? - 左边的对象如果它不是null,选择左边的对象;或者如果它是null,选择右边的对象:
android: text ="@{user.displayName ?? user.lastName}" 
 
* 自定义 Binding 类名称 在上一篇博文中介绍到会自动生成ActivityMainBinding ,这个其实是根据布局来创建的,我们上个例子中布局文件名为activity_main,将当中的下划线去掉然后下划线分割的两端首字母大写,在最后加上Binding 就构成了ActivityMainBinding。但是我们还可以自己指定一个类名:
<data  class ="com.idealist.customName" >     ... </data > <data  class ="CustomBinding" > </data > 在apppackage/databinding下生成CustomBinding;<data  class =".CustomBinding" > </data > 在apppackage下生成CustomBinding;<data  class ="com.example.CustomBinding" > </data > ` 明确指定包名和类名。
 
* 实时反馈数据的变化 我们上面介绍的可以将数据和layout上的数据进行绑定,但是当绑定数据变化后并不能立刻反映到View杀害能够。Data Binding的真正强大的地方是当数据变化时,可以通知对应的Data对象。 有三种不同的数据变化通知机制:Observable对象、ObservableFields以及observable Collections。下面就来介绍下这个用法:
继承BaseObservable的方式 实现android.databinding.Observable接口的类可以为绑定对象添加一个监听器用于监听所有你想监听的对象的变化。 要实现上述的绑定可以分成两步: 1 继承BaseObservable的基类来创建Observable对象。 2 为getter添加Bindable注释。
下面是一个例子: 这个和上一篇博文介绍的一样就不多解释了:
<?xml version="1.0"  encoding="utf-8" ?> <layout  xmlns:android ="http://schemas.android.com/apk/res/android" >     <data  class  = "CustomBindingName" >          <import  type ="com.idealist.databindingdemo.bean.UserBean" />          <variable               name ="user"              type ="UserBean" />     </data >      <LinearLayout           xmlns:tools ="http://schemas.android.com/tools"          android:layout_width ="match_parent"          android:layout_height ="match_parent"          android:orientation ="vertical"          tools:context ="com.idealist.databindingdemo.MainActivity" >         <TextView               android:gravity ="center"              android:text ="@  {user.firstName} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <TextView               android:gravity ="center"              android:text ="@  {user.lastName} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <Button               android:text ="Change Name to IOS"              android:id ="@+id/changeName"              android:layout_width ="match_parent"              android:onClick ="onChangeName1"              android:layout_height ="wrap_content"  />         <Button               android:text ="Change Name to Android"              android:id ="@+id/changeName1"              android:layout_width ="match_parent"              android:onClick ="onChangeName2"              android:layout_height ="wrap_content"  />     </LinearLayout >  </layout > 
 
这里的Userbean 继承自BaseObservable并且在对应的get方法上添加了@Bindable注释。并在set方法中添加notifyPropertyChanged(BR.lastName);来通知变化
public  class  UserBean  extends  BaseObservable {    @Bindable      public  String  getFirstName ( ) {         return  firstName;     }     public  void  setFirstName (String  firstName ) {         this .firstName  = firstName;         notifyPropertyChanged (BR .firstName );     }     @Bindable      public  String  getLastName ( ) {         return  lastName;     }     public  void  setLastName (String  lastName ) {         this .lastName  = lastName;         notifyPropertyChanged (BR .lastName );     }     private  String  firstName;     private  String  lastName;     public  UserBean (String  firstName, String  lastName) {         this .firstName  = firstName;         this .lastName  = lastName;     } } 
 
接着就可以在MainActivity中调用set方法来设置对应的值,一旦bean的值一改变对应TextView 上的文本就立刻改变。 需要注意的是上面的BR是编译阶段生成的一个类,用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry。 当数据发生变化时还是需要手动发出通知。 通过调用 notifyPropertyChanged(BR.firstName) 可以通知系统 BR.firstName 这个 entry 的数据已经发生变化,需要更新 UI。
public class  MainActivity extends AppCompatActivity {     private  UserBean userBean = null;     @Override     protected void onCreate(Bundle savedInstanceState )  {         super.onCreate(savedInstanceState ) ;         CustomBindingName binding = DataBindingUtil . setContentView(this , R.layout .activity_main ) ;         userBean = new  UserBean("Android" , "OS" ) ;         binding.setUser(userBean ) ;     }     public void onChangeName1(View view )  {         userBean.setFirstName("IOS" ) ;         userBean.setLastName("Jobs" ) ;     }     public void onChangeName2(View view )  {         userBean.setFirstName("Android" ) ;         userBean.setLastName("OS" ) ;     } } 
 
运行结果如下:
ObservableFiled 这种方法也可以实现Observable 对象的效果,它的好处是不用在set方法中添加notifyXXXXX 首先我们在原来例子的基础上添加了一个用于显示年龄的TextView
<?xml version="1.0"  encoding="utf-8" ?> <layout  xmlns:android ="http://schemas.android.com/apk/res/android" >     <data  class  = "CustomBindingName" >          <import  type ="com.idealist.databindingdemo.bean.UserBean" />          <variable               name ="user"              type ="UserBean" />     </data >      <LinearLayout           xmlns:tools ="http://schemas.android.com/tools"          android:layout_width ="match_parent"          android:layout_height ="match_parent"          android:orientation ="vertical"          tools:context ="com.idealist.databindingdemo.MainActivity" >         <TextView               android:gravity ="center"              android:text ="@  {user.firstName} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <TextView               android:gravity ="center"              android:text ="@  {user.lastName} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <TextView               android:gravity ="center"              android:text ="@  {String.valueOf(user.age)} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />                  <Button               android:text ="Change Name to IOS"              android:id ="@+id/changeName"              android:layout_width ="match_parent"              android:onClick ="onChangeName1"              android:layout_height ="wrap_content"  />         <Button               android:text ="Change Name to Android"              android:id ="@+id/changeName1"              android:layout_width ="match_parent"              android:onClick ="onChangeName2"              android:layout_height ="wrap_content"  />     </LinearLayout >  </layout > 
 
在原来的Bean中添加如下代码:
public  ObservableInt  age = new  ObservableInt ();public  ObservableInt  getAge ( ) {    return  age; } public  void  setAge (ObservableInt age ) {    this .age  = age; } 
 
除了ObservableInt,还提供了如下的类型,基本上所有的基本类型都提供了:
public  ObservableInt age = new  ObservableInt ();public  ObservableBoolean age1 = new  ObservableBoolean ();public  ObservableChar age2 = new  ObservableChar ();public  ObservableByte age3 = new  ObservableByte ();public  ObservableDouble age4 = new  ObservableDouble ();public  ObservableFloat age5 = new  ObservableFloat ();public  ObservableLong age6 = new  ObservableLong ();public  ObservableShort age7 = new  ObservableShort ();public  ObservableField<String > age8 = new  ObservableField <>();
 
接下来就是通过如下的设置bean值,一旦bean值被设置,就会立刻反应到View上:
public class  MainActivity extends AppCompatActivity {     private  UserBean userBean = null;     @Override     protected void onCreate(Bundle savedInstanceState )  {         super.onCreate(savedInstanceState ) ;         CustomBindingName binding = DataBindingUtil . setContentView(this , R.layout .activity_main ) ;         userBean = new  UserBean("Android" , "OS" ) ;         binding.setUser(userBean ) ;     }     public void onChangeName1(View view )  {         userBean.setFirstName("IOS" ) ;         userBean.setLastName("Jobs" ) ;         userBean.getAge() .set(23 );     }     public void onChangeName2(View view )  {         userBean.setFirstName("Android" ) ;         userBean.setLastName("OS" ) ;         userBean.getAge() .set(26 );     } } 
 
除了上述的基本类型ObServerable 还支持List Map等集合类型:
ObservableArrayMap<String, Object>  user  = new ObservableArrayMap<> (); user .put("firstName" , "Google" );user .put("lastName" , "Inc." );user .put("age" , 17 );
 
在layout文件中,通过String键可以访问map:
<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" /> 
 
ObservableArrayList用于键是整数:
ObservableArrayList<Object > user  = new  ObservableArrayList<>(); user .add ("Google");user .add ("Inc.");user .add (17 );
 
在layout文件中,通过索引可以访问list:
<data >     <import  type ="android.databinding.ObservableList" />      <import  type ="com.example.my.app.Fields" />      <variable  name ="user"  type ="ObservableList<Object>" />  </data > … <TextView     android:text ='@  {user[Fields.LAST_NAME]} '    android:layout_width ="wrap_content"     android:layout_height ="wrap_content" /> <TextView     android:text ='@  {String.valueOf(1 + (Integer)user[Fields.AGE])} '    android:layout_width ="wrap_content"     android:layout_height ="wrap_content" /> 
 
* 在DataBinding中使用View的id 在Bind数据后,我们有时候会需要为某个按钮添加一个响应事件,这时候我们可以这样做: 如下布局中有两个按钮,id分别为changeName以及changeName1,我们下面将要为这两个按钮添加点击事件:
<?xml version="1.0"  encoding="utf-8" ?> <layout  xmlns:android ="http://schemas.android.com/apk/res/android" >     <data  class  = "CustomBindingName" >          <import  type ="com.idealist.databindingdemo.bean.UserBean" />          <variable               name ="user"              type ="UserBean" />     </data >      <LinearLayout           xmlns:tools ="http://schemas.android.com/tools"          android:layout_width ="match_parent"          android:layout_height ="match_parent"          android:orientation ="vertical"          tools:context ="com.idealist.databindingdemo.MainActivity" >         <TextView               android:gravity ="center"              android:text ="@  {user.firstName} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <TextView               android:gravity ="center"              android:text ="@  {user.lastName} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <TextView               android:gravity ="center"              android:text ="@  {String.valueOf(user.age)} "             android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />                  <Button               android:text ="Change Name to IOS"              android:id ="@+id/changeName"              android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />         <Button               android:text ="Change Name to Android"              android:id ="@+id/changeName1"              android:layout_width ="match_parent"              android:layout_height ="wrap_content"  />     </LinearLayout >  </layout > 
 
下面就是为按钮添加事件的办法,很简单吧感觉比findviewById简单不少:
public class  MainActivity extends AppCompatActivity implements View.OnClickListener {     private  UserBean userBean = null;     @Override     protected void onCreate(Bundle savedInstanceState )  {         super.onCreate(savedInstanceState ) ;         CustomBindingName binding = DataBindingUtil . setContentView(this , R.layout .activity_main ) ;         userBean = new  UserBean("Android" , "OS" ) ;         binding.setUser(userBean ) ;         binding.changeName.setOnClickListener(this ) ;         binding.changeName1.setOnClickListener(this ) ;     }     @Override     public void onClick(View v )  {         switch (v.getId() ) {             case R . id.changeName:                 userBean.setFirstName("IOS" ) ;                 userBean.setLastName("Jobs" ) ;                 userBean.getAge() .set(23 );                 break;             case R . id.changeName1:                 userBean.setFirstName("Android" ) ;                 userBean.setLastName("OS" ) ;                 userBean.getAge() .set(26 );                 break;         }     } } 
 
Event Binding (事件绑定) 下面例子是实现的是按钮的二值状态切换:
首先定义一个接口
public  interface UserPressEvent {    void  press(View  view );     void  unPress(View  view ); } 
 
布局中使用:
<variable      name ="event"       type ="com.idealist.UserPressEvent" />       android:onClick ="@{user.isPressed? event.unPress : event.press}"  
 
在Activity实现该接口UserPressEvent:
@Override public  void  press(View  view ) {    user .isPressed(true ); } @Override public  void  unPress(View  view ) {    user .isPressed(false ); } 
 
自定义转换 有时候转换应该是自动的在特定类型之间。例如,设置背景的时候:
<View    android:background ="@{isError ? @color/red : @color/white}"     android:layout_width ="wrap_content"     android:layout_height ="wrap_content" /> 
 
这里,背景需要Drawable对象,但颜色是一个整数。不管何时有Drawable并且返回值是一个整数,那么整数类型会被转换为ColorDrawable。这个转换是通过使用带有BindingConversion注解的静态方法完成的1
@BindingConversion public  static  ColorDrawable convertColorToDrawable (int  color)   {   return  new  ColorDrawable(color); } 
 
属性 对于一个属性,Data Binding会试图找到对应的setAttribute方法。 比如下面的例子:
<com.idealist.databinding.view.GithubCard     android:layout_width ="match_parent"      android:layout_height ="200dp"      android:gravity ="center"      app:age ="27"      app:firstName ="@{@string/firstName}"      app:lastName ="@{@string/lastName}" /> 
 
这里并没有在declare-styleable 中定义 但是我们在自定义GithubCard中有age,firstName,lastName这些属性的setter方法所以可以直接使用上述的方式进行设置:
下面再来看个在网上找的一个例子,下面先列出代码然后展开分析:
<layout  xmlns:android ="http://schemas.android.com/apk/res/android"          xmlns:app ="http://schemas.android.com/apk/res-auto" >     <data >          <import  type ="com.liangfeizc.databinding.sample.attributesetter.AttributeSettersActivity" />          <variable               name ="activity"              type ="AttributeSettersActivity" />         <variable               name ="imageUrl"              type ="String" />     </data >      <LinearLayout           android:layout_width ="match_parent"          android:layout_height ="match_parent"          android:orientation ="vertical" >         <com.liangfeizc.avatarview.AvatarView               android:layout_width ="match_parent"              android:layout_height ="wrap_content"              app:error ="@  {@drawable/error} "             app:imageUrl ="@  {imageUrl} "             app:onClickListener ="@  {activity.avatarClickListener} " />    </LinearLayout >  </layout > 
 
public class  AttributeSettersActivity extends BaseActivity {     private  ActivityAttributeSettersBinding mBinding;     public View.OnClickListener avatarClickListener = new  View.OnClickListener()  {         @Override         public void onClick(View v )  {             Toast . makeText(AttributeSettersActivity.this , "Come on" , Toast.LENGTH_SHORT) .show() ;             mBinding.setImageUrl(Randoms.nextImgUrl () );         }     };     @Override     protected void onCreate(Bundle savedInstanceState )  {         super.onCreate(savedInstanceState ) ;         mBinding = DataBindingUtil . setContentView(this , R.layout .activity_attribute_setters ) ;         mBinding.setActivity(this ) ;         mBinding.setImageUrl(Randoms.nextImgUrl () );     }     @BindingAdapter({"bind:imageUrl" , "bind:error" })      public static void loadImage(ImageView view , String url , Drawable error )  {         Log . d(App.TAG, "load image" );         Picasso .with (view.getContext() ).load(url).error(error).into(view);     } } 
 
首先在AttributeSettersActivity onCreate方法中建立DataBinding后会将Activity,和ImageUri传到布局上, 我们在AttributeSettersActivity中看到如下方法:
@BindingAdapter({"bind:imageUrl", "bind:error"})     public  static void  loadImage(ImageView view , String url, Drawable error) {         Log .d(App.TAG, "load image");         Picasso.with (view .getContext()).load (url).error(error).into (view ); } 
 
这个是用来干嘛的呢?它的意思是: 如果AvatarView中的 imageUrl和 error 参数都存在并且由于imageUrl是string类型error是drawable 类型则就会调用上面定义的绑定适配器,并将对应的参数传入。在匹配适配器的时候,会忽略自定义的命名空间。