【HarmonyOS4.0】第四篇-ArkUI基础实战

一、ArkUI框架简介

ArkUI开发框架是方舟开发框架的简称,它是一套构建 HarmonyOS / OpenHarmony 应用界面的声明式UI开发框架,它使用极简的UI信息语法、丰富的UI组件以及实时界面语言工具,帮助开发者提升应用界面开发效率 30%,开发者只需要使用一套 TS / JS API,就能在多个 HarmonyOS / OpenHarmony 设备上提供既丰富又流畅的用户界面体验。

1.1.UI开发框架

什么是UI开发框架?

  • UI:即用户界面,主要包含视觉(比如文字、图像、动画等可以看到的内容)以及交互(比如点击按钮、滑动列表、放缩图片等用户操作)。
  • UI框架:是为软件应用开发者提供开发UI的基础设施,主要包括UI控件(按钮/列表等)、视图布局(摆放/排列相应的UI控件)、动画机制(动画设计以及效果呈现)、交互事件处理(点击/滑动)等,以及相应的编程语言和编程模型等。

UI编程框架提供了开发以及运行UI界面所需要的框架能力,如下图所示:

img

说明:

  • 开发模型:对开发者提供开发范式、UI控件、布局、动效、交互、编程语言等。体现出来的是开发时候的难以程度和开发效率。
  • 运行框架:UI界面渲染及交互的基础能力框架,包括相应的布局引擎、控件机制、动画引擎、事件机制、渲染管线等,并结合语言虚拟机和图形引擎,将开发者的程序运行在具体系统平台上。体现出来的是应用运行的性能体验。
  • 平台适配:承载框架的具体操作系统或平台适配层。

1.2.ArkUI框架的演进

ArkUI开发框架综合考虑了UI渲染、语言和运行效率,围绕着极简开发、高性能、跨设备跨平台进一步演进。下图是ArkUI整体架构的演进:

img

上图中左侧是 2020 年 HarmonyOS 发布的JS UI框架的架构示意图,主要支持类Web的前端开发范式,通过DSL(domain-specific language,领域特定语言)转换层,跨语言对接到声明式UI后端引擎,并结合JS引擎完成整体UI渲染。右侧是ArkUI开发框架,主要有以下几个变化:

  1. 引入了新一代的声明式UI开发范式,实现极简的UI描述语法。
  2. 设计了统一的前后端扁平化渲染机制,进一步提升UI渲染的性能并降低内存消耗。
  3. 深度结合 ArkCompiler 方舟编译器和 ArkRuntime 方舟运行时,提升语言的执行性能和跨语言通信能力。
  4. 在工具方面,针对新一代的声明式UI开发范式构建了新的编译工具链和预览引擎,提供了所见即所得的实时预览机制。

另外,在ArkUI开发框架中,类Web范式会继续保留,即类Web范式和新一代的声明式UI范式都可以支持,可以各自独立使用,但不能混用。

1.3.ArkUI的关键特性

①.极简的UI信息语法

ArkUI开发框架采用基于 TypeScript 扩展的极简的声明式UI描述界面语法,提供了类自然语言的UI描述和组合,开发者只需用几行简单直观的声明式代码,即可完成界面功能。

②.丰富的内置UI组件

ArkUI开发框架内置了丰富而精美的多态组件,可满足大部分应用界面开发的需求,开发者可以轻松地向几乎任何UI控件添加动画并选择一系列框架内置的动画能力,可为用户带来平滑而自然的体验。其中多态是指UI描述是统一的,UI呈现在不同类型设备上会有所不同。比如 Button 组件在手机和手表会有不同的样式和交互方式。

③.多维度的状态管理机制

ArkUI开发框架为开发者提供了跨设备数据绑定功能和多维度的状态管理机制(组件内/组件间/全局/分布式数据驱动UI变更),支持灵活的数据驱动的UI变更,帮助开发者节省70%代码完成跨端界面应用开发。

④.支持多设备开发

ArkUI开发框架除了提供UI开发套件外还围绕着多设备开发提供了多维度的解决方案,进一步简化开发:

  • 基础开发能力:包括基础的分层参数配置(比如色彩、字号、圆角、间距等),栅格系统,原子化布局能力(比如拉伸、折行、隐藏等)。
  • 零部件组件层:包括多态控件,统一交互能力,以及在此基础上的组件组合。
  • 面向典型场景:提供分类的页面组合模板以及示例代码。

⑤.原生性能体验

ArkUI开发框架内置了许多核心的UI控件和动效,如图片、列表、网格、属性动画、转场动画等,加持自研的 ArkCompiler 方舟编译器和 ArkRuntime 方舟运行时深度优化,这些都可以在 HarmonyOS / OpenHarmony 设备上达到移动原生应用一样的性能体验。

