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

鸿蒙应用开发从入门到入行

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

预览器上下两栏白边

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


    image.png

白边原因

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

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

  • 如下图所示


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

沉浸式效果介绍

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

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


    image.png

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

  • 以下假设状态栏白色


    image.png

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

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

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

  • 有三种方案都可以实现:

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

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

  • 设置窗体背景色实现

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


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

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

  • 效果如下


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

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

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


      image.png

使用setWindowLayoutFullScreen实现沉浸式

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

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

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


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

  • 因此,我们使用这个方法实现沉浸式时,一般还要做让页面根容器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即可

      @StorageProp('bottomRectHeight')
      bottomRectHeight: number = 0;
      @StorageProp('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.png
  • 没错,此时不需要启动模拟器,预览器也可以直接看到效果!

  • 当然,你也可以只设置让顶部沉浸式,则第二个参数只要写一个TOP即可,如下代码

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

image.png

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

  • 同学们到目前为止,发现实现沉浸式我们用了三种方案。其中第一种设置Window背景色可以无视掉,这种方法我们基本不会用。而第二种方法能实现,但又比较麻烦,第三种是最容易也最推荐的方式。

  • 但此时请我们思考下:setWindowLayoutFullScreen是否真的一点应用场景都没有?

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

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

    [图片上传失败...(image-149c08-1733965451554)]

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

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容