车速一响,转盘退场:给"魔术轮"装个保命开关

先给结论:我干了一件很小但很要命的事——当车速从 0 变成非 0,把仪表上那个还赖在"走焦选中态"的魔术轮,强行拽回普通态。

背景得交代一下。我业余在折腾一台智能仪表的界面,上面有个叫"魔术轮"的东西。平时它是个安安静静的转盘,长按之后会进入"走焦态"——相当于你抄起遥控器,焦点落在某张卡片上,然后左右旋转就能挑东西。爽是真爽,问题也正出在这儿:它能一直爽。

于是就诞生了那个经典的惊悚画面:你单脚撑地,长按魔术轮,美滋滋地左右挑卡片;挑完松手,拧油门,车走了——转盘还停在选中态,手指还搭在屏幕上,眼睛还黏在卡片里。人已经在路上了,魂还留在菜单里。这不行,太危险了。

所以这次改动一句话就能说清:车速由 0 变非 0 的那一刻,自动退出走焦态。

思路其实很朴素,分四棒往下传。

第一棒,数据层。Presenter 在 DataCenter 线程收到车辆数据,顺手做个边沿判断——这次 speed 非 0、上次 speed 为 0?是,就说明"车动了",喊一嗓子。

第二棒,页面层。这一嗓子不能原地执行,在 DataCenter 线程动界面纯属作死。于是 ReadyPageEx 先把 pendingExitHover_ 这个原子标志置 true,然后"上个闹钟"(arm 一个轮询任务),真正的活儿留给 UI 线程。

第三棒,轮询回调。这里像个守门员,得层层盘查:页面初始化了吗?当前内容真是 READY 页吗?是不是正在构建内容、宿主是不是在前台?任何一条不满足,就先把 pending 留着,下轮再说。尤其是"内容构建中"——这时候硬插一脚,大概率当场给你表演一个崩溃。

第四棒,执行。真正退出的代码在 MagicWheelSliceKeyListener 里。我把原来长按退出时那一大坨逻辑抽成了 LeaveHoverState(saveCfg),现在长按复用一次、车速触发复用一次,不重复造轮子。而且加了保险:没在走焦态就直接返回,绝不空放;退出完顺手把状态存盘。

有个小细节挺值得说:退出不是无条件执行的。isHovered 为假就什么都不做;activeContent 不是 READY 就清掉 pending 走人。毕竟车速变化是高频事件,你总不能每次刹车给油都对着一个根本没选中的转盘折腾一遍吧。

写完我自己坐车上试了一遍:长按转盘、挑卡片、拧油门——转盘"啪"地收回普通态。那一刻还挺爽的,像跟一个赖在沙发上的朋友说"走了走了",他真站起来了。

中途也纠结过几套歪招。比如让转盘自己盯着车速超时退出,懒是懒,但车速和界面分属两个线程,谁盯谁都是一笔糊涂账;又比如干脆在 UI 线程里直接读车速,简单粗暴,可这等于让界面层反过来去扒数据层的裤子,耦合得离谱。最后选了现在这套:数据层只管喊"车动了",界面层自己决定怎么收场,各司其职,清净。

总结一下,整个故事没有玄学,也没有黑魔法,就是"边沿检测 + 跨线程投递 + 状态机守卫"的常规三连。唯一的代价是,以后不能再一边骑车一边逛菜单了——不过这本来就是这次改动的全部意义。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