HarmonyOS 沉浸式状态实现的多种方式

1. HarmonyOS 沉浸式状态实现的多种方式

HarmonyOS 沉浸式状态实现的多种方式

1.1. 方法一

1.1.1. 实现讲解

(1)首先设置setWindowLayoutFullScreen(true)(设置全屏布局)。

布局将从屏幕最顶部开始到最底部结束,此时状态栏和底部规避区域还在,且与页面重叠,所以还需要设置页面根容器顶部内边距为状态栏高度,底部内边距为规避区域高度,这样页面就不会重叠。

(2)页面隐藏(或销毁)周期函数内恢复非全屏布局设置setWindowLayoutFullScreen(false)

全屏布局设置是全局生效的,一旦设置跳转到其他页面同样生效,对于其他页面不需要沉浸式状态栏需要恢复原样。

全屏布局非全屏显示,区别在于状态栏、规避区域还在,页面布局从屏幕最顶端开始

1.1.2. 代码实现

ImmersePage .ets(页面)

javascript 复制代码
import { window } from '@kit.ArkUI'
import { AppHelper, StatusBarBean } from 'zzslib';
import { AppUtil } from 'zzslib/src/main/ets/util/AppUtil';
@Entry
@Component
struct ImmersePage {
  @State statusHeight: number = 0 //状态栏高度
  @State bottomAvoidAreaHeight: number = 0 //手机底部规避区域高度
  @State windowClass: window.Window | null = null //窗口管理器
  appHelper = new AppHelper();
  aboutToAppear() {
    this.setStatusBar('#00007AFF')
    this.init()
  }

  onPageShow(): void {
    //设置全屏布局
    this.windowClass?.setWindowLayoutFullScreen(true)
  }

  onPageHide(): void {
    //取消全屏布局
    this.windowClass?.setWindowLayoutFullScreen(false)
    this.setStatusBar('#007AFF')
  }

  //初始化
  init() {
    window.getLastWindow(getContext(this), (err, windowClass) => {
      if (!err.code) {
        //保存窗口管理器
        this.windowClass = windowClass
        //获取状态栏高度
        this.statusHeight = px2vp(windowClass.getWindowAvoidArea(
          window.AvoidAreaType.TYPE_SYSTEM).topRect.height)
        //获取手机底部规避区域高度
        this.bottomAvoidAreaHeight =
          px2vp(windowClass.getWindowAvoidArea(
            window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
            .bottomRect
            .height)
        windowClass.setWindowLayoutFullScreen(true)

      }
    })
  }
  setStatusBar(statusBarBgColor: string) {
    let that = this
    let statusBarBean = new StatusBarBean()
    statusBarBean.isFullScreen = false
    statusBarBean.statusBarBgColor = statusBarBgColor
    statusBarBean.statusBarTitleColor = '#ffffff'
    that.appHelper.setStatusBar(statusBarBean)
    AppUtil.setStatusBar(true, true)
  }
  build() {
    Column() {
      //页面区域
      Column() {
        Text('沉浸式状态栏').fontColor('#fff')
      }
      .height('100%')
      .width('100%')
      .border({
        width: 1,
        color: 'red'
      })
    }
    .height('100%')
    .width('100%')
    .backgroundImage( $r('app.media.img_bg_page'))
    .backgroundImageSize({ height: '100%', width: '100%' })
    .padding({ top: this.statusHeight, bottom: this.bottomAvoidAreaHeight })
  }
}

1.1.3

. 运行效果:

1.1.3. 全局缓存窗口管理器

当项目内多个页面需要设置全屏布局时,每个页面要重新获取窗口管理器、状态栏高度、底部规避区域高度就比较麻烦,可以在entryabili内获取到上述属性值(对象)并存储在全局对象globalThis上

EntryAbility.ets

javascript 复制代码
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {

  }


  onWindowStageCreate(windowStage: window.WindowStage): void {

    //添加如下代码
    windowStage.getMainWindow((err, data) => {
      if (!err.code) {
        //全局变量添加窗口对象
        globalThis.windowClass = data;
        //全局变量添加状态栏高度单位vp
        globalThis.statusHeight = px2vp(data.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height)
        //全局添加底部规避区域高度单位vp
        globalThis.bottomAvoidAreaHeight = px2vp(data.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
          .bottomRect
          .height)
      }
    })
    //
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        return;
      }
    });
  }


}

Index.ets(页面)

javascript 复制代码
import { window } from '@kit.ArkUI'

@Entry
@Component
struct Index {


