物理公式页看起来像一个简单列表,真正做起来却同时涉及三类准确性:公式本身要正确,符号与单位要让用户看懂,ArkUI 页面还要在手机、平板和 2in1 上保持可读。只把一串 Unicode 字符放进 Text,可以快速完成首版,却不等于已经建立了可验证的知识模型。

“天体运行模拟”的 FormulaPage.ets 提供了一个真实而清晰的起点:页面用 FormulaItem 描述名称、公式、说明和分类,内置万有引力、圆轨道速度、逃逸速度、动能、引力势能、质心和天文单位七条内容;顶部用横向滚动分类条切换“全部、引力、轨道、能量、尺度”,下方用 List 呈现公式卡片。

本文从这份源码出发,分析如何在 HarmonyOS 5.0 及以上版本中组织公式数据、符号、单位和响应式布局,并明确当前边界:现有页面是静态速查,不会计算结果;公式使用 Unicode 近似排版,没有 MathML 或专业公式渲染;多数条目没有完整单位说明。建议方案会与已实现能力严格区分。

公式页文章封面

唯一复核标记:FORMULA-ONE13-GRAVITY-UNITS-20260726:公式字符串负责速查,符号与单位模型负责解释,数值计算必须先完成量纲校验。

验证基线与真实数据

本文面向 HarmonyOS 5.0 及以上版本。实际工程应用版本为 1.0.0targetSdkVersion6.0.2(22)compatibleSdkVersion6.0.1(21),入口模块覆盖 phonetablet2in1。本次只复核 FormulaPage.ets 及其真实路由入口,不把文章中的增强代码视为已上线功能。

源码静态统计如下:

项目 实际数量
公式条目 7
分类筛选项 5
引力类条目 2
轨道类条目 2
能量类条目 2
尺度类条目 1
数值计算入口 0
专业公式渲染组件 0

七条公式分别是 F = GMm / r²v = √(GM / r)vₑ = √(2GM / r)Ek = ½mv²Ep = -GMm / rR = Σmᵢrᵢ / Σmᵢ1 AU ≈ 1.496×10⁸ km。这些内容可以直接从 formulas 数组复核。

一、用 FormulaItem 统一卡片数据

当前模型只有四个字段:

interface FormulaItem {
  name: string
  formula: string
  description: string
  category: string
}

这种结构适合静态速查页。页面不需要为每条公式写独立组件,只需遍历数组:

ForEach(this.filteredFormulas(),
  (item: FormulaItem) => {
    ListItem() {
      // render item
    }
  }
)

数据与布局分离后,添加公式只需新增对象,不必复制整段 ArkUI。它也让分类筛选可以复用同一字段。

不过,formula 只是显示字符串,description 只是自然语言。若未来要做单位解释、变量高亮或计算器,这四个字段还不够。

二、七条真实公式覆盖了什么

现有条目形成四组知识:

  • 引力:万有引力、质心。
  • 轨道:圆轨道速度、逃逸速度。
  • 能量:动能、引力势能。
  • 尺度:天文单位。

这不是百科全书式堆砌,而是围绕模拟页的质量、速度、距离和轨迹形成最小知识闭环。例如圆轨道速度和逃逸速度共享 GM/r,但前者对应闭合圆轨道条件,后者对应总机械能不小于零的临界条件。

公式页标题使用“引力速查”,因此范围控制是合理的。若继续扩展,应优先补充轨道周期、角动量和开普勒第三定律,而不是随意加入无关公式。

三、万有引力公式要同时解释变量与方向

页面当前显示:

F = GMm / r²

它给出的是力的大小。变量含义应明确:

符号 含义 SI 单位
F 两个天体间引力大小 N
G 万有引力常量 m³/(kg·s²)
Mm 两个天体质量 kg
r 两质心距离 m

页面说明“随距离平方减小”容易产生歧义,更准确的表达是“力的大小与距离平方成反比”。同时还要强调方向沿两天体质心连线,彼此相反。

如果把它用于模拟计算,不能只计算标量:

const dx = x2 - x1
const dy = y2 - y1
const r2 = dx * dx + dy * dy
const r = Math.sqrt(r2)
const force = G * m1 * m2 / r2
const fx = force * dx / r
const fy = force * dy / r

