HarmonyOS电阻色环工作台连载03:五环模式与有效数字扩展【共创稿事节】
给四环页面再加一个颜色按钮,并不能直接得到五环工具。原来第三环表示倍率,进入五环后却变成第三位有效数字;如果继续按旧下标计算,界面可能正常显示五种颜色,结果却少算一位。本篇将模式、角色布局与颜色数组共同保存,让四环和五环共用计算流程,同时维持各自的编辑内容。

模式是记录的一部分
四环布局为两位有效数字、倍率和误差;五环布局为三位有效数字、倍率和误差。第一位仍不接受黑色,后续数字位接受黑至白,倍率接受规则表内全部颜色,误差仍采用棕、红、金、银四档。新增模式改变的是角色布局,不改变已有颜色的数字、指数或误差含义。
初始四环仍为棕黑红金,对应一千欧姆、百分之五;五环默认棕黑黑棕棕,前三位组成一百,棕色倍率为十,标称同样为一千欧姆,但误差为百分之一,上下限为九百九十和一千零一十欧姆。两个默认值标称相同,便于检查模式切换是否错误地改变了有效数字位数。
工程仍使用entry/src/main/ets下的模型、服务和页面三个文件。完整模型如下,颜色表沿用前两章,新增模式类型、选择记录和角色枚举。角色布局集中在一个函数中,页面标签、选项过滤与解码计算都从它取得位置含义。
// model/BandRule.ets
export interface BandRule {
code: string;
label: string;
paint: string;
digit: number;
exponent: number;
tolerance: number;
}
export const RULES: BandRule[] = [
{ code: 'black', label: '黑', paint: '#222222', digit: 0, exponent: 0, tolerance: -1 },
{ code: 'brown', label: '棕', paint: '#805333', digit: 1, exponent: 1, tolerance: 1 },
{ code: 'red', label: '红', paint: '#CD4545', digit: 2, exponent: 2, tolerance: 2 },
{ code: 'orange', label: '橙', paint: '#EC8D36', digit: 3, exponent: 3, tolerance: -1 },
{ code: 'yellow', label: '黄', paint: '#E7C83D', digit: 4, exponent: 4, tolerance: -1 },
{ code: 'green', label: '绿', paint: '#429566', digit: 5, exponent: 5, tolerance: -1 },
{ code: 'blue', label: '蓝', paint: '#456EBD', digit: 6, exponent: 6, tolerance: -1 },
{ code: 'violet', label: '紫', paint: '#8D59A8', digit: 7, exponent: 7, tolerance: -1 },
{ code: 'gray', label: '灰', paint: '#888888', digit: 8, exponent: 8, tolerance: -1 },
{ code: 'white', label: '白', paint: '#F3F3F3', digit: 9, exponent: 9, tolerance: -1 },
{ code: 'gold', label: '金', paint: '#B7954C', digit: -1, exponent: -1, tolerance: 5 },
{ code: 'silver', label: '银', paint: '#ADB3B8', digit: -1, exponent: -2, tolerance: 10 }
];
export function findBand(code: string): BandRule {
for (const rule of RULES) {
if (rule.code === code) return rule;
}
throw new Error('颜色不在当前规则表中');
}
export type ResistorMode = 'four' | 'five';
export interface BandSelection { mode: ResistorMode; codes: string[]; }
export enum BandRole { FirstDigit, Digit, Multiplier, Tolerance }
export function rolesForMode(mode: ResistorMode): BandRole[] {
if (mode === 'four') return [BandRole.FirstDigit, BandRole.Digit,
BandRole.Multiplier, BandRole.Tolerance];
if (mode === 'five') return [BandRole.FirstDigit, BandRole.Digit,
BandRole.Digit, BandRole.Multiplier, BandRole.Tolerance];
throw new Error('电阻模式无效');
}
export function roleAt(mode: ResistorMode, position: number): BandRole {
const roles: BandRole[] = rolesForMode(mode);
if (!Number.isInteger(position) || position < 0 || position >= roles.length) {
throw new Error('色环位置无效');
}
return roles[position];
}
export function allowedForRole(rule: BandRule, role: BandRole): boolean {
if (role === BandRole.FirstDigit) return rule.digit > 0;
if (role === BandRole.Digit) return rule.digit >= 0;
if (role === BandRole.Multiplier) return true;
if (role === BandRole.Tolerance) return rule.tolerance > 0;
return false;
}
export function optionsFor(mode: ResistorMode, position: number): BandRule[] {
const role: BandRole = roleAt(mode, position);
return RULES.filter((rule: BandRule) => allowedForRole(rule, role));
}
export function roleTitle(mode: ResistorMode, position: number): string {
const role: BandRole = roleAt(mode, position);
if (role === BandRole.Multiplier) return '倍率';
if (role === BandRole.Tolerance) return '误差';
return ['第一位', '第二位', '第三位'][position];
}
export function roleValue(rule: BandRule, mode: ResistorMode, position: number): string {
const role: BandRole = roleAt(mode, position);
if (!allowedForRole(rule, role)) throw new Error('颜色与角色不匹配');
if (role === BandRole.Multiplier) return `10^${rule.exponent}`;
if (role === BandRole.Tolerance) return `±${rule.tolerance}%`;
return rule.digit.toString();
}
roleAt先检查模式,再检查位置范围,避免把一个不存在的第五位置解释为误差。上一章的选项函数接收单个位置,本篇升级为模式加位置;非法位置现在抛出明确异常,而不是返回空集合。页面始终根据当前模式生成位置列表,调用服务时仍保留完整校验,以保护导入记录等其他入口。
同一个颜色在不同模式、不同位置下可以具有不同角色。金色在四环第三位置可作倍率,在五环第三位置却不能作有效数字。不能因为颜色在旧页面可以选,就把它原封不动带到新位置。记录模式比根据颜色数组长度猜测用途更清楚,错误长度会被当作输入问题处理,不会触发自动模式识别。
累积有效数字,再读取倍率和误差
计算服务先逐位验证角色,再累积有效数字。每读取一位,旧值乘十并加上新数字。四环累积两次,五环累积三次;倍率位置紧随有效数字,误差位置再往后一位。所有阻值仍以欧姆保存,显示单位转换继续放在格式化函数中。
下面完整列出service/Resistance.ets。保留decodeFour和旧的四环选择入口,已有调用点可以继续明确处理四环;新页面使用带模式的decode与selectBand。兼容入口不会将四个颜色补成五个,也不会悄悄切换模式。
// service/Resistance.ets
import { BandRule, BandSelection, BandRole, roleAt, rolesForMode,
allowedForRole, findBand } from '../model/BandRule';
export interface ResistanceResult {
ohms: number;
tolerance: number;
low: number;
high: number;
labels: string[];
}
export interface SelectionResult { codes: string[]; result: ResistanceResult; }
export interface ModeSelectionResult { selection: BandSelection; result: ResistanceResult; }
export function resistance(first: number, second: number, exponent: number): number {
return (first * 10 + second) * Math.pow(10, exponent);
}
export function decode(selection: BandSelection): ResistanceResult {
const roles: BandRole[] = rolesForMode(selection.mode);
if (selection.codes.length !== roles.length) throw new Error('颜色数量与模式不一致');
for (let i: number = 0; i < roles.length; i++) {
if (!allowedForRole(findBand(selection.codes[i]), roles[i])) {
throw new Error('色环颜色与角色不匹配');
}
}
const digitCount: number = selection.mode === 'four' ? 2 : 3;
let digits: number = 0;
for (let i: number = 0; i < digitCount; i++) {
digits = digits * 10 + findBand(selection.codes[i]).digit;
}
const multiplier: BandRule = findBand(selection.codes[digitCount]);
const tolerance: BandRule = findBand(selection.codes[digitCount + 1]);
const ohms: number = digits * Math.pow(10, multiplier.exponent);
if (!Number.isFinite(ohms) || ohms <= 0) throw new Error('阻值超出计算范围');
const delta: number = ohms * tolerance.tolerance / 100;
return { ohms, tolerance: tolerance.tolerance, low: ohms - delta,
high: ohms + delta, labels: selection.codes.map((code: string) => findBand(code).label) };
}
export function decodeFour(codes: string[]): ResistanceResult {
return decode({ mode: 'four', codes });
}
export function selectBand(selection: BandSelection, position: number,
code: string): ModeSelectionResult {
const role: BandRole = roleAt(selection.mode, position);
if (!allowedForRole(findBand(code), role)) throw new Error('当前角色不接受该颜色');
const codes: string[] = selection.codes.slice();
if (codes.length !== rolesForMode(selection.mode).length) {
throw new Error('颜色数量与模式不一致');
}
codes[position] = code;
const next: BandSelection = { mode: selection.mode, codes };
return { selection: next, result: decode(next) };
}
export function selectColor(codes: string[], position: number, code: string): SelectionResult {
const selected: ModeSelectionResult = selectBand({ mode: 'four', codes }, position, code);
return { codes: selected.selection.codes, result: selected.result };
}
export function formatOhms(value: number): string {
if (value >= 1000000) return `${(value / 1000000).toFixed(2)} MΩ`;
if (value >= 1000) return `${(value / 1000).toFixed(2)} kΩ`;
return `${Number(value.toFixed(4))} Ω`;
}
两种模式分别保存编辑内容
切换模式是一项查看与编辑上下文的变化,不是转换阻值的动作。页面分别保存四环与五环选择,切换后读取对应记录,并将编辑位置重置为该模式的误差位。当前四环的第三位置是倍率,不能把它的数字下标直接延续为五环第三位的焦点含义。
选择颜色时仍先计算成功再一起更新输入与结果,随后复制到对应模式的保存字段。不同模式之间不共享颜色数组,界面状态与保存字段也分别复制;修改五环记录不会通过数组引用改写四环记录。“恢复当前模式”只恢复正在查看的那一份默认组合,另一份编辑结果保留。
// pages/Index.ets
import { BandRule, BandSelection, ResistorMode, findBand, optionsFor,
rolesForMode, roleTitle, roleValue } from '../model/BandRule';
import { ResistanceResult, ModeSelectionResult, decode,
selectBand, formatOhms } from '../service/Resistance';
@Entry
@Component
struct Index {
@State selection: BandSelection = { mode: 'four', codes: ['brown', 'black', 'red', 'gold'] };
@State result: ResistanceResult = decode({ mode: 'four', codes: ['brown', 'black', 'red', 'gold'] });
@State position: number = 3;
@State message: string = '四环与五环分别保留选择';
private four: BandSelection = { mode: 'four', codes: ['brown', 'black', 'red', 'gold'] };
private five: BandSelection = { mode: 'five', codes: ['brown', 'black', 'black', 'brown', 'brown'] };
private positions(): number[] {
const result: number[] = [];
for (let i: number = 0; i < rolesForMode(this.selection.mode).length; i++) result.push(i);
return result;
}
private publish(selection: BandSelection, resetPosition: boolean): void {
const result: ResistanceResult = decode(selection);
const codes: string[] = selection.codes.slice();
this.selection = { mode: selection.mode, codes };
this.result = result;
if (selection.mode === 'four') this.four = { mode: 'four', codes: codes.slice() };
else this.five = { mode: 'five', codes: codes.slice() };
if (resetPosition) this.position = codes.length - 1;
}
private openMode(mode: ResistorMode): void {
this.publish(mode === 'four' ? this.four : this.five, true);
}
private choose(code: string): void {
try {
const next: ModeSelectionResult = selectBand(this.selection, this.position, code);
this.publish(next.selection, false);
this.message = '四环与五环分别保留选择';
} catch (error) {
this.message = error instanceof Error ? error.message : '颜色更新失败';
}
}
private reset(): void {
const next: BandSelection = this.selection.mode === 'four' ?
{ mode: 'four', codes: ['brown', 'black', 'red', 'gold'] } :
{ mode: 'five', codes: ['brown', 'black', 'black', 'brown', 'brown'] };
this.publish(next, true);
}
build() {
Scroll() {
Column({ space: 14 }) {
Text('电阻色环工作台').fontSize(25).fontWeight(FontWeight.Bold)
Text(this.selection.mode === 'four' ? '四环模式' : '五环模式')
Row({ space: 12 }) {
Button('四环').onClick(() => { this.openMode('four'); })
Button('五环').onClick(() => { this.openMode('five'); })
}
Row({ space: 3 }) {
ForEach(this.positions(), (position: number) => {
Button(roleTitle(this.selection.mode, position)).fontSize(12).layoutWeight(1)
.backgroundColor(this.position === position ? '#18796C' : '#829B94')
.onClick(() => { this.position = position; })
}, (position: number) => `${this.selection.mode}:${position}`)
}.width('100%')
Row({ space: 12 }) {
ForEach(this.positions(), (position: number) => {
Column({ space: 6 }) {
Text('').width(26).height(44)
.backgroundColor(findBand(this.selection.codes[position]).paint)
Text(this.result.labels[position])
}
}, (position: number) => `${this.selection.mode}:${position}`)
}
Text(`当前选择:${roleTitle(this.selection.mode, this.position)}`)
Grid() {
ForEach(optionsFor(this.selection.mode, this.position), (rule: BandRule) => {
GridItem() {
Button(`${rule.label} ${roleValue(rule, this.selection.mode, this.position)}`)
.fontSize(13).width('100%').fontColor('#173B34')
.backgroundColor(rule.code === this.selection.codes[this.position] ? '#DDF3EC' : '#FFFFFF')
.onClick(() => { this.choose(rule.code); })
}.height(68)
}, (rule: BandRule) => `${this.selection.mode}:${this.position}:${rule.code}`)
}.columnsTemplate('1fr 1fr 1fr 1fr').columnsGap(4).rowsGap(4)
.width('100%').height(212)
Text(`${formatOhms(this.result.ohms)} ±${this.result.tolerance}%`)
.fontSize(28).fontColor('#18796C')
Text(`${Number(this.result.low.toFixed(4))}–${Number(this.result.high.toFixed(4))} Ω`)
Button('恢复当前模式').onClick(() => { this.reset(); })
Text(this.message).fontSize(14)
}.padding(20).width('100%').constraintSize({ maxWidth: 520 })
}.width('100%').height('100%').backgroundColor('#F4F9F6')
}
}

