鸿蒙纪·梦始卷#03 | 沉浸状态栏与资源使用

915 阅读6分钟

《鸿蒙纪元》张风捷特烈 计划打造的一套 HarmonyOS 开发系列教程合集。致力于创作优质的鸿蒙原生学习资源,帮助开发者进入纯血鸿蒙的开发之中。本系列的所有代码将开源在 HarmonyUnit 项目中:

github: github.com/toly1994328…
gitee: gitee.com/toly1994328…


本文是《鸿蒙纪·梦始卷》 的第三章,上一篇我们搭建了一个简单的功能应用,本篇将基于这个小案例,继续优化界面表现。本文你将学到:

  • [1]. 学会资源文件的使用,包括图片、字符串、颜色、数字等。
  • [2]. 学会沉浸状态导航栏,实现全屏布局的方案。
  • [3]. 了解鸿蒙开发时文字、视图尺寸单位,统一配置常量资源。

一、资源文件的使用

资源文件在 entry/src/main/resources 文件夹下:

官方文档中对资源目录结构有详细的介绍,目前我们主要使用 basemedia 文件夹中放置媒体数据,比如图片、音频;在 element 文件夹中放置字符串、颜色等常量资源。


1. 图片文件的使用

ArkUI 中通过 Image 组件展示图片,它可以支持常规的图片格式,比如 png、jpg、webp、gif 等,也可以直接支持 svg 的图像资源:。如下所示,将 logo.webpmore.svg 放到 media 文件夹下:

现在,我们的目标是将这两个图片资源放置在计数器 AppBar 的两侧,如下所示:


上一篇中,我们通过插槽的方式,将 AppBar 左右组件交由外部传入,想要更改左侧展示内容,只需要在 Index 组件的 leading 构建方法中修改即可:
这里通过 Button 组件占据 36*36 的尺寸,内部放置 Image 组件,尺寸是 30*30;media 中的图片资源通过 $r('app.media.名称') 来访问:

---->[Index.ets#Index]----
@Builder
leading() {
  Button(){
    Image($r('app.media.logo')).width(30).height(30)
  }
  .width(36).height(36)
  .backgroundColor(Color.Transparent)
}

同理,AppBar 尾部的组件可以通过修改 tailing 组件进行构建;另外 svg 图片可以通过 fillColor 修改颜色值,这相比于普通图片更加灵活:

@Builder
tailing() {
  Button(){
    Image($r('app.media.more'))
      .width(24).height(24)
      .fillColor(Color.White)
  }
  .width(36).height(36)
  .backgroundColor(Color.Transparent)
}

2.常量数值资源

对于一些全局的常量资源,我们可以统一配置在资源中,方便统一维护,比如字体大小、颜色、字符串等。防止在 element 文件夹下,其中:

  • string.json: 放置字符串资源
  • color.json: 放置颜色资源
  • float.json: 放置数值资源

比如计数器中,按钮颜色、AppBAr 颜色都是一样的,我们想要定义一个主题色,方便统一维护。可以在 color.json 中增加一个 theme_color 的元素,指定对应的值, 在代码中通过 $r 访问资源设置即可

{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    },
    {
      "name": "theme_color",
      "value": "#317bd4"
    }
  ]
}

这样,color 中的 theme_color 修改后,就可以让所有使用到它的地方发生变化:

#ff5a5f#ff5a5f

3. 了解像素单位

对于需要统一维护尺寸、字符串,字符串和数值的资源也是同理。比如文字大小、边距间隔值、应用名称等。这里简单介绍一下像素相关的单位。如下四个分别是使用 18px18vp18数字18fp 设置文字大小的效果:

我们知道屏幕是由一个个像素点构成的, px 单位就是绝对的像素数字。
fp 表示 虚拟像素 (virtual pixel) 是一台设备针对应用而言所具有的虚拟尺寸, 它可在任何屏幕上缩放以具有统一的尺寸体量。从二三两行可以看出,在代码中,数值本身就是虚拟像素值。

fp 表示 字体像素 (font pixel) 默认情况下与 vp 相同,即默认情况下 1 fp = 1vp。所以三四行表现效果相同。但如果用户在设置中选择了更大的字体,字体的实际显示大小就会在 vp 的基础上乘以 scale 系数,即 1 fp = 1 vp * scale。
所以对于文字来说,一般取用 fp 单位,其他尺寸可以直接使用数值。

Text('鸿蒙纪元 HarmonyUnit: 18px').fontSize('18px')
Text('鸿蒙纪元 HarmonyUnit: 18vp').fontSize('18vp')
Text('鸿蒙纪元 HarmonyUnit: 18').fontSize(18)
Text('鸿蒙纪元 HarmonyUnit: 18fp').fontSize('18fp')

通过 $r('app.float.xxx') 可以访问资源中提供的尺寸值,如下所示为计数器的描述和数值,设置文字大小:

{
  "float": [
    {
      "name": "counter_value_size",
      "value": "36fp"
    },
    {
      "name": "counter_desc_size",
      "value": "18fp"
    },
    {
      "name": "app_bar_title_size",
      "value": "20fp"
    }
  ]
}

