修饰符的顺序非常重要! 非常重要!! 非常重要!!!
借助修饰符,可以修饰或扩充可组合项。可以使用修饰符来执行以下操作:
更改可组合项的大小、布局、行为和外观
添加信息,如无障碍标签
处理用户输入
添加高级互动,如使元素可点击、可滚动、可拖动或可缩放
修饰符是标准的 Kotlin 对象。通过调用某个 Modifier 类函数来创建修饰符
最佳实践是让所有可组合项都接受 modifier 参数,并将该修饰符传递给其发出界面的第一个子级。这样做可以提高代码的可重用性,使其行为更可预测且更直观。
可组合函数必须接受类型为Modifier的参数。此参数必须命名为“modifier”,并且必须作为可组合函数参数列表中的第一个可选参数出现。可组合函数不得接受多个Modifier参数。
如果可组合函数的内容具有自然最小尺寸 - 即,如果给定 minWidth 和 minHeight 约束为零,它能够以非零尺寸进行测量 - 则参数的默认值modifier必须为Modifier-表示空的Modifier类型。没有可测量内容尺寸的可组合函数(例如,Canvas,以可用尺寸绘制任意用户内容)可能需要该参数并省略默认值。
可组合函数必须将其修饰符参数传递给它们调用的根可组合函数,以便将其传递给它们发出的 Compose UI 节点。如果可组合函数直接发出 Compose UI 布局节点,则必须向该节点提供修饰符。
可组合函数可以将其他修饰符连接到接收modifier参数的末尾,然后再将连接的修饰符链传递给它们发出的 Compose UI 节点。
可组合函数在将连接的修饰符链传递给它们发出的 Compose UI 节点之前,不得将其他修饰符连接到接收到的修饰符参数的开头。
修饰符是向 Compose UI 中的元素添加外部行为的标准方法,允许从单个或基本元素 API 界面中分离出常见行为。由于修饰符用于修饰具有标准行为的元素,因此元素 API 可以更小、更集中。
修饰符函数的顺序非常重要。由于每个函数都会对上一个函数返回的 Modifier 进行更改,因此顺序会影响最终结果。
在 Compose 中,有一些修饰符只能应用于某些可组合项的子项。Compose 通过自定义作用域强制实施此安全机制。
例如,如果希望使某个子项与父项 Box 同样大,而不影响 Box 尺寸,请使用 matchParentSize 修饰符。matchParentSize 仅适用于 BoxScope,因此只能对 Box 父级中的子级使用。
作用域安全机制可防止添加在其他可组合项和作用域中不起作用的修饰符,还可节省试错的时间。
限定作用域的修饰符会将父项应知晓的关于子项的一些信息告知父项。这些修饰符通常称为“父项数据修饰符”。它们的内部构件与通用修饰符不同,但从使用角度来看,这些差异并不重要。
可以将多个修饰符链接起来,以修饰或扩充可组合项。此链是通过 Modifier 接口创建的,代表单个 Modifier.Elements 的不可变有序列表。
每个 Modifier.Element 代表一个单独的行为(例如布局、绘图和图形行为,所有与手势相关的焦点和语义行为,以及设备输入事件)。该顺序很重要:系统会首先应用最先添加的修饰符函数。
有时,将修饰符链提取到变量中并将其提升到更高的作用域有助于在多个可组合项中重复使用相同的修饰符链实例:这样做有助于提高代码可读性,还有助于提升应用性能,原因如下:
对使用修饰符的可组合项进行重组时,不会重新分配修饰符
修饰符链可能很长并且非常复杂,因此重复使用相同的链实例可以减轻 Compose 运行时在对比时所需的工作量
这种提取方式可提高整个代码库中的代码简洁性、一致性和可维护性
在可组合项中观察频繁变化的状态(如动画状态或 scrollState)时,可能会发生大量重组。在这种情况下,修饰符会在每次重组时获得分配,并可能分配给每一帧。创建Modifier 链并将其提取出来,以便在多个可组合组件中重复使用。
修饰符可以不限定作用域,也可以将作用域限定为特定可组合项。对于未限定作用域的修饰符,可以轻松地从任何可组合项之外提取它们作为简单变量,与延迟布局结合使用时,这尤为有用。在大多数情况下,建议对所有潜在的重要项目使用完全相同的修饰符。在处理作用域限定为特定可组合项的修饰符时,可以将其提取到尽可能高的级别,并在适当的情况下重复使用
此外,可以通过调用 .then() 函数进一步链接或附加提取的修饰符链,需要注意,修饰符的顺序很重要
布局阶段中的约束条件
布局阶段采用三步算法来查找每种布局节点的宽度、高度以及 x、y 坐标:
测量子节点:节点会测量其子节点(如果有)。
确定自己的大小:节点根据这些测量值自行确定大小 。
放置子节点:每个子节点都是相对于节点自身放置的。
Constraints 有助于在前两个节点找到适合节点的大小算法的步数。约束条件定义了节点的宽度和高度。当节点确定其大小时,其测量到的大小应处于此尺寸范围内
限制条件可以是以下项之一:
有边界:节点具有最大和最小宽度和高度。
无界限:节点不受任何大小的约束。最大宽度和高度边界设置为无穷大。
完全匹配:要求节点遵循确切大小。最低和最大边界设置为相同的值。
组合:节点遵循上述限制条件类型的组合。 例如,约束条件可以限制宽度, 无限制的最大高度,或设置精确宽度,但提供有界限高度。
当父节点测量其子节点时,会为每个节点提供这些约束条件,让他们知道自己的孩子可以多大或多小
概括来讲,该算法的运作方式如下:
界面树中的根节点是决定它实际占用的空间,并将相同的约束条件转发给其第一个子项。
如果子项是不影响测量的修饰符,则会转发下一个修饰符的约束条件。直到会影响测量结果的修饰符,然后通过约束条件相应地调整大小
到达没有任何子节点的节点时(称为“叶” 节点”),它会根据传入的约束条件来决定其大小,并将此解析后的尺寸返回给其父项。
父级根据这个子级的测量结果调整其约束条件,并使用这些调整后的约束调用其下一个子级。
测量完父节点的所有子节点后,父节点将尺寸传递给它自己的父级。
这样,系统会优先遍历整个树。最后,确定所有节点的尺寸,至此测量步骤就完成了。
影响限制条件的修饰符
size 修饰符可声明内容的首选尺寸。
size 修饰符会调整传入的约束条件,以匹配传递给它的值。
如果宽度和高度小于最小约束边界,或者大于最大约束边界,则修饰符会匹配传递的值同时仍然遵循已传递的限制条件
请注意,链接多个 size 修饰符不起作用。
requiredSize修饰符
如果需要节点覆盖传入的约束条件, requiredSize 修饰符将替换size传入约束条件,并将指定的尺寸作为确切边界进行传递。
width和height修饰符
size 修饰符可同时调整约束条件的宽度和高度。使用 width 修饰符,可以设置固定宽度,但无需确定高度。 使用 height 修饰符,可以设置固定高度,同时保留未定宽度
sizeIn修饰符
使用 sizeIn 修饰符,可以设置精确的最小和最大约束条件
创建自定义修饰符
可以通过多种方式实现自定义修饰符,具体取决于所需的功能。通常,实现自定义修饰符的最简单方式就是实现一个自定义修饰符工厂,将其他已定义的修饰符工厂组合在一起。如果需要更多自定义行为,请使用 Modifier.Node API 实现修饰符元素,这些 API 级别较低,但灵活性更高。
如果经常重复使用同一组修饰符,可以将它们封装到自己的修饰符中
还可以使用可组合函数创建自定义修饰符,以将值传递给现有修饰符。这称为可组合修饰符工厂。
使用可组合修饰符工厂创建修饰符允许使用更高级别的 Compose API
创建自定义修饰符时,请勿破坏修饰符链。必须始终引用 this,否则之前添加的任何修饰符都将被删除。
可以使用 this then Modifier,也可以使用 return graphicsLayer { this.alpha = alpha } 隐式使用。
使用可组合项修饰符工厂创建自定义修饰符时,CompositionLocal 从创建它们(而非使用它们)的组合树中获取相应值。这可能会导致意外结果。
因为无法跳过具有返回值的可组合函数,所以永远不会跳过可组合修饰符工厂。这意味着每次重组时都会调用修饰符函数,如果频繁重组,性能消耗可能会很高。
与所有可组合函数一样,必须从组合中调用可组合修饰符工厂。这限制了修饰符可以提升到的位置,因为它永远无法从组合中提升。相比之下,非可组合项修饰符工厂可以从可组合函数中提升出来,以便更轻松地重复使用并提高性能
Modifier.Node是用于在 Compose 中创建修饰符的较低级别的 API。它与 Compose 实现自己的修饰符的 API 相同,也是创建自定义修饰符的最高效方式。
使用 Modifier.Node 实现自定义修饰符的过程分为三个部分:
Modifier.Node实现,用于存储修饰符的逻辑和状态。
ModifierNodeElement,用于创建和更新修饰符节点实例。
可选的修饰符工厂(如上文所述)。
ModifierNodeElement 类是无状态的,每次重组都会分配新实例,而 Modifier.Node 类可以是有状态的,将在多次重组后继续存在,甚至可以重复使用。