
很多第一次用App Inventor做项目的人都会在同一个地方卡住数据稍微多一点列表就不够用了。举个很常见的例子你要做一个账本应用每一笔账要同时记住金额、分类、日期、备注。用四个列表硬扛最怕的是用户中途删掉一条记录你得同时删四个列表里同一行但凡漏一个后面数据全部错位。这种场景下字典Dictionary就是来救场的。这篇文章把App Inventor里字典的创建、取值、遍历、JSON互转、嵌套用法以及我实际项目中踩过的坑一次性讲清楚无论你是刚接触块编程的新手还是已经写了不少Screen的老手应该都能找到有用的东西。1. 字典就是个会自己找东西的档案柜先弄懂它解决了什么问题1.1 列表按序号记账字典按标签找东西列表和字典最大的区别一句话就能说清列表按“第几个”存取字典按“名字”存取。你可以把列表想象成一排编号的档案柜你只知道某个东西在3号柜但要记住每一层装的是什么得另配一张对照表。字典则不同每一个档案柜门上直接贴着标签你报出“金额”这个标签它立刻把对应的值递给你不用关心这个标签贴在第几层。在App Inventor里字典由一组组键值对组成。键就是那个标签值就是你想存的数据。看下面这段JSON就是一个典型的学生信息字典{ 姓名: 张小明, 年龄: 12, 班级: 六年级三班 }“姓名”是键“张小明”是值“年龄”是键12是值。一个字典里可以有任意多个键值对键必须唯一值则可以重复。你不需要记住“年龄”存在列表的第几个位置只需要知道它的键叫“年龄”任何时候用字典查一下就能拿到。1.2 三个判断标准什么时候该用字典不是所有数据都该用字典。我自己的判断标准有三条你希望按字段名去取值而不是按下标去取值。不同记录的字段数量或字段类型可能不一样比如有的学生有“特长”字段有的没有。你需要把一组相关联的属性打包成一个整体存进TinyDB、发送给服务器或者作为参数传给另一个逻辑。反过来说如果你的数据只是一串同类型的东西比如一个班级的花名册只需要连续显示学生姓名那用列表更合适。字典和列表不是替代关系而是互补关系。在App Inventor项目里最常见的组织方式是列表外面套字典、字典value里再放列表这个后面专门讲。1.3 一个早期项目里的对比为什么我弃了三个并列列表我以前给学校做一个班级管理工具第一次设计学生信息时用了三个平行列表一个存姓名一个存积分一个存备注。界面展示倒是简单取第i个列表的第i项就行。直到我需要实现“删除某个学生”才发现问题有多麻烦。我必须同时删除三个列表中同一位置的数据任何一个列表删错了位置整个班级的数据从这一行开始全部错位。后来我改成用一个字典键是学生姓名值是另一个字典里面再放“积分”和“备注”。删除学生只需要删一个键其他数据天然跟着消失。那次重构之后我就明白了凡是“一组属性强绑定”的数据优先考虑字典这是块编程里最值得养成的好习惯。2. 动手之前把字典区的八个核心块逐个摸清楚2.1 创建字典Make a Dictionary 与 Create Empty Dictionary 二选一App Inventor的字典区域有两个创建字典的块用起来要分场景。Make a Dictionary块适合创建内容固定的字典从块面板拖出来后块上会有一对键值插槽鼠标悬停在块上会出现蓝色加号点一下就能加一对新键值。比如要创建“商品详情”你可以直接在块上填上“价格”“库存”“产地”这些固定字段。如果字典内容在运行时才确定比如用户答题到一半你根本不知道最终会选哪些选项那就用Create Empty Dictionary创建空字典后面配合Set Pair in Dictionary不断往里加内容。我自己在几乎所有动态场景里都更推荐空字典起步因为它让代码的意图更清晰你是在“逐步构建一个未知结构”而不是“声明一个写死的结构”。2.2 存值和改值Set Pair in Dictionary 一个块解决两件事Set Pair in Dictionary是使用频率最高的字典块。它接收三个参数字典本身、键、值。运行时会自动判断如果这个键还不存在就新增一对如果已经存在就直接覆盖原来的值。所以“新增”和“修改”在字典里是同一个操作不需要额外写if判断这点比列表方便太多。新手最容易在这里犯一个错误键的位置默认放的是文本块如果你在文本块里填了一个变量名字典会把这个变量名的字面文本当成键而不是取变量的值。想用变量值当键必须把变量块直接拖到键的插槽里让插槽形状变成变量形式。做班级花名册时你希望“键是学生姓名”学生姓名是动态选出来的变量就一定要把姓名变量拖进键槽而不是把“姓名”两个字写进去。2.3 取值Look up in Dictionary 你一定要用好的notFound参数Look up in Dictionary块看起来很简单输入字典和键返回对应的值。但它的第三个参数notFound非常有用很多人会忽略。当键不存在时这个参数决定返回什么值。如果你不填键不存在时返回空文本如果你填了“未找到”之类的默认值就返回默认值。我用notFound参数做配置项读取时特别顺手。比如全局字典里存了用户设置的背景色用户可能还没设置过这个键那我在读取时填一个默认颜色作为notFound界面永远不会因为取不到值而变成空白。不过要注意notFound只能帮你兜底不能帮你判断“键到底存不存在”。如果你需要区分“键不存在”和“键存在但值是空”就要配合下面的Is Key in Dictionary块。2.4 删键、查键、拿全部键字典的日常维护三件套字典区的另外三个核心块分别是Delete from Dictionary、Is Key in Dictionary?、Get Keys in Dictionary。Delete from Dictionary从字典中删除指定键。不用担心键不存在会报错这个操作在键不存在时什么都不做。Is Key in Dictionary?返回布尔值判断某个键是否存在。这是安全的“查询前检查”尤其是在处理用户动态输入时先判断再取能省掉不少运行时错误。Get Keys in Dictionary返回字典中所有键组成的列表。注意这个列表的顺序是不保证的每次返回可能都不一样。所以如果你有“按字典顺序处理”的需求需要先把键列表取出来自己排序再按照键列表去字典里逐项取值。我用一个表格把这八个块的核心作用列出来方便你以后快速索引块名称作用常用场景Make a Dictionary创建带初始键值对的字典配置固定字段Create Empty Dictionary创建空字典动态构建数据结构Set Pair in Dictionary新增或覆盖键值对运行时追加/修改Look up in Dictionary按键取值可设默认值读取字段Delete from Dictionary按删除键移除条目Get Keys in Dictionary获取全部键的列表遍历、排序、检查Is Key in Dictionary?判断键是否存在取值前校验Walk through Dictionary遍历字典全部条目批量处理3. 把JSON翻译成字典和网络接口打交道时最快的一条路3.1 为什么Web应用绕不开JSON现在做App Inventor应用只要联网基本都会碰到JSON。天气接口返回的是JSON用户信息接口返回的是JSON你往服务器上报一条记录通常也要发JSON。JSON本身只是一串有格式的文本但它有一个特点它的对象类型和App Inventor的字典结构几乎一一对应。一个用大括号{}包起来的内容转换到App Inventor里就是一个字典。所以我在项目里有一个固定套路遇到任何Web接口先把返回的JSON文本转成字典然后再用Look up in Dictionary一层一层取值。这么做的好处是代码完全可读接口里有什么字段你的字典里就有什么键不用靠记忆去数数组下标。3.2 App Inventor的JSON双向转换块App Inventor里有两个现成的转换块Convert from JSON Text把JSON文本转成App Inventor对象Convert to JSON Text把字典或列表转成JSON文本。前者是网络请求后的必经之路后者常用于上报数据前的序列化。转换规则要记清楚JSON里的对象大括号转成字典数组方括号转成列表字符串转成文本数字转成数字或文本布尔值转成布尔值。我在一些旧版本AI2上遇到过数字被转成文本的情况做一个加法计算前最好先确认字段类型。如果发现接口里的数字被当成文本返回就先转数字再算否则十有八九算出个字符串拼接。3.3 从真实接口的嵌套JSON里提取字段看一个典型天气接口返回{ code: 200, data: { city: 上海, temp: 28, forecast: [ { date: 周一, high: 30, low: 22 }, { date: 周二, high: 31, low: 23 } ] } }用Convert from JSON Text转成App Inventor对象后最外层就是一个字典。你要取“上海”这个城市名需要两步第一步用Look up in Dictionary在顶层字典里取键“data”拿到一个嵌套字典第二步在这个嵌套字典里再取键“city”。要取“周一”的最高温则需要先取“data”再取“forecast”拿到一个列表从列表中取第1项字典再从字典里取“high”。这个过程其实就是逐层拆数据每一步都只关注当前这层是什么类型。我在做这类解析时有个习惯一旦某个接口字段很多我会先在纸上把JSON的层级关系画出来标出哪些键对应字典、哪些对应列表然后再回App Inventor拉块。直接上手拉块很容易在第三层时忘了外面套的是列表还是字典从而接错块类型。4. 值里还能再套列表和字典处理复杂数据结构的关键4.1 字典的值是列表公交时刻表问题字典的值不一定是文本或数字它可以是任何App Inventor类型包括列表和字典。这给了我们很大的建模空间。比如做一个公交时刻查询一个站名对应多个发车时间用字典就非常自然{ 人民广场: [6:00, 6:30, 7:00], 火车站: [6:15, 6:45, 7:15] }取出键“人民广场”的值后你得到一个列表再用列表操作按索引取第2个元素就能拿到“6:30”。这种模型是列表做不到的如果只用列表你得自己维护“每个站名对应哪一段区间是发车时间”相当于手工做一个索引既费劲又容易错。4.2 字典的值是字典用户信息分层管理字段多、逻辑分组明确时值套字典是一个相当清爽的做法。比如用户信息{ name: 莉莉, contact: { phone: 1380000000, email: liliexample.com } }最外层是一个用户字典键“contact”对应的是一个联系信息字典。在App Inventor里取邮箱先取顶层键“contact”得到内层字典再取内层键“email”。这种做法最大的好处是以后要增加“家庭住址”字段只需在contact字典里加一个键外层结构完全不用动也不影响其他逻辑的读取。4.3 列表中的字典批量数据怎么组织购物车、商品列表、排行榜这些“多条记录”的数据结构通常是一个列表列表里每一项都是一个字典。看这段购物车数据[ { name: 书包, price: 89, count: 1 }, { name: 铅笔, price: 2, count: 20 } ]从App Inventor里处理这种结构需要两步先用列表操作比如Select List Item取出当前这一项得到的对象是一个字典然后再用字典操作从这个字典里取“name”“price”等字段。如果你直接在一个列表上做Look up in Dictionary就会发现根本取不到值因为外层根本不是字典而是一个列表容器。要提醒的是列表中的字典每条的字段数量可以不一样。第一条有“备注”第二条没有这是完全允许的。你在取“备注”时用Look up in Dictionary并设置一个默认值就能优雅地处理这种缺字段的情况。5. 四个真实项目里的字典用法从问卷调查到关卡配置5.1 问卷调查把每道题的答案组织成字典我做过一个校园问卷调查App里面每一道题就是一个键用户选的答案就是值。全局变量里放一个空字典用户每完成一题就用Set Pair in Dictionary把“q1”“q2”这样的键和对应答案存进去。这个设计让我在问卷结束时可以非常痛快地做两件事一是直接Convert to JSON Text把整个答案字典变成一行文本发给服务器二是把字典整体存入TinyDB用户下次打开还能继续上次的进度。如果当初用几十个独立变量存答案导出和恢复数据会麻烦得多。用字典之后一份问卷的所有数据始终是一个整体这本身就在帮助代码降低复杂度。5.2 通讯录姓名到电话的映射通讯录是理解字典思想的最佳案例它天然就是“姓名→电话”的映射。在App Inventor里我直接把联系人姓名当键电话号码当值来存。查找某人号码用Look up in Dictionary加一个新联系人是Set Pair判断要不要显示“添加”按钮用Is Key in Dictionary?判断删除联系人用Delete from Dictionary。整套逻辑加起来不到十个块而且读起来和自然语言差不多。有人会问如果两个联系人同名怎么办这种情况下我会把键改成“姓名编号”的组合文本或者把值改成包含电话、备注的字典。这种思考过程本身就是用字典建模的价值你先考虑键的唯一性问题也就提前规避了数据覆盖风险。5.3 游戏关卡配置一关一个字典做一个小游戏时每个关卡有不同敌人数量、移动速度、限时时长、目标分数。如果每个参数都单独设一个全局变量那切换关卡时你得同时更新四五个变量很容易漏。我的方案是建一个全局列表列表里每一项是一个关卡配置字典[ { level: 1, enemy: 3, speed: 2, time: 60, target: 100 }, { level: 2, enemy: 5, speed: 3, time: 50, target: 200 }, { level: 3, enemy: 8, speed: 4, time: 45, target: 300 } ]进入某一关时先从列表找到对应的关卡字典再一次性把敌人的数量、速度、限时都配置好。以后加关卡只需往列表字典里追加一项游戏逻辑代码一行都不用改。这就是字典作为“配置载体”的威力数据和逻辑分离。5.4 购物车/订单列表套字典算总价购物车和订单是最标准的“列表套字典”场景。我在工程做法上是这样的定义全局变量 cart 为列表。用户点击“加入购物车”时创建一个字典设置“name”“price”“count”再把这个字典追加进 cart 列表。计算总价时遍历 cart 列表取出每一项字典把“price”和“count”相乘累加到一起。清空购物车时直接把 cart 设置为空列表。这个流程看似简单但如果你在遍历时还要对列表做删除操作就要小心性能问题和方法问题。更好的做法是先备份需要删除的项遍历结束后再统一删除这和我们下一个部分要讲的字典遍历坑思路完全一致。6. 字典用多了一定会踩的坑我的排查过程和最终方案6.1 遍历字典时修改字典我最狼狈的一次事故有一回做一个标签管理功能我需要在遍历字典时把特定键删除结果运行起来奇奇怪怪每次删除一个键后面有几个键就被跳过去好像它们在躲猫猫。折腾了一个下午才意识到问题出在遍历过程中直接修改了同一个字典。App Inventor的遍历逻辑在内部需要维护“下一个键在哪”的状态你在循环体里删除了当前或未来的条目遍历进度就乱了。最终的解决方案很简单先遍历字典把所有需要删除的键收集到一个列表里等遍历结束后再统一对原字典执行Delete from Dictionary。如果你需要“处理一批数据并清理它们”请毫不犹豫地采用这个“先收集、后处理”模式这是我在字典遍历上最想告诉你的经验。6.2 键的类型不一致查找失败的头号原因还有一个高频问题明明字典里存了键“123”你用Look up in Dictionary传了数字123结果却取不到值。原因在于App Inventor字典里的键是有类型的文本“123”和数字123不是同一个键。我排查这类问题时通常会先拖出一个Get Keys in Dictionary块把返回的键列表显示在界面或调试变量里看看键的类型到底长什么样。一旦发现类型不匹配就用文本转换或数字转换统一类型。别指望查找时自动帮你做类型转换字典不是那么机灵。6.3 不填notFound参数你以为的安全其实不安全Look up in Dictionary的notFound参数还有一个容易误用的点如果你不填键不存在时返回的是空文本。空文本在App Inventor里显示出来也像“什么都没有”很多新手以为取值成功了只是“值就是空白”。等到后面做判断发现和预期不一致又要花时间查。更严谨的做法是凡是要判断“这个键是否存在”先用Is Key in Dictionary?做一次前置判断只是想给个默认取值就用notFound参数。两种意图对应两种写法不要混在一起。6.4 字典与TinyDB退出应用后还能保住数据App Inventor的全局变量只存在于应用运行期间应用一关数据全没。如果你希望一个字典下次打开还能用就得存到TinyDB。我的经验是虽然TinyDB可以存复杂对象但在不同版本AI2里复杂对象的序列化表现并不总是一致。最稳的做法是把字典先转成JSON文本存进TinyDB读取时再从TinyDB取出JSON文本用Convert from JSON Text还原成字典。多一步转换换来的是跨版本兼容的稳定我认为非常值。这个方案也同样适用于跨Screen传递字典真实设备上的Screen间传不了复杂对象转成JSON文本就能畅通无阻地传过去另一方再转回字典。6.5 调试字典的一个实用小技巧最后补一个我常用的调试技巧如果字典里数据看着不对直接拿一个Convert to JSON Text块把整个字典转成文本再用Label或Notifier显示出来。这样你能一眼看到完整的键值对结构比一个一个Look up逐键检查快得多。尤其是遇到嵌套字典时JSON文本可以让你立刻看出哪一层结构写错了、哪个键的层级搞混了。说实话我在项目里调试数据结构的效率大幅提升靠的就是这个习惯。最后再分享一个小技巧凡是打算在App Inventor里用字典建模的数据我建议先在草稿纸上写一段JSON草稿标好哪些字段是文本、哪些是数字、哪些里面还要套列表。等草稿理清楚了再回去拉字典块就会顺很多。字典是个好工具但它的价值必须先建立在清晰的数据结构设计上这个前置功夫省不得。