【鸿蒙应用ArkTS开发系列】- 沉浸式状态栏实现

文章目录

  • 一、前言
  • 二、封装沉浸式状态栏管理类
    • 1、创建Demo工程
    • 2、封装状态栏管理类
  • 三、编写页面实现沉浸式状态栏效果
    • 1、存储windowStage实例
    • 2、Page页面中实现沉浸式开启关闭功能
      • 2.1、开启沉浸式状态栏
      • 2.2、设置标题栏偏移量

一、前言

在应用开发中,页面跟状态栏的默认显示效果一般是如下:
在这里插入图片描述
但是产品UI设计的时候,一般是会设计一个沉浸式状态的页面效果,如下:
在这里插入图片描述
那在鸿蒙应用开发中,应该怎么实现这个沉浸式状态栏的效果呢?下面我们来创建一个Demo工程进行讲解。

二、封装沉浸式状态栏管理类

1、创建Demo工程

首先我们创建一个Demo工程,在ets目录下创建common文件夹。
在这里插入图片描述

2、封装状态栏管理类

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

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;
  }
}

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

  • get- 获取管理类单例实例
  • storeWindowStage- 存储windowStage实例
    该方法在UIAbility中进行调用。
  • getWindowStage- 获取windowStage实例
  • setImmersiveStatusBar- 设置沉浸式状态栏
  • hideImmersiveStatusBar- 关闭沉浸式状态栏
  • storeStatusBarHeight- (内部私有方法)获取状态栏高度进行保存
  • getSystemBarOffset- 获取状态栏高度(沉浸式状态栏下需要调整的标题偏移量)
  • isOpenImmersiveStatusBar- 是否开启沉浸式状态栏

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

1、获取应用主窗口

let windowClass = windowStage.getMainWindowSync();

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

2、设置窗口可以全屏绘制

windowClass.setWindowLayoutFullScreen(true)

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

3、设置状态栏可见

windowClass.setWindowSystemBarEnable(['status'])

在设置全屏后,状态栏不可见,我们需要的不是全屏效果,而是状态栏沉浸式效果,因此需要将状态栏设置为可见。
这里入参是一个数组,可以设置状态栏、也可以设置底部导航栏。

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、存储状态栏高度

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

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

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

三、编写页面实现沉浸式状态栏效果

1、存储windowStage实例

具体如下:

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    StatusBarManager.get().storeWindowStage(windowStage);

    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }

在EntryAbility.ts 文件 onWindowStageCreate方法中添加如下代码:

StatusBarManager.get().storeWindowStage(windowStage);

我们这个Demo效果,是在单个Page中开启关闭沉浸式效果,因此我们需要在UIAbility中存储windowStage实例。

实际应用使用中,如果需要所有Page都开启沉浸式状态效果,则可以将上述代码调整为

StatusBarManager.get().setImmersiveStatusBar(windowStage);

2、Page页面中实现沉浸式开启关闭功能

2.1、开启沉浸式状态栏

我们拷贝如下代码到Index.ets 文件中:

import { StatusBarManager } from '../common/StatusBarManager';

@Entry
@Component
struct Index {
  @State showImmersiveStatusBar: boolean = false;

  build() {
    Column() {

      Column() {
        Column() {
          Text('这是标题')
            .fontSize(20)
            .fontColor(Color.White)
        }
        .height(50)
        .justifyContent(FlexAlign.Center)
      }
      .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);
    }
  }

  private close() {
    let windowStage = StatusBarManager.get().getWindowStage();
    if (windowStage) {
      StatusBarManager.get().hideImmersiveStatusBar(windowStage);
    }
  }
}

我们运行下代码将应用装到手机上看看效果**
(真机配置签名方式前面文章有示例讲解,不懂的同学可以翻下我之前的文章看下)**
具体效果如下:
在这里插入图片描述
我们点击下开启看下效果,
在这里插入图片描述
咋看以下好像没什么问题,但是如果手机是居中挖孔屏,那我们的标题就会显示在摄像头位置上,影响页面布局的正常显示,那这个问题应该怎么处理呢。
大家还记得我们上面封装状态栏管理器的时候,对外提供了一个获取状态栏高度偏移量的方法吗,这个时候我们就需要用到它了。

