通过表达式语言,您可以编写用于处理分派的事件的表达式 观看次数。数据绑定库会自动生成所需的类 将布局中的视图与您的数据对象绑定。
数据绑定布局文件略有不同,以
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>
data
中的 user
变量描述了可在
此布局:
<variable name="user" type="com.example.User" />
布局中的表达式使用
@{}
语法。在以下示例中,
TextView
文本已设为
user
变量的 firstName
属性:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
数据对象
假设您有一个用于描述 User
实体的普通对象:
Kotlin
data class User(val firstName: String, val lastName: String)
Java
public class User { public final String firstName; public final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
此类型的对象拥有永不改变的数据。在应用中 读取一次,之后再也不会更改的数据。您还可以使用 一个遵循一组约定的对象,比如在 Google Analytics 中使用访问器方法, Java 编程语言,如以下示例所示:
Kotlin
// Not applicable in Kotlin. data class User(val firstName: String, val lastName: String)
Java
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}
,用于
android:text
属性访问前一个类中的 firstName
字段,
getFirstName()
方法。它还被解析为
firstName()
(如果存在该方法)。
绑定数据
系统会为每个布局文件生成一个绑定类。默认情况下,
类基于布局文件的名称,转换为 Pascal 大小写形式,其中
Binding 后缀。例如,前面的布局文件名为
activity_main.xml
,因此生成的相应绑定类为
ActivityMainBinding
。
此类包含布局属性中的所有绑定,例如,
user
变量 - 添加到布局的视图,并知道如何赋值
为绑定表达式指定的值。我们建议在进行膨胀时创建绑定
布局,如以下示例所示:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMainBinding = DataBindingUtil.setContentView( this, R.layout.activity_main) binding.user = User("Test", "User") }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Test", "User"); binding.setUser(user); }
在运行时,应用会在界面中显示 Test 用户。或者,您也可以
使用
LayoutInflater
,如
示例:
Kotlin
val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
Java
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
如果您在
Fragment
、
ListView
或
RecyclerView
则建议您使用
inflate()
绑定类或
DataBindingUtil
类,如
如以下代码示例所示:
Kotlin
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false) // or val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
Java
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); // or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
表达式语言
常见功能
表达式语言与托管代码中的表达式非常相似。您 可以在表达式语言中使用以下运算符和关键字:
- 数学符号:
+ - / * %
- 字符串串联:
+
- 逻辑:
&& ||
- 二进制文件:
& | ^
- 一元:
+ - ! ~
- Shift 键:
>> >>> <<
- 比较运算符:
== > < >= <=
(<
需要转义为<
) instanceof
- 分类:
()
- 字面量,如字符、字符串、数字、
null
- Cast
- 方法调用
- 字段访问
- 数组访问:
[]
- 三元运算符:
?:
以下是一些示例:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
缺少的运算
您可以使用的表达式语法中缺少以下运算 使用以下代码:
this
super
new
- 显式泛型调用
Null 合并运算符
如果左操作数不是 null
,则 null 合并运算符 (??
) 会选择左操作数
或右边的 ID(如果前者为 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
。
视图引用
表达式可以使用以下代码,按 ID 引用布局中的其他视图。 语法:
android:text="@{exampleText.text}"
在以下示例中,TextView
视图引用了 EditText
中的
相同布局:
<EditText
android:id="@+id/example_text"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<TextView
android:id="@+id/example_output"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{exampleText.text}"/>
合集
您可以访问常见集合,例如数组、列表、稀疏列表和
映射,为方便起见使用 []
运算符。
<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<String>"/>
<variable name="map" type="Map<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]}"
您还可以使用 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)}"
android:text="@{@string/example_resource(user.lastName, exampleText.text)}"
当一个复数带有多个形参时,请传递所有形参:
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 表达式求值。
方法引用
您可以将事件直接绑定到处理程序方法,这与
分配
android:onClick
更改为
方法。与
View
onClick
属性是指
处理表达式。因此,如果该方法不存在或
签名不正确,您会收到编译时错误。
方法引用和监听器绑定之间的主要区别在于, 实际的监听器实现是在绑定数据时创建的,而不是在 事件。如果您希望在事件发生时 请使用监听器绑定。
要将事件分配给其处理脚本,请使用普通绑定表达式,将 value 为要调用的方法名称。例如,请参考以下示例 布局数据对象:
Kotlin
class MyHandlers { fun onClickFriend(view: View) { ... } }
Java
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>
监听器绑定
监听器绑定是在事件发生时运行的绑定表达式。他们 类似于方法引用,但允许您运行任意数据绑定 表达式。Android Gradle Plugin for Gradle 提供此功能 2.0 及更高版本。
在方法引用中,方法的参数必须与
事件监听器。在监听器绑定中,只有返回值必须与
监听器的预期返回值,除非预期值为 void
。对于
以下面的 Presenter 类为例,该类具有 onSaveClick()
方法:
Kotlin
class Presenter { fun onSaveClick(task: Task){} }
Java
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)}"
如果要在表达式中使用参数,可以按如下方式操作:
Kotlin
class Presenter { fun onSaveClick(view: View, task: Task){} }
Java
public class Presenter { public void onSaveClick(View view, Task task){} }
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
您可以使用包含多个形参的 lambda 表达式:
Kotlin
class Presenter { fun onCompletedChanged(task: Task, completed: Boolean){} }
Java
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
的值,则您的
表达式还必须返回相同类型的值。例如,如果您希望
倾听触摸和按住(长按)事件,表达式必须返回
布尔值。
Kotlin
class Presenter { fun onLongClick(view: View, task: Task): Boolean { } }
Java
public class Presenter { public boolean onLongClick(View view, Task task) { } }
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
如果由于 null
对象而无法对表达式求值,则数据绑定会返回
该类型的默认值,如 null
表示引用类型,0
表示引用类型
int
,或 false
(boolean
)。
如果您需要将表达式与谓词(例如,
三元类型,您可以使用 void
作为符号:
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
避免使用复杂的监听器
监听器表达式功能强大,可让您的代码更易于阅读。在 另一方面,包含复杂表达式的监听器会使布局更难 来读取和维护让表达式像传递可用数据一样简单 从界面传递到回调方法在 从监听器表达式调用的回调方法。
导入、变量和包含
数据绑定库提供导入、变量和 包括。通过导入功能,可以在布局文件中提供易于引用的类。 通过变量,您可以描述可在绑定表达式中使用的属性。 通过包含功能,您可以在整个应用中重复使用复杂的布局。
导入
通过导入,您可以在布局文件内(例如在托管代码中)引用类。
您可以在 data
元素中使用零个或多个 import
元素。通过
以下代码示例将 View
类导入布局文件:
<data>
<import type="android.view.View"/>
</data>
导入 View
类可让您从绑定表达式中引用该类。
以下示例展示了如何引用
VISIBLE
和
View
类的 GONE
常量:
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
类型别名
当类名称发生冲突时,您可以将其中一个类重命名为
别名。以下示例将 View
类重命名为
将 com.example.real.estate
软件包复制到 Vista
:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
然后,您可以使用 Vista
引用 com.example.real.estate.View
和 View
在布局文件中引用 android.view.View
。
导入其他类
您可以将导入的类型用作变量和表达式中的类型引用。通过
以下示例展示了用作变量类型的 User
和 List
:
<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
您可以使用导入的类型对表达式的一部分进行类型转换。以下
以下示例将 connection
属性转换为 User
类型:
<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
在引用静态字段和方法时,您也可以使用导入的类型,
表达式。以下代码会导入 MyStringUtils
类和引用
其 capitalize
方法:
<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.lang.*
。
变量
您可以在 data
元素中使用多个 variable
元素。每个
variable
元素描述可在要使用的布局上设置的属性
在布局文件内的绑定表达式中使用。以下示例声明了
user
、image
和 note
变量:
<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 方法
调用 - null
表示引用类型,0
表示 int
,false
表示引用
boolean
等
生成了一个名为 context
的特殊变量,用于绑定表达式
。context
的值为
Context
对象
getContext()
方法。通过
context
变量会被显式变量声明替换,
名称。
包含
您可以将变量从包含元素传递到包含布局的绑定中,
通过在属性中使用应用命名空间和变量名称来调整布局。通过
以下示例显示了来自 name.xml
和user
contact.xml
布局文件:
<?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>
数据绑定不支持将 include 元素用作 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"/>
</data>
<merge><!-- Doesn't work -->
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</merge>
</layout>