← 返回索引

范围:三个容器的适用场景、OverflowMode 三档的取舍、EntryRow 的行高与右列宽度、StackPanel 开启滚动后的滚轮与裁剪行为。

前置:快速开始;核心概念 的元素层次与 setter 规则。


容器选型

框架提供三个布局容器,分别对应一类排布需求。StackPanel 将子元素沿一个方向依次排成一行或一列,子元素只需给出尺寸、无需坐标,适用于设置页分组、按钮行与表单列。Canvas 在自己的边界内为子元素指定绝对坐标或九点锚点,适用于浮动徽标、不规则摆位与装饰层。EntryRow 将一行分为左右两列,左侧为标签文字、右侧为控件,适用于任何“名称在左、控件在右“的设置行,行高与右列限宽由容器计算。

选型规则:线性排列用 StackPanel,自由定位用 Canvas,两列设置行用 EntryRow。三者都是 LayoutElement,可以互相嵌套(例如 StackPanel 的一格内放置一条 EntryRow)。容器内部的 measure/arrange 何时触发、如何向上冒泡,见 布局系统。


StackPanel

StackPanel 沿主轴依次摆放可见子元素:主轴方向由 orientation 决定,子元素间距由 spacing 决定,交叉轴上可选择居中。设置页最常见的骨架是可滚动纵向面板,面板内排列两个按钮与一个输入框:

StackPanel vstack = new StackPanel(Orientation.VERTICAL)
    .size(300, 400)
    .spacing(8)
    .bgColor(0x80000000)            // 半透明背景;0 表示不画
    .overflow(OverflowMode.SCROLL)  // 开滚动:裁剪 + 滚轮 + 平滑动画
    .scrollbar(true);               // 再显示右侧滚动条

vstack.add(new Button("First", 200, 24).font(font));
vstack.add(new Button("Second", 200, 24).font(font));
vstack.add(new TextBox(200, 20).placeholder("Enter text...").font(font));

面板固定为 300×400 并开启滚动,子元素只设置宽高、由面板自上而下排列,间距 8px;文本组件需 .font(font) 才渲染(见 核心概念)。常用属性如下:

方法默认值作用
orientation(Orientation)构造时指定主轴方向(VERTICAL / HORIZONTAL)
direction(StackDirection)随 orientation(垂直为 TOP_TO_BOTTOM)正向 / 反向排队
spacing(int)0子元素之间的间距
centered(boolean)false子元素在交叉轴上居中
autoWidth(boolean) / autoHeight(boolean)false尺寸改为随内容总和
bgColor(int)0x80000000背景色(0 = 不画)
scrollbar(boolean)falseoverflow=SCROLL 时显示滚动条

滚动与裁剪行为

开启滚动(OverflowMode.SCROLL)后,StackPanel 的行为分为滚轮、裁剪与滚动条三项。滚轮:每格滚动将目标滚动量推进 20 像素(targetScroll ∓ v×20,夹在 0 到可滚上限之间),实际显示位置由 tick() 内的弹簧插值逐渐追上目标,因此滚动呈缓动效果;tick() 中断时滚轮完全不动(Screen 必须在 extractRenderState 里调 uiTickDriver.onRenderFrame(interactionManager),见 快速开始)。裁剪:overflow 为 HIDDEN 或 SCROLL 时容器启用裁剪,将绘制限制在自身边界内,被裁掉的部分既不可见也不可点击;VISIBLE 不裁剪,子元素可以绘制到容器外。滚动条:宽 4px、最短 16px,悬停变亮、可拖拽,仅在 SCROLL 且内容溢出时出现。滚动条是独立的元素对象(ScrollBar),挂载到 children 末位,即本容器最高层;命中查找逆序先扫到它,位于条带下方的内容元素无法截获落在条带上的点击。滚动条不参与内容尺寸与排布,也不随内容滚动,绘制在裁剪区域之外。


Canvas

Canvas 不参与线性排列,每个子元素按坐标或锚点定位,适用于角落徽标、重叠装饰与不规则布局。三种落位方式为指定坐标、锚点居中、锚点加偏移:

Canvas canvas = new Canvas(400, 300);

canvas.add(new Button("Top-Left", 100, 24), 10, 10);                          // 指定坐标
canvas.add(new Button("Center", 100, 24), Anchor.CENTER);                     // 锚点居中
canvas.add(new Button("Bottom-Right", 100, 24), Anchor.BOTTOM_RIGHT, -10, -10); // 锚点 + 偏移

