Android MVVM之Data Binding

worldwalk 8年前
   <h2><strong>Data Binding Library</strong></h2>    <h2><strong>简介:</strong></h2>    <p>Data Binding Library 是一个灵活、兼容的 support library ,可以在Android2.1(API 7)以上使用。</p>    <p>使用DataBing,Gradle的Android Plugin需要在1.5.0-alpha1以上。</p>    <h2><strong>Build Environment</strong></h2>    <p>首先需要在Android SDK manager中下载DataBing的library。然后在build.gradle中添加dataBindingg节点,如下:</p>    <pre>  <code class="language-java">android {      ....      dataBinding {          enabled = true      }  }</code></pre>    <p>如果你的应用中有其他模块或依赖的库用到了Data Binding,也需要在buidl.gradle中配置。</p>    <h2><strong>Data Binding 的布局文件</strong></h2>    <p><strong>第一个DataBinding表达式</strong></p>    <p>使用 DataBinding 后的布局文件与原来的布局文件差距很小,使用 DataBinding 后的布局文件的根布局是 layout ,并添加了 data 节点。布局格式如下</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <layout xmlns:android="http://schemas.android.com/apk/res/android">      <data>             <!-- import and variable-->      </data>      <!-- 使用Data Binding前的根布局 -->     </layout></code></pre>    <p>eg.</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <layout xmlns:android="http://schemas.android.com/apk/res/android">      <data>             <variable name="user" type="com.example.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.firstName}"/>             <TextView android:layout_width="wrap_content"                 android:layout_height="wrap_content"                 android:text="@{user.lastName}"/>       </LinearLayout>   </layout></code></pre>    <p>data 节点中的 user 变量描述了可能会在布局中用到的属性值。</p>    <p>DataBinding表达式写在 "@{}" 中。在下面的例子中,TextView的text被设置为 user 的 firstName</p>    <pre>  <code class="language-java"><TextView android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@{user.firstName}"/></code></pre>    <p><strong>Data对象 (ViewModel)</strong></p>    <p>假设你有一个User的POJO</p>    <pre>  <code class="language-java">public class User {      public final String firstName;      public final String lastName;      public User(String firstName, String lastName) {             this.firstName = firstName;          this.lastName = lastName;      }  }</code></pre>    <p>这种对象的数据永不会改变。常用于应用中只读一次永之后不改变的数据。</p>    <p>也可以使用JavaBeans:</p>    <pre>  <code class="language-java">public class User {      private final String firstName;      private final String lastName;      public User(String firstName, String lastName) {             this.firstName = firstName;          this.lastName = lastName;      }      public String getFirstName() {          return this.firstName;      }      public String getLastName() {          return this.lastName;      }   }</code></pre>    <p>在 DataBinding 中上面两个User对象是一样的。 @{user.firstName} 会访问第一个类的firstName字段第二个类的</p>    <p>getFirstName() 方法。如果存在 firstName() 方法,也可能会访问 firstName() 方法。</p>    <p><strong>绑定数据</strong></p>    <p>默认情况下,Binding类会根据布局文件的名字自动生成。如 main_activity.xml 的布局文件会生成 MainActivityBinding 。一个简单的数据绑定如下:</p>    <pre>  <code class="language-java">@Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      MainActivityBinding binding = DataBindingUtil.setContentView(this,      R.layout.main_activity);      User user = new User("Test", "User");      binding.setUser(user);  }</code></pre>    <p><strong>Event Handling</strong></p>    <p>Data Binding 提供了两种处理事件的方法。</p>    <ul>     <li> <p>Method References(方法引用) :你可以直接引用和 listener 方法签名一致的方法。当一个表达式被解析为 Method References , Data Binding 会把该方法包装成一个 listener 并设置到对应的 View 。如果表达式被解析为 null , Data Binding 会设置成对应的 listener 为 null 。</p> </li>     <li> <p>Listener Bindings :也可以使用 lambda , Data Binding 总是会创建设置到 View 上的 listener ,当事件被分发到该 View 时 lambda 表达式会被解析。</p> </li>    </ul>    <p><strong>Method References</strong></p>    <p>与可以在 android:onClick 属性指定调用的 Activity 方法类似,事件可以直接绑定到 Handler 的方法上。后者的优势在于实在编译时处理,如果方法不存在或签名不一致会得到一个编译时错误。下面的例子展示了该用法:</p>    <pre>  <code class="language-java">public class MyHandlers {      public void onClickFriend(View view) { ... }  }</code></pre>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <layout xmlns:android="http://schemas.android.com/apk/res/android">     <data>         <variable name="handlers" type="com.example.Handlers"/>         <variable name="user" type="com.example.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.firstName}"             android:onClick="@{handlers.onClickFriend}"/>     </LinearLayout>  </layout></code></pre>    <p>注意: Handler 中方法必须与监听器中方法参数及返回值一样。如上面的例子中 Handler 的方法为 public void onClickFriend(View view) , android:onClick 需要的监听器为 OnClickListener ,其方法为 public void onClick(View view)</p>    <p><strong>Listener Bindings</strong></p>    <p>Listener Bindings 只要求 Handler 中方法与监听器中的方法返回值一致即可。</p>    <pre>  <code class="language-java">public class Presenter {      public void onSaveClick(Task task){}  }</code></pre>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>    <layout xmlns:android="http://schemas.android.com/apk/res/android">        <data>            <variable name="task" type="com.android.example.Task" />            <variable name="presenter" type="com.android.example.Presenter" />        </data>        <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">            <Button android:layout_width="wrap_content" android:layout_height="wrap_content"            android:onClick="@{() -> presenter.onSaveClick(task)}" />        </LinearLayout>    </layout></code></pre>    <p>在上面的例子中问我们并没有定义传递给 onClick(android.view.View)方法的view参数。 Listener bindings 提供了两种传递监听器参数的选择:你可以忽略所有的参数。如果你更喜欢使用带参数的,上面的例子也可以改为</p>    <pre>  <code class="language-java">android:onClick="@{(view) -> presenter.onSaveClick(task)}</code></pre>    <p>或者也可以传递更多参数</p>    <pre>  <code class="language-java">public class Presenter {      public void onSaveClick(View view, Task task){}  }</code></pre>    <pre>  <code class="language-java">android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}</code></pre>    <p><strong>避免监听器混淆</strong></p>    <p>Listener 表达式非常强大,可以让你写出更易读的代码。另一方面,复杂的 Listener 表达式会让你的布局变得难以读懂、管理。 Listener 表达式应该尽量简单,只从UI传递有效数据到Callback的方法中。所有的业务逻辑都应该该在Callback的方法中实现。</p>    <p>存在一些特有的点击事件,它们的点击事件的属性并不是 android:onClick 。下面的表格列举了对应的属性:</p>    <table>     <thead>      <tr>       <th>Class</th>       <th>Listener Setter</th>       <th>Attribute</th>      </tr>     </thead>     <tbody>      <tr>       <td>SearchView</td>       <td>setOnSearchClickListener(View.OnClickListener)</td>       <td>android:onSearchClick</td>      </tr>      <tr>       <td>ZoomControls</td>       <td>setOnZoomInClickListener(View.OnClickListener)</td>       <td>android:onZoomIn</td>      </tr>      <tr>       <td>ZoomControls</td>       <td>setOnZoomOutClickListener(View.OnClickListener)</td>       <td>android:onZoomOut</td>      </tr>     </tbody>    </table>    <h3><strong>Layout Details(布局详情)</strong></h3>    <p><strong>Imports</strong></p>    <p>可以在 <data> 节点使用 import 语法,与java相同 java.lang.* 也是自动导入的。</p>    <pre>  <code class="language-java"><data>      <import type="android.view.View"/>  </data></code></pre>    <p>当有相同类名时,可以使用别名:</p>    <pre>  <code class="language-java"><import type="android.view.View"/>  <import type="com.example.real.estate.View" alias="Vista"/></code></pre>    <p><strong>Variables</strong></p>    <p>Variables 用来描述布局文件中的属性值。</p>    <pre>  <code class="language-java"><data>      <import type="android.graphics.drawable.Drawable"/>      <variable name="user"  type="com.example.User"/>      <variable name="image" type="Drawable"/>      <variable name="note"  type="String"/>  </data></code></pre>    <p>在编译期间会对 variable 进行类型检查,只有实现了 Observable 接口或是一个 observable collection 的 variable 才会被观察,即当该 variable 的数据发生变化 UI 会更新。</p>    <p>为了适配不同的屏幕你可能会有多个布局文件, Data Binding 会合并这些布局文件中的 variable ,所以这些布局文件中的 variable 不能有混淆。</p>    <p>Data Binding 会为每个 variable 自动生成对应的 setter 和 getter 方法。 variable 在调用 setter 前都会有一个默认值:引用类型-> null 、 int -> 0 、布尔值-> false 。</p>    <p>Data Binding 会自动生成一个 context 的 variable 。它来值跟布局的 getContext() 。可以被用户的 variable 覆盖。</p>    <p><strong>Includes</strong></p>    <p>variable 在使用了 <include/> 布局中的应用:</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>name.xml 和 contact.xml 布局文件都必须有 user variable</p>    <p>不支持 include 直接作为 merge 的直接子元素,如下面的布局:</p>    <pre>  <code class="language-java"><?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>     <merge>         <include layout="@layout/name"             bind:user="@{user}"/>         <include layout="@layout/contact"             bind:user="@{user}"/>     </merge>  </layout></code></pre>    <p><strong>表达式语言</strong></p>    <p><strong>共同的功能</strong></p>    <p>下面的表达式和Java中的一样</p>    <ul>     <li>Mathematical + - / * %</li>     <li>String concatenation +</li>     <li>Logical && ||</li>     <li>Binary & | ^</li>     <li>Unary + - ! ~</li>     <li>Shift >> >>> <<</li>     <li>Comparison == > < >= <=</li>     <li>instanceof</li>     <li>Grouping ()</li>     <li>Literals - character, String, numeric, null</li>     <li>Cast</li>     <li>Method calls</li>     <li>Field access</li>     <li>Array access []</li>     <li>T*ernary operator ?:</li>    </ul>    <p>eg.</p>    <pre>  <code class="language-java">android:text="@{String.valueOf(index + 1)}"  android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"  android:transitionName='@{"image_" + id}'</code></pre>    <p><strong>消失的操作</strong></p>    <p>在Java中可用,但在 Databinding 中不可用的操作</p>    <ul>     <li>this</li>     <li>super</li>     <li>new</li>     <li>Explicit generic invocation</li>    </ul>    <p><strong>Null Coalescing Operator(非空运算)</strong></p>    <pre>  <code class="language-java">android:text="@{user.displayName ?? user.lastName}"</code></pre>    <p>等于</p>    <pre>  <code class="language-java">android:text="@{user.displayName != null ? user.displayName : user.lastName}"</code></pre>    <p><strong>Property Reference(属性引用)</strong></p>    <p>无论Data采用的是那种定义方法,POJO、Java Beans或ObservableFields,在 DataBinding 的语法中引用都用下面</p>    <p>的方式。</p>    <pre>  <code class="language-java">android:text="@{user.lastName}"</code></pre>    <p><strong>Avoiding NullPointerException(自动避免空指针异常)</strong></p>    <p>Data Binding 生成的代码会自动检查 null 值,避免空指针异常。</p>    <p>例如 @{user.name} ,如果 user 为空, user,name 会被赋值为默认值( null )。如果是 int 类型的默认值为0。</p>    <p><strong>Collections (集合)</strong></p>    <p>常见的集合:arrays, lists, sparse lists, and maps可以通过 [] 访问。</p>    <pre>  <code class="language-java"><data>      <import type="android.util.SparseArray"/>      <import type="java.util.Map"/>      <import type="java.util.List"/>      <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>      …      android:text="@{list[index]}"      …      android:text="@{sparse[index]}"      …      android:text="@{map[key]}"</code></pre>    <p><strong>String Literals (字符串)</strong></p>    <p>如果需要在 @{} 表达式内使用 "" ,布局的属性值需要使用 ''</p>    <pre>  <code class="language-java">android:text='@{map["firstName"]}'</code></pre>    <p>或者使用下面的方式</p>    <pre>  <code class="language-java">android:text="@{map[`firstName`}"  android:text="@{map['firstName']}"</code></pre>    <p><strong>Resources (资源文件)</strong></p>    <p>可以使用通常的语法在表达式中访问资源文件:</p>    <pre>  <code class="language-java">android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"</code></pre>    <p>格式化的字符串和 plurals</p>    <pre>  <code class="language-java">android:text="@{@string/nameFormat(firstName, lastName)}"  android:text="@{@plurals/banana(bananaCount)}"</code></pre>    <p>一些资源需要使用指定的引用方式</p>    <table>     <thead>      <tr>       <th>Type</th>       <th>Normal Reference</th>       <th>Expression Reference</th>      </tr>     </thead>     <tbody>      <tr>       <td>String[]</td>       <td>@array</td>       <td>@stringArray</td>      </tr>      <tr>       <td>int[]</td>       <td>@array</td>       <td>@intArray</td>      </tr>      <tr>       <td>TypedArray</td>       <td>@array</td>       <td>@typedArray</td>      </tr>      <tr>       <td>Animator</td>       <td>@animator</td>       <td>@animator</td>      </tr>      <tr>       <td>StateListAnimator</td>       <td>@animator</td>       <td>@stateListAnimator</td>      </tr>      <tr>       <td>color int</td>       <td>@color</td>       <td>@color</td>      </tr>      <tr>       <td>ColorStateList</td>       <td>@color</td>       <td>@colorStateList</td>      </tr>     </tbody>    </table>    <h2><strong>Data Objects (数据对象)</strong></h2>    <p>任何一个POJO都可以用于 Data Binding ,但是修改了一个并POJO不会更新UI。 Data Binding 的real power是让你的数据对象( Data Objects )发生数据改变时能够发出通知。这里有3种数据改变通知机制: Observable objects , observable fields , observable collections 。</p>    <p>当一个实现了通知机制的数据对象的数据发生改变并且绑定到了UI,UI会自动更新。</p>    <p><strong>Observable Objects (Observable对象)</strong></p>    <p>Observable interface有一个添加和移除listeners的机制,但是发送通知取决于开发者。为了方便开发, Data Binding 提供了一个 BaseObservable 类, BaseObservable 实现了listener注册机制。实现类要负责在数据改变时发出通知,这需要通过在 getter 方法加上 @Bindable 注解并且在 setter 方法中发出通知。</p>    <pre>  <code class="language-java">private static class User extends BaseObservable {      private String firstName;      private String lastName;        @Bindable      public String getFirstName() {          return this.firstName;      }      @Bindable      public String getLastName() {            return this.lastName;        }        public void setFirstName(String firstName) {             this.firstName = firstName;             notifyPropertyChanged(BR.firstName);         }         public void setLastName(String lastName) {          this.lastName = lastName;             notifyPropertyChanged(BR.lastName);         }  }</code></pre>    <p>@Bindable 注解会在编译期间自动在 BR 类中生成一个字段, BR 与 R 类似。</p>    <h3><strong>ObservableFields</strong></h3>    <p>Data Binding 还提供了另外更细粒度、更方便的创建 Observable 类的方式: ObservableFields , ObservableBoolean , ObservableByte , ObservableChar , ObservableShort , ObservableInt , ObservableLong , ObservableFloat , ObservableDouble , and ObservableParcelable 。</p>    <p>eg.</p>    <pre>  <code class="language-java">private static class User {      public final ObservableField<String> firstName =             new ObservableField<>();      public final ObservableField<String> lastName =             new ObservableField<>();         public final ObservableInt age = new ObservableInt();  }</code></pre>    <p>如何访问:</p>    <pre>  <code class="language-java">user.firstName.set("Google");  int age = user.age.get();</code></pre>    <p><strong>Observable Collections</strong></p>    <p>一些App使用集合保存数据。 Observable Collections 允许使用通过 键 来访问数据。</p>    <p><strong>ObservableArrayMap</strong></p>    <p>用于 键 是 引用类型 ,如 String</p>    <pre>  <code class="language-java">ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();  user.put("firstName", "Google");  user.put("lastName", "Inc.");  user.put("age", 17);</code></pre>    <p>在布局文件中的使用</p>    <pre>  <code class="language-java"><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"/></code></pre>    <p><strong>ObservableArrayList</strong></p>    <p>键 是 int</p>    <pre>  <code class="language-java">ObservableArrayList<Object> user = new ObservableArrayList<>();  user.add("Google");  user.add("Inc.");  user.add(17);</code></pre>    <p>在布局文件中的使用</p>    <pre>  <code class="language-java"><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"/></code></pre>    <h2><strong>Generated Binding(生成Binding)</strong></h2>    <p>上文已经说过生成的 Binding 类名由布局文件名决定。</p>    <p><strong>Creating(创建)</strong></p>    <p>Binding 类应该尽可能早地创建,绑定layout的方式有多种,都是通过调用 Binding 类的静态方法。如:</p>    <pre>  <code class="language-java">MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);  MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);  MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);    ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,  parent, attachToParent);  ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);</code></pre>    <p><strong>Views With IDs(带id的View)</strong></p>    <p>Data Binding 会为布局文件中每个带ID的View生成一个 public final 的字段。这可以让替代 findViewById 以更快的方式访问View:</p>    <pre>  <code class="language-java"><layout xmlns:android="http://schemas.android.com/apk/res/android">  <data>     <variable name="user" type="com.example.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.firstName}"         android:id="@+id/firstName"/>     <TextView android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:text="@{user.lastName}"         android:id="@+id/lastName"/>  </LinearLayout>  </layout></code></pre>    <p>生成的Binding类中会有:</p>    <pre>  <code class="language-java">public final TextView firstName;  public final TextView lastName;</code></pre>    <p><strong>ViewStubs</strong></p>    <p>布局文件中的 ViewStub 经过binding之后会转换成 ViewStubProxy 。</p>    <p>简单用代码说明一下,xml 文件与之前的代码一样,根节点改为 layout,在 LinearLayout 中添加一个 ViewStub,添加 ID。</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <layout xmlns:android="http://schemas.android.com/apk/res/android">      <RelativeLayout           android:layout_width="match_parent"            android:layout_height="match_parent">            <ViewStub                android:id="@+id/view_stub_test"                android:layout="@layout/stub"                android:layout_width="match_parent"                android:layout_height="match_parent"/>       </RelativeLayout>  </layout></code></pre>    <p>在 Java 代码中获取 binding 实例,为 ViewStubProy 注册 ViewStub.OnInflateListener 事件:</p>    <pre>  <code class="language-java">binding.viewStubTest.setOnInflateListener(new ViewStub.OnInflateListener() {      @Override      public void onInflate(ViewStub stub, View inflated) {             StubBinding stubBinding = DataBindingUtil.bind(inflated);             ViewStubViewModel model = new ViewStubViewModel();             model.setName("boco");             stubBinding.setStubModel(model);      }   });   binding.viewStubTest.getViewStub().inflate();</code></pre>    <p><strong>Advanced Binding(高级绑定)</strong></p>    <p><strong>Dynamic Variables(动态设置Variables)</strong></p>    <p>有时候,我们并不知道具体的Binding类型,如在 RecyclerView.Adapter 中可能会有多个 layout ,我们必须在 onBindViewHolder(VH, int) 进行赋值。</p>    <pre>  <code class="language-java">public static class BindingHolder extends RecyclerView.ViewHolder {      private ViewDataBinding binding;      public BindingHolder(View itemView) {          super(itemView);      }      public ViewDataBinding getBinding() {          return binding;      }      public void setBinding(ViewDataBinding binding) {          this.binding = binding;      }  }  @Override  public BindingHolder onCreateViewHolder(ViewGroup viewGroup, int i) {      ViewDataBinding binding = DataBindingUtil.inflate(              LayoutInflater.from(viewGroup.getContext()),              R.layout.list_item,              viewGroup,              false);      BindingHolder holder = new BindingHolder(binding.getRoot());      holder.setBinding(binding);      return holder;  }  @Override  public void onBindViewHolder(BindingHolder holder, int position) {      User user = users.get(position);      holder.getBinding().setVariable(BR.user, user);      holder.getBinding().executePendingBindings();  }</code></pre>    <p><strong>Immediate Binding(立即绑定)</strong></p>    <p>当一个variable或者observable发生了改变,数据绑定会在下一帧之前发生但不是立刻发生,如果你希望数据绑定立刻发生需要使用 executePendingBindings() 方法,eg.</p>    <pre>  <code class="language-java">public void onBindViewHolder(BindingHolder holder, int position) {     final T item = mItems.get(position);     holder.getBinding().setVariable(BR.item, item);     holder.getBinding().executePendingBindings();  }</code></pre>    <p><strong>Background Thread</strong></p>    <p>你可以在后台线程中改变不是集合的数据,Data binding will localize each variable / field while evaluating to avoid any concurrency issues.</p>    <h3><strong>Attribute Setters (属性赋值)</strong></h3>    <p><strong>自动赋值</strong></p>    <p>有了 <strong>Data Binding</strong> ,即使属性没有在 declare-styleable 中定义,我们也可以通过 xml 进行赋值操作。</p>    <pre>  <code class="language-java"><android.support.v4.widget.DrawerLayout      android:layout_width="wrap_content"      android:layout_height="wrap_content"      app:drawerListener="@{fragment.drawerListener}"/></code></pre>    <p>在上面的例子中 app:drawerListener="@{fragment.drawerListener} 会调用DrawerLayout的 setDrawerListener(DrawerListener listener) 方法具体的方法参数类型和 @{} 中的参数类型一致。</p>    <p><strong>重命名</strong></p>    <pre>  <code class="language-java"><Button         android:id="@+id/email_sign_in_button"         android:layout_width="match_parent"         android:layout_height="wrap_content"         app:myText="@{login.name}"         android:onClick="@{loginHandler.onClick}"         /></code></pre>    <p>在上面的xml对应的ViewModel中加入如下注解, app:myText="@{login.name} 便会调用Button的setText方法。</p>    <pre>  <code class="language-java">@BindingMethods({          @BindingMethod(type = android.widget.Button.class,                  attribute = "myText",                  method = "setText"),  })</code></pre>    <p><strong>自定义</strong></p>    <pre>  <code class="language-java">ViewModel中的方法  @BindingAdapter({"app:myText"})   public static void showText(Button button,String newV,String oldV){       button.setText(newV);       Log.w(TAG,"new="+newV+"--oldV="+oldV);   }   or   @BindingAdapter({"app:myText"})   public static void showText(Button button,String newV){       button.setText(newV);       Log.w(TAG,"new="+newV);   }</code></pre>    <p>对应得xml代码:</p>    <pre>  <code class="language-java"><Button         android:id="@+id/email_sign_in_button"         android:layout_width="match_parent"         android:layout_height="wrap_content"         app:myText="@{login.name}"         android:onClick="@{loginHandler.onClick}"         /></code></pre>    <h2><strong>转换器 (Converters)</strong></h2>    <p>在 xml 中为属性赋值时,如果变量的类型与属性不一致,通过 <strong>DataBinding</strong> 可以进行转换。</p>    <p>例如,下面代码中如果要为属性 android:background 赋值一个 int 型的 color 变量:</p>    <pre>  <code class="language-java"><View      android:background="@{isError.get() ? @color/red : @color/white}"      android:layout_width="match_parent"      android:layout_height="wrap_content"      app:layout_height="@{height}" /></code></pre>    <p>只需要定义一个标记了 @BindingConversion 的静态方法即可( <em>方法的定义位置可以随意</em> ):</p>    <pre>  <code class="language-java">@BindingConversion  public static ColorDrawable convertColorToDrawable(int color) {      return new ColorDrawable(color);  }</code></pre>    <p> </p>    <p>来自:http://www.jianshu.com/p/e94141bbb8e4</p>    <p> </p>