本系列第 3/8 课,建议 45–60 分钟(估算)。先完成上一课。
这课解决什么
新增用独立的下一编号,修改和删除用选中 ID。删除对话框打开时固定目标 ID,确认后再处理;取消不能先修改列表。
先预测,再运行
打开删除确认框后又换了选中项,应该删哪条?
先记下自己的答案,运行起始工程与完成工程比较;答案见文末。
开始实战
以上一课完成工程为起点,先导入并运行,再另存为本课作业。
阶段工程各自保存数据;导入下一课不会自动带走手机上的旧数据。练习使用本课示例数据,迁移方法在数据课程第六课单独讲解。
新增或调整的组件
| 实例名 | 组件 | 本课配置 |
|---|---|---|
标签_标题 |
标签 | Width=Fill parent;FontSize=24;TextColor=#183153;Text=知识卡片 · 第 3课 |
文本输入框_题目 |
文本输入框 | Hint=卡片题目;Width=Fill parent;FontSize=16;Text= |
文本输入框_答案 |
文本输入框 | Hint=卡片答案;Width=Fill parent;FontSize=16;Text= |
文本输入框_分类 |
文本输入框 | Hint=分类;Width=Fill parent;FontSize=16;Text=编程 |
按钮_添加 |
按钮 | Width=Fill parent;BackgroundColor=#2563EB;TextColor=#FFFFFF;FontSize=16;Text=添加 |
按钮_修改 |
按钮 | Width=Fill parent;BackgroundColor=#2563EB;TextColor=#FFFFFF;FontSize=16;Text=修改 |
按钮_删除 |
按钮 | Width=Fill parent;BackgroundColor=#2563EB;TextColor=#FFFFFF;FontSize=16;Text=删除 |
对话框1 |
对话框 | 沿用默认值 |
微数据库1 |
微数据库 | Namespace=CardsB03 |
逐步搭建
- 增加题目、答案、分类输入及添加/修改/删除按钮,加入 TinyDB 和对话框。
- 添加先检查题目与答案;创建新字典并递增下一编号,保存整个卡片列表。
- 修改按选中 ID 找原字典;删除先记录待删除 ID,在 AfterChoosing 中处理。
- 添加、关闭重开、修改、取消删除、确认删除;逐次检查条数和具体答案。

图来自本课最终工程执行积木后的状态,为 HTML 近似预览;不是手机截图。
核心积木与数据流
下面是本课完成工程中的核心代码,包含所需全局变量和过程。可在块图中追踪数值,再回到工程对照完整事件;合并到已有工程时,同名事件合并处理。
global 卡片 = list(makedict(pair("id", 1), pair("question", "中国历史朝代顺序从哪里查?"), pair("answer", "按资料的年代字段排序"), pair("category", "历史"), pair("wrong", 0)), makedict(pair("id", 2), pair("question", "列表第一个索引是多少?"), pair("answer", "1"), pair("category", "编程"), pair("wrong", 0)))
global 显示 = []
global 选中ID = 0
global 下个ID = 3
global 关键词 = ""
global 就绪 = false
procedure 刷新() {
显示 = []
var 标题 = []
foreach 项 in 卡片 {
if 关键词 = "" or textContains(lower(join(dictget(项,"question",""), " ", dictget(项,"category",""))), lower(关键词)) {
listadd(显示, 项)
listadd(标题, join("#", dictget(项,"id",0), " · ", dictget(项,"category",""), " · ", dictget(项,"question","")))
}
}
列表显示框_卡片.Elements = 标题
选中ID = 0
标签_详情.Text = "点击卡片查看详情"
标签_状态.Text = join("显示 ", length(显示), " / ", length(卡片), " 张卡片")
}
function 输入合法() { return trim(文本输入框_题目.Text) != "" and trim(文本输入框_答案.Text) != "" and 就绪 }
procedure 保存() {
微数据库1.StoreValue("卡片", 卡片)
微数据库1.StoreValue("下个ID", 下个ID)
刷新()
}
when 按钮_添加.Click() {
if 输入合法() {
listadd(卡片, makedict(pair("id",下个ID),pair("question",trim(文本输入框_题目.Text)),pair("answer",trim(文本输入框_答案.Text)),pair("category",trim(文本输入框_分类.Text)),pair("wrong",0)))
下个ID = 下个ID + 1
保存()
} else { 标签_状态.Text = "数据库须就绪,题目和答案不能为空" }
}
中途检查与排错
本课以下控制与数据流已执行最终归档中的真实积木检查。请在伴侣或安装的 APK 中按相同场景操作,记录实际结果;自动检查通过不代表手机已实测。
- 按索引读取详情。
- 空题不保存。
- 含引号逗号换行的记录保存。
- 重启完整恢复。
- 取消删除保留。
- 确认删除正确对象。
排查时先确定出错的是输入、状态、数据读取还是显示;用标签暂时显示关键值,每次只改变一个条件。
当前课仅实现表中对应功能,导出和备份在第八课加入。SQLite 在第六课加入,本课仍使用列表或 TinyDB。
两个理解题与独立改造
- 打开删除确认框后又换了选中项,应该删哪条?
- 删除最后一个 ID 后,下一条能重新用这个 ID 吗?
独立改造: 增加“复制卡片”按钮,复制内容但给新 ID。
先完成自己的工程,再对照答案。检查成功路径,也检查空数据、重复操作和取消;写出你改动的规则。
查看预测与练习参考答案
预测: 删弹窗打开时固定的待删除 ID,不能读取后来变化的选中 ID。
追踪: 课程保持单调编号,不复用已删除 ID,避免旧引用指向新记录。
改造思路: 从选中记录复制字段,ID 用下一编号;下一编号递增并一起保存。直接复用原 ID 会破坏后续更新。
完成与下一步
保存自己的工程并导出 .aia;给文件命名以区分本课作业和参考源码。请用自己的话解释本课知识,并展示一次失败输入的处理。
扫码添加客服咨询