Android Data Binding 数据绑定技术导读

 

       这篇文档会解释如何使用数据绑定库来为应用程序的逻辑和布局编写声明式布局和最小化必需的胶水代码。

 

        数据绑定库提供了灵活性和广泛的兼容性——他是一个支持库,因此你可以使用最低到Android 2.1平台(API 级别7以上)上。

        

         AndroidStudio的版本需要在1.3.0-beta1以上。

        

这是一个Beta发布版

         请注意,数据绑定库当前处于beta版本。当数据绑定技术处于beta版本时期,开发者应该遵守以下注意事项:

         *这是一个beta发布版本,因此会生成开发者反馈数据,这些数据会包含一些bug,对你的用例没啥用处,所以需要自担风险。也就是说,我们需要你的反馈!请让我们了解有什么好用还有什么不好用。请使用问题跟踪 issue tracker.

         *数据绑定beta发布版有显著的变化,包括那些没有和你的应用相兼容的源代码,这意味着在将来更新以后会有更多的重复修改的工作。

         *开发者会感到使用数据绑定库beta版构建应用很自由,只要遵守标准Android SDK和Google Play应用服务条款,并且在使用新的库或者工具的时候进行彻底的测试是个不错的主意。

         *我们现在刚刚开始对AndroidStudio的支持工作。Android Studio的支持会在不久以后逐渐释出。

         *通过使用数据绑定库beta版,你需要遵守这些注意事项。

 

搭建环境

         要开始使用数据绑定库,首先需要从AndroidSDK管理器下载支持仓库。确保你使用的是Android Studio功能可用的版本。数据绑定插件需要在Android Studio 1.3.0-beta1或者更高的版本。

 

设定工作环境

 

要设定你的应用可以使用数据绑定技术,添加数据绑定到类包含路径中,需要修改build.gradle文件,在“android”下添加如下代码:

dependencies{
       classpath "com.android.tools.build:gradle:1.2.3"
       
classpath "com.android.databinding:dataBinder:1.0-rc0"
   }

 

然后要确保jcenter在子项目的仓库列表中

 

allprojects {
   repositories {
       jcenter()
   }
}

 

在每个Module中你希望使用数据绑定,需要应用插件。

apply plugin:‘com.android.application'
apply plugin: 'com.android.databinding'

数据绑定插件将会添加有必要的设置和依赖项目的编译配置。

 

数据绑定布局文件

编写你的第一个数据绑定表达式

数据绑定布局文件会有一点不太一样,它有一个layout命名的根标签,并紧跟着一个data元素,再就是View的根元素。这个视图元素就是你在非绑定布局文件中的根元素。以下是一个示例:

<?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>

在数据中的用户变量所描述的成员中,可以在布局文件中使用。

 

<variable name="user"type="com.example.User"/>

布局中的表达式都是写在使用“@{}”语法的属性的成员中。在这里,TextView的本文会被设定到user的firstName成员中。(这里本应该不应该翻译成成员,但是有attribute和properties都翻译成属性,这两个词却在这里有不同的意义,因此properties在这里被引申为member的含义)。

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}"/>

 

数据对象

让我们假设现在你有一个普通的Java对象(POJO)叫做User:

public class User{
   public final String firstName;
   public final String lastName;
   public User(String firstName,String lastName){
       this.firstName = firstName;
       this.lastName = lastName;
   }
}

这个类型的对象有数据,但是不会更改。应用程序中一般做法是存储数据而后不再更改。那么也就有可能使用一个JavaBean对象了:

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;
   }
}

从数据绑定的角度看,这两个类其实是等价的。表达式@{user.firstName} 用作TextView的android:text属性,在前面这个类当中将可以访问到firstName这个变量域,而在后面这个类当中会使用 getFirstName() 方法。

 

绑定数据

默认情况下,一个绑定类会生成基于名称的布局文件,转换成Pascal(??),并且后缀上去。上面的布局文件是activity_main.xml ,因此会生成类是 ActivityMainBinding. 这个类持有所有从布局文件中相应的View对应的属性(例如:user变量),并且知道如何指派变量值到绑定的表达式中。创建绑定的最简单方法是在扩展布局生成视图的时候做:

