- 原文地址:Data Binding — Lessons Learnt
- 原文做者:Chris Banes
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:Mirosalva
- 校对者:DevMcryYu
Data Binding 库(下文中以『DB 库』词语来指代)提供了一个灵活强大的方式来绑定数据到 UI 界面。可是要用一句陈词滥调:『能力越大,责任越大』,仅仅是使用数据绑定,并不意味着你能够避免成为一个优秀 UI 开发者。html
过去的几年我一直在 Android 开发中使用 data binding 库,本文会写出我这一路上了解到的与它有关的一些内容细节。前端
自定义 binding adapter 是一种给 View 控件轻松提供自定义功能的好方法。和许多开发者同样,我对 binding adapter 研究得稍微深刻,最终总结出一套包含 15 种不一样用途的适配器的类集。java
最糟糕的实践是这类适配器,它们生成格式化的字符串并设置到 TextViews
控件,这些适配器一般仅在同一个布局文件中使用:android
虽然这可能看起来很聪明,可是有三大缺点:ios
优化它们的过程太痛苦。除非你把代码组织得很是好,不然你可能会有一个包含全部适配器方法的大文件,这与代码内聚和解耦原则相违背。git
你须要使用 instrumentation 工具来作测试。根据定义,你的 binding adapter 不会有返回值,它们接收一个输入参数后设置 view 的属性。这就意味着你必须使用 instrumentation 来测试你的自定义逻辑,这样会使得测试变得既缓慢又难以维护。github
自定义 binding adapter 代码(一般)不是最佳选项。若是你查看内建文本绑定[参考这里],你将会看到已经作了许多检查来避免调用 TextView.setText()
,这样就节省了被浪费的布局检测。我以为本身陷入了这样的思惟困境:DB 库将会自动优化个人 view 更新。它确实能够作到,但仅限于你使用被谨慎优化的内建 binding adapter的状况。后端
相反的,把你的方法的逻辑抽象为内聚类(我称之为文本建立者类),而后将它们传递给 binding。这样你就能够调用你的文本建立者类并使用内建 view binding:app
这样咱们能够从内建的绑定操做过程当中提升效率,而且咱们能够很是轻松地对建立格式化字符串的代码进行单元测试。less
若是你确实须要使用自定义适配器,由于你所需的功能不存在,请尽可能使其变得高效。个人意思是使用全部标准的 Android UI 优化:尽量避免触发测量/布局操做。
这能够像检查当前使用的视图以及你设置的内容同样简单。这里有一个咱们为 android:drawable
从新实现了标准 ImageView adapter 的样例:
遗憾的是,视图并不老是可以显示咱们须要检查的状态。这里有一个在 TextView 上设置切换最大行的示例。它经过改变 TextView 的 maxLines
属性以及一个延时布局转换(android.view.ViewGroup)来实现切换。
以前 binding adapter 比较简单而且老是设置了 maxLines
属性和一个点击监听对象。TextView 在 setMaxLines()
被调用后总会触发一次布局,这就意味着每次 binding adapter 启动,一次布局就会被触发。
让咱们改变这个状况。因为此功能与 TextView 是彻底分开的(咱们只是在单击时使用不一样的值调用 setMaxLines()
),咱们须要将引用存储为当前状态。幸运的是,『DB 库』为咱们提供了一个手工方式去在 binding adapter 中接收状态。经过提供参数两次:第一个参数接收当前值,第二个参数接收新值。
因此这里咱们只需比较当前的和新的 collapsedMaxLines
值。若是值实际发生了改变,咱们才去调用 setMaxLines()
等方法。
编辑按: 感谢 Alexandre Gianquinto 在评论中提到『double parameters』功能。
我一直在慢慢的从新设计 Tivi,使用相似 MVI 的东西,使用优秀的 MvRx 库来使它变得规范化。这在实践中意味着个人 fragment/view 订阅到 ViewModel对象,而且接收 ViewStates 的实例。这些实例包含全部用于显示 UI 的必要状态。
这是一个展现 Tivi(连接)中类的样例:
你能够看到它仅仅是一个简单的数据类,包含了 UI 须要在一个 TV 秀界面上显示的全部细节 UI 元素。
听起来像是传递咱们的 data binding 实例对象的完美选项,让咱们的 binding 表达式来去更新 UI,对吧?好吧这确实有效,可是有一些须要注意的地方,这是因为『DB 库』的工做机制。
在 data binding 中你经过 <variable>
标签声明了输入,而后在书写 binding 表达式时在 view 属性处引用了这些输入变量。当任何被依赖的变量发生变化,『DB 库』都会运行你的 binding 表达式(接着会更新 view)。这个变化检测就是你能够免费获取的很棒的优化。
因此回到个人场景,个人布局最终看起来是这样的:
因此我最终获取一个包含全部 UI 状态的全局 ViewState 实例,而且你能够想象出这些状态常常会发生变化。UI 状态的任何轻微变化都会产生一个全新的 ViewState,并被传递到咱们的 data binding 实例。
因此问题是什么?因为咱们只有一个输入变量,全部的 binding 表达式将会引用变量,这就意味着『DB 库』将没法自由选择运行哪一个表达式。在实际过程当中,这意味着每次变量变化(无论多小的变化)发生时全部的 binding 表达式都会运行。
这个问题与 MVI 这点无关,特别是它只是组合状态的 artifact,与data binding 结合在一块儿使用。
有种替代方法是在布局中显式声明 ViewState 中的每一个变量,而后显式传递组合状态实例中的值,以下所示:
这显然会使开发人员维护和同步更多的代码,但它确实意味着『DB 库』能够优化去运行哪些表达式。若是你的 UI 状态不常常变化(可能在建立时有一些次)而且变量数量较少时,我会推荐使用此模式。
我我的一直在布局中使用单个变量,传入个人 ViewState 实例,并依赖于咱们的视图绑定合理地运行。这就是为何让视图绑定变得高效很是重要。
另外一个须要注意的是 Tivi 是 RecyclerView 的重度使用者,还有 Epoxy 和 Data Binding,意思就是在 DiffUtil 中会额外有一些变化相关的计算发生。因此若是你的 UI 也有大量的 RecyclerView 组成,你能够相似上文描述不费事地获取计算这方面的优化。
但愿这篇文章强调了一些能够优化数据绑定实现方案中的一些小事。了解『DB 库』的内部机制能够帮助你提升数据绑定效率,并提升你的 UI 性能。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。