harmony OS NEXT-沉浸式布局实现笔记
harmony OS NEXT-沉浸式布局实现笔记
沉浸式布局核心原理
产生白边问题的原因
区域类型 | 分辨率 | 安全区偏移量 | 系统组件 |
---|---|---|---|
状态栏 | 1280*60px | TOP: 60vp | 电池、信号、时间等 |
手势条区域 | 1280*120px | BOTTOM: 48vp | 返回手势指示条 |
方法一:窗口全局全屏 + 安全区动态适配
1. 启用全屏模式
// entryability/EntryAbility.ts
import window from '@ohos.window';
export default class EntryAbility extends Ability {
async onWindowStageCreate(windowStage: window.WindowStage) {
try {
const win = await windowStage.getMainWindow();
await win.setWindowLayoutFullScreen(true); // 启用全屏布局
this.setupAvoidAreaListeners(win); // 安全区变化监听
} catch (err) {
console.error('全屏模式设置失败:', err);
}
}
private setupAvoidAreaListeners(win: window.Window) {
win.on('avoidAreaChange', (area: window.AvoidArea) => {
AppStorage.setOrCreate<AvoidAreaModel>('avoidArea',
new AvoidAreaModel(area.topRect, area.bottomRect));
});
}
}
2. 安全区数据模型
// model/AvoidAreaModel.ts
export class AvoidAreaModel {
topHeight: number = 0; // VP单位
bottomHeight: number = 0;
constructor(topRect: rectangle.Rect, bottomRect: rectangle.Rect) {
this.topHeight = px2vp(topRect.height);
this.bottomHeight = px2vp(bottomRect.height);
}
}
3. UI层安全区适配组件
// components/SafeAreaContainer.ets
@Component
struct SafeAreaContainer {
@StorageLink('avoidArea') avoidArea: AvoidAreaModel = new AvoidAreaModel();
build() {
Column() {
// 自定义内容通过slot插入
Slot()
}
.padding({
top: this.avoidArea.topHeight,
bottom: this.avoidArea.bottomHeight
})
.backgroundColor(Color.Transparent)
.onAppear(() => {
this.checkAvoidAreaValidity();
})
}
private checkAvoidAreaValidity() {
if (this.avoidArea.topHeight === 0) {
console.warn('安全区高度未正确初始化,请检查window监听');
}
}
}
方法二:组件级区域控制(推荐方案)
1. 整合安全区功能的装饰器
// decorators/WithSafeArea.ts
export function WithSafeArea<T extends Object>(WrappedComponent: new () => T) {
@Component
struct WrapperComponent {
@State avoidArea: AvoidAreaModel = AppStorage.get('avoidArea');
async aboutToAppear() {
this.updateAvoidArea(); // 初次加载获取当前安全区
}
private async updateAvoidArea() {
try {
const win = await window.getTopWindow();
const area = await win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
this.avoidArea = new AvoidAreaModel(area.topRect, area.bottomRect);
} catch (error) {
console.error('安全区获取失败:', error);
}
}
build() {
Column() {
WrappedComponent()
}
.padding({
top: this.avoidArea.topHeight,
bottom: this.avoidArea.bottomHeight
})
}
}
return WrapperComponent;
}
2. 使用示例
@Entry
@WithSafeArea
struct HomePage {
build() {
Column() {
Text('主内容区域')
.fontSize(20)
.fontColor(Color.White)
}
.width('100%')
.height('100%')
.backgroundColor('#FF409EFF')
}
}
方案对比与最佳实践
对比维度 | 全局窗口方案 | 组件级方案 |
---|---|---|
性能消耗 | 中等(全局监听) | 低(按需加载) |
代码侵入性 | 高(需修改Ability) | 低(装饰器模式) |
动态更新支持 | 自动响应 | 需手动刷新 |
多窗口场景支持 | 部分支持 | 完全支持 |
推荐应用场景 | 全App沉浸式风格统一 | 特定页面定制化需求 |
开发建议:
-
在API version ≥ 9时优先使用
window.getWindowAvoidArea
接口 -
涉及横竖屏切换时添加防抖处理:
let resizeTimer: number = 0;win.on('windowSizeChange', () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => this.updateAvoidArea(), 300);});
-
在原子化服务场景中应与enterFullScreen方法结合使用