引言

本文是记录鸿蒙开发的初学者基于前两次的kuiklyUI的学习打造跨平台Todo应用

1.什么是KuiklyUI?

KuiklyUI 是腾讯 Oteam 推出的基于 Kotlin Multiplatform(KMP)的跨平台 UI 框架,核心是实现一套代码多端运行,兼顾原生性能、易用性与动态化能力,支持 Android、iOS、鸿蒙等多平台。

核心优势

1.跨端高效:一套 Kotlin 代码适配 Android、iOS、鸿蒙等多端,复用性高。
2.原生性能:原生渲染无桥接损耗,SDK 轻量,启动与运行速度优。
3.低学习成本:沿用 Kotlin 生态与原生工具链,无需新增技术栈。
4.开发灵活:同时支持声明式 / 响应式编程,适配不同团队需求。
5.动态迭代:支持 UI 和业务逻辑动态更新,无需重新发版。
6.企业级成熟:腾讯内部多款核心产品落地,经大规模用户验证。

2.环境准备

在开始之前,你需要有以下的开发环境:

1.JDK17+

2.Android Studio最新版本(支持KMP插件)

3. DevEco Studio 5.0+

4.鸿蒙 SDK 的环境变量

 设置系统环境变量 DEVECO_SDK_HOME 指向你的 DevEco SDK 路径

3.项目准备

3.1下载KuiklyTodo模板

1.进入AtomGit,搜索 KuiklyTodo,点击下载zip

2.下载完成后新建一个文件夹用来存储解压的KuiklyTodo文件

3.2TodoItem.kt的修改

1.按照图示导向,找到TodoItem.kt文件

2.打开文件,把里面的代码替换成下面的代码

package com.tencent.kuikly.demo.pages.todo
 
data class TodoItem(
    val id: Long,
    var content: String,
    var isCompleted: Boolean = false
)

3.3TodoPage.kt的修改

和3.2的TodoItem.kt文件在一个地方,修改方式一样

/**
 * @ProjectName : KuiklyUI
 * @Author : GuoJiaHui
 * @Time : 2026年01月29日 17:30 PM
 * @Description : 待办事项页面逻辑控制器
 * 负责管理待办事项列表的数据状态、增删改查操作
 */
package com.tencent.kuikly.demo.pages.todo
 
import com.tencent.kuikly.core.annotations.Page
import com.tencent.kuikly.core.base.ViewBuilder
import com.tencent.kuikly.demo.pages.base.BasePager
import com.tencent.kuikly.core.reactive.handler.observable
import com.tencent.kuikly.core.reactive.handler.observableList
 
/**
 * 待办事项页面类
 * 使用 @Page 注解标记为页面,路由名称为 "todo_page"
 */
@Page("todo_page")
internal class TodoPage : BasePager() {
    /** 待办事项列表数据,使用 observableList 实现响应式更新 */
    var todoList by observableList<TodoItem>()
    
    /** 输入框文本状态,使用 observable 实现双向绑定 */
    var inputText by observable("")
 
    /**
     * 页面创建生命周期回调
     * 在此初始化数据
     */
    override fun created() {
        super.created()
        // 初始化演示数据
        initData()
    }
 
    /**
     * 初始化演示数据
     * 添加默认的待办事项
     */
    private fun initData() {
        todoList.add(TodoItem(1, "学习 KuiklyUI 框架"))
        todoList.add(TodoItem(2, "创建一个 Todo 应用"))
    }
 
    /**
     * 构建页面 UI 结构
     * @return ViewBuilder UI 构建函数
     */
    override fun body(): ViewBuilder {
        return buildTodoUI(this)
    }
 
    /** 生成 ID 的计数器,起始值为 100 */
    private var nextId = 100L
 
    /**
     * 添加新的待办事项
     * 校验输入是否为空,创建新项并添加到列表,最后清空输入框
     */
    fun addTodo() {
        if (inputText.isBlank()) return
        val newItem = TodoItem(nextId++, inputText)
        todoList.add(newItem)
        inputText = ""
    }
 
    /**
     * 切换待办事项的完成状态
     * @param item 目标待办事项对象
     */
    fun toggleTodo(item: TodoItem) {
        item.isCompleted = !item.isCompleted
        // 触发列表更新:通过重新赋值触发 observableList 的更新机制
        val index = todoList.indexOf(item)
        if (index >= 0) {
            todoList[index] = item 
        }
    }
 
    /**
     * 删除指定的待办事项
     * @param item 要删除的待办事项对象
     */
    fun deleteTodo(item: TodoItem) {
        todoList.remove(item)
    }
 