@Override
protected void onCreate(Bundle savedInstanceState){
   super.onCreate(savedInstanceState);
   ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user =new User("Test","User");
   binding.setUser(user);
}

这就完事了!运行一下应用程序你会看到UI界面上的测试User。当然,这也是可以通过如下方式替换的:

MainActivityBinding binding =MainActivityBinding.inflate(getLayoutInflater());

如果你在使用ListView(列表视图)或者RecyclerView(回收器视图)进行数据绑定,你可以通过如下方式获取View的实例:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup,false);

数据绑定布局详解

导入包

在data元素中,可以有大于等于0个import元素。这些元素可以让你轻松的引用在你的布局文件中,就像Java

<data>
    <import  type="android.view.View"/>
</data>

现在View可以在绑定表达式中使用了

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

当有一些类名发生冲突的时候,可以为其中一些类名设定一个别名“alias”:

<import  type="android.view.View"/>
<import  type="com.example.real.estate.View"
        alias="Vista"/>

这样的话,Vista可以用来引用com.example.real.estate.View 而View仍然在布局文件中引用android.view.View 。导入类型可以在变量和表达式中作为类型引用。

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List&lt;User>"/>
</data>

注意:Android Studio尚且不能处理导入路径,因此为导入变量的自动完成功能在你的IDE中不能工作。你可以绕开这些IDE的问题在你的变量定义中使用完全限定的名称。

 

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

 

导入类型也可以在绑定表达式中用作引用静态变量域和方法

 

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

就像在Java中,java.lang.* 包会自动导入。

 

变量

在data元素中,可以使用任意数量的 variable 元素。每一个 variable 元素描述一个可以在布局中的绑定表达式使用的成员。

<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>

变量类型会在编译时受到检查,所以如果一个变量实现了Observable (可被观察特性),或者是一个观察者集合,它会进行类型反射。如果一个变量是一个基类或者接口,并且么有实现Observable接口,这个变量将不会被监视。

 

当有不同的布局文件有多种配置的时候(例如横屏或者竖屏),这些变量会被联合。这些布局文件中不能有变量定义的冲突。

 

生成的绑定类会为每一个变量提供一个setter和getter。这些变量会使用Java中的默认值直到使用setter调用——null为引用类型的默认值,0为int默认值,false是boolean的默认值等。

 

自定义的绑定类名称

·默认情况下,一个绑定类是根据布局文件的名字生成的,开头大写,移除下划线“_”,然后使用后面的字母,然后加上“Binding”后缀。这个类将会被放置在模块包中的数据绑定包。

比如,布局文件contact_item.xml 会生成ContactItemBinding类。如果模块包是com.example.my.app,那么将会被放置到com.example.my.app.databingding中。

绑定类会通过调整data元素的class属性对应被重新命名或者放到不同的包中。

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

这种情况下, ContactItem 会在模块包中直接生成。如果提供了一个完整的包名,任何包都可能被使用。

<data class="com.example.ContactItem">
    ...
</data>

包含布局

变量可以通过使用应用命名空间和属性变量名从绑定的包含布局传给一个被包含的布局中:

<?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>

在这里,在name.xml contact.xml 中一定都要有一个 user 变量。

 

表达式语言

通用特性

表达式语言看起来很像Java表达式。这些是相同的:

·        数学符号+ - / * %

·        字符串连接 +

·        逻辑运算符 && ||

·        二进制位运算符 & | ^

·        单目运算符 + - ! ~

·        移位运算 >> >>> <<

·        比较运算符 == > < >= <=

·        Instanceof实例断言

·        括号分组 ()

·        文字 – 字母, 字符串, 数字类型, null

·        强制类型转换

·        方法调用

·        变量域访问

·        数组元素访问 []

·        三目运算符 ?:

例如:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

不可用的操作符

表达式中有少量Java中可用的操作是缺失的