⑥.实时预览机制

ArkUI开发框架支持实时界面预览特性可帮助开发快速的所见即所得的开发和调测界面,无需连接真机设备就可以显示应用界面在任何 HarmonyOS / OpenHarmony 设备上的UI效果,预览的关键特性主要包括:

  • 一致性渲染:和目标设备一致的UI呈现效果。
  • 实时性预览:改动相应的代码,实时呈现出相应UI效果。另外,代码能够和UI双向联动,代码改动的同时UI也实时变更,UI改动的同时代码也相应地变更。
  • 多维度预览:支持页面级预览、组件级预览、多设备预览。

1.4.声明式UI开发范式的基本组成

1.4.1.创建项目

创建项目说明

img

1.4.2.基本组成说明

下面我们以一个具体的示例来说明新一代声明式UI开发范式的基本组成。如下图所示的代码示例,UI界面会显示一个 数字 按钮。当用户点击 + 按钮时,每点击一次会加 1

img

上面的案例构成说明如下:

  • 装饰器:用来装饰类、结构体、方法以及变量,赋予其特殊的含义,上面 @Component、@Entry、@State

    都是装饰器。

    • @Component 表示这是个自定义组件;
  • @Entry 则表示这是个页面入口组件,也就是根组件;

    • @State 表示组件中的状态变量,这个状态变化会引起UI变更。
  • 自定义组件:可复用的UI单元,可组合其它组件,如上述被 @Component 装饰的 struct Index 。

  • UI描述:声明式的方式来描述UI的结构,如上述 build() 方法内部的代码块。

  • 内置组件:框架中默认内置的基础组件,可直接被开发者调用,比如上面案例的```Button`。

  • 事件方法:用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()方法。

  • 属性方法:用于组件属性的配置,统一通过属性方法进行设置,如fontSize()width()height()size()等,可通过链式调用的方式设置多项属性。

上述示例中,用 @State 装饰的变量 times ,包含了一个基础的状态管理机制,即 times 的值的变化,会引起相应的UI组件Button的变化,ArkUI开发框架还提供多维度的状态管理机制,和UI相关联的数据,不仅仅在组件内使用,还可以在不同组件层级间传递,比如父子组件之间,爷孙组件之间,也可以是全局范围内的传递,还可以是跨设备传递。

二、资源管理

应用开发过程中,经常需要用到颜色、字体、间距、图片等资源,在不同的设备或配置中,这些资源的值可能不同。

  • 应用资源:借助资源文件能力,开发者在应用中自定义资源,自行管理这些资源在不同的设备或配置中的表现。
  • 系统资源:开发者直接使用系统预置的资源定义(即分层参数,同一资源ID在设备类型、深浅色等不同配置下有不同的取值)。

2.1.资源分类

移动端应用开发常用到的资源比如图片、音视频和字符串等在OpenHarmony 规定把这些应用的资源文件统一放在 resources 目录下的各子目录中便于开发者使用和维护, resoures 目录包括两大类,

  • 一类为 base 目录与限定词目录
  • 另一类为 rawfile 目录

新建 OpenHarmony 应用,默认生成的资源目录如下所示:

img

base 目录与限定词目录下面可以创建资源组目录(包括 elementmediaprofile ),用于存放特定类型的资源文件,各资源目录说明如下图所示:

目录类型说明资源文件
element表示元素资源,以下每一类数据都采用相应的JSON文件来表征(目录下仅支持文件类型)。- boolean,布尔型- color,颜色- float,浮点型- intarray,整型数组- integer,整型- pattern,样式- plural,复数形式- strarray,字符串数组- string,字符串element目录中的文件名称建议与下面的文件名保持一致。每个文件中只能包含同一类型的数据。- boolean.json- color.json- float.json- intarray.json- integer.json- pattern.json- plural.json- strarray.json- string.json
media表示媒体资源,包括图片、音频、视频等非文本格式的文件(目录下只支持文件类型)文件名可自定义,例如:icon.png。
profile表示自定义配置文件,其文件内容可通过包管理接口获取(目录下只支持文件类型)文件名可自定义,例如:test_profile.json。

2.2.资源访问

OpenHarmony 应用资源分为两类,一类是应用资源,另一类是系统资源,它们的资源访问方式如下:

1)访问应用资源

