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