HarmonyOS《柚兔学伴》项目实战04-沉浸式窗口与断点系统 沉浸式窗口与断点系统沉浸式体验是现代移动应用的重要设计目标——内容从状态栏底部延伸到导航栏实现全屏视觉冲击。同时HarmonyOS 支持手机、平板、2in1 等多种设备形态应用需要根据屏幕尺寸自适应布局。柚兔学伴通过WindowUtil实现沉浸式窗口管理通过BreakpointSystem实现多设备断点适配两者协同构建了完整的屏幕适配方案。一、沉浸式窗口实现1.1 全屏布局沉浸式窗口的核心是将内容延伸到系统栏区域。在WindowUtil.requestFullScreen中// common/src/main/ets/util/WindowUtil.etspublicstaticrequestFullScreen(windowStage:window.WindowStage,context:Context):void{windowStage.getMainWindow((err:BusinessError,data:window.Window){if(err.code){Logger.error(TAG,Failed to obtain the main window. Cause:${err.code});return;}constwindowClass:window.Windowdata;try{// HPR 设备特殊处理限制窗口尺寸if(deviceInfo.productSeriesProductSeriesEnum.HPR){WindowUtil.resetWindowSize(windowClass);}// 设置全屏布局——内容延伸到状态栏和导航栏区域constpromise:PromisevoidwindowClass.setWindowLayoutFullScreen(true);promise.then((){Logger.info(TAG,Succeeded in setting the window layout to full-screen mode.);}).catch((err:BusinessError){Logger.error(TAG,Failed to set the window layout to full-screen mode.);});// 获取设备尺寸信息WindowUtil.getDeviceSize(context);}catch{Logger.error(TAG,Failed to set the window layout to full-screen mode.);}});}setWindowLayoutFullScreen(true)使窗口内容布局覆盖整个屏幕包括状态栏和导航指示条区域。但此时系统栏仍然可见只是内容可以延伸到其下方。1.2 隐藏标题栏publicstatichideTitleBar(windowStage:window.WindowStage){windowStage.getMainWindow().then((data:window.Window){try{if(canIUse(SystemCapability.Window.SessionManager)){data.setWindowDecorVisible(false);// 隐藏窗口装饰data.setWindowDecorHeight(CommonConstants.NAVIGATION_HEIGHT);// 设置装饰高度}}catch(error){consterr:BusinessErrorerrorasBusinessError;Logger.error(TAG,Failed to set the visibility of window decor.);}});}setWindowDecorVisible(false)隐藏系统标题栏使应用完全掌控顶部区域。1.3 状态栏颜色控制沉浸式布局下需要根据内容背景调整状态栏图标颜色确保可见性publicstaticupdateStatusBarColor(context:common.BaseContext,isDark:boolean):void{window.getLastWindow(context).then((windowClass:window.Window){try{windowClass.setWindowSystemBarProperties({statusBarContentColor:isDark?StatusBarColorType.WHITE:StatusBarColorType.BLACK}).then((){Logger.info(TAG,Succeeded in setting the system bar properties.);});}catch(error){Logger.error(TAG,Failed to set the system bar properties.);}});}// 状态栏颜色枚举exportenumStatusBarColorType{WHITE#ffffff,// 深色背景使用白色图标BLACK#E5000000,// 浅色背景使用黑色图标90%透明度黑}当页面背景为深色时传入isDark: true状态栏图标变白浅色背景则变黑。二、避让区域管理全屏布局后内容可能与状态栏和底部导航指示条重叠需要获取避让区域高度并预留空间。2.1 获取避让区域publicstaticregisterBreakPoint(windowStage:window.WindowStage){windowStage.getMainWindow((err:BusinessError,data:window.Window){if(err.code){Logger.error(TAG,Failed to get main window:${err.message});return;}try{// 初始化断点系统BreakpointSystem.getInstance().updateWidthBp(data);constglobalInfoModel:GlobalInfoModelAppStorage.get(GlobalInfoModel)||newGlobalInfoModel();// 获取状态栏避让区域constsystemAvoidArea:window.AvoidAreadata.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);globalInfoModel.statusBarHeightpx2vp(systemAvoidArea.topRect.height);// 获取底部导航指示条避让区域constbottomArea:window.AvoidAreadata.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);globalInfoModel.naviIndicatorHeightpx2vp(bottomArea.bottomRect.height);AppStorage.setOrCreate(GlobalInfoModel,globalInfoModel);// 监听窗口尺寸变化data.on(windowSizeChange,()WindowUtil.onWindowSizeChange(data));// 监听避让区域变化data.on(avoidAreaChange,(avoidAreaOption){if(avoidAreaOption.typewindow.AvoidAreaType.TYPE_SYSTEM||avoidAreaOption.typewindow.AvoidAreaType.TYPE_NAVIGATION_INDICATOR){WindowUtil.setAvoidArea(avoidAreaOption.type,avoidAreaOption.area);}});}catch(e){Logger.error(TAG,getWindowAvoidArea failed.);}});}两种避让区域类型TYPE_SYSTEM顶部状态栏区域topRect.height即状态栏高度TYPE_NAVIGATION_INDICATOR底部导航指示条区域bottomRect.height即指示条高度2.2 避让区域动态更新当系统栏状态变化如横竖屏切换、折叠屏展开时通过avoidAreaChange监听器实时更新publicstaticsetAvoidArea(type:window.AvoidAreaType,area:window.AvoidArea){constglobalInfoModel:GlobalInfoModelAppStorage.get(GlobalInfoModel)||newGlobalInfoModel();if(typewindow.AvoidAreaType.TYPE_SYSTEM){globalInfoModel.statusBarHeightpx2vp(area.topRect.height);}else{globalInfoModel.naviIndicatorHeightpx2vp(area.bottomRect.height);}AppStorage.setOrCreate(GlobalInfoModel,globalInfoModel);}2.3 GlobalInfoModel 全局屏幕信息所有屏幕相关信息统一存储在GlobalInfoModel中通过AppStorage全局共享// common/src/main/ets/model/GlobalInfoModel.etsObservedexportclassGlobalInfoModel{publicfoldExpanded:booleanfalse;// 折叠屏是否展开publiccurrentBreakpoint:BreakpointTypeEnumBreakpointTypeEnum.MD;// 当前断点publicnaviIndicatorHeight:number0;// 底部指示条高度vppublicstatusBarHeight:number0;// 状态栏高度vppublicdecorHeight:number0;// 窗口装饰高度publicdeviceHeight:number0;// 设备高度vppublicdeviceWidth:number0;// 设备宽度vp}在 UI 组件中使用StorageLink响应式绑定StorageLink(GlobalInfoModel)globalInfo:GlobalInfoModelnewGlobalInfoModel();// 为内容区域添加顶部 padding避开状态栏.padding({top:this.globalInfo.statusBarHeight})// 为底部添加 padding避开导航指示条.padding({bottom:this.globalInfo.naviIndicatorHeight})三、断点系统3.1 断点定义BreakpointSystem 根据窗口宽度将设备划分为五个断点等级断点宽度范围典型设备XS 320vp小屏穿戴设备SM320vp - 600vp手机竖屏MD600vp - 840vp手机横屏/小平板LG840vp - 1440vp平板/折叠屏XL 1440vp大屏/2in1 设备exportenumBreakpointTypeEnum{XSxs,SMsm,MDmd,LGlg,XLxl,}3.2 BreakpointSystem 单例// common/src/main/ets/util/BreakpointSystem.etsexportclassBreakpointSystem{privatestaticinstance:BreakpointSystem;privatecurrentBreakpoint:BreakpointTypeEnumBreakpointTypeEnum.MD;privateconstructor(){}publicstaticgetInstance():BreakpointSystem{if(!BreakpointSystem.instance){BreakpointSystem.instancenewBreakpointSystem();}returnBreakpointSystem.instance;}publicupdateWidthBp(window:window.Window):void{try{constmainWindow:window.WindowPropertieswindow.getWindowProperties();constwindowWidth:numbermainWindow.windowRect.width;constwindowWidthVppx2vp(windowWidth);// 像素转 VP// HPR 设备直接判定为 XL 断点constdeviceTypedeviceInfo.productSeries;if(deviceTypeProductSeriesEnum.HPR){this.updateCurrentBreakpoint(BreakpointTypeEnum.XL);return;}// 根据窗口宽度 VP 判定断点letwidthBp:BreakpointTypeEnumBreakpointTypeEnum.MD;if(windowWidthVp320){widthBpBreakpointTypeEnum.XS;}elseif(windowWidthVp320windowWidthVp600){widthBpBreakpointTypeEnum.SM;}elseif(windowWidthVp600windowWidthVp840){widthBpBreakpointTypeEnum.MD;}elseif(windowWidthVp840windowWidthVp1440){widthBpBreakpointTypeEnum.LG;}else{widthBpBreakpointTypeEnum.XL;}this.updateCurrentBreakpoint(widthBp);}catch(error){Logger.error(TAG,Failed to getWindowProperties.);}}publicupdateCurrentBreakpoint(breakpoint:BreakpointTypeEnum):void{if(this.currentBreakpoint!breakpoint){this.currentBreakpointbreakpoint;constglobalInfoModel:GlobalInfoModelAppStorage.get(GlobalInfoModel)||newGlobalInfoModel();globalInfoModel.currentBreakpointthis.currentBreakpoint;AppStorage.setOrCreate(GlobalInfoModel,globalInfoModel);}}}关键点使用px2vp()将像素宽度转换为 VP保证不同像素密度的设备断点一致HPR华为 PC 类设备直接判定为 XL 断点断点变化时更新GlobalInfoModel.currentBreakpoint触发 UI 刷新3.3 BreakpointType 泛型类BreakpointTypeT是断点系统的核心工具类可以根据当前断点返回不同的值如布局参数、字体大小、组件实例exportinterfaceBreakpointTypesT{xs?:T;sm:T;md:T;lg:T;xl?:T;}exportclassBreakpointTypeT{privatexs:T;privatesm:T;privatemd:T;privatelg:T;privatexl:T;publicconstructor(param:BreakpointTypesT){this.xsparam.xs??param.sm;// xs 缺省取 sm 值this.smparam.sm;this.mdparam.md;this.lgparam.lg;this.xlparam.xl??param.lg;// xl 缺省取 lg 值}publicgetValue(currentBreakpoint:string):T{if(currentBreakpointBreakpointTypeEnum.XS)returnthis.xs;if(currentBreakpointBreakpointTypeEnum.SM)returnthis.sm;if(currentBreakpointBreakpointTypeEnum.MD)returnthis.md;if(currentBreakpointBreakpointTypeEnum.XL)returnthis.xl;returnthis.lg;}}使用示例——根据断点设置不同的列数和间距constcolumnsnewBreakpointTypenumber({sm:2,// 手机两列md:3,// 横屏三列lg:4,// 平板四列});constgridColcolumns.getValue(this.globalInfo.currentBreakpoint);xs和xl为可选参数缺省时分别回退到sm和lg简化了配置。3.4 窗口尺寸变化监听当窗口尺寸变化旋转屏幕、折叠屏开合、自由窗口拖拽时触发断点重新计算publicstaticonWindowSizeChange(window:window.Window){WindowUtil.getDeviceSize(getContext());// 更新设备尺寸BreakpointSystem.getInstance().onWindowSizeChange(window);// 更新断点}四、HPR 设备特殊处理对于 HPRHarmonyOS PC 类设备柚兔学伴做了特殊适配privatestaticresetWindowSize(windowClass:window.Window):void{if(canIUse(SystemCapability.Window.SessionManager)){constwindowSize:display.Displaydisplay.getDefaultDisplaySync();constappWidth:numberwindowSize.width*9/10;// 90% 屏幕宽度constappHeight:numberwindowSize.height*7/8;// 87.5% 屏幕高度constwindowLimits:window.WindowLimits{maxWidth:appWidth,maxHeight:appHeight,minWidth:appWidth,minHeight:appHeight,};windowClass.setWindowLimits(windowLimits);windowClass.moveWindowToAsync(windowSize.width/20,windowSize.height/16);}}HPR 设备上不使用全屏窗口而是限制为屏幕 90%×87.5% 的固定窗口模拟 PC 应用的窗口体验。五、完整适配流程应用启动 → onWindowStageCreate │ ├── WindowUtil.requestFullScreen() ← 设置全屏布局 │ └── WindowUtil.getDeviceSize() ← 读取设备宽高 │ ├── WindowUtil.registerBreakPoint() ← 注册断点与避让 │ ├── BreakpointSystem.updateWidthBp() ← 计算初始断点 │ ├── getWindowAvoidArea(TYPE_SYSTEM) ← 读取状态栏高度 │ ├── getWindowAvoidArea(TYPE_NAVIGATION_INDICATOR) ← 读取指示条高度 │ ├── on(windowSizeChange) ← 监听窗口变化 │ └── on(avoidAreaChange) ← 监听避让变化 │ └── AppStorage.setOrCreate(GlobalInfoModel) ← 全局共享 │ UI 组件通过 StorageLink 响应式读取 ├── statusBarHeight → 顶部 padding ├── naviIndicatorHeight → 底部 padding ├── currentBreakpoint → BreakpointType.getValue() → 自适应布局 └── deviceWidth / deviceHeight → 条件渲染小结柚兔学伴通过WindowUtil实现沉浸式窗口管理——全屏布局、隐藏标题栏、状态栏颜色适配、避让区域获取与动态更新通过BreakpointSystem实现多设备断点适配——基于窗口宽度 VP 划分五个断点等级提供BreakpointTypeT泛型类简化断点值映射。两者通过GlobalInfoModelAppStorage统一管理屏幕状态UI 组件只需声明式绑定即可实现自适应布局无需手动处理设备差异。