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

预览器上下两栏白边

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

白边原因

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

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

  • 如下图所示

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

沉浸式效果介绍

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

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

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

  • 以下假设状态栏白色

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

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

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

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

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

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

  • 设置窗体背景色实现

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

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

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

    • 效果如下

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

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

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

使用setWindowLayoutFullScreen实现沉浸式

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

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

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

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

  • 因此,我们使用这个方法实现沉浸式时,一般还要做让页面根容器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代表下面导航栏沉浸式,此时效果如下

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

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

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

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

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

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

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

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

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

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

HarmonyOS鸿蒙开发 - 解决上下两栏白边 - 沉浸式效果的更多相关文章

  1. Android 开发学习进程0.11 pageview relativelayout 沉浸式标题栏

    fragment与pageView fragment fragment不可以侧滑切换相关界面,但多数代码位于fragment中,易于维护,同时不会受到多个手势滑动的影响 pageView pageVi ...

  2. 鸿蒙HarmonyOS应用开发落地实践,Harmony Go 技术沙龙落地北京

    12月26日,华为消费者BG软件部开源中心与51CTO Harmony OS技术社区携手,共同主办了主题为"Harmony OS 应用开发落地实践"的 Harmony Go 技术沙 ...

  3. 【资源下载】Linux下的Hi3861一站式鸿蒙开发烧录(附工具)

    下载附件 2021春节前夕,华为发布了 HUAWEI DevEco Device Tool 2.0 Beta1,整体提供了异常强大的功能.得知消息后,我在第一时间带着无比兴奋的心情下载尝鲜,但结果却是 ...

  4. 它来了,它来了,HarmonyOS应用开发在线体验来了

    接下来是我们的两分钟科普,一分钟玩转HarmonyOS应用开发在线体验,一分钟简单了解"一次开发.多设备部署"的原理.萌新的开发者也能第一时间掌握,往下看吧~ 一分钟玩转Harmo ...

  5. css布局 - 工作中常见的两栏布局案例及分析

    突然想到要整理这么一篇平时工作中相当常见但是我们又很忽视的布局的多种处理方法.临时就在我经常浏览的网站上抓的相对应的截图.(以后看到其他类型的我再补充) 既然截了图,咱们就直接看人家使用的布局方式,毕 ...

  6. 鸿蒙开发板外设控制 之 实现按键“按下事件”和“释放事件”的通用框架(V0.0.1)

    在帖子 <鸿蒙开发板外设控制>直播图文版(2020.10.28) 中我们提到过:"开发板上的按键也可以看作一种 GPIO 外设." 因此,要捕捉按键的状态(按下或释放) ...

  7. 用鸿蒙开发AI应用(七)触摸屏控制LED

    [小年答谢,新春送礼]免费抽取1000元京东卡+更多新春好礼~查看详情>>> 目录:前言背景知识编译用户程序框架子系统基于AbilityKit开发的Ability总结 前言上一篇,我 ...

  8. 用鸿蒙开发AI应用(八)JS框架访问内核层

    目录:前言JS应用开发框架原理内置模块实现ace模块开发界面程序 前言上回说到,用C++来写UI界面的开发效率不如JS+HTML来的高,但设备开发又免不了要通过内核态来操作硬件,这里我们就要先打通从J ...

  9. CozyRSS开发记录3-标题栏再加强

    CozyRSS开发记录3-标题栏再加强 1.更精炼的标题栏 接下来,我们把窗口的边框和默认的标题栏给去掉,让Cozy看起来更像一个平板应用. 在主窗口的属性里,修改下列两个属性: 效果一目了然: 2. ...

  10. CSS 实现:两栏布局(等宽布局)

    ☊[实现要求]:两栏等宽布局 <div class="demo3"> <div class="col-1"></div> & ...

随机推荐

  1. Vue3——SVG 图标配置

    1. SVG 图标配置 安装 SVG 依赖插件 vite-plugin-svg-icons npm i vite-plugin-svg-icons -D npm install fast-glob - ...

  2. docker 安装 elasticsearch 集群

    此处部署为单个服务器启动三个elasticsearch容器 问题:本打算在三个服务器上单独部署elasticsearch 容器,elasticsearch.yml 注册用的宿主机ip,但是容器之间通信 ...

  3. 《WebGL 编程指南》读书笔记(2、3章)

    完整 demo 和 lib 文件可以在 https://github.com/tengge1/webgl-guide-code 中找到. 第 2 章 第一个 WebGL 程序 function mai ...

  4. Windows 中的硬链接、目录联接(软链接)、符号链接、快捷方式

    在Linux文件系统中经常提及硬链接(Hard Link)和符号链接(Symbolic Link),Windows中也可以创建链接,但由于丰富的图形界面操作,很少提及链接.Windows 的 NTFS ...

  5. 图片的穿透效果 -- pointer-events: none

    使用场景:当我们需要选择上传文件的时候,图片把input输入框覆盖在上面,点击的时候不能出发input输入框所以要给图片设置穿透属性 : 具体代码: #image { position: fixed; ...

  6. 两小时学会使用dubbo(直接API、spring、注解、springboot)

    最近上新的项目中需要用到dubbo,于是我决定温故知新,决定分享一下Dubbo在各种环境下的使用方式,本篇文章让你两小时就能学会使用dubbo 什么是Dubbo Dubbo是一个分布式.高性能.透明化 ...

  7. WPF中为Popup和ToolTip使用WindowMaterial特效 win10/win11

    先看效果图: 大致思路是:通过反射获取Popup内部的原生窗口句柄,然后通过前文已经实现的WindowMaterial类来应用窗口特效:对于ToolTip,为了保持其易用性,我使用了附加属性+全局样式 ...

  8. 云原生周刊:Kubernetes 十周年 | 2024.6.11

    开源项目推荐 Kubernetes Goat Kubernetes Goat 是一个故意设计成有漏洞的 Kubernetes 集群环境,旨在通过交互式实践场地来学习并练习 Kubernetes 安全性 ...

  9. KubeSphere 社区双周报 | 功能亮点抢“鲜”看 | 2022-09-16

    KubeSphere 从诞生的第一天起便秉持着开源.开放的理念,并且以社区的方式成长,如今 KubeSphere 已经成为全球最受欢迎的开源容器平台之一.这些都离不开社区小伙伴的共同努力,你们为 Ku ...

  10. 《使用Gin框架构建分布式应用》阅读笔记:p251-p271

    <用Gin框架构建分布式应用>学习第14天,p251-p271总结,总21页. 一.技术总结 1.Docker & Docker Compose version: "3. ...