用Kotlin实现OpenHarmony KMP个人收支预算分析

目录
- 概述
- 功能设计
- Kotlin 实现代码(KMP)
- JavaScript 调用示例
- ArkTS 页面集成与调用
- 数据输入与交互体验
- 编译与自动复制流程
- 总结
概述
本案例在 Kotlin Multiplatform (KMP) 工程中实现了一个 个人收支预算分析工具:
- 输入:一串数字,第一位是总收入,后面若干位是不同类别的支出,例如:
10000 5000 2000 1500。 - 输出:
- 总收入、总支出、余额统计
- 各项支出占总收入的比例明细
- 简单的预算评语(是否超支、结余多少)
- 技术路径:Kotlin → Kotlin/JS → JavaScript 模块 → ArkTS 页面调用。
相比之前的 BMI 体质指数计算器、贷款等额本息还款计算器、成绩排行榜生成与分析工具,这个案例更贴近日常生活中的理财场景,可以帮助用户快速感知自己的支出结构和预算健康程度,也进一步体现了“算法逻辑写在 Kotlin,一次编写、多端复用;界面交互交给 ArkTS 来完成”的模式。
在这个过程中,你可以看到完整的链路:
- 如何在 KMP 的
jsMain源集中实现业务算法,并用@JsExport导出给 JS 使用。 - Kotlin 代码如何被编译成 ES Module 形式的
hellokjs.mjs,同时生成类型声明hellokjs.d.ts。 - 在 OpenHarmony 的 ArkTS 页面中,如何像调用普通 JS 模块一样调用 Kotlin 导出的函数,并通过状态管理与 UI 绑定结果。
功能设计
输入数据格式
预算分析工具的输入设计得尽量简单:
- 使用空格分隔不同的数字,方便从终端、表单、记事本等地方复制粘贴。
- 第 1 个数字代表当月或当期的总收入,例如工资、奖金等的合计。
- 后续每个数字代表一项支出,可以不区分具体类别,只关注金额本身。
- 示例输入:
10000 5000 2000 1500
这个输入可以简单理解为:
- 一个月收入 10000 元
- 三项支出:5000、2000 和 1500 元
算法会计算:
- 总支出 = 5000 + 2000 + 1500 = 8500
- 余额 = 10000 - 8500 = 1500
- 各项支出占收入的比例,比如第一项 5000 / 10000 = 50%
输出信息结构
为了方便在 ArkTS 界面中直接展示,Kotlin 函数返回的是一段 多行文本,按照清晰的分区格式组织:
- 标题区:展示工具名称,比如“📊 个人收支预算分析”。
- 收支统计:列出总收入、总支出和余额。
- 支出明细:按序号给出每一项支出的金额和占收入的百分比。
- 预算评语:简单判断是否超支,并给出一句话说明。
这种返回方式有几个好处:
- 在 ArkTS 中不需要再拆分和格式化,直接绑定到
Text控件即可滚动展示。 - 同一段文本可以方便地复制到日志、调试输出或者其它终端环境中使用。
- 如果后续需要做富文本展示,也可以在字符串中保留结构,方便二次解析。
Kotlin 实现代码(KMP)
核心逻辑位于 src/jsMain/kotlin/App.kt 中,通过 @JsExport 导出给 JavaScript 使用:
@OptIn(ExperimentalJsExport::class)
@JsExport
fun personalBudgetAnalyzer(inputText: String = "10000 5000 2000 1500"): String {
// 输入格式: "总收入 支出1 支出2 ...", 例如 "10000 5000 2000 1500"
val parts = inputText.trim().split(" ").filter { it.isNotEmpty() }
if (parts.size < 2) {
return "❌ 错误: 请按 '总收入 支出1 支出2 ...' 的格式输入,例如: 10000 5000 2000 1500"
}
val income = parts[0].toDoubleOrNull()
val expenses = parts.drop(1).mapNotNull { it.toDoubleOrNull() }
if (income == null || expenses.any { it <= 0 }) {
return "❌ 错误: 总收入或支出无效\n请输入正确的正数,例如: 10000 5000 2000 1500"
}
val totalExpense = expenses.sum()
val balance = income - totalExpense
val expenseRatios = expenses.map { (it / income * 100).toInt() / 10.0 }
val expenseReport = expenses.zip(expenseRatios).mapIndexed { index, (expense, ratio) ->
" 支出${index + 1}: ${expense} 元 (${ratio}%)"
}
val budgetComment = when {
balance >= 0 -> "预算良好,剩余 ${balance} 元。"
else -> "预算超支,缺口 ${-balance} 元。"
}
return "📊 个人收支预算分析\n" +
"━━━━━━━━━━━━━━━━━━━━━\n" +
"原始输入: $inputText\n\n" +
"1️⃣ 收支统计:\n" +
" 总收入: ${income} 元\n" +
" 总支出: ${totalExpense} 元\n" +
" 余额: ${balance} 元\n\n" +
"2️⃣ 支出明细:\n" +
expenseReport.joinToString("\n") + "\n\n" +
"3️⃣ 预算评语:\n" +
" $budgetComment\n\n" +
"━━━━━━━━━━━━━━━━━━━━━\n" +
"✅ 分析完成!"
}
代码说明:
这是个人收支预算分析工具的完整 Kotlin 实现。函数使用 @JsExport 装饰器将其导出为 JavaScript 可调用的函数。首先进行输入解析,使用 trim().split(" ") 按空格分割输入字符串,并过滤掉空字符串。然后进行输入验证,检查是否至少有两项数据(总收入和至少一项支出)。接着尝试将第一项转换为总收入,后续项转换为支出列表。如果收入为 null 或任何支出为非正数,返回错误提示。核心计算包括:使用 expenses.sum() 计算总支出,计算余额为收入减去总支出,计算每项支出占收入的百分比。然后使用 zip() 和 mapIndexed() 生成格式化的支出明细报告。最后根据余额是否为正判断预算是否超支,生成相应的评语。整个结果通过字符串拼接返回,包含标题、收支统计、支出明细和预算评语等多个部分。
代码说明
- 输入解析:
- 使用
trim().split(" ")简单地以空格拆分输入字符串,并通过filter { it.isNotEmpty() }过滤掉多余空格。 - 第一项尝试转换为
Double作为总收入,后续每一项尝试转换为支出金额。
- 使用
- 健壮性处理:
- 如果输入项不足两项,直接返回带有示例格式的错误提示。
- 如果收入为
null或者任何一项支出小于等于 0,同样返回错误提示,避免后续计算出现不合理结果。
- 核心计算:
- 总支出通过
expenses.sum()一行即可完成,体现了 Kotlin 集合 API 的简洁。 - 余额
balance为收入减去总支出,是预算健康与否的关键指标。 - 比例计算时使用
(it / income * 100).toInt() / 10.0的方式,保留一位小数,并且避免过长的小数显示影响可读性。
- 总支出通过
- 结果拼接:
- 整体采用字符串拼接的方式构造输出文本,使用了多种 Emoji 和分隔线,让终端输出或 ArkTS 页面中的展示更加直观。
从这个实现可以看到,Kotlin 在处理数值运算、集合遍历和字符串拼接时非常自然,而 @JsExport 则为跨端使用打开了大门,让这段代码可以被 JS / ArkTS 直接复用。
JavaScript 调用示例
Kotlin/JS 编译完成后,会在 build/js/packages/hellokjs/kotlin/ 目录下生成 hellokjs.mjs 和 hellokjs.d.ts。在普通 JavaScript 或 TypeScript 环境中,可以像下面这样导入并调用:
// 导入 Kotlin 编译生成的 ES Module
import { personalBudgetAnalyzer } from './hellokjs.mjs';
// 使用默认示例输入
const result1 = personalBudgetAnalyzer();
console.log(result1);
// 使用自定义输入,例如加入更多支出项目
const result2 = personalBudgetAnalyzer('12000 4000 3000 1000 800 600');
console.log(result2);
代码说明:
这段代码展示了如何在 JavaScript 环境中调用 Kotlin 编译生成的函数。首先使用 ES Module 的 import 语句导入 personalBudgetAnalyzer 函数,该函数由 Kotlin 通过 @JsExport 装饰器导出。可以不传入参数调用函数,此时使用 Kotlin 中定义的默认值 “10000 5000 2000 1500”。也可以传入自定义的输入字符串,例如 “12000 4000 3000 1000 800 600”,表示总收入 12000 元,五项支出分别为 4000、3000、1000、800 和 600 元。函数返回一个格式化的分析结果字符串,可以直接打印到控制台或在网页中展示。
在实际工程中,你可以把这段调用放在 Node.js 命令行工具、Web 前端页面或任何支持 ES Module 的运行环境中。借助 hellokjs.d.ts,在 TypeScript/ArkTS 中还能获得良好的类型推断和智能提示,调用体验与原生 JS 函数几乎没有差别。
ArkTS 页面集成与调用
在 OpenHarmony 工程 kmp_ceshiapp 中,我们已经将首页 Index.ets 改造成个人收支预算分析页面。ArkTS 通过普通的 import 语句引入 Kotlin 导出的函数,并在组件生命周期和按钮事件中进行调用:
import { personalBudgetAnalyzer } from './hellokjs';
@Entry
@Component
struct Index {
@State message: string = '请输入总收入和各项支出';
@State inputText: string = '10000 5000 2000 1500';
@State resultText: string = '';
aboutToAppear(): void {
this.calculateBudget();
}
calculateBudget(): void {
try {
const input: string = this.inputText;
const result: string = personalBudgetAnalyzer(input);
this.resultText = result;
this.message = '✓ 计算完成';
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.message = `✗ 错误: ${errorMessage}`;
}
}
build() {
// 省略 UI 细节,这里重点展示调用关系和状态绑定
}
}
代码说明:
这是 OpenHarmony ArkTS 页面的完整实现,展示了如何集成和调用 Kotlin 编译生成的预算分析函数。首先通过 import 语句从 ./hellokjs 模块导入 personalBudgetAnalyzer 函数。页面使用 @Entry 和 @Component 装饰器定义为可入口的组件。定义了三个响应式状态变量:message 显示操作状态,inputText 存储用户输入的预算数据,resultText 存储分析结果。aboutToAppear() 生命周期钩子在页面加载时自动调用 calculateBudget() 进行初始化计算。calculateBudget() 方法获取输入文本,调用 Kotlin 函数进行预算分析,将结果存储在 resultText 中,并更新 message 显示计算状态。使用 try-catch 块捕获任何异常,并在错误时更新 message 显示错误信息。这种设计将算法逻辑完全交给 Kotlin,ArkTS 只负责状态管理和 UI 绑定。
这段代码演示了一个非常典型的 KMP + ArkTS 集成模式:
personalBudgetAnalyzer由 Kotlin 提供,实现和测试集中在 Kotlin 工程中完成。- ArkTS 页面只需要负责:
- 管理输入
inputText和输出resultText的状态。 - 在生命周期钩子(如
aboutToAppear)或者按钮点击事件中调用算法函数。 - 根据返回结果更新界面上的提示文字
message。
- 管理输入
当后续你要调整预算算法、增加更复杂的统计逻辑时,大部分改动只需要在 Kotlin 这一端完成,ArkTS 的调用代码基本可以保持不变,从而提高跨端项目的可维护性和可扩展性。
数据输入与交互体验
在 ArkTS 页面中,输入区域使用了 TextInput 组件来接收用户输入:
- 占位符直接给出示例:
例如: 10000 5000 2000 1500,降低用户第一次使用时的理解成本。 - 默认示例按钮会重置输入为一串合理的数据,并立即触发计算,方便用户快速看到效果。
- 清空按钮则把输入和结果都清理掉,把状态提示还原成“请输入总收入和各项支出”,适合用户重新开始一轮完全不同的预算分析。
整体交互流程大致如下:
- 页面出现时自动调用一次
calculateBudget(),用默认示例填充结果区域,让用户一眼就能看到工具的作用。 - 用户可以修改输入并点击“开始分析”按钮,调用同一个 Kotlin 函数完成重新计算。
- 如果输入格式不正确(比如只填了一个数字、出现非数字字符等),Kotlin 侧会返回带有具体说明的错误文本,ArkTS 只需要原样展示,避免在多处重复输入验证逻辑。
这种“后端算法负责严谨校验与结果组织、前端 UI 负责呈现与交互细节”的分层方式,在跨端项目中非常实用,也有利于后续增加更多工具型案例。
编译与自动复制流程
整个 KMP → JS → ArkTS 的编译和文件同步由根目录下的脚本负责,你可以按以下方式执行:
- 在项目根目录或终端中执行 Gradle 构建:
- Windows 批处理脚本:
build-and-copy.bat - 或 PowerShell 脚本:
build-and-copy.ps1
- Windows 批处理脚本:
- 脚本会自动完成:
- 运行
gradlew build,编译 Kotlin/JS 工程,生成hellokjs.mjs和hellokjs.d.ts。 - 检查输出文件是否存在,给出清晰的成功或失败提示。
- 将这两个文件复制到
kmp_ceshiapp/entry/src/main/ets/pages/目录下:hellokjs.d.ts原样复制。hellokjs.mjs复制并重命名为hellokjs.js,方便 ArkTS 通过相对路径导入。
- 运行
这样一来,每当你在 App.kt 中新增或修改 Kotlin 函数,只需要重新执行一次脚本,就能让 ArkTS 端使用到最新的逻辑,无需手动拷贝文件或调整路径。
总结
通过这个 个人收支预算分析工具 案例,我们再次走了一遍从 Kotlin 到 JavaScript,再到 ArkTS 的完整跨端链路:
- 在 Kotlin 中实现面向业务的算法逻辑,并通过
@JsExport暴露给 JavaScript 使用。 - 使用 Kotlin/JS 编译器生成 ES Module 和类型声明文件,实现类型安全与模块化复用。
- 在 OpenHarmony 的 ArkTS 页面中像使用普通 JS 库一样导入和调用 Kotlin 提供的函数。
- 通过简单明了的多行文本输出,把算法结果直接展示在移动端 UI 上。
与之前的 BMI、贷款计算、成绩分析、小游戏井字棋等案例一起,这个工具形成了一个较为系统的 KMP 跨端示例集合,覆盖了算法类工具、数据分析和互动小游戏等不同场景。你可以在此基础上继续扩展,比如:
- 为每一项支出增加类别名称,生成更详细的预算报表;
- 增加“储蓄率”“投资比例”等高级指标;
- 把本地计算与远端服务结合起来,实现云端同步或多设备共享。
无论如何,核心思路始终不变:让 Kotlin 负责严肃的计算与业务规则,让 ArkTS 负责友好的界面与交互体验,通过 KMP 把两者优雅地串联起来。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)