候选项和位置按钮的键都包含模式信息。虽然四环和五环中都存在位置二,但它们分别表示倍率和第三位有效数字;切换模式时保留同一个组件身份容易把旧选中态带入新角色。当前代码让身份包含模式、位置和颜色,在模式或角色变化时重新绑定对应选项,普通颜色变化仍使用稳定代码。
五个角色按钮保持完整顺序,手机上字号和间距适当收紧,不能把第三位藏进不明确的“更多”按钮。颜色预览始终带色名,结果卡同步显示标称值与误差。外层纵向滚动容器承载较长内容,使小高度窗口仍能到达恢复操作;后续多设备篇可以将角色选择与结果展示并列,但不改变数据契约。
用同值不同编码检查角色偏移
打开四环默认记录,确认一千欧姆、百分之五以及九百五十至一千零五十欧姆。再切到五环默认记录,确认标称值保持一千欧姆,误差改为百分之一。若结果变成一百或一万,应检查有效数字循环次数及倍率位置,不能通过调整格式化单位掩盖计算错误。
在五环中把第三位黑色改为红色,前三位变为一百零二,棕色倍率不变,标称值应为一千零二十欧姆。回到四环,记录仍应是棕黑红金;再返回五环,应保留刚才的第三位红色。恢复五环默认后,四环选择也不能发生变化。这个过程同时检查角色布局、按模式保存及数组引用隔离。

