在 JetPack Compose 中, 经常会用到 Modifier,它可以控制组件的行为和外观 如大小,背景等,还可以添加一些交互,如点击、滑动等。Modifier 功能很强大。Modifier 核心代码不到100行,Modifier 中把 Kotlin 高级函数用的很 6,以至于初看代码有点懵 😵 。所以打算写此文章来学习一下 Modifier 中代码是如何执行的。Modifier 核心源码如下 Modifier.kt
interface Modifier {
fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
fun <R> foldOut(initial: R, operation: (Element, R) -> R): R
fun any(predicate: (Element) -> Boolean): Boolean
fun all(predicate: (Element) -> Boolean): Boolean
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
interface Element : Modifier {
override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R =
operation(this, initial)
override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this)
override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this)
}
companion object : Modifier {
override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial
override fun any(predicate: (Element) -> Boolean): Boolean = false
override fun all(predicate: (Element) -> Boolean): Boolean = true
override infix fun then(other: Modifier): Modifier = other
override fun toString() = "Modifier"
}
}
class CombinedModifier(
private val outer: Modifier,
private val inner: Modifier
) : Modifier {
override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R =
inner.foldIn(outer.foldIn(initial, operation), operation)
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R =
outer.foldOut(inner.foldOut(initial, operation), operation)
override fun any(predicate: (Modifier.Element) -> Boolean): Boolean =
outer.any(predicate) || inner.any(predicate)
override fun all(predicate: (Modifier.Element) -> Boolean): Boolean =
outer.all(predicate) && inner.all(predicate)
override fun equals(other: Any?): Boolean =
other is CombinedModifier && outer == other.outer && inner == other.inner
override fun hashCode(): Int = outer.hashCode() + 31 * inner.hashCode()
override fun toString() = "[" + foldIn("") { acc, element ->
if (acc.isEmpty()) element.toString() else "$acc, $element"
} + "]"
}
Modifier.kt文件中的代码结构如下
Element
用于 Modifier 元素的抽象接口。
**Modifier **
提供是实现Modifier接口的静态单例类。
CombinedModifier
将两 Modifier 组合起来,是生成Modifier 链的重要关键。
接下来重点说一下 then
foldIn
foldOut
三个函数
fun foldIn(initial: R, operation: (R, Element) -> R): R
fun <R> foldOut(initial: R, operation: (Element, R) -> R): R
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
为了方便讲解,提供一下几个简化版的 Modifier 实现类和一些函数。
class SizeModifier : Modifier.Element {
override fun toString() = "SizeModifier"
}
class PaddingModifier : Modifier.Element {
override fun toString() = "PaddingModifier"
}
class OffsetModifier : Modifier.Element {
override fun toString() = "OffsetModifier"
}
fun Modifier.size() = this.then(SizeModifier())
fun Modifier.padding() = this.then(PaddingModifier())
fun Modifier.offset() = this.then(OffsetModifier())
then 函数
then 方法是生成 Modifier 链的重要函数。
fun main() {
val modifier = Modifier.size().padding().offset()
println(modifier)
}
---------输出---------------
[SizeModifier, PaddingModifier, OffsetModifier]
下面把 Modifier.size().padding().offset()
单步分解 Modifier 生成链的过程
第一步
val modifier1 = Modifier
⚠️ 此 Modifier 是那个静态单例的 Modifier,不是接口。
第二步
val modifier2 = modifier1.size()
根据 上面 size 函数的定义得 =>
val modifier2 = Modifier.then(SizeModifier())
再根据 静态单例 Modifier 的 then 函数
companion object : Modifier {
override infix fun then(other: Modifier): Modifier = other
}
val modifier2=SizeModifier()
好家伙,Modifier 的 then 方法不就是“吃啥吐啥”, 所以 modifier2 就是 SizeModifier
对象
第三步
val modifier3 = modifier2.padding()
根据 padding 的函数的定义得 =>
modifier3=modifier2.then(PaddingModifier())
因为 modifier2 是 SizeModifier 对象,SizeModifier 实现了Modifier.Element,所以此时看看Modifier.Element then 的函数定义,发现并没有重写 then 函数 ,那就是执行了 Modifier 接口默认的 then 函数。
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
很显然 if 条件不符合, 走了 else ,将 SizeModifier 和 PaddingModifier 组合起来了。
=> modifier3 = CombinedModifier(modifier2, PaddingModifier())
=> modifier3 = CombinedModifier(SizeModifier(), PaddingModifier())
第四步
val modifier4 = modifier3.offset()
这一步和 modifier3 差不多,因此得
//modifier4 =>
CombinedModifier(outer = modifier3, inner = OffsetModifier())
//modifier4 =>
CombinedModifier(outer = CombinedModifier(modifier2, PaddingModifier()), inner = OffsetModifier())
//modifier4 =>
CombinedModifier(outer = CombinedModifier(SizeModifier(), PaddingModifier()), inner = OffsetModifier())
最终 modifier4 得到了一个像俄罗斯套娃的东西
CombinedModifier(outer = CombinedModifier(SizeModifier(), PaddingModifier()), inner = OffsetModifier())
下面用两张图总结
foldOut 函数
有了上面生成链的分析,下面我们就来说说怎么遍历这条链的元素吧。foldOut 函数是给定一个初始值,然后根据我们传入的 operation 函数逻辑按照 Modifier 链上元素计算值并返回。
fun main() {
val modifier = Modifier.size().padding().offset()
val result = modifier.foldOut("start---") { mod, str ->
println(mod)
"$str$mod "
}
print("result=$result")
}
------------------输出----------------
OffsetModifier
PaddingModifier
SizeModifier
result=start---OffsetModifier PaddingModifier SizeModifier
很神奇,foldOut 能把 Modifier 链上的所以元素都过了一遍,它是怎么做的的呢?
为了方便分析我们把上面代码改造一下
fun main() {
// 👇代码 0️⃣
val modifier = Modifier.size().padding().offset()
val initial="start---"
val operation= { mod:Modifier, str:String ->
println(mod)
"$str$mod "
}
val result= modifier.foldOut(initial,operation)
print("result=$result")
根据上面的 Modifier 的 then 函数的分析,我们知道了它形成链的过程。由上面分析可知 代码 0️⃣ 的modifier 是 CombinedModifier 类型,其形式如下
图 2-1👆
既然已经知道是 CombinedModifier
那就看看它的 foldOut 是怎么实现的吧
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R =
outer.foldOut(inner.foldOut(initial, operation), operation)
看上去很简单就两行代码,但做的事却不少,我们将上面函数等效变形一下。
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
// 1先去执行inner的 foldOut函数
val nextInitial:R=inner.foldOut(initial, operation)
//2 把 inner的foldOut的返回值作为outer的foldOut函数的initial参数
return outer.foldOut(nextInitial, operation)
}
第一步先去执行 inner 的 foldOut 函数,由图 2-1 知此时的 inner 就是 OffsetModifier 类型的对象,OffsetModifier 最终继承 Modifier.Element ,它的 foldOut 函数实现如下
override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R =
operation(this, initial)
该函数调用了我们传入的 operation 函数,并把当前对象和传入的intial 作为参数传入。此时我们写的operation 函数体中的代码第一次执行
根据上面的分析可知 第一步的val nextInitial:R=inner.foldOut(initial, operation)
返回值就是 operation 函数的返回值,也就是 "``$``str``$``mod ``"
此时的值为 start---OffsetModifier
参数准备好了就开始准备执行第二步了,第二步时执行了outer 的foldOut 函数 ,根据 图2-1 知
此时 outer 还是 CombinedModifier 类型,它的 foldOut 函数 第一步仍然时执行 它的 inner 的 foldOut 函数,此时它的 inner 时 SizeModifer 的对象,它的类型依然是 Modifier.Element 类似,同上,此时 operation 函数体中的代码被第二次执行
接下来来说执行它的outer 的 foldOut 函数了,它的 outer 是 SizeModifer 对象 他是个Modifier.Element 所以此时 operation 函数体中的代码被第三次执行。
下面用一段伪代码演示 foldOut 执行流程
//0️⃣ CombinedModifier 的 foldOut 函数我们分解成两步
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
//1 先去执行inner的 foldOut函数
val initial1:R=inner.foldOut(initial, operation)
//2 把 inner的fodOut的返回值作为outer的foldOut函数的initial参数
return outer.foldOut(initial1, operation)
}
//1️⃣ inner 如果是Modifier.Element类型,
//根据上面的分析其实 inner的foldOut函数就是执行了operation函数
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
//这时,operation被执行了
val initial1:R=operation(inner,initial)
return outer.foldOut(initial1, operation)
}
//2️⃣ 接着执行 outer的 foldOut,如果它还是 CombinedModifier,那就重复上面步骤
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
val initial1:R=operation(inner,initial)
//假设outer可直接访问到它的inner和outer
val initial2=outer.inner.foldOut(initial1, operation)
return outer.outer.foldOut(initial2, operation)
}
//3️⃣ 如果outer.inner 还是 Modifier.Element类型,同理得
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
val initial1:R=operation(inner,initial)
val initial2:R=operation(outer.inner,initial1)
return outer.outer.foldOut(initial2, operation)
}
//4️⃣ 一直到最后outer 不是CombinedModifier 类型结束
override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
val initial1:R=operation(inner,initial)
val initial2:R=operation(outer.inner,initial1/*上一步的返回值*/)
……
val initialN:R=operation(outer.outer.......outer.inner,initialN_1/*上一步的返回值*/)
return operation(outer.outer.........outer,initialN)
}
**总结: **CombinedModifier 的 foldOut 函数先执行它的inner 的 foldOut,然后执行 outer 的 foldOut 函数,把 inner 的 foldOut 返回值作为outer的 foldOut 函数的参数
foldIn 函数
foldIn 函数和 foldOut 函数差不多,也是需要一个初始值和一个operation 函数,并依据 Modifier 链计算返回结果。但两者还是有些区别的。下面是foldIn简单示例
val modifier = Modifier.size().padding().offset()
val result = modifier.foldIn("start----") { str, mod ->
println(mod)
"$str$mod "
}
print("result=$result")
------------------输出----------------
SizeModifier
PaddingModifier
OffsetModifier
result=start----SizeModifier PaddingModifier OffsetModifier
代码 3-1👆
从输出结果可以看出 foldIn 执行顺序和foldOut 是相反的。
CombinedModifier 的 foldIn 函数
override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R =
inner.foldIn(outer.foldIn(initial, operation), operation)
此函数还是可以分解成两步
override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R{
//1.执行outer的foldIn 函数
val result1=outer.foldIn(initial, operation)
//2.把outer的foldIn 函数的返回值作为inner的foldIn的参数
val result0=inner.foldIn(result1, operation)
return result0;
}
对于代码 3-1 根据上面函数我们就可以进行展开了
//val modifier = Modifier.size().padding().offset()
//👆 等价于 => 👇
val modifier1 = Modifier.size()
val modifier2 = CombinedModifier(inner = PaddingModifier(), outer = modifier1)
val modifier3 = CombinedModifier(inner = OffsetModifier(), outer = modifier2)
//val result = modifier.foldIn("start----"){....}
//👆 等价于 => 👇
val initial = "start---"
val operation = { str: String, mod: Modifier ->
println(mod)
"$str$mod "
}
//0️⃣
modifier3.foldIn(initial, operation)
根据 CombinedModifier 的 foldIn 函数知,代码 0️⃣ 处等价于
// 1️⃣
val result1=modifier2.foldIn(initial,operation)
val result0=OffsetModifier().foldIn(result1,operation)
return result0
因为 modifier2 还是代码 CombinedModifier 类型 所以代码 1️⃣ 处等价于
val result2=modifier1.foldIn(initial,operation)
val result1=PaddingModifier().foldIn(result2,operation)
val result0=OffsetModifier().foldIn(result1,operation)
return result0
代码 3-2👆
modifier1 是 SizeModifier 对象,它是 Modifier.Element 类型,Modifier.Element 的 foldIn 函数如下
override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
此函数很简单就是执行了我们传入的 operation 函数。PaddingModifier、OffsetModifier 也是Modifier.Element类型,所以 **代码 3-2 **就等价于
val result2 = operation(initial, SizeModifier())
val result1 = operation(result2, PaddingModifier())
val result0 = operation(result1, OffsetModifier())
return result0
由此可以看出foldIn 最终按照 Modifier 链依次执行我们传入的 operation 函数。每一次的operation 结果作为下一次operation 函数的初始参数。
总结
Modifier 通过 then 函数,将两个Modifier 组合成一个 CombinedModifier,inner 指向当前节点对应的Modifier,outer 指向下个节点的Modifier,如果outer 也是 CombinedModifier,那么这条Modifier 就可以继续延伸。
foldOut & foldIn
相同点:给定一个初始值,返回一个计算值。会遍历执行 Modifier 链上的每一个元素。
不同点:两个函数遍历的顺序不一样,foldOut 是按照 Modifier 链添加顺序从后往前执行 ,foldIn 是按照Modifier链添加顺序从前往后执行 。