综合演示:XML 定义待办事项列表
文档概述
本演示展示了如何使用 XML 定义界面 + 轻语言编写逻辑的方式,开发一个完整的待办事项(Todo List)应用。涵盖数据持久化(保存配置/读取配置)、列表框数据驱动渲染(置请求加载项目回调)、自定义复合列表项(弹性布局 + 复选框 + 文本 + 删除按钮)、空状态控制、回车键添加等典型场景。
- 核心功能:添加待办、标记完成、删除待办、统计计数、空状态提示、本地存储持久化
- 技术亮点:
- 使用
列表框+置请求加载项目回调实现数据驱动渲染 - 复合列表项:弹性布局嵌套「复选框 + 文本」与「删除按钮」
- 使用
保存配置/读取配置实现 localStorage 持久化 - 键盘事件(回车添加)+ 按钮事件双触发
- 空状态标签的可视切换
- 使用
学习目标
阅读本文档后,你应当能够掌握以下内容:
- 用
.spldg(XML)定义页面布局,包含列表框、编辑框、按钮、标签等组件 - 用
.spl(轻语言)实现待办列表的增删改查逻辑 - 使用
保存配置/读取配置进行本地持久化(localStorage) - 使用
JSON解析/JSON编码在字符串与对象数组之间转换 - 列表框的「数据填充 + 渲染回调 +
渲染()」三段式数据驱动渲染 - 在渲染回调中创建复合视图(弹性布局嵌套多组件)
- 通过
置被单击回调、置选中状态被改变回调注册事件 - 列表项点击事件中捕获外层
索引(闭包语义) - 键盘事件
键盘被按下+事件源.key == "Enter"实现回车提交
涉及组件
| 组件 | 名称 | 作用 | 参考文档 |
|---|---|---|---|
| 标签 | 标签_标题、标签_统计、标签_空状态 | 标题、统计、空状态提示 | 文档文本类组件_标签 |
| 编辑框 | 编辑框_输入待办 | 输入待办内容 | 文档文本类组件_编辑框 |
| 按钮 | 按钮_添加 | 添加按钮 | 文档按钮类组件_按钮 |
| 列表框 | 列表框_待办事项 | 待办列表容器 | 文档列表类组件_列表框 |
| 弹性布局 | 代码中 创建 弹性布局() | 列表项复合根布局 | 文档布局类组件_弹性布局 |
| 复选框 | 代码中 创建 单选框() | 标记完成状态 | 文档选择类组件_单选框 |
| 绝对布局 | 根布局 | 页面根布局 | 文档布局类组件_绝对布局 |
📌 待办列表项的展示视图并非预置组件,而是在
置请求加载项目回调中动态创建的复合视图:以弹性布局为根,内含「左容器(复选框 + 文本标签)+ 删除按钮」。
涉及函数与关键字
关键字: 变量、事件 ... 结束 事件、函数 ... 结束 函数、如果 ... 否则 ... 结束 如果、变量循环 ... 结束循环
列表框方法:
添加项目(数据)—— 向数据集合追加一条数据添加项目集(数据数组)—— 批量追加一组数据置请求加载项目回调(回调)—— 设置渲染回调,框架按索引逐项调用取展示视图渲染()—— 触发按当前数据集合重新渲染清空()—— 清空数据集合与已渲染视图
渲染回调参数:
项目集合—— 全部数据集合(数组),通过项目集合[索引]取当前项索引—— 当前正在渲染的项索引(从 0 开始)
数据持久化:
保存配置(键, 值)—— 保存到 localStorage(值为字符串)读取配置(键)—— 从 localStorage 读取字符串,不存在返回空JSON编码(对象)—— 对象/数组编码为 JSON 字符串JSON解析(文本)—— JSON 字符串解析为对象/数组
组件创建:
创建 弹性布局()、创建 单选框()、创建 标签()、创建 按钮()
视图通用方法/属性:
添加组件(组件)、置被单击回调(回调)、置选中状态被改变回调(回调)宽度、高度、定位、方向、主轴对齐、侧轴对齐、内边距、间隔文本、字体大小、文本颜色、字体装饰(line-through删除线)背景颜色、边框圆角、可视、选中、标题
数组操作:
取数组成员数(数组)—— 获取数组长度添加成员(数组, 值)—— 追加元素删除成员(数组, 索引)—— 删除指定索引元素
对象操作:
删除对象属性(对象, 属性名)—— 删除对象属性
其它:
弹出提示(文本, 持续毫秒)—— 轻提示确认框(文本)—— 确认对话框,返回真/假删首尾空(文本)—— 去除首尾空白取现行时间()—— 当前时间
代办事项页面.spldg
<?xml version="1.0" encoding="utf-8"?>
<页面 标题="待办事项" 宽度="500" 高度="400">
<绝对布局 名称="根布局" 宽度="100%" 高度="100%">
<标签 名称="标签_标题" 左边="20" 顶边="20" 宽度="200" 高度="40" 文本="📋 待办事项" 字体大小="22" 文本颜色="#333" />
<编辑框 名称="编辑框_输入待办" 左边="20" 顶边="70" 宽度="340" 高度="36" 提示文本="输入待办事项,按回车添加" />
<按钮 名称="按钮_添加" 左边="370" 顶边="70" 宽度="100" 高度="36" 文本="添加" 背景颜色="#52c41a" 文本颜色="#ffffff" />
<列表框 名称="列表框_待办事项" 左边="20" 顶边="120" 宽度="450" 高度="220" 拉伸内容="假" 分隔线样式="solid" 分隔线颜色="#e8e8e8" />
<标签 名称="标签_统计" 左边="226" 顶边="20" 宽度="254" 高度="40" 文本="共 0 项" 字体大小="14" 文本颜色="#999" 内容水平对齐方式="靠右" />
<标签 名称="标签_空状态" 左边="20" 顶边="180" 宽度="450" 高度="61" 文本="🎯 暂无待办事项,添加一条吧!" 字体大小="16" 文本颜色="#ccc" 内容水平对齐方式="居中" 可视="假" />
</绝对布局>
</页面>
代办事项页面.spl
'* ******************************************************************************
'* 版权说明:Copyright(C) All Rights Reserved VcnStudio
'* 程序版本:V1.0
'* 文件描述:待办事项列表逻辑
'* 修改日志:
'* ******************************************************************************
' ========== 页面变量 ==========
变量 待办列表 = []
变量 列表项组件 = { } ' 用于存储每个待办项对应的组件,方便删除和更新
' ========== 页面加载事件 ==========
事件 页面加载完毕()
' 加载已有数据
变量 保存的数据 = 读取配置("待办列表")
如果(保存的数据 != 空)
待办列表 = JSON解析(保存的数据)
结束 如果
列表框_待办事项.添加项目集(待办列表)
' 设置列表框的渲染回调
列表框_待办事项.置请求加载项目回调((项目集合, 索引) => {
变量 项目数据 = 项目集合[索引]
' 创建每个列表项:弹性布局(水平排列)
变量 项容器 = 创建 弹性布局()
项容器.宽度 = 430
项容器.高度 = 36
项容器.定位 = 相对定位
项容器.方向 = "row"
项容器.主轴对齐 = "space-between"
项容器.侧轴对齐 = "center"
项容器.内边距 = "4px 8px"
' 左侧:复选框 + 文本
变量 左容器 = 创建 弹性布局()
左容器.方向 = "row"
左容器.侧轴对齐 = "center"
左容器.间隔 = 8
变量 复选框 = 创建 单选框()
复选框.选中 = 项目数据.已完成
复选框.标题 = ""
复选框.置选中状态被改变回调((源对象, 是否选中) => {
' 更新数据状态
项目数据.已完成 = 是否选中
待办列表[索引] = 项目数据
保存配置("待办列表", JSON编码(待办列表))
更新统计信息()
})
变量 文本标签 = 创建 标签()
文本标签.文本 = 项目数据.内容
文本标签.字体大小 = 14
如果(项目数据.已完成)
文本标签.文本颜色 = "#999"
文本标签.字体装饰 = "line-through"
否则
文本标签.文本颜色 = "#333"
文本标签.字体装饰 = "none"
结束 如果
左容器.添加组件(复选框)
左容器.添加组件(文本标签)
' 右侧:删除按钮
变量 删除按钮 = 创建 按钮()
删除按钮.文本 = "×"
删除按钮.宽度 = 28
删除按钮.高度 = 28
删除按钮.背景颜色 = "#ff4d4f"
删除按钮.文本颜色 = "#ffffff"
删除按钮.边框圆角 = 4
删除按钮.置被单击回调((源对象, 事件对象) => {
删除待办事项(索引)
})
项容器.添加组件(左容器)
项容器.添加组件(删除按钮)
' 存储组件引用以便后续更新
列表项组件[索引] = {
"容器": 项容器,
"复选框": 复选框,
"文本标签": 文本标签
}
返回 项容器
})
列表框_待办事项.渲染()
更新统计信息()
结束 事件
' ========== 刷新列表 ==========
函数 刷新列表()
' 清空并重新添加数据
列表框_待办事项.清空()
变量循环(计次 = 0, 取数组成员数(待办列表), 1)
列表框_待办事项.添加项目(待办列表[计次])
结束循环
列表框_待办事项.渲染()
' 更新统计信息和空状态
更新统计信息()
结束 函数
' ========== 更新统计信息 ==========
函数 更新统计信息()
变量 总数 = 取数组成员数(待办列表)
变量 已完成数 = 0
变量循环(计次 = 0, 总数, 1)
如果(待办列表[计次].已完成)
已完成数 = 已完成数 + 1
结束 如果
结束循环
标签_统计.文本 = "共 " + 总数 + " 项,已完成 " + 已完成数 + " 项"
' 空状态显示控制
如果(总数 == 0)
标签_空状态.可视 = 真
列表框_待办事项.可视 = 假
否则
标签_空状态.可视 = 假
列表框_待办事项.可视 = 真
结束 如果
结束 函数
函数 添加代办事项()
变量 新内容 = 编辑框_输入待办.内容
如果(删首尾空(新内容) == "")
弹出提示("请输入待办事项内容")
返回
结束 如果
变量 新事项 = {
"内容": 新内容,
"已完成": 假,
"创建时间": 取现行时间()
}
添加成员(待办列表, 新事项)
保存配置("待办列表", JSON编码(待办列表))
' 清空输入框
编辑框_输入待办.内容 = ""
' 刷新列表
刷新列表()
弹出提示("✅ 添加成功", 1500)
结束 函数
' ========== 删除待办事项 ==========
函数 删除待办事项(索引)
变量 确认 = 确认框("确定要删除这条待办事项吗?")
如果(确认 == 假)
返回
结束 如果
删除成员(待办列表, 索引)
保存配置("待办列表", JSON编码(待办列表))
' 清除组件引用
删除对象属性(列表项组件, 索引)
' 刷新列表
刷新列表()
弹出提示("🗑️ 已删除", 1500)
结束 函数
' ========== 添加按钮点击事件 ==========
事件 按钮_添加.被单击(源对象, 事件源)
添加代办事项()
结束 事件
' ========== 输入框回车事件 ==========
事件 编辑框_输入待办.键盘被按下(源对象,事件源)
如果(事件源.key == "Enter")
添加代办事项()
结束 如果
结束 事件
关键代码解读
1. 数据持久化:保存配置 + JSON 编解码
变量 保存的数据 = 读取配置("待办列表")
如果(保存的数据 != 空)
待办列表 = JSON解析(保存的数据)
结束 如果
' ...修改后...
保存配置("待办列表", JSON编码(待办列表))
保存配置/读取配置底层为浏览器 localStorage,只能存字符串- 待办列表是对象数组,必须先用
JSON编码转字符串再保存,读取时用JSON解析还原 - 任何修改(添加/删除/切换完成)后都要重新
保存配置才能持久化
2. 列表框数据驱动渲染三段式
列表框_待办事项.添加项目集(待办列表) ' ① 填充数据
列表框_待办事项.置请求加载项目回调((项目集合, 索引) => { ' ② 定义渲染回调
' ...创建项容器并返回...
返回 项容器
})
列表框_待办事项.渲染() ' ③ 触发渲染
- 第①步
添加项目集批量填数据(也可用循环添加项目) - 第②步
置请求加载项目回调定义每项的展示视图 - 第③步
渲染()让框架遍历数据集合,按索引逐项回调取视图并加入 DOM - 模型要点:数据与视图分离,
添加项目不产生 DOM,必须渲染()才触发
3. 复合列表项:弹性布局嵌套
变量 项容器 = 创建 弹性布局()
项容器.方向 = "row"
项容器.主轴对齐 = "space-between"
' 左容器:复选框 + 文本
变量 左容器 = 创建 弹性布局()
左容器.添加组件(复选框)
左容器.添加组件(文本标签)
' 右侧:删除按钮
项容器.添加组件(左容器)
项容器.添加组件(删除按钮)
返回 项容器
- 列表项根为水平弹性布局(
方向 = "row"),用主轴对齐 = "space-between"让左容器和删除按钮两端对齐 - 左容器也是弹性布局,内部
间隔 = 8让复选框与文本之间留空 - 复合视图必须以一个根容器返回,不能返回多个组件
4. 嵌套闭包中的索引捕获
列表框_待办事项.置请求加载项目回调((项目集合, 索引) => {
变量 复选框 = 创建 单选框()
复选框.置选中状态被改变回调((源对象, 是否选中) => {
待办列表[索引] = 项目数据 ' 引用外层 索引
})
变量 删除按钮 = 创建 按钮()
删除按钮.置被单击回调((源对象, 事件对象) => {
删除待办事项(索引) ' 引用外层 索引
})
返回 项容器
})
- 内层回调引用外层渲染回调的
索引参数 - 用户点击时外层回调早已返回,依赖拉姆达按调用时的值捕获(类似 JS
let语义) - 每次渲染回调调用时
索引独立,因此每项的点击都能取到正确序号 - ⚠️ 注意:若后续调用
刷新列表()重新渲染,新回调会绑定新的索引;旧的已销毁视图不会触发
5. 列表项组件引用缓存
变量 列表项组件 = { } ' 页面变量
' 渲染回调内
列表项组件[索引] = {
"容器": 项容器,
"复选框": 复选框,
"文本标签": 文本标签
}
' 删除时
删除对象属性(列表项组件, 索引)
- 用对象
列表项组件按索引缓存每项的组件引用,方便后续直接访问 - 删除待办时同步清除缓存,避免内存泄漏与脏引用
6. 切换完成状态的视图更新策略
复选框.置选中状态被改变回调((源对象, 是否选中) => {
项目数据.已完成 = 是否选中
待办列表[索引] = 项目数据
保存配置("待办列表", JSON编码(待办列表))
更新统计信息()
})
- 切换完成状态时,更新数据 + 持久化 + 刷新统计
- 文本标签的删除线/颜色样式是在渲染回调中根据
项目数据.已完成初始设置的 - ⚠️ 注意:此处没有调用
刷新列表()重新渲染整列,因此切换完成后文本的删除线样式不会立即变化。若需立即变化,可在此回调中直接操作缓存的文本标签:
复选框.置选中状态被改变回调((源对象, 是否选中) => {
项目数据.已完成 = 是否选中
待办列表[索引] = 项目数据
保存配置("待办列表", JSON编码(待办列表))
' 直接更新已缓存的文本标签样式(即时生效)
变量 缓存项 = 列表项组件[索引]
如果(是否选中)
缓存项.文本标签.文本颜色 = "#999"
缓存项.文本标签.字体装饰 = "line-through"
否则
缓存项.文本标签.文本颜色 = "#333"
缓存项.文本标签.字体装饰 = "none"
结束 如果
更新统计信息()
})
7. 空状态与列表可视切换
如果(总数 == 0)
标签_空状态.可视 = 真
列表框_待办事项.可视 = 假
否则
标签_空状态.可视 = 假
列表框_待办事项.可视 = 真
结束 如果
- 用
可视属性控制组件显示/隐藏(底层为display切换) - 空状态标签
.spldg中初始可视="假",加载时根据数据动态切换
8. 回车键添加
事件 编辑框_输入待办.键盘被按下(源对象,事件源)
如果(事件源.key == "Enter")
添加代办事项()
结束 如果
结束 事件
- 键盘事件通过
事件源.key判断按键("Enter"是回车键的标准 key 值) - 与
按钮_添加.被单击共用同一个添加代办事项()函数,避免逻辑重复
注意事项
- 持久化需 JSON 编解码:
保存配置只能存字符串,对象数组必须JSON编码后存、JSON解析后取。 - 渲染回调必须返回视图:若不
返回视图组件,对应项不会显示。 - 闭包索引捕获:内层回调引用外层
索引依赖拉姆达按值捕获语义;若遇异常应改用参数绑定或缓存对象。 - 切换状态即时更新:仅修改数据不重新渲染时,视图样式不会自动更新;如需即时变化,直接操作缓存的组件实例。
- 循环边界语义:
变量循环(计次 = 0, 取数组成员数(待办列表), 1)等效于for(i=0; i<长度; i++),即计次 < 目标值时循环、达到目标值退出,左闭右开区间,遍历数组正好不越界。 - 删除索引同步:
删除成员(待办列表, 索引)后必须刷新列表重新渲染,否则渲染回调中绑定的旧索引会错位。 .spldg与.spl成对:两个文件名称必须相同且同时存在,组件通过名称属性在.spl中被引用。弹出提示字符变体:本文档统一使用弹出提示,不同文档中可能存在弹出提示等字符显示差异,实际为同一函数。
学习要点总结
| 知识点 | 关键代码 | 适用场景 |
|---|---|---|
| 数据持久化 | 保存配置(键,值) + 读取配置(键) | localStorage 存取 |
| JSON 编解码 | JSON编码(对象) / JSON解析(文本) | 对象与字符串转换 |
| 批量填数据 | 添加项目集(数组) | 一次性加入多项 |
| 数据驱动渲染 | 置请求加载项目回调 + 渲染() | 列表项自定义展示 |
| 复合列表项 | 弹性布局嵌套多组件 | 复杂列表项 |
| 主轴对齐 | 主轴对齐 = "space-between" | 两端对齐 |
| 嵌套闭包 | 内层回调引用外层 索引 | 项点击事件 |
| 组件引用缓存 | 列表项组件[索引] = {...} | 后续直接访问 |
| 空状态切换 | 可视 = 真/假 | 空列表提示 |
| 数组操作 | 添加成员/删除成员/取数组成员数 | 列表数据维护 |
| 键盘事件 | 键盘被按下 + 事件源.key | 回车提交 |
| 删除线样式 | 字体装饰 = "line-through" | 已完成态标记 |
相关文档
- 文档
列表类组件_列表框—— 列表框完整属性与方法 - 文档
布局类组件_弹性布局—— 弹性布局方向、对齐、间隔 - 文档
布局类组件_绝对布局—— 绝对布局作为根布局 - 文档
文本类组件_标签—— 标签属性 - 文档
文本类组件_编辑框—— 编辑框与提示文本、键盘事件 - 文档
按钮类组件_按钮—— 按钮属性 - 文档
选择类组件_单选框—— 单选框/复选框与选中回调 - 文档
前端(JavaScript)函数_存储配置——保存配置/读取配置 - 文档
前端(JavaScript)函数_JSON——JSON编码/JSON解析 - 文档
前端(JavaScript)函数_数组——添加成员/删除成员/取数组成员数 - 文档
轻语言网站:循环语句体结构——变量循环、计次循环说明 - 文档
轻语言网站:变量与数组与对象的定义—— 变量、数组、对象、拉姆达 - 文档
轻语言网站:自定义设计列表内容—— 列表框/宫格框渲染基础 - 文档
综合演示:网页留言板开发—— 列表框数据驱动渲染示例 - 文档
综合演示:手机移动端记事本网页开发—— 移动端列表+本地存储示例 - 文档
轻语言网站:界面XML定义规范——.spldgXML 写法规范