数据损坏不能靠自动补位处理
带有四环模式却保存五个颜色的记录,应在数量检查处失败;五环记录只有四个颜色也同样失败。不能删除末尾颜色或复制某一环来凑长度,因为末尾承担误差角色,自动修补会改变业务含义。不存在的模式、颜色代码以及越界位置,都需要沿明确的失败路径返回调用者。
入口还应检查第一位黑色、数字位金色和误差位黑色等不匹配组合。倍率的金银仍然合法,其负指数不能被有效数字的规则误伤。对于同一选择,选项过滤接受的颜色应能被解码服务接受;如果两者不一致,应检查是否有某个页面还在调用上一章仅依据位置的过滤函数。
模式切换与阻值转换需要分开设计。如果以后提供“将四环换成等值五环编码”,它应当是独立功能,需要处理有效数字位数、倍率与可表达范围,并明确保留或调整误差。本篇的模式按钮只是恢复各自编辑上下文,不承担等值转换,因此不会替用户选择新的颜色组合。
为后续反向查色保留数值与编码
输出继续同时保留原始模式与颜色代码,以及派生的欧姆数值和误差。只保存1.00 kΩ这样的文本,会丢失有效数字位数和模式信息;只保存一个颜色数组,又缺少解释位置的契约。后续反向查色应从数值和目标模式出发寻找可用编码,不能从显示字符串的舍入结果直接决定色环。
四环和五环因此共享一套角色校验与数值计算,同时保持独立记录。新增模式不再依赖散落的下标判断,而是由明确的角色布局驱动。下一章将继续处理显示精度、单位归一与阻值反向查色,沿用本篇已经固定的模式与选择记录。
相关官方资料
更多推荐

所有评论(0)