阅读 311

Modifier源码,Kotlin高阶函数用的真6

在 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文件中的代码结构如下

Modifier.png

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())

下面用两张图总结 image.png

image.png

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 类型,其形式如下 image.png 图 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 函数体中的代码第一次执行 image.png 根据上面的分析可知 第一步的 val nextInitial:R=inner.foldOut(initial, operation) 返回值就是 operation 函数的返回值,也就是 "``$``str``$``mod ``" 此时的值为 start---OffsetModifier 参数准备好了就开始准备执行第二步了,第二步时执行了outer 的foldOut 函数 ,根据 图2-1 知 image.png 此时 outer 还是 CombinedModifier 类型,它的 foldOut 函数 第一步仍然时执行 它的 inner 的 foldOut 函数,此时它的 inner 时 SizeModifer 的对象,它的类型依然是 Modifier.Element 类似,同上,此时 operation 函数体中的代码被第二次执行 image.png 接下来来说执行它的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 就可以继续延伸。 未命名绘图.png

foldOut & foldIn 相同点:给定一个初始值,返回一个计算值。会遍历执行 Modifier 链上的每一个元素。 不同点:两个函数遍历的顺序不一样,foldOut 是按照 Modifier 链添加顺序从后往前执行 ,foldIn 是按照Modifier链添加顺序从前往后执行 。 Modifier函数流程图-第 2 页.png

文章分类
Android
文章标签