HarmonyOS鸿蒙开发 - 解决上下两栏白边 - 沉浸式效果

简介: 解决上下两栏白边

预览器上下两栏白边

  • 自从HarmonyOS升级到release版后,很多同学会问猫林老师:为什么他的预览器上下有白边,为什么明明根容器写了宽高百分百但没铺满。如下图

    image-20241210141135933

白边原因

  • 其实上面的白边,称之为状态栏。上面会放手机wifi信号、电池电量等信息。一般情况下我们不需要把应用中具有交互效果的界面延伸到上面去,免得影响操作。

  • 同样,下面的白边称之为导航栏,也即切换手机内应用的地方。会有一个小横条方便你切换不同应用以及回到桌面。

  • 如下图所示

    image-20241210142736603

  • 而HarmonyOS升级到release版本后,特意在预览器里把这上下两栏给你留白空出来,就是为了方便让开发者知道,自己的界面并没占用这两个区域,所以一般情况下,如果你的应用整体背景颜色就是白色的,其实是无需处理的。

沉浸式效果介绍

  • 根据上面说的白边情况,如果你的app背景色正好也是白色,那么可以和上下白边融为一体,显得不那么突兀。但如果你的app是别的颜色,那么可能会有明显的突兀感。

  • 举个例子:大家经常用的美团。我们看看它目前的情况,以及假设有白边的情况

    image-20241210143740406
    这是美团正常情况,会看到顶部是黄色,状态栏也变为黄色,视觉效果上浑然一体

  • 以下假设状态栏白色

    image-20241210144614483

    可以看到视觉效果上会比较突兀

  • 通过对比我们发现,确实在实际app开发过程中,状态栏上可以不放任何界面元素,但是需要将状态栏的颜色定义的与app背景色保持一致,才会视觉上显得更好看,更融为一体。像这样的效果,我们称之为沉浸式效果

  • 通过上面的描述我们已经发现沉浸式效果能提供比较好的视觉效果,但如何实现呢?

  • 有三种方案都可以实现:
    • 通过设置Window背景色来实现
    • 通过调用窗口强制全屏布局接口setWindowLayoutFullScreen() + padding避让实现 (麻烦)
    • 直接使用扩展到避让区功能

通过设置Window背景色实现沉浸式

  • 设置窗体背景色实现

    • 先看不设置的情况下,我们写的一个宽高百分百,且背景颜色为红色的界面,如下图,可以看到状态栏和整体背景色不一致,有明显突兀感

      image-20241210150551860

    • 此时,我们可以设置窗体全局背景色也为红色实现视觉沉浸,来到EntryAbility.ets,找到onWindowStageCreate生命周期函数,在windowStage.loadContent回调里设置如下代码即可

      windowStage.getMainWindowSync().setWindowBackgroundColor('#ff0000')
      
    • 注意:这里只能给16进制颜色,且必须满6位

    • 效果如下

      image-20241210151024558

      • 此时浑然一体
    • 这种方法虽然简单,但有缺点:

      1. 预览器依然会有白边,只有模拟器或真机运行才能看到效果

      2. 它写死了颜色,每个App里不管是哪个页面都是此颜色,假如你App里多个页面的主题颜色不一样,会导致非常突兀,如下图

      image-20241210151943780

