Data Binding
讲解 用于Android MVVM 的学习,用以丰富自己的知识库。
- MVVM
- 提高开发效率
- 性能高/功能强
用途
- 去掉Activities & Fragments内的UI代码
- XML变成UI的唯一真实来源
- 减少定义View id 的主要用途
类似方案
- ButterKnife
- Andriod Annotations
- RoboBinding
优势
- 去除Activity/Fragment中的U代码
- 性能超过手写代码,安全(不会id错而crash)
- 保证执行在主线程
主要劣势
- IDE支持还不那么完善
- 报错信息不那么直接
- 没有重构支持
使用Data Binding
1 | app Module |
Layout文件改写
在源layout文件外套一层标签
1 | <layout xmlns:android="http://schemas.android.com/apk/res/android" |
除去findviewbyID
- No more findviewbyID
- Binding.xxxView
- 生成规则
1 | <?xml version="1.0" encoding="utf-8"?> |
UI/事件绑定
- Bind UI setXXX setVariable
1 | <?xml version="1.0" encoding="utf-8"?> |
事件
- andriod:onClick
- android:onLongClick
- android:onTextChanged
- …
- 方法引用
- 监听器绑定
1 | <variable |
Data Binding 原理
- android.binding
- BR
- XxxBinding
开始编译–>处理layout文件–>解析表达式–>java编译–>解析依赖–>找到setter
性能:
- 0反射
- findViewById需要遍历整个viewgroup,现在只需做一次
- 使用位标记来检验更新
- 数据改变在下一次批量更新才会触发操作
- 缓存表达式,如:
1 | a?(b?c:d):e |
表达式
- 二元 &|^
- 一元 =-!~/
- 移位 >> << >>>
- 比较 == > < >= <=
- Instanceof
- Grouping()
- 文字 character,String,numeric,null
- Cast 类型转换
- 方法调用
1 | <EditText |
- Feild访问
- Array 访问[]
- 三元 ?:
1 | <ata> |
表达式-缺省
- this
- super
- new
- 显示泛型调用 Explicit generic invocation
null 合并运算符 ??
取非空表达式
1 | null 合并运算符:?? 等同于 ?: |
表达式eg
Margin @dimen+@dimen
1 | android:text="@{String.valueOf(index + 1)}" |
避免空指针
自动空指针检查
Data Binding 代码生成时会自动检查是否为 null,来避免出现 null pointer exception 错误,1
2
3例如在表达式 @{user.name} 中,
如果 user 是 null,user.name 会赋予它的默认值 null,
如果你引用 @{user.age},age 是 int 类型,那么它的默认值是 0数据越界 需要自己注意
include
- bind
1 | <?xml version=“1.0” encoding=“utf-8”?> |
- 尚不支持direct child 如 root 未 merge
1 | 据绑定不支持包括合并元素的直接子项。例如,不支持下列布局: |
viewstub
ViewStub 相比普通 View 有一些不同。ViewStub 一开始是不可见的,当它们被设置为可见,或者调用 inflate 方法时,ViewStub 会被替换成另外一个布局。
因为 ViewStub 实际上不存在于 View 结构中,binding 类中的类也得移除掉,以便系统回收。因为 binding 类中的 View 都是 final 的,所以Android 提供了一个叫 ViewStubProxy 的类来代替 ViewStub 。开发者可以使用它来操作 ViewStub,获取 ViewStub inflate 时得到的视图。
但 inflate 一个新的布局时,必须为新的布局创建一个 binding。因此, ViewStubProxy 必须监听 ViewStub 的 ViewStub.OnInflateListener,并及时建立 binding。由于 ViewStub 只能有一个 OnInflateListener,你可以将你自己的 listener 设置在 ViewStubProxy 上,在 binding 建立之后, listener 就会被触发。
1 | binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() { |
数据对象 (Data Objects)
任何 POJO 对象都能用在 Data Binding 中,但是更改 POJO 并不会同步更新 UI。Data Binding 的强大之处就在于它可以让你的数据拥有更新通知的能力。
有三种不同的动态更新数据的机制:
- Observable 对象
- Observable 字段
- Observable 容器类
当以上的 observable 对象绑定在 UI 上,数据发生变化时,UI 就会同步更新。
- Observable 对象
当一个类实现了 Observable 接口时,Data Binding 会设置一个 listener 在绑定的对象上,以便监听对象字段的变动。
Observable 接口有一个添加/移除 listener 的机制,但通知取决于开发者。为了简化开发,Android 原生提供了一个基类 BaseObservable 来实现 listener 注册机制。这个类也实现了字段变动的通知,只需要在 getter 上使用 Bindable 注解,并在 setter 中通知更新即可。
1 | public class Employee extends BaseObservable implements Serializable { |
- ObservableFields
创建 Observable 类还是需要花费一点时间的,如果想要省时,或者数据类的字段很少的话,可以使用 ObservableField 以及它的派生 ObservableBoolean、
ObservableByte 、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、
ObservableParcelable 。
ObservableFields 是包含 observable 对象的单一字段。原始版本避免了在存取过程中做打包/解包操作。要使用它,在数据类中创建一个 public final 字段:
1 |
|
ObservableBoolean
1 | public class Employee extends BaseObservable implements Serializable { |
- Observable Collections 容器类
1 | public class Employee extends BaseObservable implements Serializable { |
高级动态绑定
RecyclerView
onBindViewHolder
1 | final T item = mItems.get(position); |
总结
- data binding 生成规则
1 | contact_item.xml -> ContactItemBinding 源码如是 |
- 自定义class
1
2
3
4
<data class="ContactItem">
···
</data>
本文是在慕课网上学习的。
- Android Data Binding入门 有兴趣的朋友可以关注学习
- 推荐大神Blog Android Data Binding 系列(一) – 详细介绍与使用,提供学习。
- 本文代码在GitHub项目-LearnDevelopProject中,欢迎可以关注。