Harmony os 技术实战|拼豆制图43:压缩字符矩阵上线前如何拦住错位图纸

把 70×70 图纸压成字符行很节省体积:. 表示空格,0-9A-F 表示最多 16 种颜色。但这种格式的危险也很直接------少一个字符、混入一个全角符号,运行时仍可能继续解析,直到编号图出现错位、统计数不一致或某一行突然变短。

这篇文章给《拼豆制图》的 PatternAssetCharts 增加一层数据入口校验。目标不是等 ArkUI 渲染异常后再排查,而是在资源进入 Harmony os 应用时就给出图纸 ID、行号、列号和错误原因。

一、压缩格式必须先写成契约

当前资源模型很简洁:

ts 复制代码
export interface AssetChart {
  colors: string[];
  previewRows: string[];
  chartRows: string[];
}

仅靠接口无法表达以下约束:

  • chartRows 不得为空;
  • 每一行宽度必须相同;
  • 字符只能来自 .0-9A-F
  • token 对应的颜色索引不能超过 colors.length - 1
  • 颜色必须是六位十六进制值;
  • 预览宽高要与约定一致。

这些规则应该由一个入口统一执行,而不是散落在渲染代码中。

二、为校验结果保留位置上下文

直接抛出"资源错误"对 50 张图纸几乎没有帮助。定义结构化问题:

ts 复制代码
export interface AssetIssue {
  patternId: string;
  field: string;
  row: number;
  col: number;
  message: string;
}

export interface AssetValidationResult {
  valid: boolean;
  issues: AssetIssue[];
}

行列从 0 还是 1 开始要统一。对开发日志建议展示 1 基坐标,和编辑器行号更接近。

三、第一关:空数组与矩阵宽度

ts 复制代码
private static validateRows(
  patternId: string,
  field: string,
  rows: string[],
  issues: AssetIssue[]
): void {
  if (rows.length === 0) {
    issues.push({ patternId, field, row: 0, col: 0, message: '矩阵为空' });
    return;
  }
  const expectedWidth = rows[0].length;
  if (expectedWidth === 0) {
    issues.push({ patternId, field, row: 1, col: 0, message: '首行为空' });
  }
  for (let row = 0; row < rows.length; row++) {
    if (rows[row].length !== expectedWidth) {
      issues.push({
        patternId,
        field,
        row: row + 1,
        col: rows[row].length,
        message: `行宽应为 ${expectedWidth},实际为 ${rows[row].length}`
      });
    }
  }
}

当前 rowWidth() 只读取第一行,后续短行不会自动补齐。提前拒绝不规则矩阵,可以防止 width × height 与格子数量失配。

四、第二关:token 字符集

ts 复制代码
private static validateTokens(
  patternId: string,
  field: string,
  rows: string[],
  colorCount: number,
  issues: AssetIssue[]
): void {
  for (let row = 0; row < rows.length; row++) {
    for (let col = 0; col < rows[row].length; col++) {
      const token = rows[row].charAt(col);
      if (token === '.') {
        continue;
      }
      const index = '0123456789ABCDEF'.indexOf(token);
      if (index < 0) {
        issues.push({ patternId, field, row: row + 1, col: col + 1,
          message: `非法字符 ${token}` });
      } else if (index >= colorCount) {
        issues.push({ patternId, field, row: row + 1, col: col + 1,
          message: `颜色索引 ${index} 超出调色板` });
      }
    }
  }
}

实现时不要把未知字符静默当成空格。静默降级会让错误图纸看起来"只是少了几颗豆",反而更难定位。

五、错误位置统一转换成人类坐标

数组下标从 0 开始,而资源编辑人员习惯从第 1 行、第 1 列定位。可以把坐标转换集中到一个入口,避免某条规则报 0 基坐标、另一条规则报 1 基坐标:

ts 复制代码
private static addIssue(
  issues: AssetIssue[],
  patternId: string,
  field: string,
  rowIndex: number,
  columnIndex: number,
  message: string
): void {
  issues.push({
    patternId: patternId,
    field: field,
    row: rowIndex + 1,
    col: columnIndex + 1,
    message: message
  });
}

这样日志中的 第 12 行第 8 列 可以直接对应文本编辑器中的位置,不需要修复人员每次手动加一。自动化测试则继续向 addIssue() 传入数组下标,内部语义保持清楚。

六、第三关:颜色格式与重复色号