base 目录下的资源文件会被编译成二进制文件并且给这些资源赋予唯一的 ID ,使用相应资源的时候通过资源访问符 $r(‘app.type.name’) 的形式,

  • app 代表是应用内 resources 目录中定义的资源;
  • type 表示资源类型,可取值的有有 color 、 float 、 string 、 string 、 media 等;
  • name 表示资源的文件名字。

例如:string.json 中新加 name 为 text_string 的字符串,则访问该字符串资源为 $r(‘app.string.text_string’):

base 目录的子目录element下新建 float.json ,string.json 、color.json文件,分别存放浮点型、字体和颜色,资源内容如下图所示:

img

需要注意的是因为国际化的问题:

  • string.json中配置的内容需要在,在zh_CN和en_US的string.json中添加相同的内容,就会根据本地的语言环境进行显示:

在media准备一张图片,后面作为文本框的背景图使用:

img

创建:ResourceDemo.ets文件,应用上面的内容如下:

@Entry
@Component
struct ResourceDemo{
  build(){
    Column(){
      Text($r('app.string.hi_string')) //访问字符串资源
        .size({width:300, height:120}) //设置尺寸
        .fontSize($r('app.float.text_size'))//访问字体大小
        .fontColor($r('app.color.text_color'))//访问字体颜色
        .backgroundImage($r('app.media.Sns'), ImageRepeat.XY) //设置背景图片,ImageRepeat.XY则是图片太小时候,选择某个坐标位置的颜色填充
    }
    .width("100%") //表示将组件的宽度设置为父容器的100%
    .height("100%") //表示将组件的高度设置为父容器的100%
    .padding(10)//表示设置组件的内边距为10个单位。
  }
}

预览效果如下:

img

2)访问系统资源

除了自定义资源,开发者也可以使用系统中预定义的资源,统一应用的视觉风格。可以查看应用UX设计中关于资源的介绍,获取支持的系统资源ID及其在不同配置下的取值。在开发过程中,分层参数的用法与资源限定词基本一致。对于系统资源,可以通过“$r(‘sys.type.resource_id’)”的形式引用。其中,sys为系统资源;type为资源类型,取值包括“color”、“float”、“string”、“media”;resource_id为资源id。说明

  • 仅声明式开发范式支持使用系统资源。
  • 对于系统预置应用,建议使用系统资源;对于三方应用,可以根据需要选择使用系统资源或自定义应用资源。

案例,创建 ResourceDemo2.ets内容如下:

@Entry
@Component
struct ResourceDemo2{
  build(){
    Column(){
      Text('HelloWorld')
        .fontColor($r('sys.color.ohos_id_color_emphasize'))            // 设置字体颜色
        .fontSize($r('sys.float.ohos_id_text_size_headline5'))         // 设置字体大小
        .fontFamily($r('sys.string.ohos_id_text_font_family_medium'))  // 设置字体
        .backgroundColor($r('sys.color.ohos_id_color_palette_aux1'))   // 设置背景颜色

      Image($r('sys.media.ohos_app_icon'))
        .border({
          color: $r('sys.color.ohos_id_color_palette_aux11'),           // 设置边框颜色为辅助色11
          radius: $r('sys.float.ohos_id_corner_radius_button'),         // 设置边框圆角半径为按钮圆角半径
          width: 2                                                      // 设置边框宽度为2
        })
        .margin({
          top: $r('sys.float.ohos_id_elements_margin_horizontal_m'),    // 设置上边距为水平中等间距
          bottom: $r('sys.float.ohos_id_elements_margin_horizontal_l')  // 设置下边距为水平大间距
        })
        .height(200)                                                    // 设置高度为200
        .width(300)                                                     // 设置宽度为300
    }
    .width("100%")
    .height("100%")
    .padding(10)
  }
}

预览结果如下:

img

2.3.像素单位

1)像素单位说明

ArkUI开发框架提供了 4 种像素单位供开发者使用,分别是: pxvpfplpx ,框架采用vp为基准数据单位。它们之间的区别如下表所示:

名称描述
px屏幕物理像素单位。
vp屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素,当数值不带单位时,默认单位vp。在实际宽度为1440物理像素的屏幕上,1vp约等于3px。
fp字体像素,与vp类似适用屏幕密度变化,随系统字体大小设置变化。
lpx视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值,designWidth默认值为720。当designWidth为720时,在实际宽度为1440物理像素的屏幕上,1lpx为2px大小。
2)像素单位转换

提供其他单位与px单位互相转换的方法。