还必须设置最小距离或软化参数,避免 r = 0 导致除零。当前公式页不执行这段计算,文章示例只用于说明公式到向量实现的差异。

四、圆轨道速度隐含了适用条件

页面显示:

v = √(GM / r)

这个式子来自向心力与万有引力平衡:

mv² / r = GMm / r²

约去测试天体质量 m 后得到圆轨道速度。它隐含:

  • 中心天体质量远大于绕行天体。
  • 轨道近似圆形。
  • 只考虑两体引力。
  • 速度方向与半径方向垂直。

若用户在三体系统中直接套用它,结果只是一组近似初值。公式页最好显示“适用条件”,避免公式看起来像任何场景都成立的通用按钮。

五、逃逸速度与圆轨道速度的关系

现有逃逸速度为:

vₑ = √(2GM / r)

在相同 Mr 下:

vₑ = √2 · v圆

这个关系非常适合做同卡片关联。用户看到圆轨道速度后,可以直接理解:

  • 速度低于圆轨道速度不一定立即坠落,可能形成椭圆轨道。
  • 等于圆轨道速度且方向正确时形成圆轨道。
  • 高于圆轨道速度但低于逃逸速度时通常仍受束缚。
  • 达到逃逸速度是理想两体模型下离开束缚的临界条件。

现有描述“达到该速度后可离开引力束缚”是合适的速查表述,但计算功能还应说明不考虑大气阻力、其他天体和自转。

六、能量公式需要统一符号

源码中动能写作:

Ek = ½mv²

引力势能写作:

Ep = -GMm / r

Unicode 形式在普通 Text 中可读,但下标并不统一:EkEp 是普通字母,逃逸速度却使用 vₑ。建议统一为:

Eₖ = ½mv²
Eₚ = -GMm / r

或者统一使用 ASCII 形式,避免部分字体缺字:

E_k = 1/2 mv^2
E_p = -GMm/r

选择哪一种取决于目标字体与设备实测。不要在同一页面混用多套下标规则。

七、负引力势能不是“负能量错误”

Ep = -GMm/r 的负号来自把无穷远处势能定义为零。天体越靠近,势能越低,也就是数值更负。

对两体近似,总机械能为:

E = ½mv² - GMm/r

E < 0 时通常是束缚轨道;E = 0 对应抛物线逃逸临界;E > 0 对应非束缚轨道。这个解释可以把“能量”分类和“轨道”分类连接起来。

页面当前每条卡片只有一句说明。若内容继续增长,建议增加折叠详情,不要把所有推导都堆在列表首屏。

八、质心公式中的 R 是向量

源码显示:

R = Σmᵢrᵢ / Σmᵢ

其中 rᵢ 表示第 i 个天体的位置向量,因此 R 也是向量。在二维模拟里可以分别计算:

interface Point {
  x: number
  y: number
}

function centerOfMass(
  bodies: BodyMassPoint[]
): Point {
  let totalMass = 0
  let weightedX = 0
  let weightedY = 0

  bodies.forEach((body: BodyMassPoint) => {
    totalMass += body.mass
    weightedX += body.mass * body.x
    weightedY += body.mass * body.y
  })

  if (totalMass <= 0) {
    return { x: 0, y: 0 }
  }
  return {
    x: weightedX / totalMass,
    y: weightedY / totalMass
  }
}

若只写“共同质心旋转”,用户可能不知道它是可计算的位置。变量说明应标记 rᵢR 为位置向量。

九、天文单位是尺度换算,不是动力学公式

现有尺度条目:

1 AU ≈ 1.496×10⁸ km

它用于描述太阳系距离。更精确的定义值是:

1 AU = 149 597 870.7 km

速查页使用近似值没有问题,但要在模型中标记精度。若计算器输入 AU,再输出 km 或 m,应使用统一常量:

const AU_IN_METERS: number = 149_597_870_700

显示层可以舍入,计算层不要从显示字符串 1.496×10⁸ km 反向解析数值。

公式从数据到页面的呈现流程

十、分类筛选是一个纯派生状态

源码只保存 selectedCategory,列表通过函数派生:

private filteredFormulas(): FormulaItem[] {
  const cat =
    this.categories[this.selectedCategory]
  if (cat === '全部') {
    return this.formulas
  }
  return this.formulas.filter(
    f => f.category === cat
  )
}