    /**
     * 清除所有已完成的待办事项
     * 遍历列表并移除 isCompleted 为 true 的项
     */
    fun clearCompleted() {
        todoList.removeAll { it.isCompleted }
    }
}
 

3.4TodoPageUI.kt的修改

和上面两个一样,在同一位置

/**
 * @ProjectName : KuiklyUI
 * @Author : GuoJiaHui
 * @Time : 2026年01月29日 17:30 PM
 * @Description : 待办事项页面UI构建逻辑
 * 包含整体布局、样式定义及事件绑定
 */
package com.tencent.kuikly.demo.pages.todo
 
import com.tencent.kuikly.core.base.Color
import com.tencent.kuikly.core.base.ViewBuilder
import com.tencent.kuikly.core.pager.Pager
import com.tencent.kuikly.core.views.*
import com.tencent.kuikly.core.directives.vfor
import com.tencent.kuikly.core.directives.vif
import com.tencent.kuikly.core.base.Border
import com.tencent.kuikly.core.base.BorderStyle
import com.tencent.kuikly.core.base.BoxShadow
 
/**
 * 构建 Todo 页面的 UI 结构
 * @param page 页面上下文对象,需转换为 TodoPage 类型以访问数据
 * @return ViewBuilder UI 构建闭包
 */
