引言:本文主要是记录一个新手在前两篇文章的基础下入门KuiklyUI并开发Todo应用的过程。

1.什么是KuiklyUI?

1.1核心定位与目标

KuiklyUI 旨在提供一套代码,多端运行的开发体验,同时保证原生性能、极致易用性和动态灵活性,帮助开发者大幅提升跨平台应用的研发效率。

1.2核心特点与优势

  1. 原生性能:生成平台原生二进制文件,渲染效率比 React Native 等框架快约6 倍,接近原生开发体验
  2. 统一技术栈:使用 Kotlin 单一语言开发所有平台,无需学习多套语言和框架
  3. 动态化能力:支持动态更新 UI 和业务逻辑,无需重新发布应用
  4. 高性能优化:虚拟 DOM、延迟更新、增量布局等技术保证流畅体验
  5. 丰富生态:与 KuiklyBase(基础库)配合使用,提供完整的跨端解决方案

2.环境准备

1.JDK17+

2.Android Studio的最新版本

3.DevEco Studio5.0+

3.项目的准备

3.1下载项目模板

前往(KuiklyTodo - AtomGit | GitCode)下载项目模板并解压至你工程常用的位置

3.2TodoItem.kt的修改

前往KuiklyTodo-main\demo\src\commonMain\kotlin\com\tencent\kuikly\demo\pages\todo

该文件夹下找到TodoItem.kt文件修改代码

我这里是选择的记事本打开

package com.tencent.kuikly.demo.pages.todo

data class TodoItem(
    val id: Long,
    var content: String,
    var isCompleted: Boolean = false
)

3.3TodoPage.kt的修改

在同一个文件夹里找到TodoPage.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的修改

依旧是在同一个文件夹里找到TodoPageUI.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.Android端调试

Android 是最简单的运行平台,所以我们选择先在Android端进行调试,后续在鸿蒙端进行调试。

打开Android Studio,打开项目,等待构建

同时拿出安卓手机打开开发者模式,或者打开安卓模拟器,我这里是用的模拟器

运行项目即可

5.华为云真机测试

在Android端运行成功后我们接着在华为云真机上调试

通过证书与签名文件准备,DevEco Studio 签名配置和构建HAP包之后,申请测试云真机

结语:在KuiklyUI 入门实战,打造跨平台 Todo 应用中,重要的是代码的修改,一套代码可以在 Android 和 HarmonyOS 上运行原生的 UI,要理解如何用 Kotlin 代码构建复杂的 Flexbox 布局。希望本文能帮助Windows平台的开发者顺利进入Kuikly OpenHarmony开发领域。

Logo

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

更多推荐