  onPageShow(): void {
  //设置全屏布局
    globalThis.windowClass.setWindowLayoutFullScreen(true)
  }

  onPageHide(): void {
    //取消全屏布局
      globalThis.windowClass.setWindowLayoutFullScreen(false)
  }


  build() {
    Column() {
      //页面区域
      Column() {
        Text('沉浸式状态栏').fontColor('#fff')
      }
      .height('100%')
      .width('100%')
      .border({
        width: 1,
        color: 'red'
      })
    }
    .height('100%')
    .width('100%')
    .backgroundImage( $r('app.media.img_bg_page'))
    .backgroundImageSize({ height: '100%', width: '100%' })
    .padding({ top: globalThis.statusHeight, bottom: globalThis.bottomAvoidAreaHeight })
  }
}

1.2. 方法二

1.2.1. 实现讲解

通过NavDestination作为页面根容器,并隐藏标题栏即可快速实现沉浸式状态,NavDestination从api 11开始默认支持安全区避让特性,所以关于页面重叠问题就不需要解决

1.2.2. 代码实现

Index.ets(页面)

javascript 复制代码
@Entry
@Component
struct Index{
  build() {
    NavDestination(){
      //页面区域
      Column(){
        Text('沉浸式状态栏').fontColor('#fff')
      }.width('100%')
      .height('100%')
      .border({
        width:1,
        color:'red'
      })
    }
     .hideTitleBar(true)
  .backgroundImage( $r('app.media.img_bg_page'))
    .backgroundImageSize({height:'100%',width:'100%'})
  }
}

1.2.3. 运行效果:

1.3. 方法三

1.3.1. 实现讲解

通过expandSafeArea属性支持组件不改变布局情况下扩展其绘制区域至安全区外

1.3.2. 代码实现

javascript 复制代码
@Entry
@Component
struct Index {
  build() {
    Stack() {
      Image($r('app.media.img_bg_page'))
        .height('100%').width('100%').expandSafeArea()
      //页面区域
      Column() {
        Text('沉浸式状态栏').fontColor('#fff')
      }
      .height('100%')
      .width('100%')
      .border({
        width: 1,
        color: 'red'
      })

    }.height('100%')
    .width('100%')

  }
}

如果只需要头部区域沉浸

实现代码:

javascript 复制代码
@Entry
@Component
struct Index {
  build() {
    Column() {
      Column() {
        Text('沉浸式状态栏').fontColor('#fff')
      }.height(100).width('100%').backgroundColor('#0A7EE6').expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])

    }.height('100%')
    .width('100%')

  }
}

如果想修改状态栏文字颜色可通过:setWindowSystemBarProperties实现

javascript 复制代码
   window.getLastWindow(getContext(this), (err, windowClass) => {
      if (!err.code) {
        windowClass.setWindowSystemBarProperties({
          statusBarContentColor:"#ffffff"
        })
      }
    })

1.4.封装

1.4.1. 封装状态栏管理类

我们在common目录中创建StatusBarManager.ts文件,完整的代码如下:

javascript 复制代码
import window from '@ohos.window';
import HashMap from '@ohos.util.HashMap';
import { Log } from './Log';

/**
 * 状态栏管理器
 */
export class StatusBarManager {
  private readonly TAG = 'StatusBarManager';
  private readonly CONFIG_SYSTEM_BAR_HEIGHT = 'systemBarHeight';
  private static mInstance: StatusBarManager;
  private mWindowStage: window.WindowStage;

  private mConfig = new HashMap<string, any>();

  private constructor() {
  }

  public static get(): StatusBarManager {
    if (!this.mInstance) {
      this.mInstance = new StatusBarManager();
    }
    return this.mInstance;
  }

  /**
   * 存储windowStage实例
   * @param windowStage
   */
  public storeWindowStage(windowStage: window.WindowStage) {
    this.mWindowStage = windowStage;
  }

  /**
   * 获取windowStage实例
   * @returns
   */
  public getWindowStage(): window.WindowStage {
    return this.mWindowStage;
  }