2.2、设置标题栏偏移量

在这里插入图片描述

我们在布局中,对标题栏设置一个上方的Padding,数值为状态栏高度即可,那这个偏移量怎么获取呢,在什么时候获取呢?我们接着往下看。

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

我们封装setImmersiveStatusBar的时候,是在执行沉浸式状态栏完成后,使用Promise方式返回了结果,表明沉浸式状态栏开启完成,我们只需要使用then关键字,在代码块中调用 getSystemBarOffset获取高度后设置给titleBarPadding即可。
下面贴下修改后的完整代码:

import { StatusBarManager } from '../common/StatusBarManager';

@Entry
@Component
struct Index {
  @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;
      })
    }
  }
}

下面我们重新运行看下效果:
在这里插入图片描述
看起来效果还可以,开启沉浸式状态栏后,页面的UI展示效果明显提到了一个档次哈哈。

本文到此完毕,如果有什么疑问,欢迎评论区沟通探讨。谢谢大家的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/272505.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java代码审计鉴权漏洞InterceptorFilterShiroJWT(非常详细!!)

目录 0x00 前言 0x01 鉴权方式&审计思路 1、目前主流的鉴权方式 2、鉴权漏洞审计思路 0x02 Interceptor鉴权审计 - NewbeeMall电商系统 1、项目介绍 - NewbeeMall 2、Interceptor 补充介绍 3、NewbeeMall - Interceptor鉴权 - 代码审计 0x03 Filter鉴权审计 - 华…

PostGIS学习教程十五:几何图形的有效性

PostGIS学习教程十五&#xff1a;几何图形的有效性 在90%的情况下&#xff0c;“为什么我的查询给了我一个’TopologyException’错误"的问题的答案是"一个或多个输入的几何图形是无效的”&#xff0c;这就引出了这样一个问题:几何图形"无效"是什么意思&a…

JAVA复习三——CH5 Java Collection 、CH6 MultiThread

CH5 Java Collection(集合) 5.1 Java集合框架&#xff08;位于java.util包中&#xff09; 图一 集合框架图 从上面的集合框架图可以看到&#xff0c;Java 集合框架主要包括两种类型的容器&#xff0c;一种是集合&#xff08;Collection&#xff09;&#xff0c;存储一个元素集…

pytest分布式执行插件 pytest-xdist 的高级用法

想要使用多个CPU核心来进行测试&#xff0c;可以使用 -n 参数( 或者 --numprocesses) (使用8个核心来跑测试用例) 1 pytest -n 8 使用 -n auto 参数可以利用电脑的所有核心来跑测试用例 测试时使用的算法可以根据--dist命令参数定制&#xff1a; --dist load(默认选项)&…

手拉手Springboot整合JWT

环境介绍 技术栈 springbootmybatis-plusmysqljava-jwt 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 Json Web令牌简称JWT Token是在服务端产生的一串字符串是客户端访问资源接口(AP)时所需要的资源凭证。…

【多省市译协盖章】2023年第九届中西部外语翻译大赛

“由中西部翻译协会共同体指导发起&#xff0c;各省市译协共建学术指导委员会&#xff0c;2023年第九届中西部外语翻译大赛由中西部翻译协会共同体秘书处&#xff08;武汉公仪网络科技有限公司&#xff09;承办。” &#xff08;证书样图&#xff09; 证书盖章单位&#xff1…

文件重命名:特殊符号影响你找文件吗?来看看这个解决方法

在日常生活和工作中&#xff0c;电脑已经成为必不可少的工具&#xff0c;而文件管理也是一项重要的任务。有时候遇到文件重命名的问题&#xff0c;例如当文件名中包含特殊符号时&#xff0c;这可能会给工作带来很大的困扰。当尝试寻找一个文件时&#xff0c;却发现因为文件名中…

Linux的账号及权限管理

一.管理用户账号 1.1 用户账户的分类 1.1.1 用户账号的分类 超级用户&#xff1a;&#xff08;拥有至高无上的权利&#xff09; root用户是Linux操作系统中默认的超级用户账号&#xff0c;对本主机拥有最高的权限&#xff0c;系统中超级用户是唯一的。普通用户&#xff1a; …