接口描述
vp2px(value : number) : number将vp单位的数值转换为以px为单位的数值。从API version 9开始,该接口支持在ArkTS卡片中使用。
px2vp(value : number) : number将px单位的数值转换为以vp为单位的数值。从API version 9开始,该接口支持在ArkTS卡片中使用。
fp2px(value : number) : number将fp单位的数值转换为以px为单位的数值。从API version 9开始,该接口支持在ArkTS卡片中使用。
px2fp(value : number) : number将px单位的数值转换为以fp为单位的数值。从API version 9开始,该接口支持在ArkTS卡片中使用。
lpx2px(value : number) : number将lpx单位的数值转换为以px为单位的数值。从API version 9开始,该接口支持在ArkTS卡片中使用。
px2lpx(value : number) : number将px单位的数值转换为以lpx为单位的数值。从API version 9开始,该接口支持在ArkTS卡片中使用。

案例如下:

@Entry
@Component
struct Example {
  build() {
    Column() {
      /**Flex({ wrap: FlexWrap.Wrap })是一个Flex布局的设置,
       *其中wrap: FlexWrap.Wrap表示设置Flex容器的子元素在主轴方向上超出容器时是否换行。
       *在这里,FlexWrap.Wrap表示子元素会自动换行,以适应容器的尺寸。
       * 这样设置可以确保在容器尺寸不足以容纳所有子元素时,子元素会自动换行,而不会超出容器范围。
       */
      Flex({ wrap: FlexWrap.Wrap }) {
        // 默认不写单位就是是vp
        Column() {
          Text("width(220)")
            .width(220)  // 设置宽度为220vp
            .height(40)  // 设置高度为40vp
            .backgroundColor("#00BFC9")  // 设置背景颜色为#00BFC9
            .fontSize("12vp")  // 设置字体大小为12vp
        }.margin(5)  // 设置外边距为5vp

        // 宽度指定成px
        Column() {
          Text("width('220px')")
            .width('220px')  // 设置宽度为220px
            .height(40)  // 设置高度为40vp
            .backgroundColor("#007900")  // 设置背景颜色为#007900
            .textAlign(TextAlign.Center)  // 设置文本对齐方式为居中
            .fontColor(Color.White)  // 设置字体颜色为白色
        }.margin(5)  // 设置外边距为5vp

        // 宽度指定成vp
        Column() {
          Text("width('220vp')")
            .width('220vp')  // 设置宽度为220vp
            .height(40)  // 设置高度为40vp
            .backgroundColor("#FF9800")  // 设置背景颜色为#FF9800
            .textAlign(TextAlign.Center)  // 设置文本对齐方式为居中
            .fontColor(Color.White)  // 设置字体颜色为白色
            .fontSize('12vp')  // 设置字体大小为12vp
        }.margin(5)  // 设置外边距为5vp

        // 宽度指定成vplpx
        Column() {
          Text("width('220lpx') designWidth:720")
            .width("220lpx")  // 设置宽度为220lpx
            .height(40)  // 设置高度为40vp
            .backgroundColor("#634794")  // 设置背景颜色为#634794
            .textAlign(TextAlign.Center)  // 设置文本对齐方式为居中
            .fontColor(Color.White)  // 设置字体颜色为白色
            .fontSize('12vp')  // 设置字体大小为12vp
        }.margin(5)  // 设置外边距为5vp

        // 将vp单位的数值转换为以px为单位的数值
        Column() {
          Text("width(vp2px(220) + 'px')")
            .width(vp2px(220) + 'px')  // 将220vp转换为px单位的数值,然后设置宽度
            .height(40)  // 设置高度为40vp
            .backgroundColor('#3F56EA')  // 设置背景颜色为#3F56EA
            .textAlign(TextAlign.Center)  // 设置文本对齐方式为居中
            .fontColor(Color.White)  // 设置字体颜色为白色
            .fontSize('12vp')  // 设置字体大小为12vp
        }.margin(5)  // 设置外边距为5vp

        // fontSize('12fp')设置成fp
        Column() {
          Text("fontSize('12fp')")
            .width(220)  // 设置宽度为220vp
            .height(40)  // 设置高度为40vp
            .backgroundColor('#A14A5C')  // 设置背景颜色为#A14A5C
            .textAlign(TextAlign.Center)  // 设置文本对齐方式为居中
            .fontColor(Color.White)  // 设置字体颜色为白色
            .fontSize('12fp')  // 设置字体大小为12fp
        }.margin(5)  // 设置外边距为5vp

        // 将px单位的数值转换为以vp为单位的数值。
        Column() {
          Text("width(px2vp(220))")
            .width(px2vp(220))  // 将220px转换为vp单位的数值,然后设置宽度
            .height(40)  // 设置高度为40vp
            .backgroundColor('#E40078')  // 设置背景颜色为#E40078
            .textAlign(TextAlign.Center)  // 设置文本对齐方式为居中
            .fontColor(Color.White)  // 设置字体颜色为白色
            .fontSize('12fp')  // 设置字体大小为12fp
        }.margin(5)  // 设置外边距为5vp
      }.width('100%')  // 设置宽度为100%
    }
  }
}

