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沉浸式风格统一 特定页面定制化需求

开发建议:

  1. 在API version ≥ 9时优先使用window.getWindowAvoidArea接口

  2. 涉及横竖屏切换时添加防抖处理:

    let resizeTimer: number = 0;win.on('windowSizeChange', () => {  clearTimeout(resizeTimer);  resizeTimer = setTimeout(() => this.updateAvoidArea(), 300);});
    
  3. 在原子化服务场景中应与enterFullScreen方法结合使用

Logo

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

更多推荐