·        this

·        super

·        new

·        声明式调用

空安全运算符

空安全运算符(??)会当左操作数不为空时使用左值,否则使用右值。

android:text="@{user.displayName ?? user.lastName}"

功能等价于:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

成员属性引用

第一种情况已经在上文中编写你的第一个数据绑定表达式中讨论过:使用简短化的JavaBean引用。当一个表达式引用了一个类的成员属性,他会使用访问变量域相同的格式,getter方法,和ObservableFields(可观察域)。

android:text="@{user.lastName}"

规避空指针异常

自动生成数据绑定代码会检查空变量的存在并设法防止空指针异常。比如,在表达式 @{user.name}中,如果 user 是空, user.name 会被指派默认值(也是null)。如果你引用了

user.age, 而age是int型,那么会被默认赋值为0。

 

集合类型

通常的集合类型有:数组,列表,Sparse(稀疏)列表和map(映射),可以方便地使用 [] 操作符。

 

<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&lt;String>"/>
    <variable name="map"type="Map&lt;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]}"

字符串

当属性值使用单引号时,表达式中是可以很容易地使用双引号:

 

android:text='@{map["firstName"]}'

It is also possible to use double quotes to surround theattribute value. When doing so, String literals should either use the&quot; or back quote (`).

可能也有一种情况属性值使用双引号包起来,当使用这种做法时,字符串的表示可以使用&quot;作为引号,或者使用后引号( ` )。

android:text="@{map[`firstName`}"
android:text="@{map[&quot;firstName&quot;]}"

资源

可以使用正常的语法来访问资源,资源名可以正常的作为表达式的一部分:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

格式化字符串或者复数可以通过提供参数时被评估:

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

当一个复数接受了多个参数,所有参数都会被传递:

  Have an orange
  Have%d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"

有一些资源需要明确的类型评估。

Type

Normal Reference

Expression Reference

String[]

@array

@stringArray

int[]

@array

@intArray

TypedArray

@array

@typedArray

Animator

@animator

@animator

StateListAnimator

@animator

@stateListAnimator

color int

@color

@color

ColorStateList

@color

@colorStateList

 

数据对象延伸

任何纯粹的Java对象(POJO)可以用作数据绑定,但是改变一个POJO却不会让UI进行更新。给你的数据对象进行改变时可以进行通知才显出数据绑定真正的威力。有这么三种数据变更通知机制, Observable 可观察对象, ObservableField 可观察域,和observable collections可观察集合。

 

当这些可观察数据对象中的其中一种被绑定到UI上,当一个成员属性变更时,UI将会自动随之更新。

可观察对象

一个实现了android.databinding.Observable 接口的类将允许附加一个单独的监听器到一个绑定对象上以监听数据对象中所有成员属性发生的变更。

一个Observable 接口有一个可以添加和移除监听器的机制,但是通知是开发者决定的。要让开发者更容易使用,因此就有了一个基类BaseObservable, 被用来实现监听的注册机制。数据类的实现者仍然有责任让成员属性变化时进行通知。这是通过指派一个Bindable注解到getter方法上完成的,并且需要在setter中完成通知动作。

private static class User extends BaseObservable{
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName(){
       return this.firstName;
   }
   @Bindable
   public String getFirstName(){
       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);
   }
}

 Bindable 注解在编译时生成了一个入口在BR类中。BR类将会在模块包中生成。如果数据类的基类不能变更, Observable 接口可以使用方便的PropertyChangeRegistry 类实现来有效地存储通知监听器。

 

可观察域

创建可观察类需要做那么一点工作,因此想要省时间的开发者或者有很少的成员属性可以用ObservableField. ObservableFields是自包含有一个单独的变量域持有可观察对象的。这是为了所有的基本类型和引用类型的版本。要使用的话,要在数据类中创建一个公开的不可再赋值的变量域:

private static class User extends BaseObservable{
   public final ObservableField<String> firstName =
       newObservableField<>();
   public final ObservableField<String> lastName =
       newObservableField<>();
   public final ObservableInt age =new ObservableInt();
}

