这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
在上文中我已经介绍RV(本文中代指RecyclerView)的一个方便的类ListAdapter,可它跟声明式有什么关系呢?这篇文章中我们一起来扒一扒代码中细节,感受一下代码中的声明式编程。
是的,在第一版代码到第二版代码中RV的操作部分实际上就是命令式到声明式的转换。
细节
在第二版代码中,我先将数据类Todo的所有属性全改为val了。
data class Todo(val id:int,val content:String,val done = false)
这意味着你修改TodoList中的一个Todo元素的任意一个属性都必须重新构建一个新的对象。好在Kotlin的数据类有个非常好用的函数copy。例如你想要修改完成状态,只需要通过下面这段代码就能实现。
val todo2 = todo1.copy(done = true)
另外,命令式的代码中是通过Adapter构造器传入的可变列表,所有的操作都是对这个可变列表进行的修改,修改数据后伴随着对RV的UI刷新。可以命令式对数据的修改和UI的修改是一起的,这样很容易就增加耦合。
而声明式中你想要做的就是把修改过的新数据给submitList函数就好,差异会由DiffUtil.ItemCallback计算出来,具体的行为ListAdapter会根据差异来执行。事实上,不可变列表以及不可变属性也是为这套流程服务的。
object diffCallback : DiffUtil.ItemCallback<Todo>() {
override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean
= oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean
= oldItem.content == newItem.content
&& oldItem.done == newItem.done
}
现在来解释一下diffCallback:areItemsTheSame函数比较的是同一个位置的item是否一致,内容不一致,会触发移动或者增删行为,areContentsTheSame函数比较的是一致的item的内容是否一致,内容不一致会触发item刷新的。
总结
在声明式中,RV的任何变化来自于俩个数据。准确地说是来源于俩个数据对比出来的差异。你只需要处理好你的想要的差异就够了,剩下的都会有强大的框架帮你解决。
这也就是很多人说的,命令式是你告诉程序应该做什么,而声明式是你告诉程序你想要什么。
你可能说修改一个item也要创建另一个几乎一模一样的对象,好浪费空间哟!弹你个脑瓜崩,我就问你,项目开发中你是想要一个易迭代维护的项目,还是要一个看起来很痛苦却省资源的项目?