DataBinding(二):数据绑定语法
接着上一回DataBinding(一):开启数据绑定,相比起原先的传统布局文件,多了一个<data />子标签,这里面大有文章,需要单独一篇来详细了解一下数据绑定相关的语法。按照相关程度分了一下
1、变量定义
variable声明布局中用到的变量name以及变量类型type,放在<data />标签节点下。(提一句,为什么没有定义常量的语法呢?)
<data>
<variable
name="variable"
type="type" />
</data>
注意:这里的type类型,需要是类的全路径。对于Java中的常见类型String、Integer就不必导入否则还会编译报错,因为之前项目中加入导入 java.lang.*的类,在升级到3.5.1之后编译出线这个问题,解决办法:将项目编译出错的 xml中所有关于 java.lang包下的导入类删除。但集合List、Map等类型需要全路径。
在<data />中使用import导入,减少重复输入。比如定义了一个Employee类,DataBinding需要用到多个该类型的变量,就得重复输入那么长的完整路径类名:
<data>
<variable
name="employee1"
type="com.quibbler.jetpack.bean.Employee" />
<variable
name="employee2"
type="com.quibbler.jetpack.bean.Employee" />
<variable
name="employee3"
type="com.quibbler.jetpack.bean.Employee" />
<variable
name="employee4"
type="com.quibbler.jetpack.bean.Employee" />
</data>
使用<import type = "">导入需要使用的类,和Java一样,使用的地方不需要完整的类路径名,一定程度上简化代码。
<data>
<import type="com.quibbler.jetpack.bean.Employee" />
<variable
name="employee1"
type="Employee" />
<variable
name="employee2"
type="Employee" />
<variable
name="employee3"
type="Employee" />
<variable
name="employee4"
type="Employee" />
</data>
1.3、alias类型别名
在使用import导入类时,还可以使用alias定义别名。定义别名后,原类名就不可用,只能使用别名。
<data>
<import
alias="E"
type="com.quibbler.jetpack.bean.Employee" />
<variable
name="employee"
type="java.lang.String" />
</data>
2、变量、资源引用
前面我们知道如何在DataBinding中定义变量,那么该如果在真正的布局控件中使用呢?很简单,以TextView为例,要给它设置text,只需要使用@{variable}语法:
android:text="@{user.lastName}"
默认值,对于未初始化的变量,我们可以用default指定控件中的默认值:
android:text="@{user.lastName,default = 默认值}"
2.2、集合
集合需要指定泛型类型,使用<替代<
<variable
name="list"
type="java.util.List<String>" />
<variable
name="map"
type="java.util.Map<String, String>" />
就可以在布局中使用集合中的元素,而且并不会发生集合越界的异常:
<EditText android:hint="@{list[3]}" />
由于DataBinding的资源调用语法 @{...} 已经在“”双引号内,所以如果再需要设置字符串常量字面值,就得使用 `` 反引号:
<TextView android:text="@{`firstName`}" />
或者最外层用 '' 单引号,里面的常量用“”双引号:
<TextView android:text='@{map["firstName"]}' />
2.4、引用资源
在DataBinding中引用资源和之前一样,不过更加灵活:
android:paddingTop="@{@dimen/compat_inset}
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
在布局中通过ID转换成驼峰命名法后的名称引用对应的控件,可以访问它的属性。
//TextView中的Text引用下面EditText的
<TextView android:text="@{editText.text}" />
<EditText android:id="@+id/edit_text" />
3、方法调用
布局中的点击事件onClick如何处理呢?在布局中我们可以通过下面的属性指定事件回调的方法:
android:onClick
android:onLongClick
android:afterTextChanged
android:onTextChanged
...
需要注意的是自定义的表达式中的方法签名必须与监听器对象中的方法签名完全一致,比如android:onClick属性的定义时就提到:
<!-- Name of the method in this View's context to invoke when the view is
clicked. This name must correspond to a public method that takes
exactly one parameter of type View. For instance, if you specify
<code>android:onClick="sayHello"</code>, you must declare a
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity). -->
<attr name="onClick" format="string" />
我们自定义相同参数签名(参数类型、个数、返回值类型)的方法,方法名不必一样:
public class EventHandler {
public static void click(View view) {
Toast.makeText(view.getContext(), "EventHandler", Toast.LENGTH_SHORT).show();
}
}
将其设置给onClick属性,当View点击的时候会自动调用该回调。
<Button android:onClick="@{EventHandler::click}" />
//或者.引用
<Button android:onClick="@{EventHandler.click}" />
也可以不定义相同的签名
public class EventHandler {
public static void doSomeThing() {
//TO-DO
}
}
借助Lambda 表达式 @{() -> *** } ,可以使用不遵循默认的方法签名
<Button
android:onClick="@{() -> EventHandler.doSomeThing()}" />
可以引用各种方法,相当于把原本在代码中的一部分,移到了布局中,不知道DataBinding会不会减少代码,却把布局搞乱了:
<data class="MainLayoutBinding">
<import type="android.text.TextUtils" />
</data>
在布局中引入上面的工具类,就可以在布局中使用
<TextView
android:text="@{TextUtils.htmlEncode(`firstName`)}" />
表达式语言与托管代码中的表达式非常相似。可以在表达式语言中使用以下运算符和关键字:
- 算术运算符 + - / * %
- 字符串连接运算符 +
- 逻辑运算符 && ||
- 二元运算符 & | ^
- 一元运算符 + - ! ~
- 移位运算符 >> >>> <<
- 比较运算符 == > < >= <=(请注意,< 需要转义为 <)
- instanceof
- 分组运算符 ()
- 字面量运算符 - 字符、字符串、数字、null
- 类型转换
- 方法调用
- 字段访问
- 数组访问 []
- 三元运算符 ?:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
DataBinding优势之一就是一定程度上能够避免空指针。
android:text="@{user.displayName ?? user.lastName}"
等价于下面的三元运算符:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
生成的数据绑定代码会自动检查有没有 null 值并避免出现 Null 指针异常。例如,在表达式 @{user.name} 中,如果 user 为 Null,则为 user.name 分配默认值 null。再比如 user.age,其中 age 的类型为 int,则数据绑定使用默认值 0。
参考资料:
Data Binding:Layouts and binding expressions
GitHub/Android/Databinding Code Sample
DataBinding实用指南