从本篇文章开始我们要讲解 Modifier 修饰符,它是一个很精妙的东西,允许我们通过链式调用的方式为组件应用一系列的样式设置,如边距、字体、位移等。在 Compose 中,每个基础的 Composable 组件都有一个 modifier 参数,通过传入自定义的 Modifier 修改组件的样式。
此外,前面我们还提过 Modifier 调用链对顺序敏感,不同的调用顺序会产生不同的 Modifier 链从而影响最终的 UI 效果,这是 Compose 按照 Modifier 链来顺序完成页面布局与绘制的结果。那么 Modifier 链是如何被构建并解析的呢?本系列文章会深入讲解 Modifier 链背后的实现原理。
Modifier 系列使用的是 1.3.0 - alpha01 版本的源码,后续如有更新会显式声明。
1、Modifier 接口与伴生对象
以下是 Modifier 接口的全部内容:
interface Modifier {
/**
* 从 [initial] 初始值开始累积值,通过【从外向内】依次对当前值和每个元素应用 [operation] 操作。
*
* 元素在链表中从左到右依次包裹;在 `+` 表达式或 [operation] 参数顺序中位于左侧的 [Element]
* 会影响其后所有元素。[foldIn] 可用于从父节点或修饰符链头部开始,向最终包裹的子节点累积值。
*/
fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
/**
* 从 [initial] 初始值开始累积值,通过【从内向外】依次对当前值和每个元素应用 [operation] 操作
*
* 元素在链表中从左到右依次包裹;在 `+` 表达式或 [operation] 参数顺序中位于左侧的 [Element]
* 会影响其后所有元素。[foldOut] 可用于从子节点或修饰符链尾部开始,向父节点或头部累积值
*/
fun <R> foldOut(initial: R, operation: (Element, R) -> R): R
/**
* 若修饰符链中任意 [Element] 满足 [predicate] 条件则返回 true
*/
fun any(predicate: (Element) -> Boolean): Boolean
/**
* 若修饰符链中所有 [Element] 满足 [predicate] 条件,或修饰符链为空则返回 true
*/
fun all(predicate: (Element) -> Boolean): Boolean
/**
* 将当前修饰符与另一个修饰符连接
*
* 返回表示当前修饰符后接 [other] 的新 [Modifier]
*/
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
/**
* 修饰符链中的单个元素
*/
@JvmDefaultWithCompatibility
interface Element : Modifier { ... }
/**
* 伴生对象 Modifier 表示空的默认修饰符,不包含任何 [Element] 元素。
* 可通过修饰符扩展函数创建新修饰符:
*
* @sample androidx.compose.ui.samples.ModifierUsageSample
*
* 或作为 [Modifier] 参数的默认值:
*
* @sample androidx.compose.ui.samples.ModifierParameterSample
*/
// 伴生对象实现 `Modifier` 以便作为修饰符扩展表达式的起点
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"
}
}
Modifier 接口内定义了:
- 两组接口函数:
- foldIn() 与 foldOut() 负责遍历 Modifier 链:foldIn() 是正向遍历,从左向右遍历 Modifier 链; foldOut() 是反向遍历,从右向左遍历 Modifier 链,遍历时会执行 operation 指定的操作并累积结果
- any() 与 all() 负责条件判断:any() 是 Modifier 链上有任意一个 Element 满足 predicate 条件就返回 true,all() 是 Modifier 链上所有 Element 都满足 predicate 才返回 true
- 一个默认函数:then() 负责连接两个 Modifier,将它们合并到一个 CombinedModifier 中,只有 Modifier 的伴生对象重写了该函数
- 一个子接口:Element 接口是 Modifier 的子接口,表示一个单个的 Modifier 元素(与 CombinedModifier 这种融合的 Modifier 相对),我们写的 Modifier 链上几乎所有能用到的 Modifier 都是 Element 的实现类,下一节专门介绍它时再看其具体内容
- 一个伴生对象 Modifier:提供默认实现的 Modifier 对象,常用于 Modifier 链的起点以及函数参数的默认值
本节我们主要来说 Modifier 的伴生对象,从它的两个作用:Modifier 链的起点、函数参数的默认值讲起。
1.1 Modifier 作为调用链起点
Kotlin 使用 companion object 声明一个伴生对象,伴生对象有一个特性,就是在哪个类或接口内声明了这个伴生对象,就可以用这个类或接口的名字代表这个伴生对象。比如对于 Modifier 接口而言,访问其伴生对象的完整写法是 Modifier.Companion,但由于伴生对象的特性,Companion 可以省略,因此 Modifier 单拿出来就可以表示这个接口的伴生对象。
伴生对象的 Modifier 实现了 Modifier 接口,这不是伴生对象常用的声明方式,而是 Compose 为了让伴生对象作为 Modifier 调用链的起点,专门这么写的。此外,伴生对象实现 Modifier 接口时,只给了默认实现,或者说一个最基础的实现,比如 foldIn() 与 foldOut() 直接返回 initial 参数,any() 和 all() 无视 predicate 参数直接返回一个 Boolean 字面值,这样实现一个“白板” Modifier 对象是为了不对 Modifier 调用链的结果产生任何影响。
1.2 Modifier 作为函数参数默认值
至于让伴生对象的 Modifier 作为函数参数的默认值,也是为了便于调用者的灵活使用,提升易用性。
比如现在自定义一个 Composable 展示一个定制的 Box:
@Composable
fun Custom() {
Box(
Modifier
.size(30.dp)
.background(Color.Blue)
)
}
为了让调用方进一步控制 Box 的外观,需要让 Custom() 暴露一个 Modifier 参数给外界:
@Composable
fun Custom(modifier: Modifier) {
Box(
modifier
.size(30.dp)
.background(Color.Blue)
)
}
假如调用方就想使用默认的 Box,那就不需要传 modifier 参数,因此要给 modifier 一个默认值:
// = 左侧的 Modifier 表示接口类型,= 右侧的 Modifier 是伴生对象
@Composable
fun Custom(modifier: Modifier = Modifier) {
Box(
modifier
.size(30.dp)
.background(Color.Blue)
)
}
Compose 官方建议将 Modifier 作为可组合函数中第一个有默认值的参数,因为这样的参数有一个特权,就是在调用函数时可以不用填它的参数名,就像上面的 Custom() 内的 Box,不用写成 Box(modifier = modifier.size().background())
。
2、Modifier 的链式调用
本节会介绍促成 Modifier 链式调用的函数与类/接口,它们就是上一节贴出的 Modifier 接口内定义的内容。
2.1 then()
在 1.1 节中我们已经讲过了 Modifier 调用链的起点就是 Modifier 的伴生对象,而 Modifier 链后面的部分则都是通过 then() 来连接的:
@Suppress("ModifierFactoryExtensionFunction")
@Stable
@JvmDefaultWithCompatibility
interface Modifier {
// 若 other 与当前 Modifier 不是同一个对象则将二者合并到 CombinedModifier 中返回
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
}
then() 通过接收一个 Modifier 参数 other,将当前的 Modifier 对象与 other 合并到 Modifier 接口的直接实现类 CombinedModifier 中,最后返回 CombinedModifier 的方式实现了 Modifier 调用链的连接。而作为 Modifier 链的起点,伴生对象的 then() 就是直接取它后面的 Modifier:
// 当前版本只有 Modifier 的伴生对象重写了 then()
companion object : Modifier {
override infix fun then(other: Modifier): Modifier = other
}
链式调用的原理就是以上内容。下面我们再看看常用的 Modifier 链上的函数都是如何使用 then() 的:
fun Modifier.background(
color: Color,
shape: Shape = RectangleShape
) = this.then(
Background(
color = color,
shape = shape,
inspectorInfo = debugInspectorInfo {
name = "background"
value = color
properties["color"] = color
properties["shape"] = shape
}
)
)
@Stable
fun Modifier.size(size: Dp) = this.then(
SizeModifier(
minWidth = size,
maxWidth = size,
minHeight = size,
maxHeight = size,
enforceIncoming = true,
inspectorInfo = debugInspectorInfo {
name = "size"
value = size
}
)
)
@Stable
fun Modifier.padding(
horizontal: Dp = 0.dp,
vertical: Dp = 0.dp
) = this.then(
PaddingModifier(
start = horizontal,
top = vertical,
end = horizontal,
bottom = vertical,
rtlAware = true,
inspectorInfo = debugInspectorInfo {
name = "padding"
properties["horizontal"] = horizontal
properties["vertical"] = vertical
}
)
)
都是 this.then()
,然后给 then() 传一个 Modifier 对象,这里不论是 Background、SizeModifier 还是 PaddingModifier,都是 Modifier 接口的实现类,后面会在相应的章节中解析它们的原理。
2.2 CombinedModifier 与 Modifier.Element
之所以要将二者放在一起说,是因为 Modifier.Element 是为 CombinedModifier 服务的。Modifier.Element 在继承 Modifier 时,将其设计为一个单体的 Modifier,内部不包含任何其他的 Modifier 对象:
/**
* 一个包含在 [Modifier] 链中的单个元素
*/
@JvmDefaultWithCompatibility
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)
}
Element 的“单个元素”是与 CombinedModifier 这种复合的 Modifier 相对而言的。由于内部不包含其他的 Modifier 对象,因此在继承 Modifier 的接口函数时,只考虑自己即可。比如遍历功能的两个函数,它只需要在 operation() 中传入自己 this,判断功能的两个函数也只对自己 this 做 predicate() 判断就可以了。
而 CombinedModifier 是将两个 Modifier 合并到一起,因此在实现时需要让两个 Modifier 都参与计算:
class CombinedModifier(
internal val outer: Modifier,
internal val inner: Modifier
) : Modifier {
// 从左至右正序遍历 Modifier 链。先计算 outer,outer 计算结果作为后续计算 inner 的初始值
override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R =
inner.foldIn(outer.foldIn(initial, operation), operation)
// 与 foldIn() 方向相反,是从内层 Modifier 开始向外层进行计算,逆序从右向左遍历
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)
...
}
CombinedModifier 中的 outer 是调用函数的一方,而 inner 是函数参数。比如 modifer.background()
,outer 就是 modifier,inner 就是 Background。由于 outer 和 inner 都有可能是 CombinedModifier 这种复合的 Modifier,因此在进行计算时,需要做相应的递归计算,直到遇到单个的 Modifier,也就是 Element,才能计算出一个具体的结果,然后将结果向上合并,得出最终结果。
我们需要清楚一件事,无论 Modifier 链上有多少个 CombinedModifier,它最终总会有 Element 作为叶子节点帮你计算出一个具体的结果,然后再走递归返回的过程,计算出过程中的每一个中间结果,直到最终结果。所以我们才说,Element 就是作为 CombinedModifier 的叶子节点处理单个 Modifier 的辅助类,除了 Modifier 的伴生对象以及 CombinedModifier,
其余几乎所有的 Modifier 都会直接或间接地实现 Element。
提到 Modifier 的伴生对象,我们再回头看一下它的实现:
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、
// 判断组的函数,任一判断返回 false、全量判断返回 true 作为后续的初值,将链的计算结果交给后面
override fun any(predicate: (Element) -> Boolean): Boolean = false
override fun all(predicate: (Element) -> Boolean): Boolean = true
// Modifier.then(other) 的结果就是 other,相当于忽略了伴生对象的 Modifier
override infix fun then(other: Modifier): Modifier = other
override fun toString() = "Modifier"
}
你会发现,它的默认实现都不会影响 Modifier 链条的计算结果,除了在占位时有些存在感,其他情况下基本上就是个“透明人”。
Modifier 接口的直接子类或子接口有三个,分别是实现类 CombinedModifier、伴生对象 Modifier.Companion 以及子接口 Modifier.Element,本节过后它们已经全部登场了,后续的内容还是在它们的基础上发展的。
3、Modifier.composed() 与 ComposedModifier
2.1 节我们提到,Modifier 调用链的连接主要是靠 then()。但除了 then() 其实还有一个 Modifier 函数可以起到连接 Modifier 的作用,就是 composed()。当然了,该函数实际上是在内部调用了 then() 才具备连接功能的,而且,composed() 的主要用途也并不是连接 Modifier,而是实现有状态的 Modifier 并让该 Modifier 可以在多个地方复用。
下面让我们以 composed() 作为切入点,看看如何实现一个有状态的 Modifier 并复用它。
3.1 composed() 与 ComposedModifier 概况
composed() 必传一个生产 Modifier 的 Composable 工厂函数 factory:
/**
* 声明一个为每个被修饰元素实时组合的 [Modifier]。通过 [composed] 可实现【有状态修饰符】,
* 此类修饰符会为每个被修饰元素维护实例专属的状态,使得同一 [Modifier] 实例可安全地复用于多个元素,
* 同时保持各元素的独立状态。
*
* 若指定 [inspectorInfo],此修饰符将在开发工具中可见。需在此参数中指定原始修饰符的名称及参数。
*
* 示例用法可参考:
* @sample androidx.compose.ui.samples.InspectorInfoInComposedModifierSample
* @sample androidx.compose.ui.samples.InspectorInfoInComposedModifierWithArgumentsSample
*
* 若直接将 [Modifier] 应用到元素树节点,必须调用 [materialize] 以创建实例专属的修饰符。
*/
fun Modifier.composed(
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
factory: @Composable Modifier.() -> Modifier
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))
它将 factory 传入 ComposedModifier 后,使用 then() 将 ComposedModifier 连接到整个 Modifier 链上便结束了自己的工作。实际上,composed() 就是一个连接 ComposedModifier 的辅助函数,因为 ComposedModifier 是一个 private 类,开发者无法直接获取它,只能通过 composed() 使用它:
private open class ComposedModifier(
inspectorInfo: InspectorInfo.() -> Unit,
// 创建 Modifier 的工厂函数
val factory: @Composable Modifier.() -> Modifier
) : Modifier.Element, InspectorValueInfo(inspectorInfo)
ComposedModifier 是一个比较特殊的 Modifier,它本身没有提供什么实际功能,它只是将我们提供的 Modifier 包在一个工厂函数里,而不是直接、立即创建这个 Modifier。在 Compose 进行组合时,才会运行工厂函数生产出我们提供的 Modifier 对象。
比如说:
val modifier1 = Modifier.padding(8.dp)
val modifier2 = Modifier.composed { Modifier.padding(8.dp) }
对于 modifier1 而言,当代码执行到 Modifier.padding()
时会立即生成一个 PaddingModifier 然后赋值给 modifier1;而对于 modifier2,它只会立即得到一个 ComposedModifier,而 composed() 的尾随 lambda 表达式作为其 factory 参数被传入更底层的代码中,不会立即被执行,等到时机成熟,执行 factory 工厂函数时,才会生产出 PaddingModifier。
至于什么是成熟的时机,我们马上来看。
3.2 工厂函数执行流程
由于 ComposedModifier 的工厂函数 factory 实际上是在组合阶段被执行的,需要放在具体的 Composable 组件中才能分析具体的代码流程,因此我们将 ComposedModifier 放在 Box 中作为示例:
Box(Modifier.composed { Modifier.padding(8.dp) })
Box() 的参数是一个 ComposedModifier,进入 Box():
@Composable
fun Box(modifier: Modifier) {
Layout({}, measurePolicy = EmptyBoxMeasurePolicy, modifier = modifier)
}
执行 Layout(),ComposedModifier 被传入 materializerOf():
@Suppress("ComposableLambdaParameterPosition")
@UiComposable
@Composable inline fun Layout(
content: @Composable @UiComposable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
...
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
...,
skippableUpdate = materializerOf(modifier),
content = content
)
}
materializerOf() 又把 ComposedModifier 向下传给 Composer 的 materialize():
@PublishedApi
internal fun materializerOf(
modifier: Modifier
): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
val materialized = currentComposer.materialize(modifier)
update {
set(materialized, ComposeUiNode.SetModifier)
}
}
Composer 的 materialize() 会将逻辑修饰符转换为具体的可应用于元素的实例,主要是对 ComposedModifier、FocusEventModifier 与 FocusRequesterModifier 进行单独的处理。当正向遍历 Modifier 链遇到这三种 Modifier 时,执行它们保存的工厂函数 factory 得到开发者提供的 Modifier 并连接到被遍历的 Modifier 链上:
@Suppress("ModifierFactoryExtensionFunction")
fun Composer.materialize(modifier: Modifier): Modifier {
// 如果 modifier 不是 ComposedModifier、FocusEventModifier、FocusRequesterModifier 则直接返回
if (modifier.all {
it !is ComposedModifier && it !is FocusEventModifier && it !is FocusRequesterModifier
}
) {
return modifier
}
...
// foldIn() 正向遍历 modifier,遍历时判断每个 Modifier 的实际类型,并调用相应的工厂函数
// 生产出新的 Modifier
val result = modifier.foldIn<Modifier>(Modifier) { acc, element ->
acc.then(
if (element is ComposedModifier) {
// 取出 ComposedModifier 的 factory 强转成它声明的函数类型
@kotlin.Suppress("UNCHECKED_CAST")
val factory = element.factory as Modifier.(Composer, Int) -> Modifier
// 执行 factory 生产出开发者提供的 Modifier
val composedMod = factory(Modifier, this, 0)
// 递归调用自己,对 composedMod 内可能包含的 ComposedModifier 做相同的处理
materialize(composedMod)
} else {
var newElement: Modifier = element
if (element is FocusEventModifier) {
// 对 factory 进行类型转换后调用它
@Suppress("UNCHECKED_CAST")
val factory = WrapFocusEventModifier
as (FocusEventModifier, Composer, Int) -> Modifier
newElement = newElement.then(factory(element, this, 0))
}
// The same is true for FocusRequesterModifier and focusTarget()
if (element is FocusRequesterModifier) {
@Suppress("UNCHECKED_CAST")
val factory = WrapFocusRequesterModifier
as (FocusRequesterModifier, Composer, Int) -> Modifier
newElement = newElement.then(factory(element, this, 0))
}
newElement
}
)
}
endReplaceableGroup()
return result
}
到这一步能看出 ComposedModifier 的工厂函数是在组合过程中,当正向遍历 Modifier 链时被执行生产出开发者指定的 Modifier 并连接回 Modifier 链中。相当于用 ComposedModifier 的工厂函数生产出的 Modifier 替换掉 ComposedModifier。
这样我们再回头看使用 composed() 生成的 Modifier 与不使用 composed() 生成的 Modifier 有什么区别:
val modifier1 = Modifier.composed { Modifier.padding(8.dp) }
val modifier2 = Modifier.padding(8.dp)
唯一区别是使用 composed() 会修改其内部的 Modifier 生成的时间节点(或者说延迟到组合阶段),在显示上,二者没有区别。
3.3 有状态的 Modifier
了解工厂函数的执行流程,实际上是为了便于理解有状态的 Modifier 之间相互独立的特性。回看 composed() 的注释内容,它提醒我们 composed() 就是用来创建有状态的 Modifier 的,并且它们在不同的组件间相互独立:
声明一个 Modifier 的即时组合(just-in-time composition),该组合将用于修改每个元素。
composed
可用于实现具有每个修改元素的实例特定状态的有状态修饰符(stateful modifiers),允许安全地重用相同的 Modifier 实例用于多个元素,同时保持元素特定的状态。
Modifier 的状态与 Composable 函数的状态是一个意思,指其内部有一个或多个变量,这些变量就是它的状态。
像上面的 modifier2,它就相当于一个没有状态的 PaddingModifier。为什么说是“相当于没有状态”呢?因为 PaddingModifier 虽然定义了 5 个成员属性,但都是 val 的,这些内部状态不可变:
private class PaddingModifier(
val start: Dp = 0.dp,
val top: Dp = 0.dp,
val end: Dp = 0.dp,
val bottom: Dp = 0.dp,
val rtlAware: Boolean,
inspectorInfo: InspectorInfo.() -> Unit
)
既然 PaddingModifier “没有”状态,那么包含它的 ComposedModifier 也没有状态。所以我们换一种写法,让 ComposedModifier 有状态:
val modifier = Modifier.composed {
var padding = 8.dp
Modifier.padding(padding)
}
将一个有状态的 Modifier 用于多个组件中:
Box(modifier)
Text("James", modifier)
我们刚刚分析过 ComposedModifier 工厂函数的执行流程,它会在组件的组合阶段被执行并生产出指定的 Modifier。因此,Box 与 Text 内会各有一份相互独立的 PaddingModifier。这意味着,有状态的 Modifier 的状态是相互独立,各自管理的。这样便可以使用 composed() 将同一个 Modifier 用于多个不同的组件中,并且分别对它们做状态管理:
setContent {
val modifier = Modifier.composed {
// 没有 remember(),clickable 对 padding 的赋值就会被初始化覆盖掉
var padding by remember { mutableStateOf(8.dp) }
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
Box(modifier)
Text(text = "Test", modifier)
}
由于 modifier 是会进入到 Composable 组件内部运行,而组件会进行重组,为了让 modifier 内的状态不在重组时被重复初始化,因此需要借助 remember() 缓存 padding 的状态值。
最后我们将有状态与无状态的 Modifier 做一个对比:
setContent {
val modifier1 = Modifier.composed {
var padding by remember { mutableStateOf(8.dp) }
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
var padding by remember { mutableStateOf(8.dp) }
val modifier2 = Modifier
.padding(padding)
.clickable { padding = 0.dp }
Column {
Box(Modifier.background(Color.Blue) then modifier1)
Text(text = "Test", Modifier.background(Color.Green) then modifier1)
}
}
modifier1 使用 composed(),而 modifier2 不使用 composed(),分别将它们放入 Column 的组件中做对比测试,发现使用 modifier1 时,点击某个组件,只会改变它自己的 padding;而使用 modifier2 时,点击一个组件,所有组件的 padding 都变为 0。
这是因为 composed() 创建的 Modifier 在每个组件运行时,都会通过工厂函数创建一个单独的 Modifier 对象仅供该组件使用,组件间的 Modifier 各自独立,相互之间没有影响。而不使用 composed() 创建的 Modifier 是立即创建,在多个组件中共享使用,一个组件修改了 Modifier 会影响到所有使用该 Modifier 的组件。
3.4 用途
composed() 更多是用在自定义 Modifier 中:
fun Modifier.paddingJumpModifier() = composed {
var padding by remember { mutableStateOf(8.dp) }
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
composed() 的 factory 参数打了 @Composable 注解,因此可以提供调用 Composable 函数的上下文环境,可以在其内部调用 remember() 等需要 @Composable 调用环境的函数。
此外,在需要使用协程或 CompositionLocal 时,也可以通过 composed() 提供上下文环境:
fun Modifier.coroutineModifier() = composed {
// 通常使用协程时也是与某个状态搭配使用
var status by remember { mutableStateOf(0) }
LaunchedEffect(status) {
}
// 返回值需是 Modifier
Modifier
}
fun Modifier.localModifier() = composed {
// 访问 CompositionLocal,由于 get() 是 Composable 函数,因此需要一个 @Composable 环境
LocalContext.current
Modifier
}
源码中使用了 ComposedModifier 的地方:
@JvmDefaultWithCompatibility
interface AnimatedVisibilityScope {
@ExperimentalAnimationApi
fun Modifier.animateEnterExit(
enter: EnterTransition = fadeIn() + expandIn(),
exit: ExitTransition = fadeOut() + shrinkOut(),
label: String = "animateEnterExit"
): Modifier = composed(
inspectorInfo = debugInspectorInfo {
name = "animateEnterExit"
properties["enter"] = enter
properties["exit"] = exit
properties["label"] = label
}
) {
// 因为要调用 createModifier() 这个 @Composable 函数,所以用了 composed()
this.then(transition.createModifier(enter, exit, label))
}
}
再看 AnimationModifier,因为要用到 rememberCoroutineScope() 与 remember(),所以用了 composed():
fun Modifier.animateContentSize(
animationSpec: FiniteAnimationSpec<IntSize> = spring(),
finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
): Modifier = composed(
inspectorInfo = debugInspectorInfo {
name = "animateContentSize"
properties["animationSpec"] = animationSpec
properties["finishedListener"] = finishedListener
}
) {
// TODO: Listener could be a fun interface after 1.4
val scope = rememberCoroutineScope()
val animModifier = remember(scope) {
SizeAnimationModifier(animationSpec, scope)
}
animModifier.listener = finishedListener
this.clipToBounds().then(animModifier)
}
3.5 总结
最后我们来总结一下 Modifier.composed():
- 可以实时的创造出带有状态的 Modifier,并让这个 Modifier 在多个位置被复用
- 状态是指 remember() 包着的变量作为 Modifier 的内部状态来使用
- 复用是指 composed() 会在每一个使用处创建一个独立的 Modifier,每一个 Modifier 的内部状态就都是相互独立的,而不是在多处共享的
- 由于是在组合过程中创建 Modifier,因此工厂函数被做成 Composable 函数,可以在其内部调用其他 Composable 函数
- 当自定义 Modifier 时如果需要调用 Composable 函数,原则上,就给自定义的 Modifier 包一层 composed() 即可