C2.16.3unhandled touch cancel设计研究

应用必须处理取消事件,否则会遗留未完成的按压态

别名: 按压态残留 · 卡住高亮 · 未处理 cancel

概念解释

控件若只在结束事件里清除按压态(pressed state),遇到取消就会把高亮、缩放、拖影或“正在发送”留在屏幕上。用户已经不再触摸,界面却仍显示按着。这不是视觉皮肤问题,而是状态机少了一条终止边:开始把控件送进候选,却没有对称的出口把候选拆掉。

机制

按下通常会改三处状态:绘制(背景加深)、内部标志(isPressed)、以及可能已经启动的定时器或事务(长按计时、滚动捕获、网络请求预备)。结束事件会把这三处一起收回。取消到达时,如果处理函数空着或直接 fall-through 到“忽略未知类型”,这三处都不会动。下次真正的开始到来时,旧标志仍为真,于是出现双高亮、点了空白却触发旧按钮、或拖动累计了一次幽灵位移。更隐蔽的是捕获了触摸的滚动容器:它以为自己还在跟踪手指,会把后续无关触点吞掉。

怎么研究

做一对对照:同一按钮分别实现“只处理 end”和“end 与 cancel 走同一清理函数”。在按住后插入系统打断,拍摄按压高亮是否残留,再立刻点屏幕其他位置,看旧动作是否仍触发。因变量用残留时长、误触发次数、以及滚动容器在打断后是否还能把后续触点交给别的控件。自变量包括控件类型(瞬时按钮、可切换按钮、滑块)和打断发生在长按阈值前还是后。不要只用视觉检查,因为内部标志残留在下一帧可能暂时看不出来。

边界

有些声明式框架会在窗口失活时强制重绘,看起来“没处理 cancel 也恢复了”,那是框架替你拆了按压态,不能推广到自定义 Canvas 控件或游戏 HUD。相反,处理了 cancel 也不等于可以提交:取消路径必须禁止副作用。若应用在开始时就已经发出不可撤回的请求,再完美的清理也救不了;那是把提交点放错了,不是事件处理遗漏。无障碍模拟点击通常不走触摸取消,所以只测屏幕手指会漏掉开关和键盘路径上的类似卡住。

怎么落地

  • 把清除按压绘制、内部标志和进行中定时器抽成一个函数,让 end 与 cancel 都调用它;cancel 分支禁止提交。
  • 在 QA 脚本里加一步:按住支付或发送按钮后拉下通知栏,检查高亮、文案和网络请求都回到按下前。
  • 对自绘控件和 Web 的 touchcancel / pointercancel 写单测,断言 pressed 标志为假且没有 click 回调。

延伸

  • 同组C2.16.1 触摸事件通常分为开始、移动、结束、取消四种类型 · C2.16.2 取消事件由系统在来电、通知等场景下打断触摸序列时触发 · C2.16.4 多点触控下每个触点独立携带开始移动结束的完整生命周期
  • 相邻C2.07 按下触发与抬起触发 · C3.03 长按
  • 站内检索pressed state · pointercancel · touchcancel

同组卡片

快捷操作

分享

分享当前页面

ios_share

https://hci.top/zh/handbook/C2.16.3