" Jetpack Compose - - Modifier 系列文章 "
📑 《 深入解析 Compose 的 Modifier 原理 - - Modifier、CombinedModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - Modifier.composed()、ComposedModifier 》
📑 《 深入解析 Compose 的 Modifier 原理 - - Modifier.layout()、LayoutModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - DrawModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - PointerInputModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - ParentDataModifier 》
众所周知:原理性分析的文章,真的很难讲的通俗易懂,讲的简单了就没必要写了,讲的繁琐难懂往往大家也不乐意看,所以只能尽量找个好的角度(比如从 Demo 代码示例出发)慢慢带着大家去钻源码,如果确实能帮助到大家理解清楚原理,那就点个赞呗~😄
在正式开始分析 Modifier 相关原理之前,建议你先看看 【Compose 是如何将数据转换成 UI 的?】这篇文章,当你了解了 Compose 的“组合”、“布局”、“绘制”的思维模型后,有助于你更透彻的了解 Modifier 的原理。
📓 什么是 ComposedModifier ?
首先看下 ComposedModifier 类:
private open class ComposedModifier(
inspectorInfo: InspectorInfo.() -> Unit,
val factory: @Composable Modifier.() -> Modifier
) : Modifier.Element, InspectorValueInfo(inspectorInfo)
可以发现 ComposedModifier 是个私有函数,你是没有办法直接创建的。所以如果我们要创建一个 ComposedModifier,则需要使用 Modifier.composed() 扩展函数:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Modifier.composed { }
}
}
}
}
这样,Modifier.composed() 扩展函数会帮我们创建出一个 ComposedModifier。
fun Modifier.composed(
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
factory: @Composable Modifier.() -> Modifier
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))
看到这里,你应该会有疑问三连:
- ComposedModifier 是个啥?
- 为什么要创建一个 ComposedModifier?
- 什么时候又该创建一个 ComposedModifier?
带着这三个疑问,我们开始继续探讨 Modifier 的精髓…
首先我直接告诉你 ComposedModifier 的作用:ComposedModifier 的作用是通过 factory 工厂函数在组合过程时执行 factory 创建出 Modifier 并使用。
这段定义看不懂没关系,跟着我的节奏来,比如看下面这段简单的代码示例:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Modifier.padding(10.dp)
}
}
}
}
正常情况下这么写,当执行到 Modifier.padding() 的时候,会立即创建出一个 LayoutModifier,这个你应该很清楚了,如果不了解,可以先阅读 【 深度解析 Compose 的 Modifier 原理 – Modifier、CombinedModifier 】这篇文章。
现在我调整下代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Modifier.composed { Modifier.padding(10.dp) }
}
}
}
}
这里我把 Modifier.padding() 给包进了 ComposedModifier 里面,那么当程序执行到这段代码的时候,你觉得 Modifier.padding() 还会立即执行吗?
再来看一遍 ComposedModifier 的定义:ComposedModifier 的作用是通过 factory 工厂函数在组合过程时执行 factory 创建出 Modifier 并使用。
这个时候我们可以拆分下定义了,主要就是两个核心:
- 组合过程中
- 执行 factory 创建出 Modifier
先来看下第二点,我们其实从刚刚 Modifier.composed 函数定义就能看出来了:
那么第一点:组合过程中,该如何理解?其实很好理解,你既然创建了 Modifier,肯定要用起来,那么我们就来看看在组合过程中 ComposedModifier 是如何发挥作用的。
我现在再来改下代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Box(Modifier.composed { Modifier.padding(10.dp) })
}
}
}
}
再来看一遍 ComposedModifier 的定义:ComposedModifier 的作用是通过 factory 工厂函数在组合过程时执行 factory 创建出 Modifier 并使用。
所以现在我们就来看看 Box() 内部是在哪里执行 factory 工厂函数的。
- 跳转到 Box():
// Box.kt
@Composable
fun Box(modifier: Modifier) {
Layout({}, measurePolicy = EmptyBoxMeasurePolicy, modifier = modifier)
}
- 跳转到 Layout():
// Layout.kt
@Composable inline fun Layout(
content: @Composable @UiComposable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
... ...
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
... ...
skippableUpdate = materializerOf(modifier),
content = content
)
}
@PublishedApi
internal fun materializerOf(
modifier: Modifier
): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
val materialized = currentComposer.materialize(modifier)
... ...
}
- 跳转 materialize()
// ComposedModifier.kt
fun Composer.materialize(modifier: Modifier): Modifier {
... ...
val result = modifier.foldIn<Modifier>(Modifier) { acc, element ->
acc.then(
if (element is ComposedModifier) {
@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
}
好熟悉呀,出现了 foldIn()!来来,我们解读一下这段代码。
这么一分解,现在你应该很清楚 ComposedModifer 的工厂函数在哪里调用了吧?
但是问题又来了,这么费劲搞一个 ComposedModifier 有啥意义?
Box(Modifier.composed { Modifier.padding(10.dp) })
Box(Modifier.padding(10.dp) // 我这么写不就行了?
我们来看下 Modifier.composed 函数的说明:
注意几个关键词:1. stateful modifiers:有状态的 Modifier;2. reused:重用;3. element-specific state:状态独有。
该如何理解这几个词的意思呢?
来看以下代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
// 写法 1
val modifier1 = Modifier.composed { Modifier.padding(10.dp) }
Box(modifier1)
Text("Compose", modifier1)
// 写法 2
val modifier2 = Modifier.padding(10.dp)
Box(modifier2)
Text("Compose", modifier2)
}
}
}
}
请问:写法 1 和写法 2 有什么区别?
我相信你经过上面 ComposedModifier 的解析流程,应该可以很清晰知道:写法 1 和写法 2 唯一的区别就在于,写法 1 的 Modifier.padding() 会延迟创建(由 ComposedModifier 的 factory 函数创建),它们两者的运行显示结果没有任何区别。
实际上 Modifier.composed() 不是用在案例写的这种场景下,而是用在有状态的 Modifier 的场景。
什么叫有状态的 Modifier?按上面的例子 10.dp 就是一个状态,不过因为在这里已经写死了数值所以 Modifier.padding() 是无状态的,进而 Modifier.composed() 也是无状态的。
现在我们来改下代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
val modifier = Modifier.composed {
var padding by remember { mutableStateOf(10.dp) }
Modifier.padding(padding)
}
Box(modifier)
Text("Compose", modifier)
}
}
}
}
我们把 10.dp 提取出来,这样 Modifier.padding() 就是有状态的了。如果你对 Compose 所谓的有状态、无状态不了解的话,可以看看 【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” – 有状态、无状态、状态提升?】 这篇文章。
现在 Modifier.padding() 就是有状态的,进而 Modifier.composed() 就是有状态的,现在我们再回顾下 Modifier.composed 函数注解:
1. stateful modifiers:有状态的 Modifier;2. reused:重用;3. element-specific state:状态独有
再结合这个例子:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
val modifier = Modifier.composed {
var padding by remember { mutableStateOf(10.dp) }
Modifier.padding(padding) // 1. stateful modifiers:有状态的 Modifier
}
Box(modifier) // 2. reused:重用
Text("Compose", modifier) // 2. reused:重用
}
}
}
}
是不是满足了两条?那 “状态独有” 又是什么意思?该怎么验证呢?
来,我们再改下代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
val modifier = Modifier.composed {
var padding by remember { mutableStateOf(10.dp) }
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
Column {
Box(Modifier.background(Color.Red) then modifier)
Text("Compose", Modifier.background(Color.Blue) then modifier)
}
}
}
}
}
这里我们对 Modifier 又加了一个点击操作:修改 padding 值为 0,看下运行效果:
发现什么没?Text() 组件点击后,padding 修改为 0,却没有影响到 Box 的 padding,这就是 Modifier 状态独有!
那我们再试试不用 Modifier.composed() 函数包起来的场景,比如代码如下:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
var padding by remember { mutableStateOf(10.dp) }
val modifier = Modifier
.padding(padding)
.clickable { padding = 0.dp }
Column {
Box(Modifier.background(Color.Red) then modifier)
Text("Compose", Modifier.background(Color.Blue) then modifier)
}
}
}
}
}
这段代码不难理解吧?来看下效果:
发现没?它们恭喜了 padding,点击任何一个组件,都会影响到另外一个的 padding。
所以到这里,我们就可以总结下 Modifier.compose() 函数的作用了:它会创建一个带状态的 Modifier,这个 Modifier 可以重用,并且状态是独立的。
但是!但是!但是!细心的你应该又会有一个疑问:这有点费啊,我可以有更简单的写法啊,比如:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
var padding1 by remember { mutableStateOf(10.dp) }
val modifier1 = Modifier.padding(padding1).clickable { padding1 = 0.dp }
var padding2 by remember { mutableStateOf(10.dp) }
val modifier2 = Modifier.padding(padding2).clickable { padding2 = 0.dp }
Column {
Box(Modifier.background(Color.Red) then modifier1)
Text("Compose", Modifier.background(Color.Blue) then modifier2)
}
}
}
}
}
既然不想 Box() 和 Text() 的 padding 相互影响,那么分别给它们设置一个 Modifier 不就行了?何必要用 Modifier.composed() 这种花里胡哨,看上去更高端的方法呢?这段代码的逻辑看上去岂不是更清晰?
实际上 Modifier.composed() 的使用场景是:当我们需要创建的 Modifier 需要给它添加一些内部状态,这时候我们需要使用 Modifier.composed() 来为它提供一个 Composable 的上下文环境,从而让我们可以使用 remember。
怎么理解这段话?你可以理解成:当我们需要自定义一个 Modifier 时,同时当 Modifier 内部需要用到 remember 时,就需要用到 ComposeModifier。
比如我们修改下前面的 Modifier.composed() 代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Column {
// 2. 给外部组件调用
Box(Modifier.background(Color.Red) then Modifier.paddingJumpModifier())
Text("Compose", Modifier.background(Color.Blue) then Modifier.paddingJumpModifier())
}
}
}
}
}
// 1. 这里自定义了一个 Modifier.paddingJumpModifier
fun Modifier.paddingJumpModifier() {
var padding by remember { mutableStateOf(10.dp) }
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
如果你这么写会报错:
报错原因很简单,remember 是一个 Composable 函数,你必须要在 Composable 的上下文环境中才能调用。这时候我们就可以使用 Modifier.composed() 为它提供一个 Composable 的上下文环境,从而让我们可以使用 remember。
写法很简单:
fun Modifier.paddingJumpModifier() = composed {
var padding by remember { mutableStateOf(10.dp) }
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
这样你的代码就不会报错了,并且所有调用 Modifier.paddingJumpModifier 的地方内部的 padding 状态也是独立的。
为什么 Modifier.composed() 函数内部就可以使用 remember ?
fun Modifier.composed(
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
factory: @Composable Modifier.() -> Modifier // 看这里,它的工厂函数提供了 Composable 上下文环境
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))
所以,factory 工厂函数提供了 Composable 上下文环境,我们就可以调用 remember。
除了可以调用 remember,我们也可以在 Modifier.compose() 内部开启协程:
fun Modifier.paddingJumpModifier() = composed {
var padding by remember { mutableStateOf(10.dp) }
LaunchedEffect(Unit) {
}
Modifier
.padding(padding)
.clickable { padding = 0.dp }
}
如果你不加 = composed
,那么你调用 LaunchedEffect 就会报错,不信你试试。
最后总结一下 ComposedModifier 或 Modifier.composed():
- 按照官方注释的说法,Modifier.composed() 能创建出带有状态的 Modifier,让这个 Modifier 在多处被重用;
- 有状态的 Modifier 就是被 remember 包着的变量和 Modifier 放在一起作为它的内部状态使用;
- 重用就是 Modifier.composed() 在每一个使用的地方都创建一个内部状态独立的 Modifier,而不会互相影响
- Modifier.composed() 不仅能提供内部状态,在一些需要 Composable 上下文环境,例如 LaunchedEffect 或 CompositionLocal 等地方使用 Modifier,也可以使用它。