第一个子元素固定在 (10,10);第二个按锚点居中;第三个位于右下角并向内偏移 10px。可用锚点共九个:TOP_LEFT、TOP_CENTER、TOP_RIGHT、CENTER_LEFT、CENTER、CENTER_RIGHT、BOTTOM_LEFT、BOTTOM_CENTER、BOTTOM_RIGHT。canvas.show() / canvas.hide() 会递归切换所有子元素的可见性。


EntryRow

EntryRow 是设置页的标准行:先 .left(...) 放置标签,再 .right(...) 放置控件。left / right 在实现上都只是 addChild 调用,列的归属由添加顺序决定:第 1 个子元素为左列、第 2 个为右列,因此必须先 left 后 right,调换顺序会使两列互换。构造“分辨率“设置项:

EntryRow row = new EntryRow(300, 120)       // 整行宽 300,左列占位宽 120
    .left(new Text("Resolution", 120))      // Text 配方与 font 说明见 [核心概念](developer.CoreConcepts.md)
    .right(new SelectDropdown(160)
        .options("1920x1080", "2560x1440", "3840x2160")
        .selected(0)
        .onChanged(i -> changeResolution(i)))
    .maxRightWidth(260);                    // 覆盖右列宽度上限(默认即 260)

左列是一行静态文字(使用核心概念中的 Text,该组件内置字体回退);右列是下拉框;maxRightWidth(260) 与默认值相同,仅作示例;放宽右列上限时传入更大的数。

行高与右列宽度

行高取三者最大:保底 20 像素、左列高度、右列高度,行始终容纳两列中较高者,且不低于 20 像素(普通控件行高)。列间距固定 8 像素(源码常量 GAP)。右列宽度受两道限制:不超过可用宽度(以下公式中的 avail),且不超过 maxRightWidth(默认 260,由 .maxRightWidth(v) 覆盖);算出后右列贴右缘摆放,左右两列均在行内垂直居中。公式如下:

height = max(20, max(lh, rh))                          // 行高:保底 20,再取两列较高者
avail  = fullWidth - GAP                                 // 可用宽度(GAP = 8;与左栏无关)
w      = min(min(rw, avail), maxRightW)                // 右列宽:先卡可用宽,再卡上限(默认 260)

lh / rh 是左右两个子元素的高度,rw 是右列的固有宽度。measure / arrange 流水线、坐标与滚动的完整公式体系见 布局系统;修改公式时以该文档为基准同步。


OverflowMode 与滚动

OverflowMode 定义子元素超出容器边界时的处理方式。各档语义如下,行为细节见 StackPanel 的「滚动与裁剪行为」:

stackPanel.overflow(OverflowMode.VISIBLE);  // 默认:不裁剪,子元素自由溢出
stackPanel.overflow(OverflowMode.HIDDEN);   // 裁剪:溢出部分看不见也点不中
stackPanel.overflow(OverflowMode.SCROLL);   // 裁剪 + 滚轮(每格 20px 目标量)+ 平滑滚动 + 可选滚动条

平滑滚动依赖每帧 tick() 推进插值(Screen 在 extractRenderState 里调 uiTickDriver.onRenderFrame(interactionManager),见 快速开始);滚动条样式与拖拽行为属于 StackPanel(4px 宽、最短 16px)。输入如何命中到被裁剪的子元素、命中链的层次见 交互引擎;浮层挡住容器时的输入走向(模态/被动)见 覆盖层与浮动元素。


不变量

  • EntryRow 先 .right 后 .left → 左右列互换:容器按添加顺序判定列归属,不按方法名。
  • EntryRow addChild 超过两个 → 第 3 个子元素不参与排布:measure/arrange 只读索引 0 和 1,该子元素重叠在原点。
  • 开了 SCROLL 但 Screen 未调用 uiTickDriver.onRenderFrame(interactionManager) → 滚轮完全不动:目标量在变,但显示位置的插值永不推进。
  • overflow 为 VISIBLE 时不裁剪,子元素可绘制到容器外,溢出部分同样可点击。
  • 直接给 width / height 字段赋值 → 布局不更新:改尺寸必须调用 setter(见 核心概念 的 setter 不变量)。

连带影响

  • EntryRow 的 GAP、默认 maxRightW、行高保底这些真值一旦调整,公式段与 布局系统 的 EntryRow 段必须同步;该文档给出公式的完整叙述。
  • StackPanel 的滚轮步长(20px)、裁剪条件(HIDDEN | SCROLL)改动 → 「滚动与裁剪行为」与 OverflowMode 部分的数字、条件句需要同步更新。
  • OverflowMode 语义改动 → 牵动命中链的裁剪边界判定,交互引擎 的对应描述需一起修改。
  • 示例一律使用 核心概念 的 Text;该配方的构造签名变化时,EntryRow 示例需同步更新。

相关文档