OpenHarmony开发者论坛

标题: OpenHarmony系统之如何编写简单的hap程序 [打印本页]

作者: 深开鸿_胡瑞涛    时间: 2023-10-27 16:42
标题: OpenHarmony系统之如何编写简单的hap程序
[md]## **OpenHarmony系统之如何编写简单的hap程序**

### 简介

HAP文件是在OpenHarmony系统下编译生成的可执行文件。HAP 包是由代码、资源、第三方库以及应用配置文件打包生成的模块包,主要分为两种类型:entry 和 feature。

> entry:应用的主模块,作为 OpenHarmony 应用的入口,提供了应用的基础功能。
> feature:应用的动态特性模块,作为应用能力的扩展,可以根据用户的需求和设备的类型进行选择性安装。

OpenHarmony 用户应用程序包可以只包含一个基础的 entry 包,也可以包含一个基础的 entry 包和一个或多个功能型的 feature 包。

### 工具

1. DevEco Studio 3.1

* HUAWEI DevEco Studio For OpenHarmony是基于IntelliJ IDEA Community开源版本打造,面向OpenHarmony全场景多设备的一站式集成开发环境(IDE),DevEco Studio 3.1配套支持HarmonyOS 3.1版本及以上的应用及服务开发,提供了代码智能编辑、低代码开发、双向预览等功能,以及轻量构建工具DevEco Hvigor 、本地模拟器,持续提升应用及服务开发效率。
* **下载链接:** [HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者](https://developer.harmonyos.com/ ... tudio#download_beta)
* **安装步骤:** [工具简介-DevEco Studio使用指南-工具-HarmonyOS应用开发](https://developer.harmonyos.com/ ... 3?catalogVersion=V3)
  **注:建议在选项界面都勾选DevEco Studio,Add “bin” folder to the PATH,Add “Open Folder as Project”**
* **配置开发环境:**[配置开发环境-快速开始-DevEco Studio使用指南-工具-HarmonyOS应用开发](https://developer.harmonyos.com/ ... 3?catalogVersion=V3)

### ArkTS

- ArkTS是OpenHarmony优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,使能静态类型。
- 基于TS扩展的声明式开发范式的方舟开发框架是一套开发极简、高性能、跨设备应用的UI开发框架,支持开发者高效的构建跨设备应用UI界面。
- 基于TS扩展的声明式开发范式提供了一系列基础组件,这些组件以声明方式进行组合和扩展来描述应用程序的UI界面,并且还提供了基本的数据绑定和事件处理机制,帮助开发者实现应用交互逻辑。
  
  ```html
  @Entry                                //用@Entry装饰的自定义组件用作页面的默认入口组件,也可以理解为页面的根节点。   一个页面有且仅能有一个@Entry,只有被@Entry修饰的组件或者其子组件,才会在页面上显示。
  @Component                            //@Component装饰的struct表示该结构体具有组件化能力,能够成为一个独立的组件,这种类型的组件也称为自定义组件,在build方法里描述UI结构。
  struct Hello {                         //在声明式UI中,所有的页面都是由组件构成。组件的数据结构为struct
      @State myText: string = 'World'
      build() {                          //build函数用于定义组件的声明式UI描述,在build方法中以声明式方式进行组合自定义组件或系统内置组件。
          Column() {                     //Column:沿垂直方向布局的容器。
              Text('Hello')              //Text:显示一段文本的组件。
                  .fontSize(30)
              Text(this.myText)
                  .fontSize(32)
              Divider()                  //Divider:提供分隔器组件,分隔不同内容块/内容元素。
              Button() {                 //Button:按钮组件,可快速创建不同样式的按钮,通常用于响应用户的点击操作。
                  Text('Click me')
                  .fontColor(Color.Red)
              }.onClick(() => {
                  this.myText = 'UI'
              })
              .width(500)
              .height(200)
          }
      }
  }
  ```

### 基本概念

* 装饰器:方舟开发框架定义了一些具有特殊含义的装饰器,用于装饰类、结构、方法和变量。装饰器就是某一种修饰,给被装饰的对象赋予某一种能力,比如@Entry就是页面入口的能力,@Component就是组件化能力。
* 自定义组件:可重用的UI单元,可以与其他组件组合,如@Component装饰的struct Hello。
* UI描述:声明性描述UI结构,例如build()方法中的代码块。
* 内置组件:框架中默认内置的基本组件和布局组件,开发者可以直接调用,仅用于解释UI描述规范。如Column、Text、Divider、Button等。
* 属性方法:用于配置组件属性,如fontSize()、width()、height()、color()等。
* 事件方法:在事件方法的回调中添加组件响应逻辑。例如,为Button组件添加onClick方法,在onClick方法的回调中添加点击响应逻辑。

### 应用

#### 1. 创建

* 在DevEco Studio的欢迎页,选择Create Project开始创建一个新工程。
* 根据工程创建向导,在OpenHarmony页签,选择“Empty Ability”模板,点击Next。

![img](https://forums.openharmony.cn/fo ... es&type=fixnone)

* 点击Next,各个参数保持默认值即可,点击Finish。

![img](https://forums.openharmony.cn/fo ... es&type=fixnone)

**关于各个参数的详细介绍,请参考**[创建一个新的工程-工程管理-DevEco Studio使用指南-工具-HarmonyOS应用开发](https://developer.harmonyos.com/ ... 3?catalogVersion=V3)

* **Project name:** 工程的名称,可以自定义。
* **Project type:** 工程的类型,标识该工程是一个传统方式的需要安装的应用(Application)或原子化服务(Atomic service),默认类型为Application。
* **Bundle name:** 软件包名称,默认情况下,应用ID也会使用该名称,应用发布时,应用ID需要唯一。如果“Project type”选择了Atomic service,则Bundle name的后缀名必须是.hmservice。
* **Save location:** 工程文件本地存储路径。
* **Compile SDK:** 编译的SDK版本。
* **Model:** 选择 Stage模型,[Stage模型应用程序包结构](https://docs.openharmony.cn/page ... structure-stage.md/)(Stage模型仅Compile API为9及以上支持)。
* **Enable Super Visual:** 选择开发模式,部分模板支持低代码开发,可选择打开该开关。
* **Language:** 开发语言。
* **Compatible SDK:** 兼容的SDK最低版本。
* **Device type:** 该工程模板支持的设备类型。
* **Show in service center:** 是否在服务中心露出。
* 工程创建完成后,DevEco Studio会自动进行工程的同步,同步成功如下图所示。
* 将搭载OpenHarmony标准系统的开发板与电脑连接。
  * 点击File > Project Structure > Project > Signing Configs界面勾选“Automatically generate signature”,等待自动签名完成即可,点击“OK”。如下图所示:
  * 为了保证OpenHarmony应用的完整性和来源可靠,在应用构建时需要对应用进行签名。经过签名的应用才能在真机设备上安装、运行、和调试。如果没有配置签名,会报错:hvigor WARN: Will skip sign ‘hap’,Invalid signingConfig is configured for ‘default’ product.

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_820,h_611 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

#### 2. 目录结构

* 新项目的目录结构
  ![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_244,h_281 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")
  **entry**为应用的主模块,类似与Android Studio的app模块,一个APP中,对于同一设备类型必须有且只有一个entry类型的HAP,可独立安装运行。
* 使用**previewer**查看程序的预览图
  点击View > Tool Windows > Project > Previewer 如下图所示:
  * 方法一:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_499,h_389 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

* 方法二:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_277,h_134 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

* 成功预览后会生成.preview结构:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_260,h_97 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

* 使用**build**编译程序
  点击Build > Rebuild 进行编译; Build > Build Hap(s) /App(s) >Build Hap(s) 生成hap文件

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_295,h_139 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

* hap文件的生成路径:entry/build/outpus/default

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_336,h_221 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

* **目录结构中文件分类如下:**
  .ets结尾的eTS(extended TypeScript)文件,用于描述UI布局、样式、事件交互和页面逻辑。
  **各个文件夹和文件的作用:**
  
  * **app.ets**文件用于全局应用逻辑和应用生命周期管理。
  * **pages**目录用于存放所有组件页面。
  * **common**目录用于存放公共代码文件,比如:自定义组件和公共方法。
  
  > **说明:**
  >
  > * 资源目录resources文件夹位于src/main下,此目录下资源文件的详细规范以及子目录结构规范参看[UI开发,ArkTS声明式开发范式](https://docs.openharmony.cn/page ... opment-overview.md/)。
  > * 页面支持导入TypeScript和JavaScript文件。

#### 3. 基础组件应用

* 如上所说,基于TS扩展的声明式开发范式提供了一系列基础组件,如:基础组件,容器组件,媒体组件,绘制组件和画布组件等,本节我们主要使用容器组件。
* list列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。
* listitem用来展示列表具体item,宽度默认充满List组件,必须配合List来使用。
* text可以显示一段文本。
  编写一个简单的ui界面,其homepage为:
  
  ```html
  import ConfigData from '../../Utils/ConfigData';
  import {SubEntryComponent} from '../../component/subEntryComponent';
  
  @Entry
  @Component
  struct HomePage {
    @State message: string = 'The Test'
    build() {
      Column(){
        GridContainer({columns:12, sizeType: SizeType.Auto, gutter: vp2px(1) === 2 ? '12vp' : '0vp', margin: vp2px(1) === 2 ? '24vp' : '0vp'}) {
          Row({}) {
            Column() {}
            .width(ConfigData.WH_100_100)
            .height(ConfigData.WH_100_100)
            .useSizeType({
              xs: { span: 0, offset: 0 }, sm: { span: 0, offset: 0 },
              md: { span: 0, offset: 0 }, lg: { span: 2, offset: 0 }
            });
            Column() {
              Text(this.message)
                .fontSize($r("app.float.font_30"))
                .lineHeight($r("app.float.lineHeight_41"))
                .fontWeight(FontWeight.Bold)
                .fontFamily('HarmonyHeiTi')
                .textAlign(TextAlign.Start)
                .width(ConfigData.WH_100_100)
                .padding({left: $r('app.float.distance_26'), top: $r('app.float.distance_12'), bottom: $r('app.float.distance_17')})
            Column({space:'16vp'}){
              List(){
                ListItem(){            SubEntryComponent({targetPage:"pages/test1",titler("app.string.test1")})
                }
                .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
                ListItem(){          SubEntryComponent({targetPage:"pages/test2",titler("app.string.test2")})
                }
                .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
                ListItem(){
  SubEntryComponent({targetPage:"pages/test3",titler("app.string.test3")})
                }
                .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
                ListItem(){
  SubEntryComponent({targetPage:"pages/test4",titler("app.string.test4")})
                }
                .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
              }
            }
            .width(ConfigData.WH_100_100)
          }
          .padding({left: $r('app.float.distance_24'), right: $r('app.float.distance_24')})
          .width(ConfigData.WH_100_100)
          .height(ConfigData.WH_100_100)
          .useSizeType({
            xs: { span: 12, offset: 0 }, sm: { span: 12, offset: 0 },
            md: { span: 12, offset: 0 }, lg: { span: 8, offset: 2 }
          });
  
          Column() {}
          .width(ConfigData.WH_100_100)
          .height(ConfigData.WH_100_100)
          .useSizeType({
            xs: { span: 0, offset: 12 }, sm: { span: 0, offset: 12 },
            md: { span: 0, offset: 12 }, lg: { span: 2, offset: 10 }
          })
          }
          .width(ConfigData.WH_100_100)
          .height(ConfigData.WH_100_100);
        }
        .width(ConfigData.WH_100_100)
        .height(ConfigData.WH_100_100);
      }
      .backgroundColor($r("sys.color.ohos_id_color_sub_background"))
      .width(ConfigData.WH_100_100)
      .height(ConfigData.WH_100_100);
    }
  }
  ```
  
* 调用的subEntryComponent为:
  
  ```html
  import ConfigData from '../Utils/ConfigData';
  @Component
  export struct SubEntryComponent{
    private targetPage: string;
    private title:string | Resource;
    @State isTouched:boolean = false;
    private date: any = null;
    private deviceId: any = null;
    build() {
      Navigator({target: this.targetPage}){
        Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems:ItemAlign.Center }) {
          Row() {
            Text(this.title)
              .fontSize($r('app.float.font_30'))
              .lineHeight($r('app.float.wh_value_32'))
              .fontColor($r('app.color.font_color_182431'))
              .fontWeight(FontWeight.Medium)
              .margin({left: $r('app.float.distance_8')})
              .textAlign(TextAlign.Start)
              .height($r('app.float.wh_value_32'));
          }
          Image(('app.media.ic_settings_arrow'))
            .width($r('app.float.wh_value_12'))
            .height($r('app.float.wh_value_100'))
            .margin({right: $r('app.float.distance_8')});
        }
        .height(ConfigData.WH_100_100)
        .width(ConfigData.WH_100_100)
        .borderRadius($r('app.float.radius_12'))
        .linearGradient(this.isTouched ? {
           angle: 90,
           direction: GradientDirection.Right,
           colors: [[$r("app.color.DCEAF9"), 0.0], [$r("app.color.FAFAFA"), 1.0]]
         } : {
            angle: 90,
            direction: GradientDirection.Right,
            colors: [[$r("sys.color.ohos_id_color_foreground_contrary"), 1], [$r("sys.color.ohos_id_color_foreground_contrary"), 1]]})
        .onTouch((event: TouchEvent) => {
          if (event.type === TouchType.Down) {
            this.isTouched = true;
          }
          if (event.type === TouchType.Up) {
            this.isTouched = false;
          }
        })
      }
      .params( {date: this.date, deviceId: this.deviceId} )
      .padding($r('app.float.distance_4'))
      .height($r('app.float.wh_value_100'))
      .borderRadius($r('app.float.radius_24'))
      .backgroundColor($r("sys.color.ohos_id_color_foreground_contrary"));
    }
  }
  ```
  
* 预览图:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_226,h_481 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

#### 4. 界面跳转

在实现界面跳转时一般需要用到targetPage或者router:url方法。

* targetpage方法
  
  ```html
  targetpage:"{页面路径}"
  ```
* router方法
  
  ```html
  定义:
  const 'NAME' = '路径';
  实现:
  Router.push({ uri: PAGE_URI_TEST_NAME });
  ```
* js标签配置
  开发框架需要应用的config.json中配置相关的js标签,其中包含了实例名称、页面路由、视图窗口配置信息。pages定义每个页面入口组件的路由信息,每个页面由页面路径和页面名组成,页面的文件名就是页面名。比如:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_315,h_286 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

> **说明:**
>
> * pages列表中第一个页面为应用的首页入口。
> * 页面文件名不能使用组件名称,比如:Text.ets、Button.ets等。
> * 每个页面文件中必须包含页面入口组件(@Entry装饰)。

* 预览图:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_612,h_482 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

#### 5. api组件

* 在基于TS扩展的声明式开发范式中有众多的API参考,初学者可以使用这些组件练习,以此来加深对ets的熟悉。如:
  
  ```html
  // 提供在给定范围内选择评分的组件
  @Entry
  @Component
  struct RatingExample {
    @State rating: number = 1
    @State indicator: boolean = false
  
    build() {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
        Text('current score is ' + this.rating).fontSize(20)
        Rating({ rating: this.rating, indicator: this.indicator })
          .stars(5)
          .stepSize(0.5)
          .onChange((value: number) => {
            this.rating = value
          })
      }.width(350).height(200).padding(35)
    }
  }
  ```
* 预览图:

![#冲刺创作新星#[六]第一个hap应用-开源基础软件社区](https://dl-harmonyos.51cto.com/i ... rocess=image/resize,w_215,h_243 "#冲刺创作新星#[六]第一个hap应用-开源基础软件社区")

#### 6. 自定义组件

组件的成员变量可以通过两种方式初始化:

* 本地初始化,如:
  
  ```html
  @State counter: Counter = new Counter()
  ts
  ```
* 在构造组件时通过构造参数初始化,如:
  
  ```html
  MyComponent({counter: $myCounter})
  ```

如可以使用CustomDialogController类显示自定义弹窗

* 导入对象
  
  ```html
  dialogController : CustomDialogController = new CustomDialogController(value:{builder: CustomDialog, cancel?: () => void, autoCancel?: boolean})
  ```
* 使用open()和close()对其进行开关显示。
  open(): void
  显示自定义弹窗内容,若已显示,则不生效。
  close(): void
  关闭显示的自定义弹窗,若已关闭,则不生效。
* 示例:
  
  ```html
  class ClassA {
    public a:number
    constructor(a: number) {
      this.a = a
    }
  }
  @Entry
  @Component
  struct Parent {
    @State parentState: ClassA = new ClassA(1)
  
    build() {
      Column() {
        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          CompA({ aState: new ClassA(2), aLink: $parentState })
        }
        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          CompA({ aLink: $parentState })
        }
        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          CompA({ aState: new ClassA(3), aLink: $parentState })
        }
      }
    }
  }
  
  @Component
  struct CompA {
    @State aState: any = false
    @Link aLink: ClassA
  
    build() {
      Column() {
        CompB({ bLink: $aLink, bProp: this.aState })
        CompB({ bLink: $aState, bProp: false })
      }
    }
  }
  
  @Component
  struct CompB {
    @Link bLink: ClassA
    @Prop bProp: boolean
  
    build() {
      Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
        Text(JSON.stringify(this.bLink.a)).fontSize(30)
        Text(JSON.stringify(this.bProp)).fontSize(30).fontColor(Color.Red)
       }.margin(10)
    }
  }
  ```

**总结**

ArkTS是一种为构建高性能应用而设计的编程语言。ArkTS在继承TypeScript语法的基础上进行了优化,以提供更高的性能和开发效率。ArkTS提供对方舟开发框架ArkUI的声明式语法和其他特性的支持。

[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5