一、概念
在 NodeKind.kt 文件中根据功能对 Modifier 类型进行了分类。

1.1 什么时候用到
三大使用场景:
1.2 为组合函数添加 Modifier 参数
任何一个组合项都应该有一个 Modifier 参数,以便让调用方进行调整。
- 放在第一个可选参数位置:由于是可选参数,放在所有必传参数后面,这样调用方就可以选择是否传递那些有默认值的可选参数,否则就必须被强制性的先指定Modifier。
- 作用于内部根节点上:调用方一般只需要调整根节点的布局,对于子元素别的配置可通过传递其它的参数。
- 避免重复使用:将同一个 Modifier 传递给不同的可组合共享,可能引起不必要的重组。
- 使用时设置的效果会覆盖掉传入的Modifier效果:调用方将设置过 size 的 Modifier 传入,内部使用的时候时候又设置了 size,这样就会覆盖掉传入的效果,即可组合项内部设置的效果无法被外部控制。
@Composable
fun ParentLayout(modifier: Modifier = Modifier) {
//调用时指定对齐方式
Avatar(Modifier.align(Alignment.CenterHorizontally))
}
@Composable
fun Avatar(modifier: Modifier = Modifier) {
Image(
painter = painterResource(id = R.drawable.icon),
contentDescription = "Icon Image",
//使用时,用传入的modifier
modifier = modifier
.wrapContentSize()
.background(Color.Gray)
.padding(18.dp)
.border(5.dp, Color.Magenta, CircleShape)
.clip(CircleShape)
)
}
@Composable
fun TestComposable(a: Int, b: String, modifier: Modifier = Modifier) {...}
1.3 底层结构原理
Layout 阶段, Modifier#then 创建 Element 加入 Modifier chain 中。Element 是无状态的,重组中会重新生成,Element 会在组合中创建有状态的 Modifier Node。Modifier Node 有状态,重组中仅当状态发生变化时被更新,否则不会重新生成。Modifier Node 是 Compose 1.5 引入的新优化,目的就是通过存储 Modifier 状态参与比较,提升重组性能。
| interface Modifier { companion object : Modifier {...} class CombinedModifier {...} fun then() {...} fun Modifier.composed() {...} | |
| Element 子接口 | 调用 Modifier 不同的配置方法会返回各种 Modifier 的实现类对象(如 .size() 返回 SizeElement、.background() 返回 BackgroundElement),这些实现类又都是 Element 类型。 |
| companion 伴生对象 | 伴生对象实现了 Modifier,因此类名 Modifier 可以用作链式调用的开头。 |
| CombinedModifier | class CombinedModifier( 内部维护的数据结构,用于连接调用链中的每个 Element 结点,类似于俄罗斯套娃一样的装饰者模式。 |
| then() 函数 | 用于连接两个 Element 的方法,底层就是用的 CombinedModifier 结构。 |
| composed() 扩展函数 | fun Modifier.composed( 内部持有一个工厂 Lambda 来生产 Modifier,用于实现内部有状态的 Modifier,如监听手势的修饰符 .pointerInput() 底层就是用到 composed()。 |
1.3.1 链式调用


Modifier.size(100.dp).background(Color.Red).padding(10.dp)
当链式调用 Modifier 的时候,先调用的会包裹后调用的,最里层是 Layout Node。
- 调用 .size() 生成 SizeElement。
- 调用 .background() 生成 CombinedModifier(outer = SizeElement, inner = BackgroundElement)。
- 调用 .padding() 生成 CombinedModifier(outer = CombinedModifier(SizeElement, BackgroundElement), inner = PaddingElement)。
1.3.2 自定义 Modifier
//自定义测量和摆放
fun Modifier.XXX(): Modifier = then(
layout { measurable, constraints ->
//TODO...
}
}
//去掉涟漪效果
fun Modifier.clickableNoRipple(onClick: () -> Unit): Modifier = composed {
clickable(
onClick = onClick,
interactionSource = remember { MutableInteractionSource() },
indication = null
)
}
二、添加额外信息

在Compose的内部,是用树型结构来存储一次重组过程中每个Composable函数节点的。一颗就是我们现在看到的重组树,另外一颗则是我们看不到的语义树。
语义树完全不参与绘制和渲染工作,因此是完全不可见的,它只为 Accessibility 和 Test 服务。Accessibility需要根据语义树的节点内容进行发音,Test则需要根据语义树找到想要测试的节点来执行测试逻辑。
绝大部分情况下不需要专门为语义树去做什么事情,标准的组合项已经在内部处理好了这些工作(Button嵌套一个Text,它俩是独立控件,Talkback会单独发声,但只要控件可点击,就会自动将所有子节点合并)。若使用了一些底层API自行绘制界面(日历选中8号,只会发音选中日历),这些工作就得自己来做了。
| .semantics( 允许向当前Compose控件添加键值对形式的额外信息,但是不能覆写。 |
| .clearAndSetSemantics( 相对用得更多一些,它会把Compsoe控件之前携带的一些额外信息都清除掉。 |
本文围绕Android中Compose的Modifier展开。介绍了Modifier的使用场景、添加参数的规则及底层结构原理。还阐述了通过它修改外观(尺寸、样式等)、添加额外信息、交互功能(点击、滚动等)以及处理用户输入的方法,帮助开发者更好地运用Modifier。
973

被折叠的 条评论
为什么被折叠?



