物理常量页最容易被误认为“把几个数字排成列表”。但常量一旦同时用于学习展示、单位换算和模拟计算,就必须回答三个问题:数值来自哪里,采用哪个定义或观测口径,显示精度与计算精度是否一致。光速是定义值,地球半径可能是平均半径或赤道半径,月地距离也会随轨道变化;如果模型只保存一段字符串,这些差异会被界面悄悄抹平。

“天体运行模拟”的 ConstantsPage.ets 已经实现了一个简洁的离线天文常数页:用 AstroConstant 描述名称、符号、值和单位,内置光速、万有引力常量、天文单位、太阳质量、地球质量、地球半径、月地距离和光年八条数据,再通过 ArkUI List 渲染统一卡片。

本文基于这份真实源码,复核八条内容的结构和展示边界,并设计一套把“显示文本、计算数值、来源、口径和精度”分层的模型。当前页面没有来源字段、更新时间、数值类型或复制功能;文章中的增强方案会明确标注为建议,不会伪造成已有能力。

物理常量文章封面

唯一复核标记:CONSTANT-ONE13-SOURCE-PRECISION-20260726:常量数值、显示格式、来源口径和有效数字必须分开建模。

验证基线与源码清单

本文面向 HarmonyOS 5.0 及以上版本。实际工程应用版本为 1.0.0targetSdkVersion6.0.2(22)compatibleSdkVersion6.0.1(21),入口模块支持 phonetablet2in1

源码中的八条数据为:

名称 符号 显示值 单位
光速 c 3.0 x 10^8 m/s
万有引力常量 G 6.674 x 10^-11 N·m²/kg²
天文单位 AU 1.496 x 10^8 km
太阳质量 M_sun 1.989 x 10^30 kg
地球质量 M_earth 5.972 x 10^24 kg
地球半径 R_earth 6371 km
月地距离 D_moon 3.844 x 10^5 km
光年 ly 9.461 x 10^12 km

静态复核结果:8 条记录全部拥有名称、符号、值和单位;8 个 value 都是字符串;来源字段为 0;口径字段为 0;有效数字字段为 0;数值计算入口为 0。当前能力准确定位为“静态速查展示”。

一、现有 AstroConstant 适合展示

模型只有四个字符串字段:

interface AstroConstant {
  name: string
  symbol: string
  value: string
  unit: string
}

它的优点很直接:

  • 不需要处理浮点格式化。
  • 科学计数形式可以原样显示。
  • 不同单位可以放在同一列表。
  • ArkUI 绑定简单。

这也是为什么当前页面稳定:List 只负责展示,模型没有计算职责。

缺点同样明显:'3.0 x 10^8' 不能安全用于乘除运算,value 中可能出现任意文本,编译器无法检查单位与数值是否匹配。

二、显示字符串不能充当计算常量

如果直接解析:

parseFloat('3.0 x 10^8')

结果只会得到 3,因为 parseFloat 在空格和 x 处停止。这说明显示文本与计算值必须分开。

推荐:

interface PhysicalConstant {
  id: string
  name: string
  symbol: string
  siValue: number
  siUnit: string
  displayValue: string
  displayUnit: string
}

光速可以保存:

{
  id: 'speed_of_light',
  name: '光速',
  symbol: 'c',
  siValue: 299_792_458,
  siUnit: 'm/s',
  displayValue: '2.99792458 × 10⁸',
  displayUnit: 'm/s'
}

计算读取 siValue,页面读取 displayValue,两者不会互相解析。

三、常量、参考值和平均值要区分

页面标题叫“天文常数”,但八条数据并不全是同一种语义。

  • 光速是定义值。
  • 天文单位是定义长度。
  • 万有引力常量是测量得到的物理常量。
  • 太阳质量与地球质量是天体参数的近似值。
  • 地球半径需要说明采用平均、赤道还是极半径。
  • 月地距离会变化,384400 km 通常是平均尺度。
  • 光年是由光速和时间定义推导出的长度。

模型可以增加:

type ConstantKind =
  | 'defined'
  | 'measured'
  | 'derived'
  | 'reference'
  | 'average'

用户就能知道“为什么有些数字可以写很多位,有些只能近似”。

四、来源不是一个可选装饰字段

推荐每条数据记录:

interface ConstantSource {
  organization: string
  document: string
  edition: string
  publishedAt?: string
  url?: string
}

来源字段服务于:

  • 审核与复核。
  • 常量更新。
  • 解释不同资料的数值差异。
  • 离线页面中的引用说明。
  • 防止开发者凭记忆修改常量。

来源链接可以显示在详情页,但应用仍可离线保存必要的组织、文档和版本文本。不要让“没有网络”成为“没有来源”的理由。

五、有效数字需要显式建模

源码显示:

c = 3.0 x 10^8 m/s

这只有 2 位有效数字。它适合入门速查,但如果同一个值用于计算,会损失大量精度。

推荐增加:

interface PrecisionPolicy {
  significantDigits: number
  approximate: boolean
  uncertainty?: string
}

显示层按 significantDigits 格式化,计算层继续使用完整 siValue。近似值应显示 ,定义值可以显示 =

