这是《通关Jetpack》系列的第2篇文章
我没什么能耐,不能给你们更好的生活,唯一能做的,是挡在你们前边。——《误杀》
在上一篇文章中,通过实例介绍了Data Binding的概念和用法,本篇文章则从更丰富的细节上介绍Data Binding的种种用法。
##创建Data Binding的两种方式
在代码里,如果要获取到Data Binding对象,有2种方式
方式一:一步到位的DataBindingUtil
。
在onCreate
中,可以同时完成设置布局文件+创建Binding对象两个动作。
1 | override fun onCreate(savedInstanceState: Bundle?) { |
或者在Fragment
、ListView
或者RecyclerView
的初始方法中。
1 | val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false) |
方式二:单独使用LayoutInflater
如果已经调用了setContentView()
,则可以在之后单独使用inflate
方法来获取Binding对象。
1 | val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater()) |
对于Fragment
、ListView
或者RecyclerView
也同理。
1 | val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false) |
表达式用法
可以在布局文件中使用的表达式
可以在布局文件中使用丰富的表达式,列举如下,暂不举例。
- 数学运算符
+ - / * %
- 字符串拼接
+
- 逻辑算式
&& ||
- 二元运算符
& | ^
- 一元运算符
+ - ! ~
- 三目运算符
?:
- 移位运算
>> >>> <<
- 比较运算
== > < >= <=
(注意在xml中需要将<
转义写为<
) instanceof
- 小括号
()
- 字面量 字符、字符串、数字以及
null
- 类型转换
- 方法调用
- 属性读取
- 数组读取
[]
NULL则替换
NULL则替换表达式(??
)很好用,可以避免很多NPE的场景。
1 | // NULL则替换 |
读取属性值
其实对于 a. public属性 b. 带有getter的属性 c. 可观测的属性ObservableField
,它们在xml里的读取写法都是相同的,都是对象.属性
。
1 | android:text="@{user.name}" |
读取同一布局文件里其它的View
尽管很少用到,但还是介绍一下。
比如我有两个TextView,id无分别是text_view_name
和text_view_nickname
,在第二个TextView里可以访问第一个TextView的文本,则可以这么写,需要留意的就是会自动将id转为驼峰命名。
1 | <TextView |
集合的写法
在xml里同样可以使用集合Data,并通过[]
来获取集合中特定的元素。
1 | <data> |
String字面量
在xml文件中,如果要使用双引号"
,则可以将外部的双引号替换为单引号'
。或者在应当使用双引号的地方使用反引号。
1 | <!-- 单引号替代双引号 --> |
引用静态资源
引用多个静态资源的语法如下。
1 | android:padding="@{larege ? @dimen/largePadding : @dimen/smallPadding}" |
事件处理
Data binding允许你为View绑定各种各样的事件处理函数,作为布局文件里的一项属性,大部分命名格式为android:onXXXX
,对应的View接口为View.OnXXXXListener
。有两种方式处理事件。
- 方法引用:在表达式中使用方法签名。Data Binding会将方法与对象包装成一个Listener并设置给View。
- 监听绑定:Lambda表达式,Data Binding对此同样生成一个Listener,供事件触发时调用。
方法引用
就像你可以为onClick
指明绑定的方法一样,也可以为View的各种事件绑定ViewModel中的方法。在编译时会对此进行检查,如果方法不存在,或是签名错误,则直接报错。“方法引用”会在编译时创建一个Listener,相应的,“监听绑定”则在事件触发时才创建Listener。一个方法引用绑定的例子如下。
1 | class MyHandlers { |
我们希望在onClick
时触发onClickFriend
方法,则写法如下。注意:表达式中的签名与类文件里面方法签名必须完全一致。
1 | <?xml version="1.0" encoding="utf-8"?> |
监听绑定
监听绑定的自由度更大,它允许你运行任意的代码。限制之处则在于,方法的返回值必须与表达式期望的值相匹配。
1 | class Presneter { |
我们可以将以上方法绑定在View的onClick
上面。因为这里多了task
参数,故无法使用方法引用来写。
1 | <?xml version="1.0" encoding="utf-8"?> |
如果需要用到View本身,则可以使用android:onClick="@{(view) -> presenter.onSaveClick(task)}"
。如果处理函数里也要用到View,则函数签名写作。
1 | class Presenter { |
还可以为方法增加更多的参数,比如对于CheckBox
的isChecked
属性。
1 | class Presenter { |
1 | <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" |
对于返回值非void的情况,函数签名必须匹配。比如onLongClick
方法,要返回boolean
类型的值,表示事件是否被消费。
1 | class Presenter { |
1 | android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}" |
也可以在表达式里使用三目运算符,用void
表示不响应事件。
1 | android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}" |
忠告:把复杂的业务逻辑放在Kotlin/Java代码中处理,而非xml表达式。
导入、变量与引入
- 导入(Imports):在xml中引入外部类
- 变量(Variables):声明在xml中使用的变量
- 引入(Incluces):帮助我们构建更加复杂的UI
导入
在<data>
块进行导入,导入后就可以在xml表达式里使用相应的类。如下例的View
类。
1 | <data> |
可以在表达式中进行强制类型转换,如把user.connection
强转为User
。
1 | <TextView |
在import后,可以在表达式中使用静态方法。
1 | <data> |
作为Data Binding类库的默认行为,java.lang.*
已经自动引入。
变量
声明变量,从而在表达式中引用这些变量,变量的赋值来自于Binding对象。对于同名的portrait
和landscape
布局文件,它们的变量声明会进行合并,因此注意在处理这种场景时,不要发生命名冲突。对于未赋值的变量,在运行时会取它们的默认值:0
、false
、null
等等。
Data Binding类库同样内置了context
变量,供调用者在表达式中使用,它等价于View.getContext()
。
引入
在进行复杂UI构件时,如果用到了include
标签,可以将变量从父布局传递给子布局。如下例,user
对象被传递给了子布局。
1 | <?xml version="1.0" encoding="utf-8"?> |
但是,Data Binding并不支持merge
标签,试图在merge
过程中传递变量是无效的。
【未完待续】