Modifier

Tips:

  • Compose推荐将Modifier作为自定义Compose函数的第一个默认参数。

then

  • 用于合并Modifier的函数.
  • 如果传入的Modifier和this指针不是同一个,就进行Combined操作
infix fun then(other: Modifier): Modifier =
    if (other === Modifier) this else CombinedModifier(this, other)

CombinedModifier

  • 将两个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)   //先加入的Modifier,先实现并引用      调用者是outer(可以理解为正序遍历)
    	// modifier1.then(modifier2).then(modifier3).then(modifier4)   应用顺序,1->2->3->4   

    override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R =
        outer.foldOut(inner.foldOut(initial, operation), operation)  //后加入的Modifier,先实现并引用   调用者是outer (可以理解为倒序遍历)
    	// modifier1.then(modifier2).then(modifier3).then(modifier4)   应用顺序,4->3->2->1
    	
    override fun any(predicate: (Modifier.Element) -> Boolean): Boolean =
        outer.any(predicate) || inner.any(predicate)    //根据给定的条件,去递归每一个Modifier,判断是否有存在predicate为true的结果(包含匹配)

    override fun all(predicate: (Modifier.Element) -> Boolean): Boolean =
        outer.all(predicate) && inner.all(predicate)   //根据给定的条件,去递归每一个Modifier,判断是否全量predicate都为true的结果(全量匹配)

    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"
    } + "]"
}

Element

  • Element接口,是Modifier接口的一个子接口实现。
  • Modifier中,除了CombinedModifier、伴生对象,其他所有的Modifier对是Element接口的直接/间接实现
  • Element实现的foldIn/foldOut函数,最终会转向传入的operation()高阶函数
    interface Element : Modifier {
        override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
            operation(initial, this)//通过operation操作,将自己应用到initial

        override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R =
            operation(this, initial)//通过operation操作,将自己应用到initial

        override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this)//直接检查自己并返回

        override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this)//直接检查自己并返回
    }
  • 可以简单理解成Element是每一个Modifier的默认实现

ComposedModifier

  • 实现了Element接口
private open class ComposedModifier(
    inspectorInfo: InspectorInfo.() -> Unit,
    val factory: @Composable Modifier.() -> Modifier  //通过factory,将开发者的Modifier真正的创建并应用(这个行为在真正的Compose的时候,才会执行)
) : Modifier.Element, InspectorValueInfo(inspectorInfo)

Modifier.composed()

fun Modifier.composed(
    inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
    factory: @Composable Modifier.() -> Modifier
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))//factory中传入的Modifier,会被装载到ComposedModifier中


Box(Modifier.composed{// 这种写法,知道Modifier被真正使用的时候,才会真的被创建出来
    	Modifier.padding(20.dp)
    }
)

  • 如上述代码,在Box创造之前,Modifier.composed()已经执行过了,但是lambda内部的Modifier.padding(20.dp)并没有执行,因为没有invoke过factory。
  • 随后Modifier.composed()的返回值,被Box作为入参,一层层往里传递,最终调用到ComposedModifier.kt内部的materialize()函数

materialize

@Suppress("ModifierFactoryExtensionFunction")
fun Composer.materialize(modifier: Modifier): Modifier {
    if (modifier.all { it !is ComposedModifier }) return modifier

    // This is a fake composable function that invokes the compose runtime directly so that it
    // can call the element factory functions from the non-@Composable lambda of Modifier.foldIn.
    // It would be more efficient to redefine the Modifier type hierarchy such that the fold
    // operations could be inlined or otherwise made cheaper, which could make this unnecessary.

    // Random number for fake group key. Chosen by fair die roll.
    startReplaceableGroup(0x48ae8da7)

    val result = modifier.foldIn<Modifier>(Modifier) { acc, element ->  // 就这一句比较关键,使用的是foldIn进行正序遍历应用
        acc.then(//acc 就是foldIn函数中的 initial: R  也就是外面传入的Modifier
            if (element is ComposedModifier) { // 如果穿入的Modifier是ComposedModifier,那么调用factory(),并且持续递归,将Tree内部的所有Modifier,都是用factory创建出来,并替换ComposedModifier.
                @kotlin.Suppress("UNCHECKED_CAST")
                val factory = element.factory as Modifier.(Composer, Int) -> Modifier
                val composedMod = factory(Modifier, this, 0)
                materialize(composedMod)
            } else element
        )
    }

    endReplaceableGroup()
    return result
}

composed编写的代码

val modifier = Modifier.composed {
    val padding by remember { mutableStateOf(8.dp) }
    Modifier.padding(padding).clickable { padding = 16.dp }
}

Column{
    Box(modifier)
    Text("ryanhuen", modifier)
}
  • 上述代码,会创建一个Box和Text。并且应用了同一个modifier。当点击他们其中任意一个的时候,并不会导致他们所有的padding都生效。
  • 原因是,composed的factory创建出来的modifier,其实是两份内存数据,因为如上面所说,composed的factory是真正在Box和Text内部采取创建的,所以自然就是两份内存数据。
  • 这样,我们写的一份Modifer代码,就能起到“有状态”的作用,分别作用给不同的UI控件。

useless

  • 说实在的,上面这种写法,一点用都没有,徒增麻烦,写一些让人看不懂的代码,以为超级牛批。
  • 实际上,如果我们需要让两个组件不复用Modifier,那我们写两份就好了。代码清晰可见,才是最优先的。

真实应用场景


Column{
    Box(CustomGlobalPadding)
    Text("ryanhuen", CustomGlobalPadding)
}

fun Modifier.CustomGlobalPadding() = Modifier.composed{
    val padding by remember { mutableStateOf(8.dp) }
    Modifier.padding(padding).clickable { padding = 16.dp }
}
  • 上述代码,就是一个真实的典型用例,因为我们需要封装一个全局很多组件使用的Modifer的时候,那么composed的作用,就体现出来了

协程Modifier

  • 也会有制造一个协程环境的Modifer
fun Modifier.CoroutineModifier() = composed{
    LaunchedEffect(){
    	
    }
    Modifier.padding(8.dp)
}

总结:

  • 当需要自定义可复用的Modifier,并且Modifer内部需要调用Composable函数的时候,Modifer.compose{}才有用

 评论