Goal: Encapsulate utility classes to achieve immersive effects.

Typical application full screen window UI elements include a status bar, application interface, and bottom navigation bar, where the status bar and navigation bar are typically referred to as avoidance zones in immersive layouts; The area outside the avoidance zone is called the safety zone. Developing immersive effects for applications mainly refers to reducing the abruptness of system interfaces such as status bars and navigation bars by adjusting the display effects of status bars, application interfaces, and navigation bars, in order to provide users with the best UI experience.

The following design elements should be considered when developing immersive effects for applications:
-UI element avoidance handling: The bottom area of the navigation bar can respond to click events, but other interactive UI elements and key application information are not recommended to be placed in the navigation bar area. The status bar displays system information. If there is a conflict with interface elements, it is necessary to consider avoiding the status bar.
-Immersive effect processing: Match the color of the status bar and navigation bar with the color of the interface elements, without any obvious abruptness.

Actual combat:

import { Rect } from '@ohos.application.AccessibilityExtensionAbility';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { UIAbility } from '@kit.AbilityKit';
import { KeyboardAvoidMode } from '@kit.ArkUI';

export namespace StageModelKit {
  export class StageModel {
    static UIAbility: Map = new Map();
    static UIAbilityContext: Map = new Map();
    static WindowStage: Map = new Map();

    /**
     * 登记
     * @param UIAbilityContext
     * @param WindowStage
     */
    static register(UIAbilityContext: Map, WindowStage: Map) {
      UIAbilityContext.forEach((value: Context, key: string, map: Map) => {
        StageModel.UIAbilityContext.set(key, value)
      })

      WindowStage.forEach((value: window.WindowStage, key: string, map: Map) => {
        StageModel.WindowStage.set(key, value)
      })
    }
  }

  export class Window {
    private windowStageName: string;
    windowStage: window.WindowStage;
    avoidArea: AvoidArea;
    keyboardHeight: number;

    constructor(windowStageName: string) {
      this.windowStageName = windowStageName
      this.windowStage = new Object() as window.WindowStage
      const zeroRect: Rect = {
        left: 0,
        top: 0,
        width: 0,
        height: 0
      }
      this.avoidArea = new AvoidArea(zeroRect, zeroRect)
      this.keyboardHeight = 0
    }

    init() {
      //初始化 windowStage
      const windowStage = StageModel.WindowStage.get(this.windowStageName)
      if (windowStage) {
        this.windowStage = windowStage
      } else {
        throw new Error(`[异常][未获取到windowStage,请检查StageModel和windowStageName是否正确引用] windowStage is ${JSON.stringify(windowStage)}`)
      }
      //初始化 avoidArea
      const getWindow = this.windowStage.getMainWindowSync(); // 获取应用主窗口
      const topRect = getWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect // 系统状态栏顶部区域
      const bottomRect =
        getWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRect // 导航条底部区域
      this.avoidArea = new AvoidArea(rect_px2vp(topRect), rect_px2vp(bottomRect))
    }

    /**
     * 沉浸式效果
     */
    setImmersiveEffect() {
      this.watchAvoidArea()
      this.watchKeyboardHeight()
      this.setFullScreen()
      // 设置虚拟键盘抬起时压缩页面大小为减去键盘的高度
      this.windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);
    }

    /**
     * 监控避让区
     */
    watchAvoidArea() {
      this.windowStage.getMainWindowSync().on('avoidAreaChange', (data) => {
        if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {
          let avoidArea = this.avoidArea as AvoidArea
          avoidArea.topRect = rect_px2vp(data.area.topRect)
          this.avoidArea = avoidArea
        } else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
          let avoidArea = this.avoidArea as AvoidArea
          avoidArea.bottomRect = rect_px2vp(data.area.bottomRect)
          this.avoidArea = avoidArea
        }else if (data.type == window.AvoidAreaType.TYPE_KEYBOARD) {
          // this.keyboardHeight = px2vp(data.area.bottomRect.height) //键盘高度
          // DeepalLogUtils.debug(`[日志]watchAvoidArea, keyboardHeight=${JSON.stringify(this.keyboardHeight)}`);
        }
      });
    }

    /**
     * 监控软键盘高度
     */
    watchKeyboardHeight() {
      this.windowStage.getMainWindowSync().on('keyboardHeightChange', (data: number) => {
        this.keyboardHeight = px2vp(data);
      });
    }

    /**
     * 设置全屏
     */
    setFullScreen() {
      this.windowStage.getMainWindowSync()
        .setWindowLayoutFullScreen(true)
        .then(() => {
          console.info('Succeeded in setting the window layout to full-screen mode.');
        })
        .catch((err: BusinessError) => {
          console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
        });
    }

    /**
     * 取消全屏
     */
    cancelFullScreen() {
      this.windowStage.getMainWindowSync()
        .setWindowLayoutFullScreen(false)
        .then(() => {
          console.info('Succeeded in setting the window layout to full-screen mode.');
        })
        .catch((err: BusinessError) => {
          console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
        });
    }

    /**
     * 隐藏头部状态栏
     */
    hideSystemTopStatusBar() {
      this.windowStage.getMainWindowSync()
        .setSpecificSystemBarEnabled('status', false)
        .then(() => {
          console.info('Succeeded in setting the status bar to be invisible.');
        })
        .catch((err: BusinessError) => {
          console.error(`Failed to set the status bar to be invisible. Code is ${err.code}, message is ${err.message}`);
        });
    }

    /**
     * 显示头部状态栏
     */
    showSystemTopStatusBar() {
      this.windowStage.getMainWindowSync()
        .setSpecificSystemBarEnabled('status', true)
        .then(() => {
          console.info('Succeeded in setting the status bar to be invisible.');
        })
        .catch((err: BusinessError) => {
          console.error(`Failed to set the status bar to be invisible. Code is ${err.code}, message is ${err.message}`);
        });
    }

    /**
     * 隐藏底部导航条
     */
    hideSystemBottomNavigationBar() {
      this.windowStage.getMainWindowSync()
        .setSpecificSystemBarEnabled('navigationIndicator', false)
        .then(() => {
          console.info('Succeeded in setting the navigation indicator to be invisible.');
        })
        .catch((err: BusinessError) => {
          console.error(`Failed to set the navigation indicator to be invisible. Code is ${err.code}, message is ${err.message}`);
        });
    }

    /**
     * 显示底部区域
     */
    showSystemBottomNavigationBar() {
      this.windowStage.getMainWindowSync()
        .setSpecificSystemBarEnabled('navigationIndicator', true)
        .then(() => {
          console.info('Succeeded in setting the navigation indicator to be invisible.');
        })
        .catch((err: BusinessError) => {
          console.error(`Failed to set the navigation indicator to be invisible. Code is ${err.code}, message is ${err.message}`);
        });
    }
  }

  /**
   * 避让区
   */
  class AvoidArea {
    topRect: Rect;
    bottomRect: Rect;

    constructor(topRect: Rect, bottomRect: Rect) {
      this.topRect = topRect
      this.bottomRect = bottomRect
    }
  }

  /**
   * 将矩形的px单位的数值转换为以vp为单位的数值
   * @param rect
   * @returns
   */
  function rect_px2vp(rect: Rect): Rect {
    return {
      left: px2vp(rect.left),
      top: px2vp(rect.top),
      width: px2vp(rect.width),
      height: px2vp(rect.height)
    } as Rect
  }
}