尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

HarmonyOS 7.0 / API 26 DynamicLayout 性能排查:窗口拖拽时如何压住重复测量和刷新抖动

HarmonyOS 7.0 / API 26 DynamicLayout 性能排查:窗口拖拽时如何压住重复测量和刷新抖动 HarmonyOS 7.0 / API 26 DynamicLayout 性能排查窗口拖拽时如何压住重复测量和刷新抖动问题从哪里来HarmonyOS 7.0 / API 26 做多设备适配时DynamicLayout 这类动态布局能力能帮页面应对手机、折叠屏、平板和窗口态。但布局能动起来不代表体验就稳。用户拖动窗口宽度、折叠屏展开、平板分屏比例变化时页面可能会连续触发测量、重排、列表刷新和详情请求。如果处理不好用户看到的不是“自适应”而是列表闪、详情白、按钮跳、接口重复请求。这个问题很常见因为窗口变化不是一次事件而是一串连续事件。页面如果每收到一次变化就完整重算就会被自己的适配逻辑拖慢。这篇只讲一个点DynamicLayout 场景下的性能稳定。目标是让布局变化时页面只做必要更新状态不丢接口不炸列表不抖。先复现一个典型问题假设页面有列表、筛选区和详情区。窗口从 760vp 拖到 980vp 时页面从单栏切成主从结构。很多代码会在每次 width 变化时重新计算 layoutMode然后触发列表和详情刷新。typeLayoutModesingleColumn|masterDetail|threePane;interfaceWindowChangeEvent{widthVp:number;heightVp:number;timestamp:number;}classBadLayoutController{privatemode:LayoutModesingleColumn;onWindowChange(event:WindowChangeEvent):void{this.modeevent.widthVp900?masterDetail:singleColumn;this.reloadList();this.reloadDetail();this.rebuildFloatLayer();}privatereloadList():void{}privatereloadDetail():void{}privaterebuildFloatLayer():void{}}这段代码看起来没问题但它会把“窗口拖动中的每一帧”都当成最终布局来处理。拖动 1 秒可能触发几十次页面当然会抖。正确思路布局事件先合并再决定是否重排我会把窗口变化分成两层原始变化和稳定变化。原始变化只记录不立刻刷新列表等窗口宽度稳定一小段时间后再判断布局模式有没有真正变化。interfaceLayoutDecision{mode:LayoutMode;widthBucket:compact|medium|expanded;shouldReloadData:boolean;shouldRebuildPanel:boolean;}exportclassDynamicLayoutStabilityController{privatelastDecision:LayoutDecision{mode:singleColumn,widthBucket:compact,shouldReloadData:false,shouldRebuildPanel:false};privatependingTimer:number-1;privatependingEvent?:WindowChangeEvent;onWindowChanging(event:WindowChangeEvent,apply:(decision:LayoutDecision)void):void{this.pendingEventevent;if(this.pendingTimer!-1){clearTimeout(this.pendingTimer);}this.pendingTimersetTimeout((){if(!this.pendingEvent)return;constnextthis.resolve(this.pendingEvent.widthVp);constdecisionthis.diff(this.lastDecision,next);this.lastDecisionnext;apply(decision);this.pendingTimer-1;},120);}privateresolve(widthVp:number):LayoutDecision{if(widthVp1200){return{mode:threePane,widthBucket:expanded,shouldReloadData:false,shouldRebuildPanel:true};}if(widthVp900){return{mode:masterDetail,widthBucket:medium,shouldReloadData:false,shouldRebuildPanel:true};}return{mode:singleColumn,widthBucket:compact,shouldReloadData:false,shouldRebuildPanel:true};}privatediff(prev:LayoutDecision,next:LayoutDecision):LayoutDecision{return{...next,shouldReloadData:false,shouldRebuildPanel:prev.mode!next.mode||prev.widthBucket!next.widthBucket};}}这里有一个关键点窗口变化不等于数据变化。布局变了只需要调整页面结构除非筛选条件、详情 ID 或数据版本变了否则不应该重新请求。案例一拖动窗口 30 次只重排 1 次下面这个例子模拟窗口连续拖动。宽度从 850vp 到 930vp中间变化很多次但最终只需要在稳定后切到 masterDetail。constcontrollernewDynamicLayoutStabilityController();constdecisions:LayoutDecision[][];for(leti0;i30;i){controller.onWindowChanging({widthVp:850i*3,heightVp:760,timestamp:Date.now()i*16},decisiondecisions.push(decision));}setTimeout((){console.info(decisions.length);// 期望1console.info(decisions[0].mode);// 期望masterDetailconsole.info(decisions[0].shouldReloadData);// 期望false},200);这个验证能直接抓出问题。如果 decisions 出现十几条说明页面还是在跟着窗口拖动频繁重排。正式页面里这种频繁重排会把列表、图片解码和浮层定位全部拖慢。案例二跨断点才重排同断点内只调整尺寸第二个案例更细一点。用户在平板分屏里从 960vp 拖到 1040vp都属于 masterDetail。此时不应该重建左右面板只需要调整列宽。interfacePanelSizeResult{mode:LayoutMode;listWidthVp:number;detailMinWidthVp:number;rebuild:boolean;}exportclassPanelSizeResolver{resolve(widthVp:number,previousMode:LayoutMode):PanelSizeResult{constmode:LayoutModewidthVp1200?threePane:widthVp900?masterDetail:singleColumn;if(modesingleColumn){return{mode,listWidthVp:widthVp,detailMinWidthVp:0,rebuild:previousMode!mode};}if(modemasterDetail){constlistWidthMath.min(360,Math.max(300,Math.round(widthVp*0.36)));return{mode,listWidthVp:listWidth,detailMinWidthVp:widthVp-listWidth,rebuild:previousMode!mode};}return{mode,listWidthVp:360,detailMinWidthVp:widthVp-600,rebuild:previousMode!mode};}}这段代码把“模式变化”和“尺寸变化”分开了。模式没变就不要重建组件只调整宽度即可。这样折叠屏和平板窗口变化时页面会稳很多。页面里怎么接页面层不要直接把 width 变化绑到所有子组件。建议只让布局控制器输出一个稳定 decision然后页面根据 decision 更新布局。Componentstruct DynamicLayoutPerfPage{privatestabilitynewDynamicLayoutStabilityController();privatesizeResolvernewPanelSizeResolver();Stateprivatemode:LayoutModesingleColumn;StateprivatelistWidthVp:number390;StateprivatedetailMinWidthVp:number0;onWindowSizeChange(widthVp:number,heightVp:number){this.stability.onWindowChanging({widthVp,heightVp,timestamp:Date.now()},decision{constpanelthis.sizeResolver.resolve(widthVp,this.mode);this.modepanel.mode;this.listWidthVppanel.listWidthVp;this.detailMinWidthVppanel.detailMinWidthVp;if(decision.shouldReloadData){this.reloadData();}});}privatereloadData():void{// 只有筛选条件、账号、数据版本变化时才请求不因为窗口拖动请求}build(){if(this.modesingleColumn){Column(){Text(列表)}.width(100%)}else{Row(){Column(){Text(列表)}.width(this.listWidthVp)Column(){Text(详情)}.layoutWeight(1)}}}}这套结构能把性能问题压在布局层。窗口变化时页面先等稳定再跨断点重排同断点内只调尺寸不重新拉数据。方案取舍方案开发速度性能稳定适合程度每次窗口变化都刷新页面快差不适合正式页面只做防抖不做断点 diff中一般简单页面可用防抖 断点 diff 数据不重拉中好多设备页面优先所有内容做成静态布局快表面稳定交互复杂后会失控我会选第三种。它没有过度设计只是把窗口变化当成高频事件来处理这一点很重要。多设备适配不是把大屏摆满而是窗口一直变化时页面还稳。验收清单连续拖动窗口 1 秒列表不重复请求同一断点内调整尺寸不销毁列表和详情跨断点时才重建面板结构搜索词、选中项、滚动位置保留浮层和详情区域跟随最终布局不跟着每一帧抖日志能看到本次是尺寸变化、断点变化还是数据变化。最后总结HarmonyOS 7.0 / API 26 的多设备适配不能只看页面能不能铺开。窗口拖动、折叠屏展开和平板分屏都会带来连续布局事件。DynamicLayout 相关页面要稳核心是三件事合并高频窗口变化、区分断点变化和尺寸变化、不要因为布局变化重复拉数据。把这三件事做好页面从手机到折叠屏、平板、鸿蒙电脑窗口态都会更稳定用户也不会在窗口变化时看到列表闪烁和详情白屏。
返回列表