fun buildTodoUI(page: Pager): ViewBuilder {
    val ctx = page as TodoPage
    return {
        View {
            attr {
                // 设置页面主容器充满全屏
                flex(1f)
                backgroundColor(Color.WHITE)
                // 处理安全区域内边距,适配刘海屏/动态岛
                padding(
                    top = 16f + ctx.pageData.safeAreaInsets.top,
                    left = 16f + ctx.pageData.safeAreaInsets.left,
                    right = 16f + ctx.pageData.safeAreaInsets.right,
                    bottom = 16f + ctx.pageData.safeAreaInsets.bottom
                )
            }
 
            // 标题栏组件
            Text {
                attr {
                    text("待办事项清单")
                    fontSize(24f)
                    fontWeightBold()
                    marginBottom(20f)
                    color(Color.BLACK)
                }
            }
 
            // 顶部输入区域容器
            View {
                attr {
                    flexDirectionRow() // 水平布局
                    marginBottom(20f)
                    height(50f)
                    alignItemsCenter() // 垂直居中
                }
 
                // 文本输入框组件
                Input {
                    attr {
                        flex(1f) // 占据剩余宽度
                        text(ctx.inputText) // 绑定输入文本
                        placeholder("需要做什么?")
                        fontSize(16f)
                        backgroundColor(Color(0xFFF5F5F5))
                        borderRadius(8f)
                        height(44f)
                    }
                    event {
                        // 监听文本变化事件,更新数据模型
                        textDidChange { params ->
                            ctx.inputText = params.text
                        }
                    }
                }
 
                // 添加按钮组件
                View {
                    attr {
                        width(80f)
                        height(44f)
                        marginLeft(10f)
                        backgroundColor(Color(0xFF007AFF))
                        borderRadius(8f)
                        justifyContentCenter() // 内容居中
                        alignItemsCenter()
                        // 增加按钮投影效果
                        boxShadow(BoxShadow(0f, 2f, 4f, Color(0x40007AFF)))
                    }
                    Text {
                        attr {
                            text("添加")
                            color(Color.WHITE)
                            fontWeightBold()
                            fontSize(16f)
                        }
                    }
                    event {
                        // 绑定点击事件,调用添加逻辑
                        click { ctx.addTodo() }
                    }
                }
            }
 
            // 列表显示区域
            View {
                attr {
                    flex(1f) // 占据剩余空间
                }
 
                // 空状态提示:当列表为空时显示
                vif({ ctx.todoList.isEmpty() }) {
                    View {
                        attr {
                            flex(1f)
                            justifyContentCenter()
                            alignItemsCenter()
                        }
                        Text {
                            attr {
                                text("暂无待办事项")
                                fontSize(18f)
                                color(Color(0xFF999999))
                                marginBottom(8f)
                            }
                        }
                        Text {
                            attr {
                                text("快去添加一个吧!")
                                fontSize(14f)
                                color(Color(0xFFCCCCCC))
                            }
                        }
                    }
                }
                
                // 列表渲染循环:遍历 todoList 生成列表项
                vfor({ ctx.todoList }) { item ->
                    View {
                        attr {
                            flexDirectionColumn()
                        }
                        
                        // 列表项内容行
                        View {
                            attr {
                                flexDirectionRow()
                                alignItemsCenter()
                                padding(top = 12f, bottom = 12f)
                            }
 
                            // 复选框组件(自定义 View 模拟)
                            View {
                                attr {
                                    width(24f)
                                    height(24f)
                                    borderRadius(12f)
                                    // 根据完成状态设置边框和背景色
                                    border(Border(2f, BorderStyle.SOLID, if (item.isCompleted) Color(0xFF007AFF) else Color(0xFFCCCCCC)))
                                    backgroundColor(if (item.isCompleted) Color(0xFF007AFF) else Color.TRANSPARENT)
                                    marginRight(12f)
                                    justifyContentCenter()
                                    alignItemsCenter()
                                }
                                event {
                                    // 点击复选框切换状态
                                    click { ctx.toggleTodo(item) }
                                }
                                // 勾选图标
                                Text {
                                    attr {
                                        text("✓")
                                        color(Color.WHITE)
                                        fontSize(14f)
                                        fontWeightBold()
                                        // 控制勾选标记的透明度
                                        opacity(if (item.isCompleted) 1f else 0f)
                                    }
                                }
                            }
 
                            // 待办事项文本内容
                            Text {
                                attr {
                                    text(item.content)
                                    fontSize(16f)
                                    // 完成状态文字颜色变浅
                                    color(if (item.isCompleted) Color(0xFF999999) else Color(0xFF333333))
                                    flex(1f)
                                    // 完成状态添加删除线
                                    if (item.isCompleted) textDecorationLineThrough() else "textDecoration" with "none"
                                }
                                event {
                                    // 点击文字也可切换状态
                                    click { ctx.toggleTodo(item) }
                                }
                            }
 
                            // 删除按钮
                            View {
                                attr {
                                    padding(8f) // 增加点击热区
                                }
                                event {
                                    click { ctx.deleteTodo(item) }
                                }
                                Text {
                                    attr {
                                        text("✕")
                                        color(Color(0xFFFF3B30)) // 红色警示色
                                        fontSize(18f)
                                    }
                                }
                            }
                        }
                        
                        // 分割线
                        View {
                            attr {
                                height(1f)
                                backgroundColor(Color(0xFFEEEEEE))
                                marginLeft(36f) // 缩进以对齐文本
                            }
                        }
                    }
                }
            }
 
            // 底部状态栏与操作区(仅当列表不为空时显示)
            vif({ ctx.todoList.isNotEmpty() }) {
                View {
                    attr {
                        flexDirectionColumn()
                    }
 
                    // 顶部分割线
                    View {
                        attr {
                            height(1f)
                            backgroundColor(Color(0xFFEEEEEE))
                        }
                    }
 
                    // 底部内容容器
                    View {
                        attr {
                            flexDirectionRow()
                            justifyContentSpaceBetween() // 两端对齐
                            alignItemsCenter()
                            padding(top = 16f)
                        }
                        
                        // 剩余待办计数
                        Text {
                            attr {
                                text("${ctx.todoList.count { !it.isCompleted }} 项待办")
                                fontSize(14f)
                                color(Color(0xFF666666))
                            }
                        }
                        
                        // 清除已完成按钮
                        View {
                            attr {
                               padding(8f)
                            }
                            event {
                                click { ctx.clearCompleted() }
                            }
                            Text {
                                attr {
                                    text("清除已完成")
                                    fontSize(14f)
                                    color(Color(0xFF007AFF))
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
 
 

4.跨端运行

4.1 Android 运行

1.打开Android Studio

2.导入KuiklyTodo文件,等待构建完成

3.打开第三方模拟器,Android Studio会自动识别

4.快捷键shift+F10运行,运行后效果如图所示

4.2华为云真机运行

4.2.1配置DevEco Studio

1.打开DevEco Studio

2.选择Open Project,打开项目中的ohosApp目录

3.等待Sync完成

4.2.2生成证书与签名文件
4.2.3DevEco Studio签名配置
4.2.4构建HAP包
4.2.5部署华为云真机

4.2.2—4.2.5的内容请参考这篇文章(Windows平台KuiklyUI华为云真机部署记录-CSDN博客

结语

在KuiklyUI 入门实战,打造跨平台 Todo 应用中,重要的是代码的修改,一套代码可以在 Android 和 HarmonyOS 上运行原生的 UI。希望本文能帮助Windows平台的开发者顺利进入Kuikly OpenHarmony开发领域。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