Epoxy - 在RecyclerView中构建复杂界面 - 2

545 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Model属性

每个Model持有的数据, 最终会绑定到视图上面. 而这些数据, 便用属性来表示:

  • 在自定义视图中, 属性是在setter上面添加@ModelProp注解来声明的.
  • 在数据绑定布局中, 每一个variable都表示了一个Model属性.
  • 在ViewHolder中, @EpoxyAttribute字段注解声明了一个属性.

每一个属性都必须是实现了equalshashcode的类型. 这些实现必须在属性值发生变更的时候能够准确地识别出来. Epoxy依赖于此来实现diff. 如果Epoxy并不知道属性发生了改变的话, 属性便不能在视图上进行更新. 不过也有意外情况, 比如回调, 比如点击监听器, 这些通常应该使用DoNotHash.

在生成的EpoxyModel里面, 每一个属性都有setter和getter; 在创建新的Model的时候, 每一个属性都通过setter进行赋值.

new MyModel_()
       .id(1)
       .title("title")

Id是每一个Model都必须要有的属性.

Model状态

Model的状态由它的equalshashCode决定, 当然, 也是基于Model的全部属性值.

状态用于Diff, 以判断Model什么时候发生了改变, 由此Epoxy好更新视图.

当然, 这两个方法是自动生成的, 无须手动创建.

创建视图

每一个Model都代表了它所支持的View的类别. Model实现了buildView函数创建新的视图类型的实例. 此外, getViewType返回了Int值, 该值表示了用于视图回收的视图类型.

在Epoxy收到了RecyclerView.Adapter#getItemViewTypeRecyclerView.Adapter#onCreateViewHolder的调用的时候, Epoxy委托到了Model上的这些方法.

这些方法都是自动生成的, 除非你手动地创建Model.

绑定视图

RecyclerView.Adapter#onBindViewHolder调用时, Epoxy使用EpoxyModel#bind(View)在请求位置委托给了Model. 这是Model的时机, 将Model中的属性填充到View中.

相似的, Epoxy委托RecyclerView.Adapter#onViewRecycled方法到了EpoxyModel#unbind(View), 给了Model时间来释放任何关联到View的资源. 这是很好的时机来清理视图的大而重的数据, 例如Bitmap.

因为RecyclerView在任何可能的时候重用视图, 视图也许会绑定多次, 而在多次绑定期间, unbind却没有被调用. 应该确保EpoxyModel#bind(View)根据Model中的数据完全地更新视图.

生成的Model中已经有了onBind函数, 这个函数可以用来在Model绑定视图的时候注册回调.

model.onBind((model, view, position) -> // Do something!);

相似地, onUnbind能够用来解绑回调.

这两个函数是有用的, 尤其是需要在视图进出屏幕时需要采取些措施的时候.

视图更新

在Model状态改变和Model绑定到屏幕上的视图的时候, Epoxy只用发生了改变的属性来重新绑定视图. 这种局部绑定比重新绑定完整的视图更加高效.

如果在使用自定义视图或者数据绑定自动生成的Model, 那么局部重新绑定会自动生成的.

如果在使用手动创建的Model, 则可以通过实现EpoxyModel#bind(T view, EpoxyModel<?> previouslyBoundModel)来查看哪些属性发生了变化, 然后按需要更新视图.

注意: 局部更新只有EpoxyController工作, 遗留的EpoxyAdapter并不支持.

点击监听器

如果Model持有View.OnClickListener类型的监听器, 那么自动生成的Model将会包含额外的, 经过覆盖的方法来设置监听器. 覆盖的方法将以为OnModelClickListener为参数.

经过覆盖的监听器有2个参数: Model的类型和Model的视图类型. 监听器必须实现void onClick(T model, V parentView, View clickedView, int position)方法. 该回调显示了视图被点击的Model, 被点击的根视图(如果在使用EpoxyModelWithHolder的话, 则是ViewHolder), 接收点击的视图以及适配器中该Model的位置.

提倡使用监听器提供的位置, 而不是保存Model被添加到Adapter/Controller时的位置的引用. 这是因为如果Model被移动的话, RecyclerView提供了优化以不重新绑定Model, 因为Model知道数据并没有发生变化. 这也意味着onClick返回的Model没必要是最近创建的Model, 如果Model并不需要重新绑定视图的话.

OnCheckedChangeListener

相似地, 如果Model持有CompoundButton.OnCheckedChangeListener类型的属性的话, OnModelCheckedChangeListener类型的覆盖方法会在Model上自动生成 - 在监听器被触发的时候提供对Model和位置的访问.