匿名过程入门:倒计时与回调 - App Inventor 2 案例教程

« 返回首页

你要做的是什么

这一篇做一个匿名过程练习场:先学会”把一段过程存进变量、随时调用”,再玩它最闪亮的两个用途——后台运行 + 回调(大计算不卡界面,算完自动把结果送回来)和延时运行(3、2、1 倒计时)。

匿名过程就是不带名字的过程:像数字一样能存进变量、能塞进别的块的插槽。概念详解见过程块参考文档的匿名过程一章,看不懂没关系,动手做一遍自然就懂了。

每一关都是”搭块 → 点按钮 → 看到现象”的完整闭环。

第 0 关:准备器材

  1. 新建项目,命名为 匿名过程。
  2. 设计视图里从上到下放 6 个组件:
组件(调色板名) 改名为 用途 建议属性
标签 标签1 第 1、2 关显示问候 字号 20
按钮 按钮_说话 第 1、2 关的触发 文本:说句话
标签 标签_状态 第 3 关显示后台状态 字号 20
按钮 按钮_开始 第 3 关的触发 文本:后台计算
标签 标签_倒计时 第 4 关显示倒数 字号 30
按钮 按钮_倒计时 第 4 关的触发 文本:倒计时

连上 AI 伴侣准备测试。

第 1 关:把过程存进变量

目标:点 按钮_说话,标签显示”你好!”——但你会发现我们没有创建任何命名过程,靠的是一个存在变量里的匿名过程。

搭块步骤:

  1. 从变量抽屉拖 初始化全局变量,命名 问候。
  2. 从过程抽屉拖 无返回值的匿名过程(就是”to do …”形状但没有名字的那种),塞进变量。
  3. 匿名过程里放 设 标签1.文本 为 "你好!"。
  4. 从 按钮_说话 抽屉拖 当 按钮_说话.被点击,里面放过程抽屉的 调用过程对象 语句块,过程 槽接 取全局变量 问候。
<xml xmlns="http://www.w3.org/1999/xhtml"><block type="global_declaration" x="20" y="20"><field name="NAME">问候</field><value name="VALUE"><block type="procedures_defanonnoreturn"><statement name="STACK"><block type="component_set_get"><mutation component_type="Label" instance_name="标签1" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签1</field><field name="PROP">Text</field><value name="VALUE"><block type="text"><field name="TEXT">你好!</field></block></value></block></statement></block></value></block><block type="component_event"><mutation component_type="Button" instance_name="按钮_说话" event_name="Click"></mutation><field name="COMPONENT_SELECTOR">按钮_说话</field><statement name="DO"><block type="procedures_callanonnoreturn"><value name="PROCEDURE"><block type="lexical_variable_get"><field name="VAR">global 问候</field></block></value></block></statement></block></xml>

跑通看现象:点 说句话 按钮,标签变成”你好!”。

这一关的关键理解:变量里存的不是数字,是”一段会做事的过程”。调用它,就等于把那段过程执行一遍。命名过程像”存在抽屉里的工具”,匿名过程像”拿在手里随时递给别人的纸条”。

第 2 关:给匿名过程加输入项

目标:同样点一个按钮,显示”你好,小明”——名字作为参数传进匿名过程。

搭块步骤(在第 1 关的基础上改):

  1. 点匿名过程左上角蓝色齿轮,把一个 输入项 拖进去,改名为 名字。过程块上会多出一个 名字 插槽。
  2. 把过程里的文本设置改成 合并文本("你好,", 名字)——名字 从变量抽屉拖(它会出现在过程输入项里)。
  3. 给按钮事件里的 调用过程对象 也用蓝色齿轮加上一个参数插槽,填文本 小明。
