Skip to content

表达式语言

layout支持使用的一些表达式。

常见功能

可以在layout中使用的表达式。 - 算术运算符 + - / * % - 字符串连接运算符 + - 逻辑运算符 && || - 二元运算符 & | ^ - 一元运算符 + - ! ~ - 移位运算符 >> >>> << - 比较运算符 == > < >= <=(请注意,< 需要转义为 <) - instanceof - 分组运算符 () - 字面量运算符 - 字符、字符串、数字、null - 类型转换 - 方法调用 - 字段访问 - 数组访问 [] - 三元运算符 ?:

示例

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

示例中android:transitionName='@{"image_" + id}',最外围是单引号,里面的字符串用双引号。

缺少的运算(不支持)

以下功能是不支持的。 - this - super - new - 显式泛型调用

Null 合并运算符

判断是否为null的更简单的方法。

示例

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

在功能上等效于

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

属性引用

表达式可以使用以下格式在类中引用属性,这对于字段、getter 和 ObservableField 对象都一样:

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

避免出现 Null 指针异常

生成的数据绑定代码会自动检查有没有 null 值并避免出现 Null 指针异常。 例如,在表达式 @{user.name} 中,如果 user 为 Null,则为 user.name 分配默认值 null。如果您引用 user.age,其中 age 的类型为 int,则数据绑定使用默认值 0。

集合

为方便起见,可使用 [] 运算符访问常见集合,例如数组、列表、稀疏列表和映射。

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;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]}"

注意:要使 XML 不含语法错误,您必须转义 < 字符。例如:不要写成 List 形式,而是必须写成 List&lt;String>

您还可以使用 object.key 表示法在映射中引用值。例如,以上示例中的 @{map[key]} 可替换为 @{map.key}。

字符串字面量

您可以使用单引号括住特性值,这样就可以在表达式中使用双引号,如以下示例所示:

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

注意上面的单引号和双引号。

也可以使用双引号括住特性值。如果这样做,则还应使用反单引号 ` 将字符串字面量括起来:

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

资源

可以使用以下语法访问表达式中的资源:

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

某些资源需要显式类型求值,如下表所示:

类型 常规引用 表达式引用
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

事件处理

通过数据绑定,您可以编写从视图分派的表达式处理事件(例如,onClick() 方法)。事件特性名称由监听器方法的名称确定,但有一些例外情况。例如,View.OnClickListener 有一个 onClick() 方法,所以该事件的特性为 android:onClick

有一些专门针对点击事件的事件处理脚本,这些处理脚本需要使用除 android:onClick 以外的特性来避免冲突。您可以使用以下特性来避免这些类型的冲突:

监听器 setter 特性
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

可以使用以下机制处理事件:

  • 方法引用:在表达式中,您可以引用符合监听器方法签名的方法。当表达式求值结果为方法引用时,数据绑定会将方法引用和所有者对象封装到监听器中,并在目标视图上设置该监听器。如果表达式的求值结果为 null,则数据绑定不会创建监听器,而是设置 null 监听器。
  • 监听器绑定:这些是在事件发生时进行求值的 lambda 表达式。数据绑定始终会创建一个要在视图上设置的监听器。事件被分派后,监听器会对 lambda 表达式进行求值。

方法引用

事件可以直接绑定到处理脚本方法,类似于为 Activity 中的方法指定android:onClick 的方式。与 View onClick 特性相比,一个主要优点是表达式在编译时进行处理,因此,如果该方法不存在或其签名不正确,则会收到编译时错误。

方法引用和监听器绑定之间的主要区别在于实际监听器实现是在绑定数据时创建的,而不是在事件触发时创建的。如果您希望在事件发生时对表达式求值,则应使用监听器绑定。

要将事件分配给其处理脚本,请使用常规绑定表达式,并以要调用的方法名称作为值。例如,请考虑以下布局数据对象示例:

    public class MyHandlers {
        public void onClickFriend(View view) { ... }
    }

绑定表达式可将视图的点击监听器分配给 onClickFriend() 方法,如下所示:

<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="handlers" type="com.example.MyHandlers"/>
           <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>

注意:表达式中的方法签名必须与监听器对象中的方法签名完全一致。

监听器绑定

监听器绑定是在事件发生时运行的绑定表达式。它们类似于方法引用,但允许您运行任意数据绑定表达式。此功能适用于 Gradle 2.0 版及更高版本的 Android Gradle 插件。

在方法引用中,方法的参数必须与事件监听器的参数匹配。在监听器绑定中,只有您的返回值必须与监听器的预期返回值相匹配(预期返回值无效除外)。例如,请参考以下具有 onSaveClick() 方法的 presenter 类:

    public class Presenter {
        public void onSaveClick(Task task){}
    }

然后,您可以将点击事件绑定到 onSaveClick() 方法,如下所示:

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

在表达式中使用回调时,数据绑定会自动为事件创建并注册必要的监听器。当视图触发事件时,数据绑定会对给定表达式求值。与常规绑定表达式一样,在对这些监听器表达式求值时,仍会获得数据绑定的 Null 值和线程安全。

在上面的示例中,我们尚未定义传递给 onClick(View) 的 view 参数。监听器绑定提供两个监听器参数选项:您可以忽略方法的所有参数,也可以命名所有参数。如果您想命名参数,则可以在表达式中使用这些参数。例如,上面的表达式可以写成如下形式:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

或者,如果您想在表达式中使用参数,则采用如下形式:

    public class Presenter {
        public void onSaveClick(View view, Task task){}
    }
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

您可以在 lambda 表达式中使用多个参数:

    public class Presenter {
        public void onCompletedChanged(Task task, boolean completed){}
    }
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
          android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

如果您监听的事件返回类型不是 void 的值,则您的表达式也必须返回相同类型的值。例如,如果要监听长按事件,表达式应返回一个布尔值。

    public class Presenter {
        public boolean onLongClick(View view, Task task) { }
    }
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果由于 null 对象而无法对表达式求值,则数据绑定将返回该类型的默认值。例如,引用类型返回 null,int 返回 0,boolean 返回 false,等等。

如果您需要将表达式与谓词(例如,三元运算符)结合使用,则可以使用 void 作为符号。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

避免使用复杂的监听器

监听器表达式功能非常强大,可以使您的代码非常易于阅读。另一方面,包含复杂表达式的监听器会使您的布局难以阅读和维护。这些表达式应该像将可用数据从界面传递到回调方法一样简单。您应该在从监听器表达式调用的回调方法中实现任何业务逻辑。