2.4.资源管理器

ArkUI开发框架在 @ohos.resourceManager 模块里提供了资源管理器 ResourceManager,它可以访问不同的资源,比如获取获取字符串资源,获取设备配置信息等等,参考地址:https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/js-apis-resource-manager-0000001478181625-V3,resourceManager 模块 API 如下:

declare namespace resourceManager {
  // 省略部分代码
  export interface ResourceManager {
      // 获取字符串资源
      getString(resId: number, callback: AsyncCallback<string>): void;
      // 获取字符串数组资源
      getStringArray(resId: number, callback: AsyncCallback<Array<string>>): void;
      // 获取媒体资源
      getMedia(resId: number, callback: AsyncCallback<Uint8Array>): void;
      // 获取设备信息,比如当前屏幕密度,设备类型是手机还是平板等
      getDeviceCapability(callback: AsyncCallback<DeviceCapability>): void;
      // 获取配置信息,比如当前屏幕方向密度,当前设备语言
      getConfiguration(callback: AsyncCallback<Configuration>): void;
      // 释放ResourceManager资源
      release();
  }
}
export default resourceManager;

使用 ResourceManager 之前先调用 getContext(this) 方法获取当前组件的 Context,该 Conetxt 内部定义了一个 ResourceManager 的属性,因此可以直接使用 ResourceManager 的各种 getXXX() 方法获取对应资源, ResourceManager应用流程如下:

1)引入 resourceManager

import resourceManager from '@ohos.resourceManager';

2)获取 ResourceManager

aboutToAppear() {
  // 获取ResourceManager
  let manager = getContext(this).resourceManager;
}

3)使用 ResourceManager

manager.getString(0x1000001, (innerError, data) => {
  if(data) {
    // 获取资源成功
  } else {
    console.log("error: " + JSON.stringify(innerError))
  }
})

4)释放 ResourceManager

this.manager.release();

完整案例如下:

import resourceManager from '@ohos.resourceManager';  // 导入资源管理器模块

@Entry
@Component
struct Example02{
  @State text_string: string = "";  // 定义文本字符串状态
  @State capability: string = "";  // 定义设备能力状态
  @State configuration: string = "";  // 定义设备配置状态

  private resManager: resourceManager.ResourceManager;  // 声明资源管理器

  build(){
    Column({space:10}){  // 创建一个垂直布局的Column组件,设置子元素间距为10
      Text(this.text_string)  // 显示文本字符串资源
        .size({width:300, height:120})  // 设置尺寸为宽300,高120
        .fontSize($r('app.float.text_size'))  // 使用资源管理器获取字体大小
        .fontColor($r('app.color.text_color'))  // 使用资源管理器获取字体颜色
        .backgroundImage($r('app.media.Sns'))  // 使用资源管理器设置背景图片

      Text(this.capability)  // 显示设备能力信息
        .fontSize(20)  // 设置字体大小为20

      Text(this.configuration)  // 显示设备配置信息
        .fontSize(20)  // 设置字体大小为20
    }.width('100%')  // 设置宽度为100%
    .height('100%')  // 设置高度为100%
    .padding(10)  // 设置内边距为10
  }

  aboutToAppear(){
    this.resManager = getContext(this).resourceManager;  // 获取上下文中的资源管理器
    this.resManager.getStringValue(0x1000001,(innerError,data)=>{  // 获取字符串资源值
      if(data){
        this.text_string = data;  // 将获取到的字符串资源值赋给text_string状态
      }else{
        console.log("error:"+JSON.stringify(innerError));  // 打印错误信息
      }

      this.resManager.getDeviceCapability((innerError, deviceCapability)=>{  // 获取设备能力信息
        if(deviceCapability){
          this.capability = JSON.stringify(deviceCapability);  // 将设备能力信息转换为JSON字符串并赋给capability状态
        }
      })

      this.resManager.getConfiguration((innerError, configuration)=>{  // 获取设备配置信息
        if(configuration){
          this.configuration = JSON.stringify(configuration);  // 将设备配置信息转换为JSON字符串并赋给configuration状态
        }
      })

    })
  }

  aboutToDisappear(){
    this.resManager?.release();  // 释放资源管理器资源
  }
}

注意:渲染出来的 mock string 是因为在预览器上暂时不支持 ResourceManager 的用法,在实际设备上可以执行。

  • 44
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值