六、科学计数符号要统一

源码使用 ASCII 字符串:

3.0 x 10^8
6.674 x 10^-11

在中文科学展示中,更规范的视觉形式通常是:

3.0 × 10⁸
6.674 × 10⁻¹¹

Unicode 上标可用于短文本,但不应手工维护所有指数。格式化函数可以生成:

const SUPERSCRIPT: Record<string, string> = {
  '-': '⁻',
  '0': '⁰',
  '1': '¹',
  '2': '²',
  '3': '³',
  '4': '⁴',
  '5': '⁵',
  '6': '⁶',
  '7': '⁷',
  '8': '⁸',
  '9': '⁹'
}

如果目标字体缺少上标,回退到 e 记法:

6.674e-11

七、光速条目如何分离展示与定义

当前显示值是:

3.0 x 10^8 m/s

作为入门近似没有问题。更完整模型可以同时保存:

{
  siValue: 299_792_458,
  displayDigits: 9,
  exact: true
}

页面提供两种展示:

  • 简明:3.00 × 10⁸ m/s
  • 精确:299 792 458 m/s

同一数值只维护一次,避免卡片和换算器使用不同常量。

八、万有引力常量需要保留测量语义

源码显示:

G = 6.674 x 10^-11 N·m²/kg²

G 的量级正确,但它不是像光速那样的定义值。专业模型应允许保存不确定度或来源版本:

{
  kind: 'measured',
  approximate: true,
  uncertainty: 'source-defined'
}

文章不应仅靠多写几位小数制造“更准确”的错觉。没有来源和不确定度说明时,合理做法是明确显示近似号。

九、天文单位要避免米和千米重复维护

源码以千米显示:

AU = 1.496 x 10^8 km

计算层建议只保存 SI 米值,显示时转换:

function metersToKilometers(
  meters: number
): number {
  return meters / 1000
}

如果同时手工维护米值和千米值,一个值更新后另一个可能忘记同步。单一真源加单位换算更可靠。

十、太阳质量与地球质量是近似参考量

源码:

M_sun   = 1.989 x 10^30 kg
M_earth = 5.972 x 10^24 kg

它们适合教学量级。若模拟引擎使用归一化质量 M,不要把页面中的 kg 直接传入模拟。应建立显式映射:

interface SimulationScale {
  massUnitInKg: number
  lengthUnitInMeters: number
  timeUnitInSeconds: number
}

公式页展示现实尺度,模拟页使用教学单位,两者可以关联,但不能混为同一数据域。

物理常量从来源到页面的流程

十一、地球半径必须写明口径

源码显示:

R_earth = 6371 km

这个值常被用作平均半径。地球并非完全球体,赤道半径和极半径不同。模型可以增加:

interface MeasurementContext {
  qualifier?: string
  epoch?: string
  referenceFrame?: string
}

对应条目:

context: {
  qualifier: 'mean radius'
}

页面中文可显示“地球平均半径”,比只写“地球半径”更准确。

十二、月地距离不是固定轨道半径

源码显示:

D_moon = 3.844 x 10^5 km

月球绕地球运行的距离会变化。速查页可以保留平均尺度,但应把名称改为“月地平均距离”或在说明中标记近似。

如果未来加入近地点、远地点和实时轨道数据,它们应是不同条目,不应覆盖当前参考值。

十三、光年是长度单位

源码把光年显示为:

ly = 9.461 x 10^12 km

光年名称包含“年”,但它表示距离。详情中应说明:

光在真空中一个儒略年传播的距离

单位换算页如果加入 ly,必须放在长度组。常量页与换算页应该共享同一个 PhysicalConstants 模块,避免两处写不同值。

十四、建立单一常量目录

当前八条数据写在页面状态中:

@State constants: AstroConstant[] = [...]

它们其实是静态目录,不需要 @State。可以移到模型文件:

export const ASTRO_CONSTANTS:
  readonly PhysicalConstant[] = [
  // ...
]

页面只读取目录:

private constants:
  readonly PhysicalConstant[] =
  ASTRO_CONSTANTS

单位换算、公式页和模拟说明也从同一模块导入,减少重复。

十五、页面状态与静态数据要分开

@State 适合会触发 UI 更新的可变数据。八条常量在当前页面不会变化,把它们标为 @State 会暗示页面可能修改目录。

真正需要状态的可能是:

  • 搜索词。
  • 当前分类。
  • 展开的详情 ID。
  • 显示精度模式。
  • 复制成功提示。

常量目录则应保持只读。这样用户交互不会意外改写基础数据。

十六、格式化器应该是纯函数

推荐:

interface FormatOptions {
  significantDigits: number
  scientificThreshold: number
}

function formatConstant(
  value: number,
  options: FormatOptions
): string {
  const abs = Math.abs(value)
  if (abs === 0) return '0'

  if (abs >= options.scientificThreshold ||
      abs < 1 / options.scientificThreshold) {
    return value.toExponential(
      options.significantDigits - 1
    )
  }

  return value.toPrecision(
    options.significantDigits
  )
}