这种方式不会维护第二份过滤数组,避免分类变化后手工同步。七条数据规模下,每次过滤开销可以忽略。

风险在于分类仍是普通字符串。如果把某个条目误写成“轨道学”,编译器不会报错,该条目只会在“全部”中出现。可以收紧为联合类型:

type FormulaCategory =
  | '引力'
  | '轨道'
  | '能量'
  | '尺度'

interface FormulaItem {
  name: string
  formula: string
  description: string
  category: FormulaCategory
}

十一、横向滚动分类条适合窄屏

分类条放在:

Scroll() {
  Row({ space: 8 }) {
    // categories
  }
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)

这样手机窄屏不必压缩五个标签。被选项使用主色背景,未选项使用卡片背景和分隔线,同时保留文字颜色差异。

不过,关闭滚动条后,用户不一定知道右侧还有内容。当前只有五项,通常能通过首屏露出下一项形成提示;未来分类增加时,可以让末尾保留部分可见宽度,或在平板上改为不滚动的 Flex 布局。

十二、公式卡片的视觉层级

每张卡片依次显示:

  1. 名称。
  2. 21vp 粗体公式。
  3. 说明。
  4. 分类标签。

公式使用主色,名称使用正文色,说明使用次级色,阅读顺序明确。List({ space: 10 }) 负责纵向虚拟化和滚动,适合公式继续增加后的性能。

卡片内部使用固定 padding(16),边框与背景来自主题常量。圆角使用 Constants.SMALL_RADIUS,避免每个页面自行定义。

十三、Unicode 公式的优势与边界

Text('F = GMm / r²') 的优势是:

  • 无额外依赖。
  • 与 ArkUI 字体、颜色和无障碍树自然集成。
  • 加载快,离线可用。
  • 适合一行短公式。

边界也很明确:

  • 复杂分式不能垂直排版。
  • 多层上下标可读性下降。
  • 根号覆盖范围只能靠括号表达。
  • 字体可能缺少部分数学字符。
  • 无法对单个变量单独着色或点击解释。

对于当前七条短公式,Unicode 是保守而有效的选择。只有当产品需要矩阵、积分、长分式或交互变量时,才值得引入更专业的排版方案。

十四、把符号说明做成结构化模型

建议扩展模型:

interface FormulaSymbol {
  symbol: string
  meaning: string
  unit: string
}

interface FormulaItem {
  id: string
  name: string
  formula: string
  description: string
  category: FormulaCategory
  symbols: FormulaSymbol[]
  assumptions: string[]
}

万有引力条目可定义:

{
  id: 'universal_gravity',
  name: '万有引力',
  formula: 'F = GMm / r²',
  category: '引力',
  symbols: [
    { symbol: 'F', meaning: '引力大小', unit: 'N' },
    { symbol: 'M', meaning: '中心天体质量', unit: 'kg' },
    { symbol: 'm', meaning: '测试天体质量', unit: 'kg' },
    { symbol: 'r', meaning: '质心距离', unit: 'm' }
  ],
  assumptions: ['点质量近似', '仅考虑两体作用']
}

这样详情页、无障碍朗读和计算器可以共享同一份语义,不必从公式字符串猜变量。

十五、数值计算前先定义单位策略

当前模拟页使用 Mpxv 等教学单位,公式页展示 SI 物理公式和 AU。二者不能直接代入,除非建立尺度映射。

推荐明确两层:

interface DisplayQuantity {
  value: number
  unit: 'M' | 'px' | 'v' | 'AU' | 'km'
}

interface SIQuantity {
  value: number
  unit: 'kg' | 'm' | 'm/s' | 'N' | 'J'
}

教学模拟可以继续使用归一化单位,但公式计算器必须告诉用户:

  • 当前输入是现实 SI 单位还是模拟单位。
  • G 使用真实常量还是归一化常量。
  • 距离 px 如何映射到 m
  • 输出是否只用于趋势演示。

如果这些规则没有定义,就不应添加“计算”按钮。

十六、量纲校验可以提前发现错误

以圆轨道速度为例:

[G] = m³·kg⁻¹·s⁻²
[M] = kg
[r] = m

因此:

[GM/r] = m²·s⁻²
√[GM/r] = m·s⁻¹

结果确实是速度单位。逃逸速度多了无量纲系数 2,量纲不变。

对于引力:

[GMm/r²] = kg·m·s⁻² = N

