查看原文
其他

鸿蒙纪·系列教程#03 | 沉浸状态栏与资源使用

鸿洋
2024-12-13

The following article is from 编程之王 Author 张风捷特烈

系列教程:
鸿蒙纪·系列教程#01 - 环境搭建与项目结构
鸿蒙系列教程#02 | 从计数器认识布局基础



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

github: https://github.com/toly1994328/HarmonyUnit
gitee: https://gitee.com/toly1994328/HarmonyUnit
本文是《鸿蒙纪·梦始卷》 的第三章,上一篇我们搭建了一个简单的功能应用,本篇将基于这个小案例,继续优化界面表现。本文你将学到:
 [1]. 学会资源文件的使用,包括图片、字符串、颜色、数字等。
[2]. 学会沉浸状态导航栏,实现全屏布局的方案。

[3]. 了解鸿蒙开发时文字、视图尺寸单位,统一配置常量资源。

1资源文件的使用

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

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

1. 图片文件的使用
ArkUI 中通过 Image 组件展示图片,它可以支持常规的图片格式,比如 png、jpg、webp、gif 等,也可以直接支持 svg 的图像资源:。如下所示,将 logo.webp 和 more.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. 了解像素单位
对于需要统一维护尺寸、字符串,字符串和数值的资源也是同理。比如文字大小、边距间隔值、应用名称等。这里简单介绍一下像素相关的单位。如下四个分别是使用 18px、18vp、 18数字、18sp 设置文字大小的效果:

我们知道屏幕是由一个个像素点构成的, 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-资源使用[4]。

2沉浸状态类与全屏展示

上面的截图可以看出,应用会有顶部的标题栏和底部导航栏,导致应用并没有全屏展示,下面就来看一下如何处理。参考官方文档 《开发应用沉浸式效果》[5]
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)
  }
}

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

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

3尾声


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

[1] 张风捷特烈: 

https://juejin.cn/user/149189281194766
[2] HarmonyUnit:
[3] 资源目录结构: 

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/resource-categories-and-access-V5#%E8%B5%84%E6%BA%90%E7%9B%AE%E5%BD%95
[4] 计数器-v4-资源使用: 

https://github.com/toly1994328/HarmonyUnit/tree/91b21c46756c7876a0a153eae656fe88c23d9459
[5] 《开发应用沉浸式效果》: 

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-develop-apply-immersive-effects-V5#section202081847174413
[6] 计数器-v5-沉浸标题栏: 

https://github.com/toly1994328/HarmonyUnit/tree/c9174be6182616a1f6b5c3944d63491bf905d1b9
[7] 张风捷特烈: 

https://juejin.cn/user/149189281194766
[8] 张风捷特烈: 

https://space.bilibili.com/390457600




最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!


推荐阅读

Android系统native进程之我是installd进程
安卓应用跳转回流的统一和复用
一文搞懂Window、PhoneWindow、DercorView、WindowManager



扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

继续滑动看下一个
鸿洋
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存