素材说明:本文所有界面、DevEco Studio、模拟器和 HiLog 配图均为人工制作的技术演示,非真实编译截图、真机测试或上架审核结果。代码供工程设计讨论,需结合目标 SDK 在项目中编译和验证。
一、点中的是第三格,业务却认成了第四个位置
我给一个仓储盘点小工具改货架选位时,最先遇到的并不是 Grid 的样式问题。货架有三层,每层四个格子,一共十二个物理坐标。但现场有些位置临时被锁定:有的是设备检修,有的是物料尚未验收。界面仍然要让操作员看见它们,不能简单从数组中删掉。删掉以后,后面的卡片自动往前补位,屏幕上看似整齐,实际位置编码却全错了。
这个 Demo 叫ShelfAtlas,页面是ShelfSlotPage,演示任务SH-3412。最终要展示的规则版本是rev=5:R1C3、R2C2、R3C2三格禁选,其他九格可用。操作员之前选了R2C2,规则更新时它突然变成禁选格,页面将选择修复到同一行右侧最近的R2C3,标记FALLBACK_OK。这些数值全部是专门为文章构造的静态演示数据,不对应真实仓库。
问题看起来像“禁用按钮”,但我更在意三件事:物理坐标不能随着数据过滤改变;禁选格点击时不能把旧选择清空;规则变化后不能把一个已经失效的选中值继续带到提交接口。一个环节做错,用户看到的蓝框和真正提交的货格 ID 就可能不是同一位置。
二、先把十二个物理槽位建完整,再决定谁能点
ArkUI 的Grid和GridItem用来组织网格显示,四列可以通过columnsTemplate声明。这里没有使用系统提供的仓储选位服务——系统负责布局,位置编号、可选规则和回退策略全部放在应用层。之所以坚持先建完整矩阵,是因为ForEach的数据顺序是渲染输入,不能拿过滤后数组的下标充当物理列号。
这段代码解决坐标错位:生成十二个固定 ID,锁定集合只改变可选标记,不删除位置。row、col从零基索引计算,但界面展示一基编号;如果将来有第五列,需要同时调整总数和列数,不能只改视图模板。
interfaceShelfCell{id:string;row:number;col:number;locked:boolean;}functionmakeShelfCells(lockedIds:string[]):ShelfCell[]{constlocked=newSet<string>(lockedIds);constcells:ShelfCell[]=[];for(letindex=0;index<12;index++){constrow=Math.floor(index/4)+1;constcol=(index%4)+1;constid=`R${row}C${col}`;cells.push({id,row,col,locked:locked.has(id)});}returncells;}返回数组长度始终是十二。禁选位置R1C3仍占第一行第三列,第二行第二列也不会因为撤掉一张卡片变成“第二行第一列”。维护时要保证规则接口返回的是稳定坐标 ID,而不是临时渲染下标。若后台传来R8C1这样的越界值,应当记录配置异常而不是把它插入布局。这个版本只在固定三乘四模型下演示,跨尺寸布局和动态扩容不在本例结论范围内。
三、点击禁选格时,不要先清空已选择值
我曾见过一种写法:点击任意单元格先给selectedId赋值,再在异步判断中发现禁选,把状态改回去。结果是页面短暂高亮了不允许选择的格子。更麻烦的是,如果中间已经触发确认操作,回退值和提交值可能发生竞态。
这段代码解决交互顺序:先验证 ID 必须存在、且当前非禁选,才允许改变选择。非法点击返回原值,不产生中间过渡状态。提交层还会再次按最新规则校验,不能把点击拦截等同于安全保证。
functionchooseShelfCell(requestId:string,cells:ShelfCell[],selectedId:string):string{constmatched=cells.find((cell:ShelfCell)=>cell.id===requestId);if(!matched||matched.locked){returnselectedId;}returnmatched.id;}functionavailableCount(cells:ShelfCell[]):number{returncells.filter((cell:ShelfCell)=>!cell.locked).length;}这样操作员点击R1C3,界面维持原来的有效选中,不会发生“红框跳一下再回来”。当原选中就是禁选值时则不能套用这条规则,那不是普通点击,而是规则热更新带来的数据修复。两种情况必须分开处理:交互拦截保留合法状态,规则修复主动寻找替代位置。页面销毁后不再保留触摸回调;如果接入远程规则订阅,还需在离开页面时解绑监听。
四、规则从 rev=4 到 rev=5,选中的格子必须重新验算
这个场景的关键证据不是第一次点击,而是后台规则变化。rev=4时只有R1C3和R3C2被锁,R2C2可以正常选中。切到rev=5后新增锁定R2C2,仍让蓝框停在原处就等于向操作员撒谎。我采用一个可解释的回退:先看旧位置是否仍可选;否则优先找同一行中列号大于旧列号的最近空位,再找同一行其他空位;如果整行都锁,才扫描全货架。
这段代码解决热更新回退:不是根据当前屏幕上的可见索引找下一格,而是按固定坐标排序;如果全锁定则返回空 ID,提交按钮应禁用,绝不能悄悄选到第一个数组元素。
functionrepairShelfSelection(oldId:string,cells:ShelfCell[]):string{constactive=cells.find((c:ShelfCell)=>c.id===oldId);if(active&&!active.locked)returnoldId;constold=cells.find((c:ShelfCell)=>c.id===oldId);if(!old)returncells.find((c:ShelfCell)=>!c.locked)?.id??'';constrow=cells.filter((c:ShelfCell)=>c.row===old.row&&!c.locked);constright=row.find((c:ShelfCell)=>c.col>old.col);if(right)returnright.id;if(row.length>0)returnrow[0].id;returncells.find((c:ShelfCell)=>!c.locked)?.id??'';}样例的旧位置是R2C2,同排最近的右侧空位恰好是R2C3。这个策略不是系统规定的交互规范,而是仓储业务可以向使用者解释的明确规则;其他行业也许应当直接清空并要求重新选择。更重要的是,修复行为要落在一次受控的规则替换流程里:更新锁定集合、重建完整矩阵、校验并修复所选 ID,然后才将新版本发布给界面。不能让锁定集已经变更、选中值还停在旧数据上。
开发演示图固定显示左侧ShelfAtlas/entry/src/main/ets/pages/ShelfSlotPage.ets目录、中间的repairShelfSelection代码、右侧格子矩阵和底部 HiLog。演示日志为SH-3412 rev=5 locked=3 available=9 selected=R2C3 state=FALLBACK_OK。日志只是文章中的预期业务轨迹,不是假装真实运行的控制台录屏。核对图片时,最应该检查的是编号位置:第一行第三格、第二行第二格、第三行第二格必须一直被占位。
五、页面显示和提交快照不能只靠同一个变量
进入页面时可以先拿到rev=4的快照,但确认动作必须读取提交时的最新版本。假设操作员在旧页签里停留许久,远端已经进入rev=5,即便他从未再点击一次,旧的R2C2也不能过关。界面在演示结束时显示十二个物理槽位、三个禁选、九个可选,选中R2C3,但后台真正落库还应携带坐标和规则修订号,再由服务端对照权威规则复核。
我把手机演示图做成不带边框的纯屏幕形式。状态栏有时间、网络信号、Wi-Fi 和电量;主标题是“货架选位演示”,任务SH-3412,规则rev=5,底部状态FALLBACK_OK。这种图的作用是说明工程验收希望看见什么,不代表真的在 HarmonyOS 设备上完成操作。尤其是“可选 9/12”与“禁选 3 格”必须同时成立,不能为了画面好看随意改其中一个数字。
六、把最容易漏掉的三个边界留在验收表
第一是空货架或全锁定。空结果不应访问cells[0],全锁定也不允许默认选中第一项;两者都应明确给出不可提交状态和空值原因。第二是规则逆序到达:应用只有在新修订号更大时才替换本地快照,不能把rev=5又被迟到的rev=4覆盖。本文没有写网络订阅实现,只给出了选位层如何消费可靠版本的接口边界。
第三是坐标与视觉顺序发生变化。纵向屏幕可能有四列,折叠屏横向可能希望展示六列;如果货架的物理行列仍是三乘四,就不能仅凭 Grid 的屏幕换列给物理坐标重新编号。正确做法是让业务坐标成为身份键,让视觉布局是独立的投影。当前示例选择四列完全是为了演示直观,实际项目应继续针对无障碍焦点、屏幕旋转和超长文本测量做设备级测试。
这次改造让我重新确认了一件事:网格能把卡片摆整齐,但不能替业务守住“这个格子究竟在哪里”。在规则热更新的瞬间,先把数据身份、禁选判定和回退选择做成纯函数,再把结果交给 ArkUI 展示,故障就更容易复现,也更容易向一线人员解释。本文使用的所有数据和截图均属静态演示,尚无目标 SDK 编译或真机验证结论。
七、参考资料与复查范围
华为官方 ArkUI 网格元素拖拽文档说明了 Grid、GridItem 的布局与交互背景,本文没有启用拖拽动画:https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/arkts-grid-element-drag-exchange 。ArkTS 基础能力及容器使用参照官方简介:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-overview 。请根据项目实际 SDK 版本复查组件属性及边界行为。本文仓储锁定规则、回退次序和任务编号皆为应用层演示设计。