c++学习笔记(6)-类型转换

1、概念 C类型转换是将一种数据类型转换为另一种数据类型的过程。 2、分类 C中的类型转换可以从3个角度来划分&#xff1a; 根据类型转换是由程序员显式指定&#xff0c;还是由编译器自动完成&#xff0c;分为显式类型转换和隐式类型转换&#xff1b;根据参与类型转换的变量…

智能优化算法应用:基于减法平均算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于减法平均算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于减法平均算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.减法平均算法4.实验参数设定5.算法结果6.…

测试用例要如何写

​ &#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试…

IDEA基本设置

本博客适用于纯新手小白&#xff0c;或者刚下载IDEA想要优化开发添加配置的读者。 基础设置 不区分大小写代码补全 打开 IntelliJ IDEA。转到 “File”&#xff08;文件&#xff09; > “Settings”&#xff08;设置&#xff09;&#xff08;Windows/Linux&#xff09;或 “…

swagger1.2 apiPost工具测试接口没有问题,换成swagger 接口调测时报错 Required request body is missing

把 请求方法由get换成post GetMapping换成 PostMapping 原因apiPost自动把请求json参数封装到请求体里了&#xff0c; 但swagger没有封装&#xff0c;通过networker可以看到载荷里并没有任何东西

(C++)DS哈希查找—二次探测再散列(附思路和详细注释)

Description 定义哈希函数为H(key) key%11。输入表长&#xff08;大于、等于11&#xff09;&#xff0c;输入关键字集合&#xff0c;用二次探测再散列构建哈希表&#xff0c;并查找给定关键字。 Input 测试数据组数 1≤&#xfffd;≤50. 每组测试数据格式如下&#xff1a…

面试题:Zabbix 和 Prometheus 到底怎么选?

文章目录 前言历史简介PrometheusZabbix 架构对比PrometheusZabbix 综合对比总结 前言 新公司要上监控&#xff0c;面试提到了 Prometheus 是公司需要的监控解决方案&#xff0c;我当然是选择跟风了。 之前主要做的是 Zabbix&#xff0c;既然公司需要 Prometheus&#xff0c;…

【如何破坏单例模式(详解)】

✅如何破坏单例模式 &#x1f4a1;典型解析✅拓展知识仓✅反射破坏单例✅反序列化破坏单例✅ObjectlnputStream ✅总结✅如何避免单例被破坏✅ 避免反射破坏单例✅ 避免反序列化破坏单例 &#x1f4a1;典型解析 单例模式主要是通过把一个类的构造方法私有化&#xff0c;来避免重…

鸿蒙系统的分布式技术:重塑智能终端的未来

华为鸿蒙系统自发布以来&#xff0c;凭借其创新的分布式技术&#xff0c;改变了我们对智能终端的认知和使用方式。鸿蒙系统的分布式技术是一种全新的设计理念&#xff0c;它将不同设备、不同应用场景视为一个整体&#xff0c;通过共享、协同和无缝连接&#xff0c;为用户带来前…

android setText不生效问题

1.直接说解决方案&#xff1a; 在代码没问题的情况下&#xff0c;将你的TextView的Id改一下&#xff0c;然后再重启编译器即可(注意&#xff0c;不修改TextView的ID&#xff0c;单独重启是没有作用的&#xff01;) 2.出现问题的过程&#xff1a; 产品新增一个需求&#xff0c…

SpringBoot整合JWT+Spring Security+Redis实现登录拦截(一)登录认证

一、JWT简介 JWT 全称 JSON Web Token&#xff0c;JWT 主要用于用户登录鉴权&#xff0c;当用户登录之后&#xff0c;返回给前端一个Token&#xff0c;之后用户利用Token进行信息交互。 除了JWT认证之外&#xff0c;比较传统的还有Session认证&#xff0c;如何选择可以查看之前…

MAGVIT: Masked Generative Video Transformer

Paper name MAGVIT: Masked Generative Video Transformer Paper Reading Note Paper URL: https://arxiv.org/abs/2212.05199 Project URL: https://magvit.cs.cmu.edu/ Code URL: https://github.com/google-research/magvit TL;DR 2023 年 CMU、google 等发表 CVPR20…