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
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
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
fun Modifier.CoroutineModifier() = composed{
LaunchedEffect(){
}
Modifier.padding(8.dp)
}
总结:
- 当需要自定义可复用的Modifier,并且Modifer内部需要调用Composable函数的时候,Modifer.compose{}才有用