样式与修饰符

样式在设计上与修饰符不同。样式不会取代修饰符;相反,这两个系统会共存,但目标不同。在内部,样式是一种修饰符。您可以使用修饰符实现样式的所有功能,但修饰符中的部分功能在样式中不可用。

以下是样式与修饰符的比较:

功能 修饰符 样式
主要目标 定义行为、语义和复杂布局。修饰符可动态操控特定可组合项中的各个元素,并且不会从主题向下传递。 定义视觉外观、各个商品的大小和可设置主题的属性。样式在主题级别运行,可在组件级别覆盖。它们会向下传递,并跨不同的可组合项应用样式。
逻辑 相加 - 修饰符组合在一起形成新的结果。 可覆盖 - 样式中设置的最后一个属性生效。样式充当单个属性层,这些属性会根据定义的优先级层次结构相互替换。
主题 难以提升效果为主题,通常单独使用。 根据设计,样式可设置主题(可访问 CompositionLocal),并且只需定义一次即可在多个组件中使用。
性能 更新通常需要 Compose 的所有三个阶段:组合、布局和绘制。如需实现良好的修饰符动画性能,通常需要编写基于 lambda 的版本。 跳过组合阶段,仅在布局和绘制阶段处于活动状态,从而减少重组。需要更少的对象分配。
动画 需要使用单独的动画原语,例如 animate*AsState 内置 animate { } API,可为您处理一些动画。

修饰符的限制

在当前的 Compose 环境中,修饰符有很多优势。不过,样式解决了修饰符的一些限制,如下所示:

  • 修饰符通常是在组合阶段创建的。更新可能会强制完全重新运行 Composition、Layout 和 Draw,即使是颜色等细微的视觉变化也是如此,除非您创建基于 lambda 的修饰符。
  • 条件修饰符需要在流畅链中实现破坏性的 if-else 逻辑。 为它们添加动画效果需要手动设置状态样板,并且缺少高性能的“自动动画”机制。
  • 修饰符会叠加,而不是替换。您无法替换组件的默认边框,只能在上面绘制第二个边框。
  • 修饰符很难抽象为全局主题。因此,主题通常存储原始值,而不是可重复使用的修饰符配置。

样式的限制

虽然样式可以弥补修饰符的一些不足,但它们也存在一些限制,这表明它们无法完全取代修饰符:

  • 样式是专门的修饰符。虽然修饰符可以执行样式能执行的任何操作,但反之却不成立。因此,样式可以补充修饰符,但不能取代修饰符。
  • 样式仅限于视觉配置(背景、内边距、边框)。 它们无法处理点击逻辑、手势检测或无障碍语义等行为。
  • 将样式解析为最终状态比应用单个修饰符更耗费资源。系统必须生成一个包含所有可能属性值的数据结构,而继承属性的查找会进一步增加复杂性。

何时使用样式而非修饰符

虽然是否使用样式很大程度上取决于您的应用和使用场景,但以下指南有助于确定何时优先选择样式而不是修饰符:

  • 为了实现主题范围内的一致性:样式旨在“提升”到全局主题中。您可以在主题中定义单个样式,以便在整个应用中创建统一的外观,而无需向每个组件传递重复的 Modifier。
  • 在执行频繁动画时:样式会在布局和绘制阶段进行评估,从而允许颜色或缩放等属性在完全绕过组合阶段的情况下实现动画效果。这可显著减少性能开销。在执行视觉属性动画时,请使用样式而不是修饰符。
  • 替换与堆叠:如果您需要替换默认属性,请使用样式。修饰符是累加的(添加一个边框会堆叠第二个边框),而样式使用“后写胜出”逻辑,因此可以更轻松地替换背景或内边距,而不会造成视觉混乱。
  • 自定义 Material 组件:如果某个 Material 组件提供 Style 参数,建议使用此参数进行自定义。借助这些样式,您可以访问和修改可组合函数内部结构中可能无法访问的特定属性。