这就OK了!要访问值,使用set和get访问方法:

user.firstName.set("Google");
intage = user.age.get();

可观察集合

有一些引用使用动态数据结构去保持数据。可观察集合允许键值访问这些数据对象。ObservableArrayMap在作为键值引用时非常有用,比如使用String。

ObservableArrayMap<String,Object> user =new ObservableArrayMap<>();
user.put("firstName","Google");
user.put("lastName","Inc.");
user.put("age",17);

在布局中,映射会通过字符串键名去访问。

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;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);

在布局中,列表可以使用索引访问具体元素:

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList&lt;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"/>

生成的绑定类

生成的绑定类链接了布局中的布局变量。之前讨论过,名称和包都是可以定制的。生成的绑定类都继承了android.databinding.ViewDataBinding

创建过程

绑定过程应该在扩展出布局以后紧接着进行以确保View视图层次不会因为布局中整合的绑定View的表达式所干扰。有这么几种方法绑定到一个布局上。最通用的方式是使用在绑定类上使用静态方法。扩展方法扩展出视图层次并且一次性绑定。还有一个更简单的版本,只要使用 LayoutInflater 和 ViewGroup 即可:

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(LayoutInflater, viewGroup,false);

如果布局已经使用一个不同的机制被扩展出来,他会被分开绑定。

MyLayoutBinding binding =MyLayoutBinding.bind(viewRoot);

有时候绑定不能提前知道,这种情况下,绑定可以通过DataBindingUtil类来创建。

ViewDataBinding binding =DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding =DataBindingUtil.bindTo(viewRoot, layoutId);

有ID的View

会为布局中每一个有ID的View都会生成一个公开的最终变量。绑定过程中,在View层次上会有一个单独的过程,抽出有ID的View。这个机制对于一些View来说会比调用findViewById更快。例如:

<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>

会生成附带有这些内容的绑定类:

public final TextView firstName;
public final TextView lastName;

对于没有数据绑定来说,ID没啥大用,但是仍然有一些实例,从代码中会访问View是很有必要的。

变量

每个变量都将会给与访问方法。

<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>

会在绑定类中生成setter和getter:

public abstract com.example.User getUser();
public abstract void setUser(com.example.User user);
public abstract Drawable getImage();
public abstract void setImage(Drawable image);
public abstract String getNote();
public abstract void setNote(String note);

ViewStub

ViewStub 会和通常的View不太一样。他们起初是不可见的,并且当他们被置于课件状态或者被明确告知要进行扩展时,他们会在布局中将自己替换成另一个布局。

因为ViewStub在视图层次中基本上就是消失状态,绑定了对象的View也必须消失。因为View是不可以改变的, ViewStubProxy对象会替代ViewStub的位置,当它存在时就可以让开发者能够访问ViewStub,并且当ViewStub被扩展时也可以扩展视图层次。

当扩展另一个布局的时候,一个绑定类必须为新的布局建立起来。这样的话,ViewStubProxy必须监听ViewStub的OnInflateListener,并且同时建立绑定。由于只能存在一个,ViewStubProxy允许开发者设置一个OnInflateListener在建立绑定以后调用。

高级绑定

动态变量

有时候,特定的绑定类不会是已知的类型。比如,一个RecyclerView 适配器Adapter在操作任意布局的时候不知道是哪个特定的绑定类。必须在onBindViewHolder中指派绑定值。

在这个例子中,RecyclerView绑定的所有布局都有一个”item“变量。BindingHolder有一个getBinding方法会返回ViewDataBinding 。

public void onBindViewHolder(BindingHolder holder,int position){
   final T item = mItems.get(position);
   holder.getBinding().setVariable(BR.item, item);
   holder.getBinding().executePendingBindings();
}

立即绑定

当一个变量或者可观察对象改变时,绑定类会计划在下一帧前变更。然而有时候绑定必须立即执行,要强制执行,就使用 executePendingBindings()方法。

