鸿蒙Kuikly实战:跨平台汇率计算器开发
是 Kuikly 官方提供的精简模板,剔除了 Web、H5、小程序等冗余模块,只保留了 Android、iOS、HarmonyOS 核心渲染与桥接能力,非常适合作为轻量级跨平台应用的开发起点。模板,完整实现了跨平台汇率计算器的开发流程,覆盖了 Kuikly 框架的架构设计、原生入口配置、Kotlin DSL 开发、网络请求封装、跨端调试等核心知识点。private val targetCurren
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
在跨平台开发领域,Kuikly 凭借「Kotlin DSL 驱动原生渲染」的独特架构,为我们提供了一套兼顾性能与开发效率的解决方案。而汇率计算器作为高频刚需的轻量级工具,恰好是学习 Kuikly 全流程开发的绝佳案例。
本文将从需求分析、环境搭建、项目初始化,到核心业务开发、双端原生入口配置、调试打包,完整复刻 Kuikly 汇率计算器的开发过程。全程不依赖 AI,只提供可落地的工程化代码与步骤,让你在实战中掌握 Kuikly 开发的核心能力。
项目模板:KuiklyUI-mini
成品项目:Kuikly-exchange-rate
Kuikly-exchange-rate - AtomGit | GitCode
https://gitcode.com/Goway_Hui/Kuikly-exchange-rate?isLogin=1
一、项目背景与架构哲学
1.1 为什么选择 Kuikly?
在 Flutter 和 React Native 盛行的时代,Kuikly 选择了一条独特的道路:Kotlin DSL 驱动原生渲染。
- 性能极致:它不依赖 WebView,也不像 Flutter 那样自带渲染引擎,而是将 DSL 映射为平台原生组件(Android 的 View,iOS 的 UIView,HarmonyOS 的 ArkUI 组件)。这意味着它拥有原生的启动速度和内存占用。
- 语言统一:业务逻辑完全使用 Kotlin 编写(KMP),利用 Kotlin 强大的类型系统和语法糖,避免了 JavaScript/Dart 的上下文切换,大幅提升开发效率。
- 原生体验:UI 组件直接映射为各平台原生控件,视觉风格与系统完全一致,用户体验更接近原生应用。
1.2 KuiklyUI-mini 模板架构
KuiklyUI-mini 是 Kuikly 官方提供的精简模板,剔除了 Web、H5、小程序等冗余模块,只保留了 Android、iOS、HarmonyOS 核心渲染与桥接能力,非常适合作为轻量级跨平台应用的开发起点。
项目结构清晰,分为「共享层」与「宿主层」两大核心:
|
KuiklyUI-mini/ ├── androidApp/ // Android 宿主工程(启动入口+原生能力桥接) ├── iosApp/ // iOS 宿主工程(启动入口+UIKit 容器) ├── ohosApp/ // HarmonyOS 宿主工程(启动入口+ArkUI 容器) ├── shared/ // KMP 共享层(核心!所有跨端复用代码) │ ├── src/commonMain/ // 通用代码目录(UI、业务、模型、工具类) │ └── build.gradle.kts // 共享层构建配置 └── settings.gradle.kts // 项目模块管理 |
- Shared 模块:包含汇率计算器的所有核心代码——UI 布局(Kotlin DSL)、汇率数据模型、网络请求封装、计算逻辑、本地存储等,编译后会生成对应平台的中间产物,供宿主层调用。
- Host Apps 模块:仅负责启动应用、提供原生能力桥接(如网络权限、本地存储)、配置原生入口,不参与核心业务逻辑,保证跨端代码的纯粹性。
二、项目初始化
2.1 环境准备
在开始项目前,需严格按照以下版本配置环境,避免构建失败:
|
工具/环境 |
推荐版本 |
核心作用 |
|
JDK |
17+ |
Kotlin 与 Kuikly 编译的基础环境 |
|
Android Studio |
最新稳定版 |
Android 端开发、调试、打包 |
|
DevEco Studio |
5.0+ |
HarmonyOS 端开发、调试、打包 |
|
Gradle |
8.0+ |
项目构建工具,Kuikly 工程核心依赖 |
|
Kuikly 插件 |
1.1.0+ |
提供 Kuikly 工程模板、编译支持 |
2.2 项目克隆与初始化
- 克隆模板项目:
|
git clone https://gitee.com/Goway_Hui/KuiklyUI-mini.git cd KuiklyUI-mini |
- 重命名项目:将项目名称修改为 Kuikly-exchange-rate,并批量替换包名,例如将 com.goway.kuiklymini 替换为 com.goway.exchangerate。
- 导入项目:用 Android Studio 打开项目根目录,等待 Gradle 自动下载依赖。若出现依赖下载失败,可在 build.gradle.kts 中配置国内镜像源加速。