纯函数可以用测试向量验证,不依赖 ArkUI 生命周期。

十七、精度模式可以服务不同读者

页面可以提供分段控制:

  • 入门:3 到 4 位有效数字。
  • 标准:来源推荐精度。
  • 详细:完整存储值和来源说明。

切换模式只改变格式化,不改变 siValue。用户从简明切到详细后不会触发重新加载,也不会产生两份常量。

当前源码没有精度切换,本文只是说明可扩展方向。

十八、List 卡片结构已经适合扩展

每个列表项分为:

  • 左侧 70vp 符号块。
  • 右侧名称。
  • 数值与单位行。

符号使用主色,数值使用橙色,单位使用提示色。视觉层级清晰,List({ space: 8 }) 也便于增加更多条目。

符号块设置:

.maxLines(1)
.textOverflow({
  overflow: TextOverflow.Ellipsis
})

这能防止长符号撑破布局,但 M_earth 在大字体下可能被截断。更好的显示是 Unicode 下标或缩短视觉符号,并通过无障碍文本朗读完整名称。

十九、长单位与大字体需要约束

N·m²/kg²kg 长。当前值和单位放在同一 Row,在窄屏或大字体下可能拥挤。

可以:

  • 给数值使用 layoutWeight(1)
  • 单位允许换行或移动到下一行。
  • 设置 maxLines 和明确溢出策略。
  • 平板上保持卡片最大宽度。
  • 让详情页展示完整来源和单位。

不要为了塞进一行而把单位字体缩到不可读。

物理常量的数据分层

二十、多设备布局与安全区

根容器使用:

.width('100%')
.height('100%')
.padding({
  top: this.statusBarHeight,
  bottom: this.bottomBarHeight
})

手机单列列表合理。平板可以采用两列,但应保持阅读顺序稳定;2in1 可以增加键盘搜索和复制操作。不同设备共享同一常量目录、来源和精度规则,布局层不能修改数值语义。

需要验证状态栏、底部导航、横屏小窗口、系统字体放大,以及长来源文本的滚动。

二十一、复制功能应复制机器可用值

用户点击复制时,可以提供:

G = 6.674 × 10⁻¹¹ N·m²/kg²

也可以提供 ASCII:

G = 6.674e-11 N*m^2/kg^2

前者适合文档,后者适合代码和计算器。不要只复制卡片上截断后的字符串。

当前源码没有复制功能,新增时需要明确格式并提供成功反馈。

二十二、常量目录的启动校验

function validateConstants(
  items: readonly PhysicalConstant[]
): string[] {
  const errors: string[] = []
  const ids = new Set<string>()

  items.forEach((item: PhysicalConstant) => {
    if (ids.has(item.id)) {
      errors.push(`duplicate id: ${item.id}`)
    }
    ids.add(item.id)

    if (!Number.isFinite(item.siValue)) {
      errors.push(`invalid value: ${item.id}`)
    }
    if (item.siUnit.length === 0) {
      errors.push(`empty unit: ${item.id}`)
    }
    if (!item.source) {
      errors.push(`missing source: ${item.id}`)
    }
  })

  return errors
}

开发构建可以执行完整校验,正式构建至少保留安全回退,避免单条错误阻塞整个页面。

二十三、测试矩阵

测试 预期
初始进入 展示 8 条真实记录
每条记录 名称、符号、值、单位均非空
返回按钮 调用 router.back()
窄屏 符号块与数值不重叠
大字体 长单位可读,不遮挡下一项
来源增强后 每条都有来源版本
精度切换 只改变显示,不改变 siValue
单位换算 AU 与光年读取同一常量目录
非有限数值 校验失败,不进入页面目录
重复 ID 启动校验报告错误

现有源码可以复核前四项的结构基础;来源、精度切换和共享目录属于建议实现。

二十四、发布前检查

  • 常量 ID 唯一且稳定。
  • 计算数值与显示字符串分离。
  • SI 值只维护一份。
  • 来源组织、文档和版本可复核。
  • 定义值、测量值、推导值和平均值有区分。
  • 有效数字与近似号一致。
  • 地球半径明确口径。
  • 月地距离标注平均或参考性质。
  • 光年归入长度。
  • 页面与单位换算共享常量目录。
  • 长单位、大字体和多设备不溢出。
  • 复制内容可选择科学显示或 ASCII。

二十五、总结

当前 ConstantsPage.ets 用统一模型和 ArkUI List 完成了八条天文数据的离线展示,结构清楚、页面轻量,适合作为学习工具的第一版。它的边界也很明确:所有值都是显示字符串,没有来源、口径、有效数字和计算类型。

真正可靠的常量管理要把五件事分开:数值、单位、显示格式、来源和测量语义。计算层使用单一 SI 真源,展示层按读者需要控制有效数字,来源层说明定义或观测口径。这样公式页、换算器和模拟说明才能共享同一份可验证数据,而不是各自维护一串看起来相近的数字。

说明:本文基于真实 HarmonyOS/ArkTS 源码进行整理,部分文字与示例由 AI 辅助生成;所有现有能力与建议改造已明确区分。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