您的位置:首页 > 其它

Data Binding官方用户指南

2015-09-10 14:04 323 查看

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)}"


有些资源需要详细的类型,转换方式如下:

TypeNormal ReferenceExpression 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"/>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: