编译环境
要将应用配置为使用数据绑定,请在应用模块的build.gradle
文件中添加dataBinding
元素:
android {
...
dataBinding {
enabled = true
}
}
使用数据绑定库
数据绑定的布局文件以根标记layout
开头,后跟data
元素和view
根元素:
在data
中使用了包名为com.jetpackdemo
的User
文件,并为此对象设置了name:user
。
在TextView
中:android:text="@{user.name}"
表示为TextView
设置user
的name
属性值。
完成布局文件后,系统会为每一个布局文件生成一个绑定类,默认情况下,类名基于布局文件的名称,将xml
文件的名称转换为驼峰大小写,并在末尾添加Binding
一词。
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.user = User("hello")
}
如果在Fragment
、ListView
和RecyclerView
适配器中使用数据绑定,则可以使用DataBindingUtil
的inflate
方法:
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
布局表达式
可以在布局文件的使用一下运算符和关键字:
- 算术运算符
+ - * / %
- 字符串连接运算符
+
- 逻辑运算符
&& ||
- 二元运算符
& | ^
- 一元运算符
+ - ! ~
- 移位运算符
>> >>> <<
- 比较运算符
== > < >= <=
(<需要转义为<
) - instanceof
- 分组运算符
()
- 字面量运算符-字符、字符串、数字、
null
- 类型转换
- 方法调用
- 字段访问
- 数组访问
[ ]
- 三元运算符
?:
缺少的布局表达式
this
super
new
- 显式泛型调用
Null
合并运算符
android:text="@{user.name ?? user.sex}"
如果左边运算不为null
则选择左边运算,否则选择右边运算。
属性引用
android:text="@{user.name}"
避免出现Null
指针异常
生成的数据绑定代码会自动检查有没有null
值并避免出现null
指针异常。如果在表达式中String
为null
则分配默认值null
。
集合表达式
为了方便访问,可使用[ ]
运算符访问常见集合,例如数组、列表、稀疏列表和映射。
...
android:text="@{list[index]}"
...
android:text="@{sparse[index]}"
...
android:text="@{map[key]}"
在集合中,必须使用转义<字符。例如:不要写成
List
形式,而是必须写成List<String>
。
获取map中的值
android:text='@{map.get("name")}'
事件处理
通过数据绑定,您可以编写从视图分派的表达式处理事件(例如:onClick()
方法)。事件名称由监听器方法的名称确定。
可以使用以下机制处理事件:
方法引用:在表达式中,您可以引用符合监听器方法签名的方法,当表达式求值结果为方法引用时,数据绑定会将方法引用和所有者对象封装到监听器中,并在目标视图上设置该监听器。如果表达式的求值结果为
null
,则数据绑定不会创建监听器,而是设置null
监听器。
监听器绑定:这些是在事件发生时进行求值的lambda
表达式。数据绑定始终会创建一个要在视图上设置的监听器。事件被分派后,监听器会对lambda
表达式进行求值。
方法引用
一个主要优点是表达式在编译时进行处理,因此,如果该方法不存在或其签名不正确,则会收到编译时错误。
class MyHandlers {
fun onClickFriend(view: View) { ... }
}
方法引用中的签名必须和方法中的签名保持一致,并且参数也保持一致,不能自定义参数。
监听器绑定
监听器绑定是在事件发生时运行的绑定表达式。它们类似于方法引用,但允许您运行任意数据绑定表达式。此功能适用于 Gradle 2.0 版及更高版本的 Android Gradle 插件。
在方法引用中,方法的参数必须与事件监听器的参数匹配。在监听器绑定中,只有您的返回值必须与监听器的预期返回值相匹配(预期返回值无效除外)。
class Presenter {
fun onSaveClick(task: Task){}
}
然后,您可以将点击事件绑定到 onSaveClick() 方法
如果监听的事件返回类型不是void的值,那么表达式也必须返回相同类型的值(比如长按事件返回值是boolean)。
fun onSaveClick(task: Task): Boolean{}
从以上说明可以看出,方法引用更适合简单的事件处理,并且方法不需要自定义参数。监听器则更适合复杂的事件处理,方法可以自定义参数。
避免使用复杂的监听器
监听器表达式功能非常强大,可以使您的代码非常易于阅读。另一方面,包含复杂表达式的监听器会使您的布局难以阅读和维护。这些表达式应该像将可用数据从界面传递到回调方法一样简单。您应该在从监听器表达式调用的回调方法中实现任何业务逻辑。
导入、变量和包含
导入
通过导入功能,您可以轻松地在布局文件中引用类,就像在托管代码中一样。您可以在 data
元素使用多个 import
元素,也可以不使用。以下代码示例将View
类导入到布局文件中:
上述方法中导入了View
类,可以引用View
的属性,比如设置显示:
android:visibility="@{user.man? View.VISIBLE : View.GONE}"
类型别名
当类名有冲突时,其中一个类可使用别名重命名。以下示例将com.example.real.estate
软件包中的View
类重命名为Vista
:
导入其他类
还可以通过使用导入的类型来对表达式的一部分进行类型转换。以下示例将connection
属性强制转换为类型User
:
在表达式中引用静态字段和方法时,也可以使用导入的类型。以下代码会导入MyStringUtils
类,并引用其 capitalize
方法:
…
包含
通过使用应用命名空间和特性中的变量名称,变量可以从包含的布局传递到被包含布局的绑定。以下示例展示了来自 name.xml 和 contact.xml 布局文件的被包含 user 变量:
数据绑定不支持 include 作为 merge 元素的直接子元素。例如,以下布局不受支持: