Data Binding Guide
Data Binding即数据绑定,Data Binding 库实现在布局文件中实现数据绑定申明,使数据的变化引起视图的自动更新,减少了逻辑代码,在Android中可以很方便的实现MVVM的开发模式。
最低运行环境: Android 2.1 (API level 7+) 环境要求:Android Plugin for Gradle 1.3.0-beta4或者更高
Beta release
当前是beta版本,可能会有一些bug,也有可能现在不适合在你的项目中使用。Google希望大家使用它,并给出一个好的或者不好的反馈。另外需要注意的是,这个beta版跟后面的正式版使用方式上可能会有比较大的变化,在实际项目中慎用。
配置环境Setting Up Work Environment:
在project最上层的build.gradle android标签下面加入:
dependencies {
classpath "com.android.tools.build:gradle:1.3.0-beta4"
classpath "com.android.databinding:dataBinder:1.0-rc1"
}
同时确保jcenter在版本仓库列表中
allprojects {
repositories {
jcenter()
}
}
在所有需要使用到Data Binding的module中,都需要在build.gradle的android plugin下面增加:
apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'
data binding插件的作用是增加必须的编译配置依赖到项目中。
Data Binding布局文件
Data-binding布局文件稍微有些不同,在根标签下面有一个data标签和root 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"></variable>
</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>
<textview android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"></textview>
</linearlayout>
</layout>
user 是会在这个布局中用到的一个变量。
<variable name="user" type="com.example.User"></variable>
在布局中使用的使用要使用“@{}”,如下代码表示:TextView中文本设置为user的firstName
<textview android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"></textview>
数据对象Data Object
假设我们有个User的POJO:
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;
}
}
从data binding的角度来看,这两个类是一样的。对于@{user.firstName}
,在给TextView的android:text赋值的时候,系统可以像第一个类中那样直接读取firstName
的值,或者像第二个类中调用getFirstName()
,当然,如果firstName()
存在的话,也是会被调用到的。(至于同时存在的时候,调用优先顺序请自己测试。)
绑定数据Binding Data
默认情况下,会有一个基于layout文件名字产生一个Binding类,然后在后面加个Binding. 上面的main_activity.xml
产生的类名字就是MainActivityBinding
这个类持有了layout中所有的绑定属性(比如user变量)。最简单的做法是在view填充的时候创建绑定bindings.
@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);
}
至此,运行应用就可以在UI中看到User的数据了。
下面是另一种写法:
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
如果你在ListView或者RecyclerView 的Adaptper中使用data binding.使用如下代码:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
绑定事件Binding Events
事件可以简单的绑定到handler方法中,就像android:onClick被分配到Activity的onClick(View v)中一样。
比如我们Handler中有下面两个方法:
public class MyHandlers {
public void onClickFriend(View view) { ... }
public void onClickEnemy(View view) { ... }
}
在view中分配点击监听的代码如下:
< ?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>
<variable name="user" type="com.example.User"></variable>
</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="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"></textview>
<textview android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"></textview>
</linearlayout>
</layout>
布局相关详细Layout Details
导入Imports
如果在data元素中药使用其他的类,代码和java差不多:
<data>
<import type="android.view.View"></import>
</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}"></textview>
如果出现类名重复的情况,可以指定alias:
<import type="android.view.View"></import>
<import type="com.example.real.estate.View"
alias="Vista"></import>
下面是变量定义写法:
<data>
<import type="com.example.User"></import>
<import type="java.util.List"></import>
<variable name="user" type="User"></variable>
<variable name="userList" type="List<User>"/>
</variable></data>
注意:现在 Android Studio还没有在IDE实现layout文件中自动补全import。 类型转换什么的也是阔以的。
<textview android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></textview>
导入类后,可以使用其中的静态方法:
<data>
<import type="com.example.MyStringUtils"></import>
<variable name="user" type="com.example.User"></variable>
</data>
…
<textview android:text="@{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></textview>
和java一样,java.lang.*
下面的包是自动导入的。
Variables
<data>
<import type="android.graphics.drawable.Drawable"></import>
<variable name="user" type="com.example.User"></variable>
<variable name="image" type="Drawable"></variable>
<variable name="note" type="String"></variable>
</data>
变量类型是在编译是检查的。如果一个类实现了Observable ,那么变量值变化后UI会跟着变化的。
如果同一个同一布局有多个适配文件,文件中的变量最终会合并到一起,所以这些文件中不能存在同名的变量。
生成的绑定类binding class针对每个变量都有getter/setter方法,再调用setter方法前,这些变量都会有默认值,引用类型为null,int为0…
自定义绑定类名字Custom Binding Class Names
默认情况下,绑定类名字是布局文件名字去掉“_”,首字母大写,最后加上Binding。这个文件会被放在modul包下面的package包中,比如contact_item.xml
生成ContactItemBinding
如果module名字是com.example.my.app
这个ContactItemBinding.java
会被放在 com.example.my.app.databinding
下面。
绑定类可以被重命名或者放在其他package下面,调整方式如下:
<data class="ContactItem">
...
</data>
上面的文件还是会生成在databinding 下面。如果想生成在其他package下面,和manifest类似,以”.”开头:
<data class=".ContactItem">
...
</data>
当然也可以像下面一样:
<data class="com.example.ContactItem">
...
</data>
嵌套Includes
在嵌套的布局中使用绑定类,需要使用bind命名空间xmlns:bind="http://schemas.android.com/apk/res-auto
< ?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"></variable>
</data>
<linearlayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/name"
bind:user="@{user}"></include>
<include layout="@layout/contact"
bind:user="@{user}"></include>
</linearlayout>
</layout>
注意:name.xml
和contact.xml
中必须要有user变量。
下面的 merge 这个是不支持的。
< ?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"></variable>
</data>
<merge>
<include layout="@layout/name"
bind:user="@{user}"></include>
<include layout="@layout/contact"
bind:user="@{user}"></include>
</merge>
</layout>
表达式语法Expression Language
共有特性Common Features
下面的用法和java一致
Mathematical + - / * % String concatenation + Logical && || Binary & | ^ Unary + - ! ~ Shift >> >>> < < Comparison == > <>= < = instanceof Grouping () Literals - character, String, numeric, null Cast Method calls Field access Array access [] Ternary operator ?:
Examples:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
不支持的操作Missing Operations
this super new Explicit generic invocation
Null Coalescing Operator
“??”的用法是左边不为空取左边,否则取右边。
android:text="@{user.displayName ?? user.lastName}"
等同于:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
防止NPE Avoiding NullPointerException
Generated data binding code automatically checks for nulls and avoid null pointer exceptions. For example, in the expression @{user.name}, if user is null, user.name will be assigned its default value (null). If you were referencing user.age, where age is an int, then it would default to 0. 系统已经针对null做了处理,如果user为null,@{user.name}
默认为null。user.age(int类型),会默认为0
集合操作Collections
方便起见,如下集合烈性: arrays, lists, sparse lists, and maps, 都可以通过 [] 进行操作.
<data>
<import type="android.util.SparseArray"></import>
<import type="java.util.Map"></import>
<import type="java.util.List"></import>
<variable name="list" type="List<String>"/>
</variable><variable name="sparse" type="SparseArray<String>"/>
</variable><variable name="map" type="Map<String, String>"/>
</variable><variable name="index" type="int"></variable>
<variable name="key" type="String"></variable>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"
字符串字面值String Literals
为了避免双引号(“)转义的问题,可以配合单引号(‘)一起使用
android:text='@{map["firstName"]}'
下面的写法也ok
android:text="@{map[`firstName`}"
android:text="@{map["firstName"]}"
资源文件相关Resources
根据屏幕尺寸加载不同的padding值
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 |
数据对象Data Objects
pojo对象可以通过数据绑定来展示到ui,但是修改pojo对象不会使UI刷新。数据绑定真正强大的地方是数据变化的时候UI可以自动刷新。有以下三种机制可以实现:Observable objects, observable fields, observable collections.
当与ui绑定上面的对象数据发生变化时,UI会自动刷新。
Observable Objects
实现Observable 接口的类可以依附attach一个listner到绑定的对象上面,监听绑定对象数据变化。 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 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);
}
}
Bindable 注解编译时会在modul目录生成BR 类文件。
ObservableFields
创建Observable 可能有点费时间,如果想省时一些,可以考虑ObservableField 或者其子类: ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable. 在数据类中创建变量:
private static class User {
public final ObservableField<string> firstName =
new ObservableField<>();
public final ObservableField</string><string> lastName =
new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
使用:
user.firstName.set("Google");
int age = user.age.get();
Observable Collections
对于复杂一些的结构,可以参考下面的写法 如果key是String类型:
ObservableArrayMap</string><string , Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
In the layout, the map may be accessed through the String keys:
<data>
<import type="android.databinding.ObservableMap"></import>
<variable name="user" type="ObservableMap<String, Object>"/>
</variable></data>
…
<textview android:text='@{user["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"></textview>
<textview android:text='@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"></textview>
如果key是integer,建议用ObservableArrayList
ObservableArrayList<object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);
In the layout, the list may be accessed through the indices:
<data>
<import type="android.databinding.ObservableList"></import>
<import type="com.example.my.app.Fields"></import>
<variable name="user" type="ObservableList<Object>"/>
</variable></data>
…
<textview android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"></textview>
<textview android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"></textview>
Binding生成Generated Binding
创建
Binding应在inflation之后就立马创建,以确保View层次结构不在之前打扰layout中的binding到views上的表达式。有几个方法可以绑定到一个layout。最常见的是在Binding类上使用静态方法.inflate方法载入View的层次结构并且绑定到它只需这一步。还有一个更简单的版本,只需要LayoutInflater还有一个是采用ViewGroup:
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
如果使用不同的机制载入layout,他可以分开绑定:
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
有时Binding不能提前知道,对于这种情况,可以使用DataBindingUtil类来创建Binding:
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
带ID的Views
在layout中对于每个带ID的View会生成一个public final字段。Binding在View层次结构上做单一的传递,提取带ID的Views。这种机制比起某些Views使用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>
它会生成如下的Binding类:
public final TextView firstName;
public final TextView lastName;
IDs不像没有Data Bindings那样几乎没有必要,但是仍然会有一些实例需要从代码中访问Views。
变量Variables
每个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>
它会在Binding中生成setters和getters:
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);
ViewStubs
ViewStubs跟正常的Views略有不同。他们开始时是不可见的,当他们要么设置为可见或被明确告知要载入时,它们通过载入另外一个layout取代了自己。
由于ViewStub基本上从View的层次结构上消失,在Binding对象的View也必须消失来允许被收集。因为Views是最后的,一个ViewStubProxy对象取带ViewStub,给开发者获得了ViewStub,当它存在以及还可以访问载入的View层次结构时当ViewStub已被载入时。
当载入另一个layout,为新的布局必需创建一个Binding。因此,ViewStubProxy必需监听ViewStub的OnInflateListener监听器并在那个时候建立Binding。因为只有一个可以存在,ViewStubProxy允许开发者在其上设置一个OnInflateListener它会在建立Binding后调用。
Binding进阶
动态Variables
有时,不知道具体的Binding类,例如,一个RecyclerView适配器对layouts任意操作并不知道具体的Binding类。它仍然必需在onBindViewHolder(VH, int)期间赋值给Binding。
在这个例子中,该RecyclerView绑定的所有layouts有一个“item”的Variable。该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();
}
直接BindingImmediate Binding
当一个variable或observable变化时,binding会在下一帧之前被计划要改变。有很多次,但是在Binding时必须立即执行。要强制执行,使用executePendingBindings()方法。
后台线程
只要它不是一个集合,你可以在后台线程中改变你的数据模型。在判断是否要避免任何并发问题时,Data Binding会对每个Varialbe/field本地化。
###属性Setters
每当绑定值的变化,生成的Binding类必须调用setter方法。Data Binding框架有可以自定义赋值的方法。
自动Setters
对于一个属性,Data Binding试图找到setAttribute方法。与该属性的namespace并不什么关系,仅仅与属性本身名称有关。
例如,有关TextView的android:text属性的表达式会寻找一个setText(String)的方法。如果表达式返回一个int,Data Binding会搜索的setText(int)方法。注意:要表达式返回正确的类型,如果需要的话使用casting。Data Binding仍会工作即使没有给定名称的属性存在。然后,您可以通过Data Binding轻松地为任何setter“创造”属性。例如,DrawerLayout没有任何属性,但大量的setters。您可以使用自动setters来使用其中的一个。
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
重命名的SettersRenamed Setters
一些有setters的属性按名称并不匹配。对于这些方法,属性可以通过BindingMethods注解相关联。这必须与一个包含BindingMethod注解的类相关联,每一个用于一个重命名的方法。例如,android:tint属性与setImageTintList相关联,而不与setTint相关。
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
以上例子,开发者需要重命名setters是不太可能了,android架构属性已经实现了。
自定义Setters
有些属性需要自定义绑定逻辑。例如,对于android:paddingLeft属性并没有相关setter。相反,setPadding(left, top, right, bottom)是存在在。一个带有BindingAdapter注解的静态绑定适配器方法允许开发者自定义setter如何对于一个属性的调用。
Android的属性已经创造了BindingAdapters。举例来说,对于paddingLeft:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
Binding适配器对其他定制类型非常有用。例如,自定义loader可以用来异步载入图像。
当有冲突时,开发人员创建的Binding适配器将覆盖Data Binding默认适配器。
您也可以创建可以接收多个参数的适配器。
@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}”/>
如果对于一个ImageViewimageUrl和error都被使用并且imageUrl是一个string类型以及error是一个drawable时,该适配器会被调用。
- 匹配的过程中自定义namespaces将被忽略。
- 你也可以为Android namespaces写适配器。
事件处理器只能用在只有一个抽象方法的接口或者抽象类中。比如:
@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
View.OnLayoutChangeListener newValue) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (oldValue != null) {
view.removeOnLayoutChangeListener(oldValue);
}
if (newValue != null) {
view.addOnLayoutChangeListener(newValue);
}
}
}
当一个listener有多个方法的时候,它必须被分隔成多个listener。比如View.OnAttachStateChangeListener
有两个方法:onViewAttachedToWindow()
和onViewDetachedFromWindow()
。我们必须创建两个带有不同属性和handler处理器的接口
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
void onViewAttachedToWindow(View v);
}
由于修改一个listener会影响到其他的,我们必须定义三个不懂的binding adpater.一个是所有方法的,两个分开的。
@BindingAdapter("android:onViewAttachedToWindow")
public static void setListener(View view, OnViewAttachedToWindow attached) {
setListener(view, null, attached);
}
@BindingAdapter("android:onViewDetachedFromWindow")
public static void setListener(View view, OnViewDetachedFromWindow detached) {
setListener(view, detached, null);
}
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
public static void setListener(View view, final OnViewDetachedFromWindow detach,
final OnViewAttachedToWindow attach) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
final OnAttachStateChangeListener newListener;
if (detach == null && attach == null) {
newListener = null;
} else {
newListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
if (attach != null) {
attach.onViewAttachedToWindow(v);
}
}
@Override
public void onViewDetachedFromWindow(View v) {
if (detach != null) {
detach.onViewDetachedFromWindow(v);
}
}
};
}
final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
newListener, R.id.onAttachStateChangeListener);
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener);
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener);
}
}
}
转换
对象转换
当从Binding表达式返回一个对象,一个setter会从自动、重命名以及自定义的setters中选择。该对象将被转换为所选择的setter的参数类型。
这是为了方便那些使用ObservableMaps来保存数据。例如:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
在userMap返回一个对象并且该对象将自动转换为setText(CharSequence)的参数类型。当有关参数类型可能混乱时,开发人员需要在表达式中转换。
自定义转换
有时候转换应该是自动的在特定类型之间。例如,设置背景的时候:
<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) {
return new ColorDrawable(color);
}
注意:转换仅仅发生在setter级别,因此它是不允许以下混合类型:
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>