三、需求分析与路由设计
3.1 需求拆解
汇率计算器的核心需求可分为以下几类:
- 基础功能:
- 支持输入金额,实时切换币种并计算结果
- 覆盖人民币、美元、欧元、日元、英镑等 20+ 主流币种
- 对接公开汇率接口,获取实时汇率数据
- 扩展功能:
- 本地存储近期换算记录,支持快速复用
- 支持深色模式与主题切换
- 汇率更新时间提示,增强数据可信度
- 非功能需求:
- 界面简洁,无广告,打开即用
- 多端适配,在手机、平板、电脑上均有良好展示效果
- 离线可用,网络异常时使用缓存汇率数据
3.2 路由系统设计
路由是连接「宿主层」与「共享层」的桥梁,在 shared/src/commonMain/kotlin/com/goway/exchangerate/Routes.kt 中定义:
|
object Routes { // 模板默认首页(功能列表页) const val ROUTER_PAGE = "router" // 汇率计算器核心页面 const val EXCHANGE_RATE_PAGE = "exchange_rate" } |
四、双端原生入口配置
4.1 Android 端原生入口配置
- 修改启动页面:
打开 androidApp/src/main/kotlin/com/goway/exchangerate/KuiklyRenderActivity.kt,修改默认启动页面:
|
class KuiklyRenderActivity : AppCompatActivity() { private val pageName: String get() { val pn = intent.getStringExtra(KEY_PAGE_NAME) ?: "" return if (pn.isNotEmpty()) { pn } else { // 默认启动汇率计算器页面 Routes.EXCHANGE_RATE_PAGE } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 初始化 Kuikly 渲染逻辑 initKuikly() } private fun initKuikly() { // 渲染指定页面 renderPage(pageName, createPageData()) } private fun createPageData(): Map<String, Any> { return emptyMap() } } |
- 添加网络权限:
在 androidApp/src/main/AndroidManifest.xml 中添加:
|
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
4.2 HarmonyOS 端原生入口配置
- 修改启动页面:
打开 ohosApp/entry/src/main/ets/pages/Index.ets,配置 Kuikly 组件:
|
import { Routes } from '../shared/Routes' @Entry @Component struct Index { private pageName: string = Routes.EXCHANGE_RATE_PAGE; private pageData: object = {}; private kuiklyViewDelegate: KuiklyViewDelegate = new KuiklyViewDelegate(); private nativeManager: KRBridgeModule = new KRBridgeModule(); build() { Stack() { Kuikly({ pagerName: this.pageName, pagerData: this.pageData, delegate: this.kuiklyViewDelegate, nativeManager: this.nativeManager }) } .width('100%') .height('100%') } } |
- 添加网络权限:
在 ohosApp/entry/src/main/module.json5 中添加:
|
"abilities": [ { "permissions": [ "ohos.permission.INTERNET" ] } ] |
五、核心业务开发(Shared 层)
5.1 数据模型定义
在 shared/src/commonMain/kotlin/com/goway/exchangerate/model 目录下创建:
|
// Currency.kt data class Currency( val code: String, val name: String, val symbol: String ) // ExchangeRate.kt data class ExchangeRate( val base: String, val rates: Map<String, Double>, val updateTime: Long ) |
5.2 网络请求封装
在 shared/src/commonMain/kotlin/com/goway/exchangerate/service 目录下创建 ApiService.kt:
|
import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.request.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json private const val EXCHANGE_RATE_URL = "https://api.exchangerate-api.com/v4/latest/USD" object ApiService { private val client by lazy { HttpClient { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } } suspend fun getExchangeRate(): ExchangeRate? { return try { client.get(EXCHANGE_RATE_URL).body<ExchangeRate>() } catch (e: Exception) { println("获取汇率失败:${e.message}") null } } } |
5.3 UI 布局开发
在 shared/src/commonMain/kotlin/com/goway/exchangerate/pages 目录下创建 ExchangeRatePage.kt:
|
import com.tencent.kuikly.core.base.Color import com.tencent.kuikly.core.pager.Pager import com.tencent.kuikly.core.views.* import com.tencent.kuikly.core.directives.vif import kotlinx.coroutines.launch internal class ExchangeRatePage : Pager() { private val sourceCurrency = mutableStateOf(Currency("CNY", "人民币", "¥")) private val targetCurrency = mutableStateOf(Currency("USD", "美元", "$")) private val inputAmount = mutableStateOf("") private val resultAmount = mutableStateOf("0.00") private val exchangeRate = mutableStateOf<ExchangeRate?>(null) private val isLoading = mutableStateOf(false) override fun body() = buildUI() private fun buildUI() = View { attr { flex(1f) backgroundColor(Color(0xFFF5F7FA)) padding(20f) flexDirectionColumn() alignItemsCenter() } // 金额输入区 Input { attr { width("100%") height(60f) fontSize(24f) placeholder("请输入换算金额") backgroundColor(Color.WHITE) borderRadius(12f) padding(left = 16f, right = 16f) text(inputAmount.value) } event { textDidChange { params -> inputAmount.value = params.text calculateResult() } } } // 币种切换区 View { attr { width("100%") height(80f) flexDirectionRow() justifyContentSpaceBetween() alignItemsCenter() marginVertical(20f) } CurrencyItem(sourceCurrency.value) { sourceCurrency.value = it calculateResult() } Text { attr { text("⇄") fontSize(24f) color(Color(0xFF007AFF)) } } CurrencyItem(targetCurrency.value) { targetCurrency.value = it calculateResult() } } // 结果展示区 View { attr { width("100%") height(100f) backgroundColor(Color.WHITE) borderRadius(12f) justifyContentCenter() alignItemsCenter() boxShadow(0f, 2f, 8f, Color(0x10000000)) } Text { attr { text("${targetCurrency.value.symbol} ${resultAmount.value}") fontSize(32f) fontWeightBold() color(Color.BLACK) } } } // 加载/更新提示 vif({ isLoading.value }) { Text { attr { text("正在获取最新汇率...") fontSize(14f) color(Color(0xFF999999)) marginTop(20f) } } } ?: { Text { attr { val time = exchangeRate.value?.updateTime ?: 0L text("汇率更新于:${formatTime(time)}") fontSize(14f) color(Color(0xFF999999)) marginTop(20f) } } } } private fun calculateResult() { val amount = inputAmount.value.toDoubleOrNull() ?: 0.0 val rateData = exchangeRate.value ?: return val sourceRate = rateData.rates[sourceCurrency.value.code] ?: 1.0 val targetRate = rateData.rates[targetCurrency.value.code] ?: 1.0 if (sourceRate == 0.0) { resultAmount.value = "0.00" return } val result = amount * (targetRate / sourceRate) resultAmount.value = String.format("%.4f", result) } private fun formatTime(timestamp: Long): String { return "2026-02-24 12:00" } override suspend fun onInit() { super.onInit() isLoading.value = true exchangeRate.value = ApiService.getExchangeRate() isLoading.value = false calculateResult() } } // 币种选择组件 fun ViewBuilder.CurrencyItem(currency: Currency, onSelect: (Currency) -> Unit) { View { attr { width(120f) height(50f) backgroundColor(Color.WHITE) borderRadius(8f) justifyContentCenter() alignItemsCenter() border(Border(1f, Color(0xFFE5E7EB), BorderStyle.SOLID)) } Text { attr { text("${currency.code} (${currency.name})") fontSize(16f) color(Color.BLACK) } } event { click { onSelect(Currency("EUR", "欧元", "€")) } } } } |
六、多端调试与打包
6.1 Android 端调试
- 在 Android Studio 中选择 androidApp 模块,连接模拟器或真机。
- 点击「运行」按钮,应用启动后直接进入汇率计算器页面。
- 验证功能:输入金额、切换币种,检查计算结果是否正确。


6.2 HarmonyOS 端调试
- 用 DevEco Studio 打开 ohosApp 目录,完成签名配置。
- 选择 entry 模块,连接鸿蒙模拟器或真机。
- 点击「运行」按钮,验证页面加载与功能是否正常。

6.3 打包上线
- Android 端:执行 assembleRelease 任务,生成 APK 文件。
- HarmonyOS 端:执行 Build Release Hap(s),生成 HAP 文件。
七、常见问题与优化方向
7.1 常见问题
- Gradle 依赖下载失败:检查网络代理,或配置国内镜像源。
- 页面不显示:检查路由配置是否正确,确保宿主层与共享层路由一致。
- 网络请求失败:确认已添加网络权限,且接口地址可正常访问。
7.2 优化方向
- 完善本地存储,实现汇率数据持久化缓存。
- 丰富币种选择器,支持搜索与更多币种。
- 添加历史记录功能,支持一键回填。
- 适配深色模式,提升用户体验。
结语
本文基于 KuiklyUI-mini 模板,完整实现了跨平台汇率计算器的开发流程,覆盖了 Kuikly 框架的架构设计、原生入口配置、Kotlin DSL 开发、网络请求封装、跨端调试等核心知识点。
通过本项目,你不仅能掌握 Kuikly 跨平台开发的基础能力,还能巩固 Kotlin 语法、网络请求、状态管理等核心编程技能。后续可基于本项目进一步扩展功能,或尝试开发其他轻量级跨平台应用。
更多推荐



所有评论(0)