  /**
   * 设置沉浸式状态栏
   * @param windowStage
   * @returns
   */
  public setImmersiveStatusBar(windowStage: window.WindowStage): Promise<void> {

    let resolveFn, rejectFn;
    let promise = new Promise<void>((resolve, reject) => {
      resolveFn = resolve;
      rejectFn = reject;
    });

    // 1.获取应用主窗口。
    try {
      let windowClass = windowStage.getMainWindowSync();
      Log.info(this.TAG, 'Succeeded in obtaining the main window. Data: ' + JSON.stringify(windowClass));

      // 2.实现沉浸式效果:设置窗口可以全屏绘制。
      // 将UI内容顶入状态栏下方
      windowClass.setWindowLayoutFullScreen(true)
        .then(() => {
          //3、设置状态栏 可见
          windowClass.setWindowSystemBarEnable(['status']).then(() => {
            //4、设置状态栏透明背景
            const systemBarProperties: window.SystemBarProperties = {
              statusBarColor: '#00000000'
            };
            //设置窗口内导航栏、状态栏的属性
            windowClass.setWindowSystemBarProperties(systemBarProperties)
              .then(() => {
                Log.info(this.TAG, 'Succeeded in setting the system bar properties.');
              }).catch((err) => {
              Log.error(this.TAG, 'Failed to set the system bar properties. Cause: ' + JSON.stringify(err));
            });
          })

          //5、存储状态栏高度
          this.storeStatusBarHeight(windowClass);

          resolveFn();
        });

    } catch (err) {
      Log.error(this.TAG, 'Failed to obtain the main window. Cause: ' + JSON.stringify(err));
      rejectFn();
    }

    return promise;

  }

  /**
   * 关闭沉浸式状态栏
   * @param windowStage
   * @returns
   */
  public hideImmersiveStatusBar(windowStage: window.WindowStage): Promise<void> {

    let resolveFn, rejectFn;
    let promise = new Promise<void>((resolve, reject) => {
      resolveFn = resolve;
      rejectFn = reject;
    });
    // 1.获取应用主窗口。
    try {
      let windowClass = windowStage.getMainWindowSync();
      Log.info(this.TAG, 'Succeeded in obtaining the main window. Data: ' + JSON.stringify(windowClass));

      windowClass.setWindowLayoutFullScreen(false)
        .then(() => {
          //存储状态栏高度
          this.mConfig.set(this.CONFIG_SYSTEM_BAR_HEIGHT, 0);
          resolveFn();
        });

    } catch (err) {
      Log.error(this.TAG, 'Failed to obtain the main window. Cause: ' + JSON.stringify(err));
      rejectFn(err);
    }
    return promise;

  }


  /**
   * 获取状态栏高度进行保存
   * @param windowClass
   * @returns
   */
  private storeStatusBarHeight(windowClass: window.Window) {

    try {
      const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
      // 保存高度信息
      this.mConfig.set(this.CONFIG_SYSTEM_BAR_HEIGHT, avoidArea.topRect.height);
      Log.info(this.TAG, 'Succeeded in obtaining the area. Data:' + JSON.stringify(avoidArea));

    } catch (err) {
      Log.error(this.TAG, 'Failed to obtain the area. Cause:' + JSON.stringify(err));
    }
  }

  /**
   * 未开启沉浸式状态栏,偏移量为0,开启, 偏移量为状态栏高度
   * @returns
   */
  public getSystemBarOffset(): number {
    let height = 0;
    if (this.mConfig.hasKey(this.CONFIG_SYSTEM_BAR_HEIGHT)) {
      height = this.mConfig.get(this.CONFIG_SYSTEM_BAR_HEIGHT) as number;
    }
    return height;
  }

  /**
   * 是否开启沉浸式状态栏
   * @returns
   */
  public isOpenImmersiveStatusBar(): boolean {
    return this.getSystemBarOffset() > 0;
  }
}

1.4.2.StatusBarManager 管理类主要提供以下常用的方法:

(1)get- 获取管理类单例实例

(2)storeWindowStage- 存储windowStage实例

(3)该方法在UIAbility中进行调用。

(4)getWindowStage- 获取windowStage实例

(5)setImmersiveStatusBar- 设置沉浸式状态栏

(6)hideImmersiveStatusBar- 关闭沉浸式状态栏

(7)storeStatusBarHeight- (内部私有方法)获取状态栏高度进行保存

(8)getSystemBarOffset- 获取状态栏高度(沉浸式状态栏下需要调整的标题偏移量)

(9)isOpenImmersiveStatusBar- 是否开启沉浸式状态栏

下面我们主要讲解下setImmersiveStatusBar方法,设置沉浸式状态栏,这个过程主要分为五个步骤:

(1)获取应用主窗口

c 复制代码
let windowClass = windowStage.getMainWindowSync();

我们通过传入的windowStage,同步获取一个主窗口实例。

(2)设置窗口可以全屏绘制

