积分139 / 贡献0

提问0答案被采纳4文章4

作者动态

[经验分享] OpenHarmony系统之如何编写简单的hap程序

深开鸿_胡瑞涛 显示全部楼层 发表于 2023-10-27 16:42:33

OpenHarmony系统之如何编写简单的hap程序

简介

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

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

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

工具

  1. DevEco Studio 3.1

ArkTS

  • ArkTS是OpenHarmony优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,使能静态类型。

  • 基于TS扩展的声明式开发范式的方舟开发框架是一套开发极简、高性能、跨设备应用的UI开发框架,支持开发者高效的构建跨设备应用UI界面。

  • 基于TS扩展的声明式开发范式提供了一系列基础组件,这些组件以声明方式进行组合和扩展来描述应用程序的UI界面,并且还提供了基本的数据绑定和事件处理机制,帮助开发者实现应用交互逻辑。

    @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

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

img

关于各个参数的详细介绍,请参考创建一个新的工程-工程管理-DevEco Studio使用指南-工具-HarmonyOS应用开发

  • 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模型应用程序包结构(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应用-开源基础软件社区

2. 目录结构

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

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

  • 方法二:

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

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

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

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

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

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

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

  • 目录结构中文件分类如下: .ets结尾的eTS(extended TypeScript)文件,用于描述UI布局、样式、事件交互和页面逻辑。 各个文件夹和文件的作用:

    • app.ets文件用于全局应用逻辑和应用生命周期管理。
    • pages目录用于存放所有组件页面。
    • common目录用于存放公共代码文件,比如:自定义组件和公共方法。

    说明:

    • 资源目录resources文件夹位于src/main下,此目录下资源文件的详细规范以及子目录结构规范参看UI开发,ArkTS声明式开发范式
    • 页面支持导入TypeScript和JavaScript文件。

3. 基础组件应用

  • 如上所说,基于TS扩展的声明式开发范式提供了一系列基础组件,如:基础组件,容器组件,媒体组件,绘制组件和画布组件等,本节我们主要使用容器组件。

  • list列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。

  • listitem用来展示列表具体item,宽度默认充满List组件,必须配合List来使用。

  • text可以显示一段文本。 编写一个简单的ui界面,其homepage为:

    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",title:$r("app.string.test1")})
                  }
                  .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
                  ListItem(){          SubEntryComponent({targetPage:"pages/test2",title:$r("app.string.test2")})
                  }
                  .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
                  ListItem(){
    SubEntryComponent({targetPage:"pages/test3",title:$r("app.string.test3")})
                  }
                  .padding({top: $r('app.float.distance_2'), bottom: $r('app.float.distance_2')})
                  ListItem(){
    SubEntryComponent({targetPage:"pages/test4",title:$r("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为:

    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应用-开源基础软件社区

4. 界面跳转

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

  • targetpage方法

    targetpage:"{页面路径}"
  • router方法

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

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

说明:

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

#冲刺创作新星#[六]第一个hap应用-开源基础软件社区

5. api组件

  • 在基于TS扩展的声明式开发范式中有众多的API参考,初学者可以使用这些组件练习,以此来加深对ets的熟悉。如:

    // 提供在给定范围内选择评分的组件
    @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应用-开源基础软件社区

6. 自定义组件

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

  • 本地初始化,如:

    @State counter: Counter = new Counter()
    ts
  • 在构造组件时通过构造参数初始化,如:

    MyComponent({counter: $myCounter})

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

  • 导入对象

    dialogController : CustomDialogController = new CustomDialogController(value:{builder: CustomDialog, cancel?: () => void, autoCancel?: boolean})
  • 使用open()和close()对其进行开关显示。 open(): void 显示自定义弹窗内容,若已显示,则不生效。 close(): void 关闭显示的自定义弹窗,若已关闭,则不生效。

  • 示例:

    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的声明式语法和其他特性的支持。

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部