第5篇:HarmonyOS 鸿蒙Next实现沉浸式效果(鸿蒙应用0-1开发)

发布于 1周前 作者 vueper 来自 鸿蒙OS

第5篇:HarmonyOS 鸿蒙Next实现沉浸式效果(鸿蒙应用0-1开发)
<markdown _ngcontent-ioa-c237="" class="markdownPreContainer">

第5篇:实现沉浸式效果

上一篇我们实现拦截系统返回的弹窗时,遗留了个问题,那就是顶部状态栏和底部导航栏并没有覆盖到。而这一节通过设置全屏来规避这个问题,而正巧的是实现沉浸式也是需要全屏处理。首先看看效果:

沉浸式+拦截系统返回的弹窗

1.实现的关键流程

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>
  1. 因此,我们采用第二种方式:设置窗口为全屏布局,配合设置导航栏、状态栏的透明度、背景/文字颜色及高亮图标等属性,与主窗口显示保持协调一致。
 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.设置状态栏和导航栏的占位

  1. 上面我们通过AppStorage.setOrCreate把高度保存起来,现在我们在MainPageHomePage页面取出来:
[@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>
  1. 取出来后,我们根据高度设置占坑高度,如在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(() =&gt; {
      <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: () =&gt; {
          <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.自定义沉浸式头部组件

  1. 根据以往的操作,新建module:common/component,接着在entry和login模块进行导入。

  2. 新建组件: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(() =&gt; {
          <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>

  1. 在各个页面使用,如登录页完整代码:
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(() =&gt; {
        <span class="hljs-keyword"><span class="hljs-keyword">const</span></span> record: Record&lt;<span class="hljs-built_in"><span class="hljs-built_in">string</span></span>, Object&gt; = {
          <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 =&gt; {
  <span class="hljs-keyword"><span class="hljs-keyword">const</span></span> record = cxt.pathInfo.param as Record&lt;<span class="hljs-built_in"><span class="hljs-built_in">string</span></span>, object&gt;;
  <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>
1 回复

关于HarmonyOS 鸿蒙Next实现沉浸式效果的问题,以下是一些专业解答:

在HarmonyOS中,实现沉浸式效果主要需调整状态栏、应用界面和导航条的显示效果。具体步骤如下:

  1. 设置窗口全屏布局:通过调用setWindowLayoutFullScreen()方法,将布局系统调整为全屏布局,使界面元素延伸到状态栏和导航条区域。
  2. 获取并避让布局遮挡区域:使用getWindowAvoidArea()方法获取状态栏和导航条的高度,然后对界面元素进行避让处理,避免被遮挡。
  3. 设置系统栏属性:可自定义状态栏和导航栏的背景颜色、文字颜色以及图标的高亮状态,以与界面元素相匹配,实现沉浸式效果。

请注意,实现沉浸式效果时,需确保界面元素不会覆盖到可交互区域,如导航条底部区域,以保持良好的用户体验。

HarmonyOS 鸿蒙Next视频详解:https://www.itying.com/category-93-b0.html

回到顶部