背景线程

只要不是集合,你可以在背景线程中改变你的数据模型。数据绑定会本地化变量/变量域,同时会评估防止任何并发问题。

属性赋值方法setters

无论何时一个绑定的值改变了,生成的绑定类一定会对使用了绑定表达式的 View调用setter方法(译者:说的那么绕,实际就是重新赋值)。数据绑定框架会有一些途径去决定使用哪个方法去设置值。

自动Setter

对于一个attribute,数据绑定会尝试寻找方法setAttribute。对于attribute的命名空间不需要在意,只要关心attribute本身。比如,一个表达式关联的TextView属性 android:text ,会寻找setText(String)。如果表达式返回的是整形,数据绑定会搜寻setText(int)方法。使用表达式要小心的返回正确的类型,必要时强制类型转换。要注意数据绑定即便没有给定的名称的属性存在也会有效。你也可以轻易的”创造”一个属性通过数据绑定给任何setter。例如,支持库DrawerLayout没有任何属性,但是有大量的setter,你可以自动化的使用这些setter方法任何一个。

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}"/>

重命名Setter

有一些属性有setter但不能通过命名匹配。对于这些方法,一个属性能够通过BindingMethods注解去手动关联setter。这这必定会关联一个包含BindingMethod注解的类,对应每一个重新命名的方法。例如, android:tint 属性实际上关联的是setImageTintList方法,而不是setTint方法。

@BindingMethods({
       @BindingMethod(type ="android.widget.ImageView",
                      attribute ="android:tint",
                      method ="setImageTintList"),
})

开发者一般不可能需要重命名setters,Android框架的属性都已经实现了。

自定义Setter

有一些属性需要自定义绑定逻辑。比如,没有任何setter关联 android:paddingLeft属性。

然而却存在 setPadding(left,top, right, bottom) 方法。一个带有BindingAdapter 注解的静态绑定适配的方法可以允许开发者去定制如何为一个属性调用setter。

Android自带属性已经建立了 BindingAdapter比如就有一个 paddingLeft:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view,int padding){
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}

绑定适配器对于其他类型的定制很有用。比如,一个定制的加载器能够调用另一个线程加载图像。

当出现冲突时开发者创建的绑定适配器会覆盖数据绑定默认的适配器。

你也可以让适配器接收多个参数:

@BindingAdapter({"bind:imageUrl","bind:error"})
public static void loadImage(ImageView view,String url,Drawable error){
   Picasso.with(view.getContext()).load(url).error(error).into(view);
}
<ImageView 
app:imageUrl=“@{venue.imageUrl}”
app:error=“@{@drawable/venueError}”/>

如果 imageUrl 和error 都用在ImageView上,imageUrl 是一个字符串并且error是一个绘制对象,这个适配器将会调用。

·        自定义命名空间会在匹配时被忽略

·        你可以为Android命名空间写适配器

转换器

对象数据转换

当一个对象从绑定表达式中返回数据,一个setter将会自动被选择,重命名并定制setter,这个对象就会被强制类型转换到选择的setter方法中中参数的类型。

这是一个方便的使用ObservableMaps保持数据的例子:

<TextView
   android:text='@{userMap["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

userMap会返回一个对象,那个对象会被自动强转成setText(CharSequence) setter中发现的参数类型。当可能有模糊不清的参数类型的时候,开发者需要在表达式中使用强制类型转换。

自定义转换

有时候转换应该在特定的类型上自动转换。比如:当设置了背景的时候:

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

在这里,背景使用了Drawable,但是色彩是整形。无论何时Drawable都是被期望类型,而返回是整形,整形应该被转换为ColorDrawable。这种转换可以通过一个使用了BindingConversion注解的静态方法完成。

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color){
   retur new ColorDrawable(color);
}

要注意一些转换仅仅发生在setter级别,因此这种混合类型转换是万万不可的:

<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

 

 

 

 

 

 

 

 

 

 转载请标明来源地址:http://blog.csdn.net/lp8800/article/details/46353037

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值