第5篇:HarmonyOS 鸿蒙Next实现沉浸式效果(鸿蒙应用0-1开发)
第5篇:HarmonyOS 鸿蒙Next实现沉浸式效果(鸿蒙应用0-1开发)
<markdown _ngcontent-ioa-c237="" class="markdownPreContainer">
第5篇:实现沉浸式效果
上一篇我们实现拦截系统返回的弹窗时,遗留了个问题,那就是顶部状态栏和底部导航栏并没有覆盖到。而这一节通过设置全屏来规避这个问题,而正巧的是实现沉浸式也是需要全屏处理。首先看看效果:
- 第1篇:鸿蒙APP开发怎么样开始?
- 第2篇:如何使用Navigation+tab搭建路由页面?
- 第3篇:手把手教你如何实现对Navigation路由框架的封装!
- 第4篇:对话框是每个项目的基础,那么禁止系统返回的对话框应该如何实现呢?
1.实现的关键流程
1.1.实现全屏
实现全屏其实也有两种方案
- 第一种方式:设置导航栏、状态栏不显示,这种方式会直接把状态栏的内容直接去掉,比如电量、信号、通知消息等等。如下:
let names: Array<'status' | 'navigation'> = [];
windowClass.setWindowSystemBarEnable(names, (err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in setting the system bar to be visible.');
});
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 因此,我们采用第二种方式:设置窗口为全屏布局,配合设置导航栏、状态栏的透明度、背景/文字颜色及高亮图标等属性,与主窗口显示保持协调一致。
let isLayoutFullScreen = true;
windowClass.setWindowLayoutFullScreen(isLayoutFullScreen, (err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in setting the window layout to full-screen mode.');
});
let sysBarProps: window.SystemBarProperties = {
statusBarColor: '#ff00ff',
navigationBarColor: '#00ff00',
// 以下两个属性从API Version 8开始支持
statusBarContentColor: '#ffffff',
navigationBarContentColor: '#ffffff'
};
windowClass.setWindowSystemBarProperties(sysBarProps, (err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('Failed to set the system bar properties. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in setting the system bar properties.');
});
})
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
1.2.获取状态栏和导航栏高度
在EntryView
中:
aboutToAppear(): void {
window.getLastWindow(getContext()).then((lastWindow) => {
let areas = lastWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);//以状态栏为避让
const statusHeight = px2vp(areas.topRect.height);
areas = lastWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);<span class="hljs-comment"><span class="hljs-comment">// 以导航条避让</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">const</span></span> navHeight = px2vp(areas.bottomRect.height);
<span class="hljs-comment"><span class="hljs-comment">//保存到到全局变量</span></span>
AppStorage.setOrCreate(<span class="hljs-string"><span class="hljs-string">'statusHeight'</span></span>, statusHeight);
AppStorage.setOrCreate(<span class="hljs-string"><span class="hljs-string">'navHeight'</span></span>, navHeight);
});
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
1.3.设置状态栏和导航栏的占位
- 上面我们通过
AppStorage.setOrCreate
把高度保存起来,现在我们在MainPage
和HomePage
页面取出来:
[@StorageProp](/user/StorageProp)('statusHeight') statusHeight: number = 0;
[@StorageProp](/user/StorageProp)('navHeight') navHeight: number = 0;
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 取出来后,我们根据高度设置占坑高度,如在
MainPage
中:
build() {
Column() {
Stack() {
if (this.selectIndex === 0) {
HomePage()
} else if (this.selectIndex === 1) {
ToolPage()
} else if (this.selectIndex === 2) {
MyPage()
}
}
.width('100%')
.layoutWeight(1)
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tabBuilder()
<span class="hljs-comment"><span class="hljs-comment">//TODO ---》把底部导航栏的高度露出来</span></span>
Line().height(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.navHeight).visibility(Visibility.Hidden)
}
.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
1.4.通过设置状态栏和标题栏颜色,从而实现沉浸式效果
HomePage
页面的完整代码:
/*
* [@Desc](/user/Desc):
* [@Author](/user/Author): qincji
* [@Date](/user/Date): 2024/6/12
*/
import { RouterNav, RouterPage } from '[@common](/user/common)/router/Index';
import { HintParm } from '[@common](/user/common)/dialog/Index';
import { log } from '[@common](/user/common)/utils';
const TAG = ‘HomePage’;
@Component
export struct HomePage {
@State value: string = ‘’;
@StorageProp(‘statusHeight’) statusHeight: number = 0;
@StorageProp(‘navHeight’) navHeight: number = 0;
aboutToAppear(): void {
log.i(TAG, 获取系统状态栏和导航栏高度: ${this.statusHeight} | ${this.navHeight}
)
}
build() {
Column() {
//沉浸式效果
Column() {
//把顶部状态栏的高度露出来
Line().height(this.statusHeight).visibility(Visibility.Hidden)
Text(“标题”)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(Color.White)
}
.width(‘100%’)
.linearGradient({
angle: 0, // 0点方向顺时针旋转为正向角度,线性渐变起始角度的默认值为180°
colors: [
[’#74C678’, 0.0], // 颜色断点1的颜色和比重,对应组件在180°方向上的起始位置
[’#266B29’, 1.0],// 颜色断点2的颜色和比重,对应组件在180°方向上的终点位置
]
})
.height(this.statusHeight + 50)
Text(<span class="hljs-string"><span class="hljs-string">'这是首页'</span></span>).fontSize(<span class="hljs-number"><span class="hljs-number">35</span></span>).margin({ top: <span class="hljs-number"><span class="hljs-number">100</span></span> })
Text(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.value).fontSize(<span class="hljs-number"><span class="hljs-number">35</span></span>)
Button(<span class="hljs-string"><span class="hljs-string">'对话框'</span></span>)
.onClick(() => {
<span class="hljs-keyword"><span class="hljs-keyword">const</span></span> parm: HintParm = {
content: <span class="hljs-string"><span class="hljs-string">"Command Line Tools集合了HarmonyOS应用开发所用到的系列工具,包括代码检查codelinter、三方库包管理ohpm、命令行解析hstack、编译构建hvigorw。"</span></span>,
showNo: <span class="hljs-keyword"><span class="hljs-keyword">false</span></span>,
outsideCancel: <span class="hljs-keyword"><span class="hljs-keyword">false</span></span>,
pressBackCancel: <span class="hljs-keyword"><span class="hljs-keyword">false</span></span>,
alignment: Alignment.Center,
noTitle: <span class="hljs-keyword"><span class="hljs-keyword">true</span></span>,
okMsg: <span class="hljs-string"><span class="hljs-string">"同意,并继续!"</span></span>,
onOk: () => {
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.value = <span class="hljs-string"><span class="hljs-string">"点击了确定!"</span></span>
}
}
RouterNav.push(RouterPage.HINT_DIALOG, parm)
})
}
.width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
2.自定义沉浸式头部组件
根据以往的操作,新建module:
common/component
,接着在entry和login模块进行导入。新建组件:
Header
,详细代码:
/*
* [@Desc](/user/Desc): 沉浸式状态栏和标题栏的设置
* [@Author](/user/Author): qincji
* [@Date](/user/Date): 2024/6/23
*/
[@Component](/user/Component)
export struct Header {
[@Require](/user/Require) [@Prop](/user/Prop) title: string | Resource; //标题
onKeyBack?: () => void; //监听返回事件,如果不设置则隐藏返回键
[@BuilderParam](/user/BuilderParam) rightLayout?: () => void; //自定义的右边布局
titleBarHeight: Length = 45; //标题栏高度
titleSize: number | string | Resource = '18fp'; //标题字体大小
titleAttrModifier: AttributeModifier<TextAttribute> = {}; //标题控件的样式
bgTopColor: ResourceColor = '#74C678'; //状态栏和标题栏背景 顶部颜色
bgBottomColor: ResourceColor = '#266B29'; //状态栏和标题栏背景 底部颜色
titleColor: ResourceColor = Color.White; //标题字体颜色
[@StorageProp](/user/StorageProp)('statusHeight') statusHeight: number = 0; //状态栏高度
build() {
Stack() {
RelativeContainer() {
Text(this.title)
.fontSize(this.titleSize)
.width(‘50%’)
.height(‘100%’)
.fontColor(this.titleColor)
.fontWeight(FontWeight.Medium)
.ellipsisMode(EllipsisMode.END)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(1)
.textAlign(TextAlign.Center)
.alignRules({
middle: { anchor: “container”, align: HorizontalAlign.Center },
})
.id(“i1”)
.attributeModifier(this.titleAttrModifier)
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.onKeyBack) {
Image($r(<span class="hljs-string"><span class="hljs-string">'app.media.ic_back'</span></span>))
.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.padding({
left: <span class="hljs-number"><span class="hljs-number">16</span></span>,
top: <span class="hljs-number"><span class="hljs-number">11</span></span>,
bottom: <span class="hljs-number"><span class="hljs-number">11</span></span>,
right: <span class="hljs-number"><span class="hljs-number">11</span></span>
})
.fillColor(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.titleColor)
.objectFit(ImageFit.Contain)
.alignRules({
left: { anchor: <span class="hljs-string"><span class="hljs-string">"__container__"</span></span>, align: HorizontalAlign.Start },
})
.id(<span class="hljs-string"><span class="hljs-string">"i2"</span></span>)
.onClick(() => {
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.onKeyBack?.()
})
}
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.rightLayout) {
Row() {
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.rightLayout?.()
}
.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.justifyContent(FlexAlign.End)
.alignRules({
right: { anchor: <span class="hljs-string"><span class="hljs-string">"__container__"</span></span>, align: HorizontalAlign.End },
})
.id(<span class="hljs-string"><span class="hljs-string">"i3"</span></span>)
}
}
.width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.height(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.titleBarHeight)
}
.width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.padding({ top: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.statusHeight })
.backgroundColor(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.bgTopColor)
.linearGradient({
angle: <span class="hljs-number"><span class="hljs-number">0</span></span>, <span class="hljs-comment"><span class="hljs-comment">// 0点方向顺时针旋转为正向角度,线性渐变起始角度的默认值为180°</span></span>
colors: [
[<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.bgTopColor, <span class="hljs-number"><span class="hljs-number">0.0</span></span>], <span class="hljs-comment"><span class="hljs-comment">// 颜色断点1的颜色和比重,对应组件在180°方向上的起始位置</span></span>
[<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.bgBottomColor, <span class="hljs-number"><span class="hljs-number">1.0</span></span>],<span class="hljs-comment"><span class="hljs-comment">// 颜色断点2的颜色和比重,对应组件在180°方向上的终点位置</span></span>
]
})
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 在各个页面使用,如登录页完整代码:
import { RouterNav, RouterPage } from '[@common](/user/common)/router';
import { Header } from '[@common](/user/common)/component';
@Component
export struct LoginPage {
@State result: string = “”;
@State register: string = ‘注册’;
build() {
NavDestination() {
Column() {
Header({
title: ‘登录’,
onKeyBack: () => {
RouterNav.pop()
},
//TODO - 注意:写成 rightLayout: this.rightRegisterBuilder 会调不到本类的属性
rightLayout: () => {
this.rightRegisterBuilder()
}
})
Text(‘这是登录页面, 有NavDestination’).fontSize(35)
Text(接收到的数据:${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.result}
).fontSize(35)
Button(<span class="hljs-string"><span class="hljs-string">'返回--》给点数据'</span></span>)
.onClick(() => {
<span class="hljs-keyword"><span class="hljs-keyword">const</span></span> record: Record<<span class="hljs-built_in"><span class="hljs-built_in">string</span></span>, Object> = {
<span class="hljs-string"><span class="hljs-string">'from'</span></span>: <span class="hljs-string"><span class="hljs-string">'login/login'</span></span>,
<span class="hljs-string"><span class="hljs-string">'text'</span></span>: <span class="hljs-string"><span class="hljs-string">"登录给你的"</span></span>,
<span class="hljs-string"><span class="hljs-string">'age'</span></span>: <span class="hljs-number"><span class="hljs-number">19</span></span>,
}
RouterNav.pop(record)
})
}
.width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
}
.onReady(cxt => {
<span class="hljs-keyword"><span class="hljs-keyword">const</span></span> record = cxt.pathInfo.param as Record<<span class="hljs-built_in"><span class="hljs-built_in">string</span></span>, object>;
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.result = JSON.stringify(record);
})
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(<span class="hljs-keyword"><span class="hljs-keyword">true</span></span>)
}
@Builder
private rightRegisterBuilder() {
Text(this.register).fontSize(13).margin({ right: 16 }).fontColor(Color.White).onClick(() => {
RouterNav.replace(RouterPage.REGISTER)
})
}
}
@Builder
function getPage(_value: object): void {
LoginPage();
}
RouterNav.registerPage(RouterPage.LOGIN, wrapBuilder(getPage))
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
到这里,所有的内容已经结束了!本章的完整源码已经上传到gitee了:鸿蒙应用0-1开发。
</markdown>关于HarmonyOS 鸿蒙Next实现沉浸式效果的问题,以下是一些专业解答:
在HarmonyOS中,实现沉浸式效果主要需调整状态栏、应用界面和导航条的显示效果。具体步骤如下:
- 设置窗口全屏布局:通过调用
setWindowLayoutFullScreen()
方法,将布局系统调整为全屏布局,使界面元素延伸到状态栏和导航条区域。 - 获取并避让布局遮挡区域:使用
getWindowAvoidArea()
方法获取状态栏和导航条的高度,然后对界面元素进行避让处理,避免被遮挡。 - 设置系统栏属性:可自定义状态栏和导航栏的背景颜色、文字颜色以及图标的高亮状态,以与界面元素相匹配,实现沉浸式效果。
请注意,实现沉浸式效果时,需确保界面元素不会覆盖到可交互区域,如导航条底部区域,以保持良好的用户体验。
HarmonyOS 鸿蒙Next视频详解:https://www.itying.com/category-93-b0.html。