对每条公式保存 resultUnit 和输入单位,可以在开发测试中执行量纲断言。页面速查不必展示完整推导,但模型应能支持核验。

十七、页面状态还可以更完整

当前公式数据写在页面里,始终有内容,所以不需要加载状态。若将来从资源文件或数据库读取,应补齐:

  • loading
  • content
  • empty
  • error

即使保持静态数据,也应处理无匹配分类。当前分类数组与公式分类一致,不会出现该情况;类型收紧后,这个不变量更容易保持。

错误状态不应伪造成空状态。“暂无公式”和“公式数据加载失败”对用户是两件不同的事。

十八、多设备布局策略

源码根容器使用:

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

手机上单列列表合理。平板与 2in1 可以根据可用宽度增强,而不改变数据模型:

  • 小于 600vp:单列卡片。
  • 600vp 到 960vp:两列公式网格或主列表加详情。
  • 更宽窗口:左侧分类导航,右侧公式详情。

不要单纯把卡片拉到超宽。长行会降低说明文字可读性,可限制内容最大宽度并居中。

十九、状态栏、底部安全区与滚动

页面通过 @StorageProp 获取状态栏和底栏高度,根容器增加上下 padding。List 使用 layoutWeight(1) 占据剩余空间,顶部导航和分类条保持可见。

需要设备验证:

  • 手势导航与三键导航。
  • 横屏小窗口。
  • 平板分屏。
  • 2in1 窗口缩放。
  • 系统字体放大。

底部 List 的 padding.bottom 当前为 12,再叠加根容器 bottomBarHeight。如果全局高度已包含系统避让,应避免重复留白;具体取决于入口处如何写入该状态值。

公式页的数据、语义与布局分层

二十、无障碍不能只朗读视觉公式

屏幕阅读器直接朗读 v = √(GM / r) 可能不够自然。结构化符号模型可以提供更清晰的替代文本:

圆轨道速度等于万有引力常量乘中心天体质量除以轨道半径,再开平方。

分类标签需要足够触控面积和选中状态语义;公式卡片可以组合为一个可聚焦单元,也可以让名称与符号说明分别聚焦,但不要让装饰性分类标签重复朗读。

颜色不能成为唯一选中提示。当前选中项同时改变背景、文字和边框,方向是正确的,还应在真机无障碍服务下核验。

二十一、可执行的测试清单

测试 当前源码预期
初始进入 展示全部 7 条
选择“引力” 展示万有引力、质心,共 2 条
选择“轨道” 展示圆轨道速度、逃逸速度,共 2 条
选择“能量” 展示动能、引力势能,共 2 条
选择“尺度” 只展示天文单位,共 1 条
点击返回 调用 router.back()
横向窄屏 分类条可滚动
纵向长内容 List 可滚动且卡片不重叠

增强模型上线后还要增加:

  • 每个公式 ID 唯一。
  • 每个分类属于联合类型。
  • 每个变量具有含义和单位。
  • 结果单位与量纲断言一致。
  • Unicode 字符在目标字体中可显示。
  • 大字体下公式与说明不截断。

二十二、发布前公式页检查

  • 公式符号与说明一致。
  • r 明确表示质心距离。
  • 圆轨道速度标出两体与圆轨道近似。
  • 逃逸速度说明理想模型边界。
  • 势能负号有解释。
  • 质心位置标明向量含义。
  • AU 的近似精度有标识。
  • 模拟单位与 SI 单位不混用。
  • 分类字符串由类型约束。
  • 字体放大后公式不溢出。
  • 手机、平板、2in1 保持合适行宽。
  • 状态栏、底部导航和返回操作可用。

二十三、总结

当前 FormulaPage.ets 用很少的代码完成了可用的离线速查:七条真实公式由统一模型驱动,五个分类通过纯派生函数过滤,横向 Scroll 保护窄屏分类条,List 负责公式卡片的纵向呈现。对于短公式,原生 Text 加 Unicode 是低成本且稳定的实现。

继续演进时,重点不应只是增加公式数量,而是补上符号、单位、适用条件、量纲和无障碍描述。显示字符串服务于阅读,结构化知识模型服务于验证与计算;在单位映射没有确定之前,宁可保持诚实的“速查页”,也不要提供看似精确但量纲不明的计算结果。

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

Logo

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

更多推荐