报验表单从窄屏变为宽屏时,最容易被忽视的不是卡片有没有排成两列,而是阅读顺序有没有悄悄变化。设想一个核验员先读合同编号,再核对企业名称和联系人,最后看验证结论。视觉上只要有一块卡片跑到前面,就可能让人把“待确认”理解成另一份资料的状态。本文用 FieldRoute 做一个可复核的布局样例,讨论如何把业务字段顺序从栅格排列规则里剥离出来。文中界面、日志和数据均为设计演示,不是实测记录。## 一、把要保护的东西写在布局之前FieldRoute 的页面叫 FormPage,任务号是 FR-052。四个字段依次为“合同编号 HT-2307”“企业名称 江澜器材”“联系人 张工”“验证结论 待确认”。页面还有一个“核对阅读顺序”按钮。这个顺序不是一个视觉建议,而是审核员逐项核对时的业务顺序。因此实现时不应该在折叠前后各维护一份字段数组,更不应该依据窗口尺寸对原数组排序。有些自适应页面会把右侧字段先绘制出来,再把左侧字段补上。宽窗口看上去没问题,缩回单列以后,读屏顺序、键盘焦点和视觉阅读顺序却未必还能对上。尤其是字段较长、系统字级变大、辅助访问开启时,这种不一致会比按钮换行更难发现。这里的判断是:先保证数据数组稳定,再允许容器决定每行摆几项。本例把“断点”当作布局信号,不把它当作“折叠态”检测结果。窗口可以因为分屏、自由窗口、横竖屏而改变宽度;同一台折叠设备也可能给应用不同大小的可用区域。以窗口作为参照,比根据设备名称猜测更容易复现。示例使用三个宽度区间:小于 600vp 是 xs;600vp 至小于 840vp 是 sm;不小于 840vp 是 md。它们只是本 Demo 的布局决策,不是产品推荐的通用阈值。## 二、数据身份必须先于屏幕位置先解决字段与显示位置的绑定问题。每个字段有独立 id,卡片标题和值放在同一个数据对象里。即使未来更换标签、加入审核提示或调整栅格跨度,身份仍由 id 决定,而不是由第几行、第几列决定。下面是 FormPage 中的模型与页面状态;为了专注布局,值使用静态演示数据。arktsinterface FormField { id: string; label: string; value: string;}@Entry@Componentstruct FormPage { private readonly taskId: string = 'FR-052'; @State breakpoint: string = 'xs'; @State checkStatus: string = '待核对'; @State fields: FormField[] = [ { id: 'contract', label: '合同编号', value: 'HT-2307' }, { id: 'company', label: '企业名称', value: '江澜器材' }, { id: 'contact', label: '联系人', value: '张工' }, { id: 'result', label: '验证结论', value: '待确认' } ]; // build() 与核对方法见下文}这里的 fields 不是“窄屏数组”,而是任务 FR-052 的唯一显示顺序。breakpoint 只记录栅格报告的当前区间;checkStatus 只给演示核对按钮回显状态。真实项目中字段应来自有校验的业务模型,并在数据变化时整体替换或按所用状态管理版本更新,不能依赖把数组原地变异后自动重绘。示例省略网络请求,也就没有伪造加载时长或接口成功率。如果接口返回缺失字段,不应为了填满两列插入一个拥有相同 id 的空卡片。更可取的做法是明确展示“未提供”,或者先在数据层拒绝不完整的核验任务。重复 id 会影响稳定键,缺失标签会影响可访问性,两者都不是 GridRow 能替业务修正的问题。## 三、列数改变,声明顺序不改变现在处理界面。官方 GridRow 与 GridCol 是一对栅格组件,GridCol 应作为 GridRow 的子组件使用;span 表示一个条目占多少栅格列。为使窄窗口一行一个字段,示例给 xs 总列数 4、每项跨度 4;到 sm 时总列数 8、每项跨度 4;md 总列数 12、每项跨度 6。这样窗口增长改变的是每行容纳数量,而不是字段数组的先后次序。arktsbuild() { Column({ space: 16 }) { Text('字段阅读顺序核对').fontSize(23).fontWeight(FontWeight.Bold) Text(`任务 ${this.taskId} · ${this.breakpoint}`) GridRow({ columns: { xs: 4, sm: 8, md: 12 }, gutter: { x: 12, y: 12 }, breakpoints: { value: ['600vp', '840vp'], reference: BreakpointsReference.WindowSize } }) { ForEach(this.fields, (field: FormField) => { GridCol({ span: { xs: 4, sm: 4, md: 6 } }) { Column({ space: 6 }) { Text(field.label).fontSize(14).fontColor('#64748B') Text(field.value).fontSize(19) }.width('100%').padding(14).backgroundColor(Color.White) } }, (field: FormField) => field.id) } .width('100%') .onBreakpointChange((bp: string) => { this.breakpoint = bp; console.info(`[FieldRoute] task=FR-052 breakpoint=${bp}`); }) Button('核对阅读顺序').onClick(() => this.verifyOrder()) Text(this.checkStatus) }.width('100%').padding(20)}关键点不是用了几个栅格参数,而是跨度总能整除列数:xs 的 4/4 对应单列,sm 的 4/8 和 md 的 6/12 对应两列。视觉结果可预测,阅读顺序依旧跟着 ForEach 的声明顺序走。onBreakpointChange 只是一个观察入口,不能在回调里重新给字段排序,否则会把排版需求反向写回数据源。需要特别注意官方文档对列数默认值的版本变化。这里显式写 columns,就是避免项目迁移时默默接受新默认值;断点值和引用坐标也同样显式填写。界面最终几列由应用窗口有效宽度决定,不应把当前 xs 回调等同于“设备一定处于折叠态”。## 四、用一条核对日志暴露顺序漂移只看截图,很容易漏掉一个字段被视觉搬到了前面的情况。因此 Demo 加一条非常窄的诊断路径:按钮按下后提取数据数组中的 id,和事先确定的业务顺序比较。它不假装读取系统无障碍树,也不把日志当成真实布局检测;它能做的是防止“为了适配宽度而重排模型”这种回归被忽略。arktsprivate verifyOrder(): void { const order: string = this.fields.map((field: FormField) => field.id).join(','); const expected: string = 'contract,company,contact,result'; const passed: boolean = order === expected; this.checkStatus = passed ? 'PASS · 顺序已核对' : 'BLOCKED · 字段顺序变更'; console.info(`[FieldRoute] order=${order}`); console.info(`[FieldRoute] check=${passed ? 'PASS' : 'BLOCKED'}`);}这段比较故意没有拿 span 或窗口尺寸参与判定。因为我们要守护的是字段身份顺序,而不是“第几项一定占左列”。如果未来支持隐藏空字段,预期序列要先从业务规则计算,不能让隐藏行为绕过核对。出现 BLOCKED 时也不要直接回滚用户交互;应该定位是哪一步更新了字段集合,再决定是修复映射还是修订正式顺序规范。对照日志设计为三行:[FieldRoute] task=FR-052 breakpoint=xs、[FieldRoute] order=contract,company,contact,result、[FieldRoute] check=PASS。这几行是预期输出文本,不代表已经在真机 HiLog 中采集。对真实项目,我会把断点变化、字段数组改变、核对触发作为三个独立事件记录,避免连续布局回调带来无意义刷屏。量产日志应去掉联系人等业务敏感内容,保留字段键即可。## 五、窄屏运行画面应该证明什么图中的窄屏示意窗口处在 xs 区间,字段竖向呈现,任务 FR-052 和核对结果 PASS 同时可见。上方依次是 HT-2307、江澜器材、张工、待确认,下方是“核对阅读顺序”按钮。这张图能帮助读者看到设计意图,却不能替代折叠屏、平板、自由窗口和系统大字体的真实回归测试。真实调试时建议在 DevEco 中对同一组件树依次改变可用窗口宽度,并记录 onBreakpointChange,再逐项核对四个字段的视觉顺序、焦点移动顺序和读屏播报顺序。重点看 600vp 与 840vp 附近来回拉伸时是否发生突然裁剪。若标签值长到跨行,应让卡片自然增高,不能用固定高度硬压。对宽度特别小的多窗口场景,还需要关注容器内边距是否让 GridRow 的实际可用宽度小于预期。这套实现没有替代正式的无障碍测试,也不保证所有屏幕一定排成预想的列数。GridRow 解决的是响应式栅格,阅读行为还受 Text 字体缩放、组件可访问性和用户的交互方式影响。把“数据顺序受控”和“实际阅读顺序被验证”分成两张检查单,比一句“已完成多端适配”更可信。## 六、把演示边界写进交付说明FieldRoute 的收口是三个约束:字段列表有稳定身份;跨窗口宽度变化只改列跨度;任何重排序都必须经过业务规则而不是 UI 回调。页面销毁后没有需释放的网络连接或定时器;如果后续引入窗口监听或异步字段获取,应在生命周期结束时解除订阅、拒绝迟到回调。核对按钮反映的只是当前演示数组,不是系统自动完成的业务审核。官方参考资料:华为《响应式栅格布局 GridRow/GridCol》(2026-09-08 更新)以及《GridRow》组件参考,重点核对了 columns、gutter、breakpoints、GridCol.span 与 onBreakpointChange 的使用边界。示例在本文写作环境中没有进行 HarmonyOS SDK 编译和真实设备测试;读者应结合自身目标 API 和 DevEco 工程再次校验。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