DirectSurface UIDirectSurface UI
开始使用
文档/核心概念

RenderObject 基础

本文中的业务组件示例使用:

import { RenderButton } from 'ds-ui'

RenderObject 是 DirectSurface UI 的核心模型。所有可见组件最终都是 RenderObject。

职责

一个 RenderObject 通常负责:

  • 接收布局约束。
  • 计算自己的尺寸。
  • 布局子对象。
  • 绘制自身。
  • 参与命中测试。
  • 接收指针、键盘、焦点等事件。
  • 在销毁时释放资源。

Layout

布局阶段由父级传入约束:

performLayout(constraints, context): void

组件必须在布局阶段设置自己的 size。如果有子节点,也要给子节点执行 layout 并设置 offset。

Paint

绘制阶段通过 PaintContextDrawList 输出绘制命令:

performPaint(context, offset): void

绘制应该只依赖当前布局结果和组件状态,不应该在 paint 中修改会触发布局的状态。

Hit Test

交互组件需要实现命中和事件处理:

onPointerDown(event): void
onPointerMove(event): void
onWheel(event): boolean

滚动容器或列表组件要在边界时正确处理事件传播,避免内部滚动到顶/底后异常影响外层容器。

可见性

所有 RenderObject 都有 visible 属性,默认值为 true。它是组件级的折叠隐藏能力:

  • visible = false 时,组件尺寸为 0 x 0
  • 不执行自身 performLayout(),父布局组件也不会为该子节点保留间距或位置。
  • 不绘制,不参与 transient paint。
  • 不参与 hit test,也不会接收指针事件。
  • 如果组件拥有 popup,隐藏时会关闭该组件拥有的 popup。
  • 对可聚焦控件,隐藏时会退出焦点并从焦点系统注销。

业务权限、页面模式、流程状态只需要控制某个组件是否出现时,优先设置组件实例的 visible。例如:

const canAuditCurrentRecord = (): boolean => trueconst auditButton = new RenderButton({ label: '审核' })auditButton.visible = canAuditCurrentRecord()

visible 表示从布局流中移除。需要“保留占位但不绘制”时,使用布局包装组件 RenderVisibility({ mode: 'hidden' })

状态变更

组件状态变化后调用:

markNeedsPaint()
markNeedsLayout()

只影响视觉的变化用 markNeedsPaint()。影响尺寸、子节点位置、可见项数量的变化用 markNeedsLayout()

paintBounds 表示节点可能影响的全局绘制区域,默认等于 globalOffset + size。如果自定义组件绘制了超出布局矩形的阴影、光晕或装饰,应覆盖 paintBounds 并返回包含这些视觉溢出的保守边界。

仅改变节点位置、不改变布局尺寸时,应在修改 offset 前保存旧的 paintBounds,修改后调用:

const previousPaintBounds = this.paintBounds
this.offset = nextOffset
this.markNeedsPaintForGeometryChange(previousPaintBounds)

该方法会累计移动前后的绘制区域,同时保持 paint-only。不要用每帧 markNeedsLayout() 或全层重绘掩盖位置变化的损伤区域。

生命周期

组件持有事件、缓存、图片、定时器、popup、输入会话时,必须在 dispose() 中释放。

override dispose(): void {
  // 释放本组件资源
  super.dispose()
}

自定义 RenderObject 注意事项

  • 不要在 layout 中调用外部业务回调。
  • 不要在 paint 中做昂贵扫描。
  • 优先使用 ds-ui 已公开的组件和布局;只有公共能力无法覆盖时,才在业务项目中实现自定义 RenderObject。
  • 自定义对象应明确所属应用模块、生命周期、测试范围和性能边界,不要依赖框架包内路径。