c 复制代码
windowClass.setWindowLayoutFullScreen(true)

我们将窗口设置为全屏模式。

(3)设置状态栏可见

c 复制代码
windowClass.setWindowSystemBarEnable(['status'])

在设置全屏后,状态栏不可见,我们需要的不是全屏效果,而是状态栏沉浸式效果,因此需要将状态栏设置为可见。

这里入参是一个数组,可以设置状态栏、也可以设置底部导航栏。

(4)设置窗口内状态栏背景为透明

c 复制代码
const systemBarProperties: window.SystemBarProperties = {
              statusBarColor: '#00000000'
};
windowClass.setWindowSystemBarProperties(systemBarProperties)
              .then(() => {
                Log.info(this.TAG, 'Succeeded in setting the system bar properties.');
              }).catch((err) => {
              Log.error(this.TAG, 'Failed to set the system bar properties. Cause: ' + JSON.stringify(err));
            });

状态栏设置为显示状态后,我们给状态栏的背景色设置为透明,这里才能达到沉浸式的效果。

(5)存储状态栏高度

c 复制代码
const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
// 保存高度信息
this.mConfig.set(this.CONFIG_SYSTEM_BAR_HEIGHT, avoidArea.topRect.height);

我们通过上述代码可以获取系统状态栏的高度,并将其保存起来,后续页面通过该高度来判断是否是开启了沉浸式状态栏。

这样我们的状态栏管理类就封装完毕,下面我们来写下页面UI实现沉浸式页面状态栏效果。

1.4.3.完整代码

javascript 复制代码
import { StatusBarManager } from '../../../utils/StatusBarManager';

@Entry
@Component
struct Immerse3Page {
  @State showImmersiveStatusBar: boolean = false;
  @State titleBarPadding: number = 0;

  build() {
    Column() {

      Column() {
        Column() {
          Text('这是标题')
            .fontSize(20)
            .fontColor(Color.White)
        }
        .height(50)
        .justifyContent(FlexAlign.Center)
      }
      .padding({ top: `${this.titleBarPadding}px` })
      .width('100%')
      .backgroundColor('#ff007dfe')

      Column() {
        Text('点击开启沉浸式状态栏')
          .fontSize(16)

        Button(this.showImmersiveStatusBar ? '关闭' : '开启')
          .fontSize(16)
          .margin({ top: 20 })
          .padding({ left: 50, right: 50 })
          .onClick(() => {

            if (this.showImmersiveStatusBar) {
              this.close();
            } else {
              this.open();
            }
            this.showImmersiveStatusBar = !this.showImmersiveStatusBar;
          })
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)
    }
    .height('100%')
  }

  private open() {
    let windowStage = StatusBarManager.get().getWindowStage();
    if (windowStage) {
      StatusBarManager.get().setImmersiveStatusBar(windowStage)
        .then(() => {
          this.titleBarPadding = StatusBarManager.get().getSystemBarOffset();
        });
    }
  }

  private close() {
    let windowStage = StatusBarManager.get().getWindowStage();
    if (windowStage) {
      StatusBarManager.get().hideImmersiveStatusBar(windowStage).then(() => {
        this.titleBarPadding = 0;
      })
    }
  }
}
相关推荐
Damon小智5 小时前
HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别
华为·harmonyos
匹马夕阳8 小时前
华为笔记本之糟糕的体验
华为·笔记本电脑
egekm_sefg8 小时前
华为、华三交换机纯Web下如何创关键VLANIF、操作STP参数
网络·华为
岳不谢1 天前
华为DHCP高级配置学习笔记
网络·笔记·网络协议·学习·华为
爱笑的眼睛111 天前
uniapp 极速上手鸿蒙开发
华为·uni-app·harmonyos
K.P1 天前
鸿蒙元服务从0到上架【第三篇】(第二招有捷径)
华为·harmonyos·鸿蒙系统
K.P1 天前
鸿蒙元服务从0到上架【第二篇】
华为·harmonyos·鸿蒙系统
敲代码的小强1 天前
Flutter项目兼容鸿蒙Next系统
flutter·华为·harmonyos
程序猿会指北1 天前
纯血鸿蒙APP实战开发——Text实现部分文本高亮和超链接样式
移动开发·harmonyos·arkts·openharmony·arkui·组件化·鸿蒙开发
鸿蒙自习室1 天前
鸿蒙开发——关系型数据库的基本使用与跨设备同步
前端·数据库·华为·harmonyos·鸿蒙