<xml xmlns="http://www.w3.org/1999/xhtml"><block type="global_declaration" x="20" y="20"><field name="NAME">问候</field><value name="VALUE"><block type="procedures_defanonnoreturn"><mutation><arg name="名字"></arg></mutation><field name="VAR0">名字</field><statement name="STACK"><block type="component_set_get"><mutation component_type="Label" instance_name="标签1" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签1</field><field name="PROP">Text</field><value name="VALUE"><block type="text_join"><value name="ADD0"><block type="text"><field name="TEXT">你好,</field></block></value><value name="ADD1"><block type="lexical_variable_get"><field name="VAR">名字</field></block></value></block></value></block></statement></block></value></block><block type="component_event"><mutation component_type="Button" instance_name="按钮_说话" event_name="Click"></mutation><field name="COMPONENT_SELECTOR">按钮_说话</field><statement name="DO"><block type="procedures_callanonnoreturn"><mutation items="1"></mutation><value name="PROCEDURE"><block type="lexical_variable_get"><field name="VAR">global 问候</field></block></value><value name="ARG0"><block type="text"><field name="TEXT">小明</field></block></value></block></statement></block></xml>

跑通看现象:点按钮显示 你好,小明。把调用处的”小明”换成”小红”再点,问候跟着变。

对齐规则:调用块的参数个数必须和过程的输入项一样多——少了报 not enough arguments,多了报 too many arguments。这是匿名过程最常见的报错。

第 3 关:后台运行 + 回调(本篇高潮)

目标:点 按钮_开始,标签先显示”正在后台计算…”,后台算完后显示”完成!结果 = …”。界面全程不卡。

在后台运行块有两个过程插槽,各司其职:

  • 过程(后台跑):只能做计算,不能碰界面;
  • 回调过程(后台结束后自动跑):在界面线程运行,负责把结果显示出来。

搭块步骤:

  1. 拖 当 按钮_开始.被点击,先 设 标签_状态.文本 为 "正在后台计算…"。
  2. 下面接控制抽屉的 在后台运行。
  3. 过程 插槽放有返回值的匿名过程(”to do result”形状)。这里用控制抽屉的 执行模块 & 返回结果 包一条”设 全局变量 累计 为 0”,返回 累计——先拿最简单的计算验证机制(想换成真计算见下面”升级”)。
  4. 回调过程 插槽放无返回值的匿名过程,蓝色齿轮加输入项 result,里面 设 标签_状态.文本 为 合并文本("完成!结果 = ", result)。
<xml xmlns="http://www.w3.org/1999/xhtml"><block type="component_event"><mutation component_type="Button" instance_name="按钮_开始" event_name="Click"></mutation><field name="COMPONENT_SELECTOR">按钮_开始</field><statement name="DO"><block type="component_set_get"><mutation component_type="Label" instance_name="标签_状态" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签_状态</field><field name="PROP">Text</field><value name="VALUE"><block type="text"><field name="TEXT">正在后台计算…</field></block></value><next><block type="controls_run_in_background"><value name="PROCEDURE"><block type="procedures_defanonreturn"><value name="RETURN"><block type="controls_do_then_return"><statement name="STM"><block type="lexical_variable_set"><field name="VAR">global 累计</field><value name="VALUE"><block type="math_number"><field name="NUM">0</field></block></value></block></statement><value name="VALUE"><block type="lexical_variable_get"><field name="VAR">global 累计</field></block></value></block></value></block></value><value name="CALLBACK"><block type="procedures_defanonnoreturn"><mutation><arg name="result"></arg></mutation><field name="VAR0">result</field><statement name="STACK"><block type="component_set_get"><mutation component_type="Label" instance_name="标签_状态" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签_状态</field><field name="PROP">Text</field><value name="VALUE"><block type="text_join"><value name="ADD0"><block type="text"><field name="TEXT">完成!结果 = </field></block></value><value name="ADD1"><block type="lexical_variable_get"><field name="VAR">result</field></block></value></block></value></block></statement></block></value></block></next></block></statement></block></xml>

跑通看现象:点 后台计算,标签先变”正在后台计算…”,眨眼间变成”完成!结果 = 0”。0 就是后台过程返回的值——后台过程返回什么,回调的 result 就收到什么,App Inventor 自动帮你传递。

升级:把 执行模块 & 返回结果 里的语句换成真家伙,比如一个”从 1 循环到 100000000 累加”的循环(执行模块 里放 从范围循环,循环里 设 累计 为 累计 + 变量)。计算期间你会发现界面照样能点能滑——这就是后台运行的意义。

铁律:后台 过程 里不要放任何设置组件属性、弹提示的块,会直接报错 Can not operate views in background task。要改界面,一律写在 回调过程 里。

第 4 关:延时倒计时 3、2、1