ts 复制代码
private static validateColors(patternId: string, colors: string[], issues: AssetIssue[]): void {
  const seen = new Set<string>();
  for (let i = 0; i < colors.length; i++) {
    const value = colors[i].toUpperCase();
    if (!/^#[0-9A-F]{6}$/.test(value)) {
      issues.push({ patternId, field: 'colors', row: i + 1, col: 0,
        message: `颜色格式错误:${colors[i]}` });
      continue;
    }
    if (seen.has(value)) {
      issues.push({ patternId, field: 'colors', row: i + 1, col: 0,
        message: `重复颜色:${value}` });
    }
    seen.add(value);
  }
}

重复颜色不一定导致崩溃,但会让两个色号视觉相同,用户备料时很容易误判,因此值得作为资源问题暴露出来。

七、统一校验入口

ts 复制代码
static validate(patternId: string, asset: AssetChart): AssetValidationResult {
  const issues: AssetIssue[] = [];
  AssetChartValidator.validateColors(patternId, asset.colors, issues);
  AssetChartValidator.validateRows(patternId, 'previewRows', asset.previewRows, issues);
  AssetChartValidator.validateRows(patternId, 'chartRows', asset.chartRows, issues);
  AssetChartValidator.validateTokens(patternId, 'previewRows', asset.previewRows,
    asset.colors.length, issues);
  AssetChartValidator.validateTokens(patternId, 'chartRows', asset.chartRows,
    asset.colors.length, issues);
  return { valid: issues.length === 0, issues };
}

Repository 只能接收 valid 的资源。开发构建可以直接抛错;线上构建可以隐藏损坏条目并记录 ID,但不应继续把坏矩阵交给详情页。

八、批量资源校验

50 张资源适合一次性遍历:

ts 复制代码
const ids = PatternAssetCharts.ids();
const allIssues: AssetIssue[] = [];
for (let i = 0; i < ids.length; i++) {
  const asset = PatternAssetCharts.get(ids[i]);
  if (asset !== null) {
    allIssues.push(...AssetChartValidator.validate(ids[i], asset).issues);
  }
}
expect(allIssues).assertEqual([]);

若数据由脚本生成,还应让生成脚本和 ArkTS 测试使用同一套 token 契约,避免两端规则逐渐偏离。

九、常见问题与修复

问题 表现 修复
未知字符当空格 图案缺豆但不报错 . 的未知字符必须失败
只校验首行宽度 中间行错位 遍历每一行比较宽度
token 合法但无对应颜色 某色块变白 对比索引与调色板长度
颜色字符串解析出 NaN 导出颜色异常 入口校验六位十六进制

问题信息要优先服务于资源修复人员。与其只写"图纸非法",不如给出图纸 ID、矩阵字段、行列位置、实际字符和允许范围。信息越接近源数据,越不需要在页面、仓库和资源文件之间反复定位。

十、验证清单

  • 空矩阵会返回明确问题。
  • 任意短行都能定位到具体行号。
  • 全角数字、空格和小写外字符不会被吞掉。
  • 超出调色板的 token 会被拒绝。
  • 每个问题包含图纸 ID 与字段名。
  • 批量资源测试在新增图纸时自动执行。

提交新资源前应同时跑单张边界用例和 50 张批量用例。单张用例负责证明每条规则能够报出预期位置,批量用例负责证明现有资源整体闭合;两者结合,才不会出现规则本身写错却被全量数据"恰好通过"的情况。

十一、总结

字符矩阵压缩了资源体积,也把很多错误推迟到了运行时。为宽度、字符集、调色板和颜色格式建立统一入口校验,Harmony os 应用才能把"某张图看起来不对"变成一条可定位、可复现、可阻断的问题记录。

标签:Harmony os、ArkTS、数据校验、字符矩阵、资源工程

相关推荐
IT_陈寒1 小时前
Redis内存警告竟是因为这个不起眼的配置项
前端·人工智能·后端
进击ing小白1 小时前
Qt 无边框窗口进阶指南
开发语言·qt
点我头像干啥1 小时前
004:多个PDF文件合并
开发语言·pdf·php
En^_^Joy2 小时前
Cadence Skill脚本语言快速入门指南
开发语言
kyriewen2 小时前
我踩了3次同一个坑才明白:JavaScript里比0.1+0.2更隐蔽的5个数字陷阱
前端·javascript·面试
10mAh2 小时前
【Java】HashMap 的 put 到底做了什么?——冲突、扩容与树化实测
java·开发语言·hash
徐小夕2 小时前
表格、文档、甘特、大屏、表单一站打通:pxcharts超级表格4.0正式上线!
前端·算法·github
ASKED_20192 小时前
从矩阵到秩:理解大模型参数与 LoRA 微调
线性代数·矩阵
用户594404103562 小时前
Vue3 + TypeScript + Leaflet.js 实战:构建企业级地理信息应用
前端