使用setWindowLayoutFullScreen实现沉浸式

  • 这是Window提供的一个方法,可以设置让App整屏(即覆盖状态栏与导航栏)实现整块屏幕都可以布局,但是大部分使用时必须配合避让偏移,否则会有问题。至于什么问题呢,我们往下看。

  • 首先来到EntryAbility.ets,继续找到onWindowStageCreate生命周期函数,在windowStage.loadContent回调里设置如下代码即可

    windowStage.getMainWindow().then(w => {
         
        // 设置占用全屏
        w.setWindowLayoutFullScreen(true)
    })
    
  • 这样虽然实现了沉浸式效果,但也存在了问题,例如,我们第一页中本来有Button,但是此时Button位置跑到原来的状态栏去了,如下图

    image-20241210152535258

  • 这样的话,会导致原本不该布局的区域也会存在我们的布局元素。第一巨丑,第二用户也点击不了。

  • 因此,我们使用这个方法实现沉浸式时,一般还要做让页面根容器padding避让。也即让我们布局的组件,通过padding的方式挪动他们位置,避让原本的状态栏和导航栏。

  • 例:

    Column() {
         
        Button('去下一页')
          .onClick(() => {
         
            router.pushUrl({
         
              url: 'pages/Second'
            })
          })
      }
      .width('100%')
      .height('100%')
      .backgroundColor(Color.Red)
      .padding({
          top: 50, bottom: 50 })
    
  • 此时,我们发现写的按钮确实不会被刘海屏挡住了。但是细心的同学发现了,我们这里写死的50vp。不合理,有可能给少了,也有可能给多了。毕竟不同设备的状态栏可能不一样。所以如果我们使用这种方案还需要获取屏幕的状态栏与导航栏的高度。然后把高度存到本地存储里,方便所有页面都可以使用并设置padding

  • 具体步骤:继续来到onWindowStageCreate,填写如下代码

    onWindowStageCreate(windowStage: window.WindowStage): void {
         
        // Main window is created, set main page for this ability
        hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    
        windowStage.loadContent('pages/Index', (err) => {
         
          // windowStage.getMainWindowSync().setWindowBackgroundColor('#ff0000')
          if (err.code) {
         
            hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
            return;
          }
    
          // 以下是设置沉浸式,以及获取设备导航条、状态栏高度的代码
          windowStage.getMainWindow().then(w => {
         
            // 设置沉浸式 
            w.setWindowLayoutFullScreen(true)
            // 获取设备区域参数
            let avoidArea = w.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM)
            let bottomRectHeight = avoidArea.bottomRect.height; // 获取到导航条区域的高度
            AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight); // 存到本地存储
            let topRectHeight = avoidArea.topRect.height; // 获取状态栏区域高度
            AppStorage.setOrCreate('topRectHeight', topRectHeight); // 存到本地存储
    
            // 当区域发生改变(例如竖屏变横屏),重新获取一次再保存
            w.on('avoidAreaChange', (data) => {
         
              if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {
         
                let topRectHeight = data.area.topRect.height;
                AppStorage.setOrCreate('topRectHeight', topRectHeight);
              } else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
         
                let bottomRectHeight = data.area.bottomRect.height;
                AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
              }
            });
          })
          hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
        });
      }
    
  • 好了,上面每句代码都有注释,可以根据注释去理解。当然咯,我知道你们没兴趣看代码,所以需要用可以直接复制,反正这个代码是固定的

  • 然后来到页面里,先取出本地存储的值,且用@StorageProp装饰器,设置状态自动更新。

      @StorageProp('bottomRectHeight')
      bottomRectHeight: number = 0;
      @StorageProp('topRectHeight')
      topRectHeight: number = 0;
    
  • 然后把这两个变量,设置给根容器的padding即可

      ('bottomRectHeight')
      bottomRectHeight: number = 0;
      ('topRectHeight')
      topRectHeight: number = 0;
    
      build() {
         
        Column() {
         
          Button('去下一页')
            .onClick(() => {
         
              router.pushUrl({
         
                url: 'pages/Second'
              })
            })
        }
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Red)
        .padding({
          top: px2vp(this.topRectHeight), bottom: px2vp(this.bottomRectHeight) })
    
  • 同样的,其他页面也如此设置即可

  • 这样,我们通过一系列操作。以及一段代码实现了沉浸式效果。但大家也发现明显的缺点

    1. 预览器依然没效果,需要真机或模拟器查看

    2. 代码过多。好多没耐心的同学看到这,可能都已经烦躁的想打人了

    3. 这样子会让所有页面都被迫使用沉浸式,如果哪个页面不需要沉浸式,还需要再此页面的about里禁用

      aboutToAppear(): void {
             
          window.getLastWindow(getContext())
            .then(win => {
             
              win.setWindowLayoutFullScreen(false)
            })
      }
      
  • 因此,我们还有最为简单的一种方式,请往下继续看

使用expandSafeArea设置沉浸式(推荐)

  • expandSafeArea是一个按需方式的沉浸式方案,它能完美起到哪个页面需要沉浸式,就在哪个页面使用即可,绝对不会让整个App每个页面都强制沉浸式。而且使用起来非常简单,只需要在需要沉浸式的页面的根容器里设置即可,例

    Column() {
         
          Button('去下一页')
            .onClick(() => {
         
              router.pushUrl({
         
                url: 'pages/Second'
              })
            })
        }
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Red)
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    
  • 解释:参数1固定,参数2是设置需要沉浸式的区域,SafeAreaEdge.TOP代表上面状态栏沉浸式,SafeAreaEdge.BOTTOM代表下面导航栏沉浸式,此时效果如下

    image-20241210161011646

    • 没错,此时不需要启动模拟器,预览器也可以直接看到效果!
  • 当然,你也可以只设置让顶部沉浸式,则第二个参数只要写一个TOP即可,如下代码

    Column() {
         
          // 生略里面代码
      }
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Red)
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
    
  • 效果如下

    image-20241210161130556