对于这种数值资源,IDE 也会自动展示对应的数值,也保证一定的代码可读性:


4. 国际化资源配置

一般应用程序中展示的资源字符串需要适应系统中不同的语言,鸿蒙开发中提供了资源字符串国际化的方案。在项目中有对应语言的文件夹,在其中的 string.json 中定义映射关系,就可以支持国际化。

---->[src/main/resources/base/string.json]----
{
  "name": "counter_title",
  "value": "计数器"
},
{
  "name": "counter_tips",
  "value": "下面是你点击按钮的次数:"
}

---->[src/main/resources/en_US/element/string.json]----
{
  "name": "counter_title",
  "value": "Counter"
},
{
  "name": "counter_tips",
  "value": "You have pushed the button this many times:"
}

---->[src/main/resources/zh_CN/element/string.json]----
{
  "name": "counter_title",
  "value": "计数器"
},
{
  "name": "counter_tips",
  "value": "下面是你点击按钮的次数:"
}

如下所示,如果希望在 系统语言 切换时,应用程序可以展示不同的字符串资源。只要在不同语言的文件夹中提供字符串映射即可,如果当前系统语言没有支持,会使用 base 中的资源。通过 $r('app.string.xxx') 访问对应的字符串:

注:资源对象类型是 Resource,可以将 AppBar 组件中 title 属性类型设置为 string | Resource ,表示该属性既可以设置为 string 也可以设置为 Resource 对象:

@Component
struct AppBar {
  private title: string | Resource = '';
系统中文系统系统英文语言

这里提交一个小里程碑 计数器-v4-资源使用


二、沉浸状态栏与全屏展示

上面的截图可以看出,应用会有顶部的标题栏底部导航栏,导致应用并没有全屏展示,下面就来看一下如何处理。参考官方文档 《开发应用沉浸式效果》


1. 窗口全屏处理

首先我们需要在窗口启动时,将窗口设置为全屏。这个操作需要在 entry/src/main/ets/entryability/EntryAbility.ets 文件中处理。 onWindowStageCreate 回调可以监听到窗口创建的时机:

---->[ets/entryability/EntryAbility.ets]----
let windowClass: window.Window = windowStage.getMainWindowSync(); // 获取应用主窗口

// 1. 设置窗口全屏
let isLayoutFullScreen = true;
windowClass.setWindowLayoutFullScreen(isLayoutFullScreen).then(() => {
  console.info('Succeeded in setting the window layout to full-screen mode.');
}).catch((err: BusinessError) => {
  console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
});

如果只是设置为全屏,那么顶部和底部的界面元素将会出现重叠的问题(左图)。我们应该获取区域的高度,让界面元素避让状态栏和底部导航栏:

状态栏遮挡期望的效果

2. 获取窗口状态栏和导航栏高度

还是在刚才的 EntryAbility 文件中,在窗口创建时可以获取顶部状态栏和底部导航栏的高度,通过 AppStorage 可以将对象在全局存储起来;另外可以监听 avoidAreaChange 回调,当避让区域发生变化时,也可以及时更新:

---->[ets/entryability/EntryAbility.ets]----
// 2. 获取布局避让遮挡的区域
let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR; // 以导航条避让为例
let avoidArea = windowClass.getWindowAvoidArea(type);
let bottomRectHeight = avoidArea.bottomRect.height; // 获取到导航条区域的高度
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
type = window.AvoidAreaType.TYPE_SYSTEM; // 以状态栏避让为例
avoidArea = windowClass.getWindowAvoidArea(type);
let topRectHeight = avoidArea.topRect.height; // 获取状态栏区域高度
AppStorage.setOrCreate('topRectHeight', topRectHeight);

// 3. 注册监听函数,动态获取避让区域数据
windowClass.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);
  }
});

3. 避让高度的获取和使用

顶部展示的组件是 AppBar,所以我们可以将避让的逻辑封装在 AppBar 中,如下所示,

  • [1] : 通过 @StorageProp('名称') 可以访问存储在全局的对象。
  • [2]: 然后让 AppBar 的高度增加顶部的避让高度,并且内容距离上方有相应的内边距:
  • [3]: 获取的避让高度是像素值,需要通过 px2pv 转换成虚拟像素。
@Component
struct AppBar {
  private title: string | Resource = '';
  @StorageProp('topRectHeight')
  topRectHeight: number = 0;

 /// 略同...

  build() {
    /// 略同...
    .height(56+ px2vp(this.topRectHeight))
    .padding({ left: 8, right: 8,top: px2vp(this.topRectHeight) })
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

同理,底部区域的避让,可以让整体距离底部指定高度:

06

这里提交一个小里程碑 计数器-v5-沉浸标题栏。大家可以把这篇文章的知识点通过树形结构记录一下,下一篇开始时我会给出我的 ~


尾声

到这里,我们就了解了资源使用的方式,以及如何处理沉浸标题栏。后续将继续鸿蒙纪元的进程,通过一些有趣的小功能,体验和入门鸿蒙的开发,我们下次再见~

更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。