健康菜谱助手在 module.json5 里声明了三种设备,上架后的第一周,应用市场评论区出现了一条一星:「折叠屏展开后界面一直跳,没法用。」那条评论把我按在了真机前——模拟器里好好的页面,在真机折叠过程中抖得像筛糠。这篇记录从「能跑」到「敢用」的完整改造,核心是三味药和一份清单。

一、先分清抖的是哪一层

折叠过程的界面抖动看起来是一个病,其实是三种,药方完全不同:

第一种:断点档位横跳。折叠过程中窗口宽度连续扫过 600、840 两个阈值,断点在 md 和 lg 之间来回切,布局跟着整块重排。用户手停在阈值附近时,防抖结束了照样抖——因为防抖只能延迟响应,挡不住连续跨越阈值。药是迟滞:升档阈值 600、降档阈值 580,中间留 20vp 缓冲带。一次跨过去就不再回跳,这是硬件传感器领域用了几十年的老办法。

第二种:连续回调驱动全量布局。折叠动画进行中 windowSizeChange 连续回调,每次都触发全量 measure/layout。药是节流:回调里只记录尺寸,100ms 批量应用一次。这里用节流不用防抖——防抖会吃掉最后一帧,节流只是把三帧并成一帧。

第三种:双实例闪屏。按断点 if/else 渲染两套组件,切换瞬间销毁重建,视觉上闪一下。药是单实例:同一套组件只改属性,栅格列数用动态列数而不是换组件树。

三味药下去,折叠全程的布局切换从十几次降到两三次。这个数字是 Profiler 帧时间线里数出来的——优化前后各录一次,对比是唯一可信的验收方式。

二、状态保持:切换不丢东西才是底线

抖动治完,第二个坑浮上来:展开瞬间,用户正在填的购物清单丢了最后三项。排查发现根因是状态写在了双实例组件的内部 @State 里——组件销毁重建,状态陪葬。

改造原则一句话:视图可以换,状态必须外移。把列表滚动位置、输入草稿、多选状态全部收敛到页面级或 AppStorage,组件树随便重建,状态原地不动。滚动位置用 Scroller 的 currentIndex 记录,aboutToAppear 恢复;输入草稿绑到外部对象,切换零丢失。

还有一个隐蔽的:展开态的图片。菜谱封面图在折叠态 2 列瀑布流、展开态 4 列——同一张图两个尺寸都要清晰,不能简单拉伸。给 Image 配两档资源按断点切换,多花一倍图片体积,换来展开态不发糊。

三、五维度自测清单

提测前我给自己定了一份清单,按序过一遍,测试同学提的折叠屏 bug 少了一半以上:

  1. 尺寸连续性:窗口从最小拖到最大全程观察,重点盯档位切换瞬间和 lg 刚进档的内容溢出;
  2. 状态保持:折叠↔展开前后,核对滚动位置、输入框内容、多选状态、播放进度;
  3. 图片与媒体:每个图片位置两态各看一次(变形/白边/裁切主体),视频全屏切换不黑屏;
  4. 手势与交互:边缘 40vp 起手的滑动不与系统返回冲突,长按、拖拽两态各测;
  5. 深色模式与字体缩放:新写的展开态页面最容易漏 dark 资源,系统字体最大档再走一遍。

四、真机才是裁判

这次改造最大的教训在验证环节:模拟器的折叠是瞬时的,不存在「手停在阈值附近」的中间态;Previewer 的断点切换是干净的阶跃,不产生连续回调。三种抖动里有两种只在真机上复现。所以折叠屏适配的验证铁律只有一条:慢速折叠、展开各十次,中途停在半折叠态两秒——这一套动作下来,九成的适配问题会自己现形。

收束

折叠屏适配的本质不是适配一块更大的屏幕,而是适配「屏幕正在变化」这个过程。迟滞防横跳、节流防高频、单实例防闪屏治过程;状态外移保结果;五维清单守底线。那条一星评论在 1.0.2 版本后变成了三星:「折叠能用,就是图有点糊」——然后 1.0.3,四星。适配没有终点,但有方法论。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