扩展思考:setWindowLayoutFullScreen是否很鸡肋?

  • 同学们到目前为止,发现实现沉浸式我们用了三种方案。其中第一种设置Window背景色可以无视掉,这种方法我们基本不会用。而第二种方法能实现,但又比较麻烦,第三种是最容易也最推荐的方式。
  • 但此时请我们思考下:setWindowLayoutFullScreen是否真的一点应用场景都没有?

  • 要想回答这个问题,我们可以从setWindowLayoutFullScreen的特点入手,大家还记得吗?setWindowLayoutFullScreen最大的特点是让app所有页面都强制全屏(沉浸式),那么大家仔细想想,有没有哪种App是需要任意页面都强制全屏的呢?

    • 没错,答案是游戏!如下图

    点击放大

  • 像这样的,如果以后是游戏类App,我们必然需要使用setWindowLayoutFullScreen一次性设置所有页面全屏

  • 因此,这个方法,大家也需要有点印象哦!万一哪天要用到呢?

  • 请思考:还有没有除了游戏以外也可能要用到setWindowLayoutFullScreen的场景呢?把你的想法可以打在评论区

相关文章
|
5天前
|
调度 云计算 芯片
云超算技术跃进,阿里云牵头制定我国首个云超算国家标准
近日,由阿里云联合中国电子技术标准化研究院主导制定的首个云超算国家标准已完成报批,不久后将正式批准发布。标准规定了云超算服务涉及的云计算基础资源、资源管理、运行和调度等方面的技术要求,为云超算服务产品的设计、实现、应用和选型提供指导,为云超算在HPC应用和用户的大范围采用奠定了基础。
179570 18
|
12天前
|
存储 运维 安全
云上金融量化策略回测方案与最佳实践
2024年11月29日,阿里云在上海举办金融量化策略回测Workshop,汇聚多位行业专家,围绕量化投资的最佳实践、数据隐私安全、量化策略回测方案等议题进行深入探讨。活动特别设计了动手实践环节,帮助参会者亲身体验阿里云产品功能,涵盖EHPC量化回测和Argo Workflows量化回测两大主题,旨在提升量化投研效率与安全性。
云上金融量化策略回测方案与最佳实践
|
14天前
|
人工智能 自然语言处理 前端开发
从0开始打造一款APP:前端+搭建本机服务,定制暖冬卫衣先到先得
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。
9197 23
|
18天前
|
Cloud Native Apache 流计算
资料合集|Flink Forward Asia 2024 上海站
Apache Flink 年度技术盛会聚焦“回顾过去,展望未来”,涵盖流式湖仓、流批一体、Data+AI 等八大核心议题,近百家厂商参与,深入探讨前沿技术发展。小松鼠为大家整理了 FFA 2024 演讲 PPT ,可在线阅读和下载。
4885 12
资料合集|Flink Forward Asia 2024 上海站
|
18天前
|
自然语言处理 数据可视化 API
Qwen系列模型+GraphRAG/LightRAG/Kotaemon从0开始构建中医方剂大模型知识图谱问答
本文详细记录了作者在短时间内尝试构建中医药知识图谱的过程,涵盖了GraphRAG、LightRAG和Kotaemon三种图RAG架构的对比与应用。通过实际操作,作者不仅展示了如何利用这些工具构建知识图谱,还指出了每种工具的优势和局限性。尽管初步构建的知识图谱在数据处理、实体识别和关系抽取等方面存在不足,但为后续的优化和改进提供了宝贵的经验和方向。此外,文章强调了知识图谱构建不仅仅是技术问题,还需要深入整合领域知识和满足用户需求,体现了跨学科合作的重要性。
|
26天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
14天前
|
人工智能 容器
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
本文介绍了如何利用千问开发一款情侣刮刮乐小游戏,通过三步简单指令实现从单个功能到整体框架,再到多端优化的过程,旨在为生活增添乐趣,促进情感交流。在线体验地址已提供,鼓励读者动手尝试,探索编程与AI结合的无限可能。
三句话开发一个刮刮乐小游戏!暖ta一整个冬天!
|
13天前
|
消息中间件 人工智能 运维
12月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
1025 68