在跨平台开发日益普及的今天,Kotlin Multiplatform (KMP) 为开发者提供了一种高效、优雅的代码复用方案。本文将深入剖析一个基于 KMP 构建的随机抽奖模拟工具,展示如何将 Kotlin 核心逻辑无缝集成到鸿蒙(OpenHarmony)ArkTS 页面中,实现真正的跨端复用。无论你是 KMP 新手还是寻求跨端解决方案的开发者,都能从中获得启发。
案例背景与价值
本案例实现了一个简单而实用的随机抽奖模拟工具,其核心功能包括:
- 输入解析:解析一行包含候选人列表和抽取人数的文本。
- 随机抽取:基于公平随机算法产生中奖与未中奖名单。
- 跨端输出:结果以格式化文本返回,供前端展示。
技术路径为:Kotlin → Kotlin/JS → JavaScript 模块 → ArkTS 页面。这个案例覆盖了教学随机点名、活动抽奖、随机分组等常见场景,同时演示了字符串处理、列表操作、随机算法在 KMP 中的最佳实践。与 Python、JavaScript、TypeScript 等语言类似,Kotlin 在跨端场景下同样展现出强大的表现力。
功能设计与输入格式
随机抽奖工具的输入格式设计得尽量自然、简洁:
- 使用逗号(
,)分隔候选人名称。 - 使用空格将候选人列表与抽取人数分隔开。
示例输入:
小明,小红,小刚,小李 2解析逻辑:
- 左侧(
小明,小红,小刚,小李)解析为候选人列表。 - 右侧(
2)解析为抽取人数。
工具遵循以下基本约束:
- ✅ 候选人列表不能为空。
- ✅ 抽取人数必须是正整数。
- ✅ 抽取人数不能大于候选人数。
当输入不满足约束时,工具会返回带有示例的错误提示文本,方便前端直接展示。这种设计理念与 Go、C++ 等语言中常见的防御式编程一致,确保系统健壮性。
Kotlin 核心实现
核心逻辑位于 src/jsMain/kotlin/App.kt 中,通过 @JsExport 暴露给 JS/ArkTS 调用:
@OptIn(ExperimentalJsExport::class)
@JsExport
fun randomLotterySimulator(inputText: String = "小明,小红,小刚,小李 2"): String {
// 输入格式: "候选人1,候选人2,... 抽取人数",例如 "小明,小红,小刚,小李 2"
val parts = inputText.trim().split(" ")
if (parts.isEmpty()) {
return "❌ 错误: 请输入候选人列表和抽取人数,例如: 小明,小红,小刚,小李 2"
}
val namesPart = parts.first()
val countPart = parts.drop(1).firstOrNull() ?: "1"
val candidates = namesPart.split(",").map { it.trim() }.filter { it.isNotEmpty() }
val pickCount = countPart.toIntOrNull() ?: 1
if (candidates.isEmpty() || pickCount <= 0) {
return "❌ 错误: 候选人列表或抽取人数无效\n示例: 小明,小红,小刚,小李 2"
}
if (pickCount > candidates.size) {
return "❌ 错误: 抽取人数不能大于候选人数量\n当前候选人数量: ${candidates.size}"
}
// 打乱列表并抽取前 N 个
val shuffled = candidates.shuffled()
val winners = shuffled.take(pickCount)
val others = shuffled.drop(pickCount)
return " 随机抽奖模拟工具\n" +
"━━━━━━━━━━━━━━━━━━━━━\n" +
"候选人列表: ${candidates.joinToString(", ")}\n" +
"抽取人数: ${pickCount}\n\n" +
"1️⃣ 中奖名单:\n" +
winners.joinToString("\n") { " - $it" } + "\n\n" +
"2️⃣ 未中奖名单:\n" +
if (others.isNotEmpty()) others.joinToString("\n") { " - $it" } else " (无)" + "\n\n" +
"3️⃣ 提示:\n" +
" • 每次调用都会随机打乱列表,结果仅供模拟演示使用\n" +
" • 如果需要可重复抽奖,可以多次点击按钮重新抽取\n\n" +
"━━━━━━━━━━━━━━━━━━━━━\n" +
"✅ 抽奖完成!"
}这是随机抽奖模拟的核心实现函数,展示了完整的抽奖算法。函数使用 @OptIn(ExperimentalJsExport::class) 和 @JsExport 注解,使其可以被编译成 JavaScript 并在 ArkTS 中调用。函数首先进行输入解析,使用 split(" ") 分割输入字符串,分离候选人列表和抽取人数。使用 split(",") 按逗号拆分候选人,并通过 trim() 去除多余空格,使用 filter { it.isNotEmpty() } 排除空字符串。进行边界检查,验证候选人列表不为空、抽取人数为正数且不超过候选人数量。使用 shuffled() 随机打乱候选人列表,使用 take(pickCount) 取前 N 个作为中奖名单,剩余部分作为未中奖名单。最后使用字符串模板和 joinToString() 格式化输出完整的抽奖结果,包括候选人列表、中奖/未中奖名单和提示说明。这个函数展示了如何将随机抽奖算法转换为用户友好的输出。
代码说明:
- 输入拆分:使用
split(" ")将输入分割为最多两部分:候选人串 + 抽取人数。如果缺少抽取人数,默认使用1。 - 候选人列表处理:使用
split(",")按逗号拆分候选人,并通过trim()去除多余空格。使用filter { it.isNotEmpty() }排除空字符串,保证候选人列表有效。 - 边界检查:当候选人列表为空或抽取人数小于等于 0 时,返回错误提示。当抽取人数大于候选人数时,同样返回带有候选人数信息的错误文本。
- 随机抽取:使用
candidates.shuffled()打乱列表顺序。使用take(pickCount)取前 N 个作为中奖名单。剩余部分作为未中奖名单。 - 结果拼接:将候选人列表、抽取人数、中奖/未中奖名单以及提示说明整合为一段多行文本,方便 ArkTS 页面直接显示。
JavaScript 调用示例
在 Kotlin/JS 编译完成后,randomLotterySimulator 会被导出到 hellokjs.mjs 模块中,可以在 Node.js 或浏览器环境中这样调用:
import { randomLotterySimulator } from './hellokjs.mjs';
// 使用默认示例
const result1 = randomLotterySimulator();
console.log(result1);
// 自定义候选人和抽取人数
const result2 = randomLotterySimulator('A,B,C,D,E 3');
console.log(result2);这段代码展示了如何在 JavaScript 环境中调用编译后的 Kotlin 函数。首先使用 import 语句从编译后的 JavaScript 模块 hellokjs.mjs 中导入 randomLotterySimulator 函数。调用 randomLotterySimulator() 不带参数时使用默认值 “小明,小红,小刚,小李 2”。调用 randomLotterySimulator('A,B,C,D,E 3') 时传入自定义的候选人列表和抽取人数。使用 console.log() 输出结果。这展示了 KMP 的跨端能力,同一份 Kotlin 代码可以在 JavaScript 环境中无缝调用。编译器自动处理了 Kotlin 的类型系统到 JavaScript 的转换,使得调用方式和普通 JavaScript 函数完全相同。这种体验与使用 TypeScript 编写类型安全的 JavaScript 模块类似,但更加底层和自动。
输出示例(简化):
随机抽奖模拟工具
━━━━━━━━━━━━━━━━━━━━━
候选人列表: A, B, C, D, E
抽取人数: 3
1️⃣ 中奖名单:
- C
- A
- E
2️⃣ 未中奖名单:
- B
- D
3️⃣ 提示:
• 每次调用都会随机打乱列表,结果仅供模拟演示使用
• 如果需要可重复抽奖,可以多次点击按钮重新抽取
━━━━━━━━━━━━━━━━━━━━━
✅ 抽奖完成!ArkTS 页面集成与调用
在 OpenHarmony 工程 kmp_ceshiapp 中,首页 Index.ets 已经改成随机抽奖工具页面,核心调用逻辑如下:
import { randomLotterySimulator } from './hellokjs';
@Entry
@Component
struct Index {
@State message: string = '请输入候选人列表和抽取人数';
@State inputText: string = '小明,小红,小刚,小李 2';
@State resultText: string = '';
aboutToAppear(): void {
this.runLottery();
}
runLottery(): void {
try {
const input: string = this.inputText;
const result: string = randomLotterySimulator(input);
this.resultText = result;
this.message = '✓ 计算完成';
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.message = `✗ 错误: ${errorMessage}`;
}
}
build() {
// 省略 UI 细节,只保留调用关系
}
}这段 ArkTS 代码是鸿蒙应用的用户界面实现,展示了如何在 ArkTS 中导入和调用编译后的 Kotlin 函数。首先使用 import 语句从编译后的 JavaScript 模块中导入 randomLotterySimulator 函数。使用 @State 装饰器定义三个响应式状态变量:message 用于显示状态信息,inputText 存储用户输入的候选人列表和抽取人数,resultText 存储抽奖结果。aboutToAppear() 生命周期函数在组件加载时自动调用 runLottery() 进行初始化。runLottery() 方法调用 Kotlin 编译的 JavaScript 函数 randomLotterySimulator() ,将用户输入的文本传入,获取抽奖结果,然后更新 resultText 和 message。使用 try-catch 块捕获异常,如果发生错误则显示错误信息。build() 方法构建整个 UI 布局。这个结构实现了清晰的前后端职责划分:ArkTS 只负责收集输入和展示结果,所有抽奖逻辑都集中在 Kotlin 中。
ArkTS 侧只需要:
- 管理三个状态:
message、inputText、resultText。 - 在
aboutToAppear和按钮点击时调用runLottery(),内部通过randomLotterySimulator获取结果。 - 将
resultText绑定到一个Text组件显示多行文本即可。
数据输入与交互体验
首页 UI 保持与前几个案例一致的风格,只是文案换成了抽奖语义:
- 顶部标题栏:显示“KMP 鸿蒙跨端”和“随机抽奖模拟工具”。
- 输入区域:
Column() {
Text('输入候选人和抽取人数 (示例: 小明,小红,小刚,小李 2):')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#1f2937')
.margin({ bottom: 8 })
TextInput({ placeholder: '例如: 小明,小红,小刚,小李 2', text: this.inputText })
.width('100%')
.height(80)
.padding(8)
.border({ width: 1, color: '#d1d5db' })
.borderRadius(6)
.onChange((value: string) => {
this.inputText = value;
})
.margin({ bottom: 12 })
Button('开始抽奖')
.width('100%')
.height(40)
.margin({ top: 4 })
.backgroundColor('#f59e0b')
.fontColor(Color.White)
.onClick(() => {
this.runLottery();
})
}这段 ArkTS 代码构建了用户输入区域的 UI 组件。使用 Column 布局容器垂直排列各个元素。第一个 Text 组件显示输入说明,设置字体大小为 14,加粗,颜色为深灰色。TextInput 组件用于接收用户输入,设置占位符文本为 “例如: 小明,小红,小刚,小李 2”,初始值为 this.inputText 。设置宽度为 100%、高度为 80、内边距为 8。添加边框和圆角以提高视觉效果。使用 onChange 事件监听器实时更新 inputText 状态。Button 组件用于触发抽奖操作,设置宽度为 100%、高度为 40、背景色为橙色、文字颜色为白色。使用 onClick 事件监听器在点击时调用 runLottery() 方法。这个输入区域设计简洁直观,用户可以轻松输入候选人列表和抽取人数。
- 结果区域:使用
Scroll+Text展示多行文本结果。 - 底部按钮:“默认示例”:重置为
小明,小红,小刚,小李 2并调用runLottery();“清空”:清空输入和结果,把提示重置为“请输入候选人列表和抽取人数”。
整个交互流程非常简单:用户输入一行文本并点击按钮,就能看到一轮抽奖结果,点击多次可以模拟多轮抽奖。
[AFFILIATE_SLOT_2]️ 编译与自动复制流程
和其它案例一样,Kotlin 代码更新后,只需在项目根目录执行 build-and-copy.bat 脚本,即可完成编译和文件复制:
cd d:\flutter_Obj\kmp_openharmony
.\build-and-copy.bat这段代码展示了如何执行自动化构建脚本。首先使用 cd 命令切换到项目根目录 d:\flutter_Obj\kmp_openharmony 。然后执行 build-and-copy.bat 脚本,该脚本会自动完成编译和文件复制的全部流程。
脚本会自动:
- 执行
gradlew build构建 Kotlin/JS。 - 在
build/js/packages/hellokjs/kotlin/下生成/更新:hellokjs.mjs、hellokjs.d.ts。 - 将上述文件复制到 ArkTS pages 目录:
kmp_ceshiapp/entry/src/main/ets/pages/hellokjs.d.ts、kmp_ceshiapp/entry/src/main/ets/pages/hellokjs.js。
每次你在 App.kt 中修改 randomLotterySimulator 或其它导出函数后,只要重新执行这个脚本,ArkTS 端就会自动使用最新的实现。这种自动化流程与 C++ 项目中使用 CMake 或 Python 项目中使用的 Makefile 类似,都是为了提升开发效率和减少人工错误。
总结
“随机抽奖模拟工具”是一个非常适合作为 KMP + ArkTS 教学的小案例:
- Kotlin 侧展示了字符串解析、列表处理和随机算法的基本用法。
- Kotlin/JS 将这段逻辑导出为 JS 模块,使其可以在 Node.js、Web、ArkTS 中反复复用。
- ArkTS 侧只需要少量代码即可构建一个可视化的抽奖界面,方便在 OpenHarmony 设备上演示。
配合之前的 BMI、贷款、成绩分析、个人预算、学习打卡、环境舒适度等案例,这个抽奖工具进一步丰富了你的跨端示例集,覆盖了日常工具、数据分析、学习/健康/环境等多个方向。
核心理念始终不变:把通用算法逻辑写在 Kotlin 中,用 KMP 输出到 JS,再让 ArkTS 专注界面和交互,通过一套代码服务多端。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
浙公网安备 33010602011771号