目标:点 按钮_倒计时,标签依次显示 3 → 2 → 1 → 开始!,每个数字停 1 秒。

延时运行块的用法:等多少毫秒(1000 = 1 秒)之后,运行一次匿名过程。

搭块步骤:

  1. 拖 当 按钮_倒计时.被点击,先 设 标签_倒计时.文本 为 "3"。
  2. 接三个 延时运行 块(一条接一条往下串),毫秒分别填 1000、2000、3000,每个的匿名过程里设置一个新文本:2、1、开始!。
<xml xmlns="http://www.w3.org/1999/xhtml"><block type="component_event"><mutation component_type="Button" instance_name="按钮_倒计时" event_name="Click"></mutation><field name="COMPONENT_SELECTOR">按钮_倒计时</field><statement name="DO"><block type="component_set_get"><mutation component_type="Label" instance_name="标签_倒计时" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签_倒计时</field><field name="PROP">Text</field><value name="VALUE"><block type="text"><field name="TEXT">3</field></block></value><next><block type="controls_run_after_period"><value name="MILLIS"><block type="math_number"><field name="NUM">1000</field></block></value><value name="PROCEDURE"><block type="procedures_defanonnoreturn"><statement name="STACK"><block type="component_set_get"><mutation component_type="Label" instance_name="标签_倒计时" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签_倒计时</field><field name="PROP">Text</field><value name="VALUE"><block type="text"><field name="TEXT">2</field></block></value></block></statement></block></value><next><block type="controls_run_after_period"><value name="MILLIS"><block type="math_number"><field name="NUM">2000</field></block></value><value name="PROCEDURE"><block type="procedures_defanonnoreturn"><statement name="STACK"><block type="component_set_get"><mutation component_type="Label" instance_name="标签_倒计时" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签_倒计时</field><field name="PROP">Text</field><value name="VALUE"><block type="text"><field name="TEXT">1</field></block></value></block></statement></block></value><next><block type="controls_run_after_period"><value name="MILLIS"><block type="math_number"><field name="NUM">3000</field></block></value><value name="PROCEDURE"><block type="procedures_defanonnoreturn"><statement name="STACK"><block type="component_set_get"><mutation component_type="Label" instance_name="标签_倒计时" property_name="Text" set_or_get="set" is_generic="false"></mutation><field name="COMPONENT_SELECTOR">标签_倒计时</field><field name="PROP">Text</field><value name="VALUE"><block type="text"><field name="TEXT">开始!</field></block></value></block></statement></block></value></block></next></block></next></block></next></block></statement></block></xml>

跑通看现象:点按钮,数字 3 立刻出现,1 秒后变 2,2 秒后变 1,3 秒后变成”开始!”。

为什么是 1000/2000/3000,不是三个 1000? 三个延时块是同时开始计时的——像一次定好三只闹钟:1 秒的那只先响,2 秒的其次,3 秒的最后。要是都写 1000,三只闹钟同一秒一起响,数字直接跳到”开始!”。

动动脑:想要每秒数字自动减一的”真·倒计时”,或者倒到一半能取消?用时钟组件的 计时事件 做——需要”到点自动运行”的复杂逻辑时,时钟是更灵活的选择。

排错表

症状 多半是因为 怎么办
not enough arguments / too many arguments 调用块的参数个数 ≠ 过程输入项个数 蓝色齿轮两边对齐:过程加了几个输入项,调用就给几个参数
Can not operate views in background task 后台 过程 里放了改界面的块 把改界面的块全部挪到 回调过程 里
回调里 result 显示 假 后台 过程 用了无返回值的匿名过程 换成有返回值的匿名过程,把要传的结果放在”返回”插槽
倒计时数字一次全变 三个延时块都写了同一个毫秒数 错开写 1000 / 2000 / 3000(同时起表,各自倒数)
点按钮没反应 匿名过程存变量时塞错了槽(比如塞进了语句槽) 初始化全局变量 的值槽只能放值块;无返回值的匿名过程恰好算一种”过程值”,能放进去

你已经学会了

匿名过程存进变量、带输入项调用、后台运行 + 回调传值、延时运行排闹钟——这就是”回调”思维的全部基础:把过程当作可以递来递去的东西。

下一步:

文档反馈