跳至主要內容

鸿蒙开发

大约 16 分钟约 4669 字

鸿蒙是华为自主研发的一款国产操作系统,学习鸿蒙编程,即可为多种鸿蒙终端开发应用,包含手机、平板、手表甚至是电视、汽车、嵌入式等。

配置环境 & IDE安装

鸿蒙开发需要使用的软件为DevEco Studio,它集成IntelliJ,为鸿蒙开发提供代码检查、自动补全、代码高亮以及提供常用工具,支持中文开发。

为书写方便,以下称 DevEco Studio 为IDE。

IDE的下载地址为https://developer.huawei.com/consumer/cn/deveco-studio/,进入后下载对应版本即可。open in new window

环境可在IDE下载完成之后在IDE中下载和配置。

下载完成后解压压缩包,得到一个exe安装包。

由于IDE基于IntelliJ构建,它的安装和使用界面和IDEA的旧UI很相似。

安装IDE

运行安装程序后,一路点击next即可。遇到协议则同意协议。

在安装完成后进入IDE的初始化界面,需要安装NodeJS、Ohpm和鸿蒙SDK。

初始化安装环境
初始化安装环境

第一次先安装NodeJS,由于华为鸿蒙对NodeJS等依赖的版本要求很苛刻,因此都选安装,不使用本地的。下一步还会安装ohpm,也是选择在线安装。

安装鸿蒙SDK
安装鸿蒙SDK

在 Summary中,继续即可。

初始界面
初始界面

进入安装界面后,先点击 Configure - Plugins,搜索Chinese,启用中文插件,然后重启IDE。

然后点击“新建项目”,创建我们的第一个鸿蒙项目。

创建项目
创建项目

点击next。

创建项目
创建项目

点击finish。

IDE界面组成

由于IDE和IDEA的界面很相似,这里只介绍大体的布局。

IDE界面
IDE界面

最左边树状的是文件树结构,中间为代码编辑区,点击右边的“预览器”,可以查看该页面的实时预览。

我们在刚学习鸿蒙开发时,只需要关注index.ets文件即可。在编写代码时,不要改变原有的代码,在其上面或下面写上自己的代码,然后在日志中查看即可。

ArkTS基础

方舟开发框架(简称ArkTS)为HarmonyOS应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。

ArkTS是编写鸿蒙应用的一门编程语言,它和TypeScript语法很相似,比如定义变量和常量用const和let,打印用console.log等等。这里不详细介绍ArkTS的各种类型,因为都很简单,并且在TypeScript笔记中都记录了下来。

数据类型

ArkTS有三种基础数据类型:string、number、boolean。

let num: number = 123;
console.log("数字", num);

let bool: boolean = true;
console.log("布尔值", bool);

let str: string = "这是一个字符串";
console.log("字符串", str);

console.log()函数需要两个参数,第一个参数为信息,第二个参数为信息内容,这一点和JavaScript是不同的。

运行结果:

03-19 23:07:30.951 I A0c0d0/JSApp: app Log: 数字 123
03-19 23:07:30.951 I A0c0d0/JSApp: app Log: 布尔值 true
03-19 23:07:30.951 I A0c0d0/JSApp: app Log: 字符串 这是一个字符串

运行结果可在预览窗口更新以后,点击下方状态栏的日志即可查看。

###变量和常量

和JavaScript相似,ArkTS也具有变量和常量之分。变量使用let定义,常量使用const定义。

let variable = "这是一个变量";
variable = "变量的值可改变";
const constant = "这是一个常量";
constant = "常量的值不可以改变"; // 错误,常量的值不可改变
image-20240319231217739
image-20240319231217739

变量的命名规则如下。

  • 只能使用数字、字母、下划线、$,不能以数字开头;
  • 不能使用关键字;
  • 严格区分大小写。

基本和其他编程语言一致。

数组

数组可以一次性存储多个数据。声明数组的语法为let 变量名: 类型名[] = [数据1, 数据2, ...]

let arr: number[] = [1, 2, 3, 4]

数组具有索引,从0开始,通过数组名[索引]的方式获取数组元素的值。

let arr = ["黑龙江", "辽宁", "江苏", "北京"];
console.log("第三个元素", arr[2]);  // 第三个元素 江苏

函数

函数是一段可以重复使用的代码块。我们之前使用的console.log就是系统封装的日志打印函数。

定义函数的语法为function 函数名(参数列表) {函数体},调用函数的语法为函数名(参数列表)

function add(a, b) {
  console.log("两个数相加的结果为", a + b);
}

add(1, 2); //两个数相加的结果为 3

ArtTS支持定义箭头函数,定义箭头函数的语法是(参数列表) => {函数体}

let add = (a: number, b: number): number => {
  return a + b
}

console.log("两个数相加:",add(1, 2));

接口

接口约定了一个对象必须要包含的属性。interface关键字用于定义接口,然后在接口里面声明属性和类型。

interface Person {
  name: string,   // 必须包含的属性
  age: number, 
  gender?: string,  // 可以包含的属性
  sayHi: () => void   // 必须包含的属性,此属性是一个成员方法
}

这时接口指定的名称就是我们的自定义类型。我们可以将对象指定为此类型,对象就必须包含该类型的属性。

let person: Person = {
  name: "张三",
  age: 18,
  sayHi: () => console.log("提示信息", `你好,我是张三`)
}

console.log("名字:", person.name)

定义接口的方法时,也可以指定方法的参数和返回值。

interface Obj {
  func: (a: number, b: string, c: boolean) => void
}

联合类型

联合类型可以允许一个变量有多种不同类型,以应变不同的需求。ArkTs使用管道符“|”隔开不同的变量类型。

let number: number | null = null;
let str: string | null = null;

此外,若想限制一个变量只能是一些值中的一种,可以将这些值当作类型使用,放在变量后面限制它们,并用管道符隔开。这一点和TypeScript中的字面值类型很像。

let gender: "男" | "女" | "保密" | null = null;

枚举类型

枚举类型约定变量只能在一组数据范围内选择值。枚举类型使用enum关键字定义,然后在里面声明枚举常量。每个枚举常量的值从0开始,以此类推,也可以在枚举类型内用赋值的形式给声明每一个枚举常量。

enum Month {
  January,
  February,
  March,
  // ...
}

enum Gender {
  male = "男性",
  female = "女性"
}

由于枚举内的内容为常量,即枚举常量,直接使用即可。

enum Month {
  January,
  February,
  March,
  // ...
}

enum Gender {
  male = "男性",
  female = "女性"
}

console.log("月份", Month.February)
console.log("性别", Gender.male)

function print(gender: Gender): void {
  console.log("你选择的性别为", gender)
}

print(Gender.male)

界面开发 - 组件

学习界面开发,工具仍然是IDE,代码写在build函数中,然后在预览中查看界面。

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {  // 在这里面写代码
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}
image-20240321164713646
image-20240321164713646

布局思路

编写界面应用时,应该先考虑排版,后填充内容。

ArkUI(方舟开发框架)是一套构建鸿蒙应用界面的框架。

构建页面的最小单位就是组件。

组件分为基础组件(如按钮、图片、文字)和容器组件(如行和列)。

容器组件的语法为容器组件(){ 组件内容 },基础组件的语法为基础组件(参数)

下面我们修改默认代码,对布局有一个基本的认识。

@Entry
@Component
struct Index {
  build() {
    Column() {
      Text("学生列表")
      Row() {
        Text("张三")
        Text("李四")
        Text("王五")
      }
    }
  }
}
image-20240321172106821
image-20240321172106821

需要注意,build()内部只能有一个根节点,不能再添加一个并列的容器组件。

组件属性方法

组件的属性方法可以更改组件的外观。语法:

组件() {
    // 组件内容
}.属性方法(参数)
 .属性方法(参数) //...
组件属性方法描述
.width(200)宽度
.height(200)高度
.backgroundColor(Color.pink)背景色
.fontSize(50)字体大小
.fontWeight(FontWeight.Bold)字体粗细

下面接着上方的代码为这些字体加一些样式。

@Entry
@Component
struct Index {
  build() {
    Column() {
      Text("学生列表")
      Row() {
        Text("张三").backgroundColor(Color.Red)
          .fontColor(Color.Orange)
          .fontSize(50)
        Text("李四").backgroundColor(Color.Blue)
          .fontColor(Color.Pink)
          .fontStyle(FontStyle.Italic)
        Text("王五")
          .backgroundColor(Color.Gray)
          .fontWeight(FontWeight.Bolder)
      }
    }
  }
}
image-20240321181945528
image-20240321181945528

字体颜色

语法:.fontColor(颜色值)。其中,颜色值可以是Color的枚举值,或者为#开头的十六进制颜色图,和CSS一样。如:

Text("灰色").fontColor(Color.Gray)
Text("红色").fontColor("#FF0000")
image-20240321182852757
image-20240321182852757

文字溢出省略号、行高

  1. 文字溢出省略号

语法:.textOverflow({ overflow: Textflow.XXX })。需要配合.maxLines(行数)使用。

XXX的可选值有Clip、Ellipsis、None。

  1. 行高

语法:.lineHeight(数字)

Text("HarmonyOS开发初体验")
    .fontSize(20)
    .fontWeight(FontWeight.Bolder)
    .width('100%')
    .lineHeight(30)
Text("方舟开发框架(简称ArkTS)为HarmonyOS应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。")
    .textOverflow({overflow: TextOverflow.Ellipsis})
    .maxLines(2)
image-20240321184015031
image-20240321184015031

图片组件

图片组件用于向界面中展示图片。图片的路径一般放在项目目录下的main/resources/base/media目录中。

图片组件用Image表示。若我们的图片位于本地且放在了上述目录下,那么使用图片组件的语法就是Image($r('app.media.文件名'))

// 使用网络上的图片
Image("https://imqi1.com/static/img/imqi1-144.png")
    .width('50%')
Text("ImQi1图标")

若要使用本地图片,则需要将图片放入src/main/resources/base/media目录下,然后代码中使用$r来引用本地图片。

Image($r("app.media.icon")).width('50%')
Text("应用图标")

输入框

输入框可以接收用户输入的文本,并将其存储到变量中。输入框使用TextInput函数,语法为TextInput({placeholder: '占位符文本'}).属性方法()

.type(InputType.XXX)可以设置输入框的类型。

TextInput({placeholder: "请输入用户名"})
TextInput({placeholder: "请输入密码"}).type(InputType.Password)
image-20240321202322860
image-20240321202322860

按钮

按钮是可以点击的组件,创建语法是Button("按钮文本")

TextInput({placeholder: "请输入用户名"}).width("80%")
TextInput({placeholder: "请输入密码"}).type(InputType.Password).width("80%")
Button("登录").width("40%")
image-20240321202531541
image-20240321202531541

设计资源 - svg图标

svg是一种向量形式的图片,它对于png、jpg等图片格式相比,可以任意放大缩小且不失真,可以改颜色。

我们可以从鸿蒙设计官网下载图标。网址为https://developer.harmonyos.com/cn/design/harmonyos-icon/。open in new window

选好图标后,将图标复制到src/main/resources/base/media目录下,然后参照上方的语法引用即可。

布局组成

和H5界面开发一样,一个盒子由边框、内边距和组件内容组成。盒子与盒子之间还有外边距来互相隔开一段距离。

我们可以为组件添加内边距来让组件内容和边框存在一段距离,语法为.padding(距离)

Text("组件内容")
    .fontSize(50)
    .backgroundColor(Color.Gray)
    .fontColor(Color.White)
    .padding(20)
image-20240324132413107
image-20240324132413107

我们也可以为padding传入一个对象,分别设置四个方向的距离。语法如下。

Text("组件内容")
    .fontSize(50)
    .backgroundColor(Color.Gray)
    .fontColor(Color.White)
    .padding({
    right: 10,
    bottom: 20,
    left: 30,
    top: 40
})
image-20240324132548709
image-20240324132548709

设置外边距margin也是这样的语法,这里不再演示。

布局组成中的边框

边框的属性要更多一些,因为边框是由线条组成的,线条可以具有宽度、颜色、风格等。

设置边框的语法为.border(样式),其中样式需要是一个对象。

Text("设置四周同样的边框")
    .padding(20)
    .border({
    width: 4,  //设置宽度
    color: Color.Red,  //设置红色
    style: BorderStyle.Solid  //设置实线
})

初次之外,还可以为每个边框单独设置样式。

Text("四个边框单独设置")
    .padding(20)
    .border({
    width: {
        left: 2,
        right: 4
    },
    color: {
        left: Color.Red,
        right: Color.Blue
    },
    style: {
        left: BorderStyle.Dashed,  //虚线
        right: BorderStyle.Dotted   //省略号样式
    }
})

组件圆角

组件圆角的语法为.borderRadius(参数)。参数可以使用数值,代表每个方向的圆角大小,也可以使用一个对象,单独设置每个角。对象可用的属性分别是topLeft、topRight、bottomLeft、bottomRight,分别代表四个方向。

Text("没有圆角")
    .padding(20)
    .backgroundColor(Color.Blue)
    .fontColor(Color.White)
Text("使用数值设置圆角")
    .backgroundColor(Color.Gray)
    .padding(20)
    .borderRadius(20)
Text("四个圆角单独设置")
    .backgroundColor(Color.Green)
    .borderRadius({
    topLeft: 10,
    topRight: 20,
    bottomLeft: 30,
    bottomRight: 40
}).padding(20)
   .width(200)
   .height(70)
image-20240324152112333
image-20240324152112333

背景图片

背景样式包含背景色、背景图,可以为背景图设置位置、尺寸等等。

背景图使用.backgroundImage(背景图地址[, 是否平铺])来设置。背景图地址可以使用$r引用资源文件夹文件,也可以使用URL引用远程地址的资源。是否平铺是一个可选参数,可选值为ImageRepeat的几个值,有X、Y、XY和NoRepeat。

Text("设置背景图")
    .padding(50)
    .border({
    width: 1,
    style: BorderStyle.Dashed
}).backgroundImage($r("app.media.icon"))
image-20240324154923086
image-20240324154923086
Text("设置背景图")
    .padding(50)
    .border({
    width: 1,
    style: BorderStyle.Dashed
}).backgroundImage($r("app.media.icon"), ImageRepeat.XY)
image-20240324155029646
image-20240324155029646

设置背景图具体位置

背景图位置使用.backgroundImagePosition(坐标对象 或 位置枚举)来设置。

Text("设置背景图")
    .padding(50)
    .border({
    width: 1,
    style: BorderStyle.Dashed
}).backgroundImage($r("app.media.icon"))
  .backgroundImagePosition({x: 100, y: 100})
image-20240324155345399
image-20240324155345399

也可以使用位置枚举,如让它居中。

Text("设置背景图")
  .padding(50)
  .border({
    width: 1,
    style: BorderStyle.Dashed
  })
  .backgroundImage($r("app.media.icon"))
  .backgroundImagePosition(Alignment.Center)
image-20240324155431887
image-20240324155431887

Alignment的属性包含:

  • TopStart,

  • Top,

  • TopEnd,

  • Start,

  • Center,

  • End,

  • BottomStart,

  • Bottom,

  • BottomEnd。

单位问题与vp2px

由于不同终端的分辨率和长宽有很大差别,导致像素块大小不一致。若简单地以像素作为显示单位,则在不同终端的显示大小或比例会有很大差别。由此产生了一种虚拟像素单位vp,它相对于不同的像素会自动转换,保证不同设备视觉一致。函数vp2px用于将vp单位的长度转换为px单位的长度。

我们使用.width().height()设置长宽时,单位默认为vp;但我们设置图像时,图片背景相对位置的单位为px。于是我们可以使用vp2px函数将它们进行转换。

Text("设置背景图")
  .padding(50)
  .width(400)
  .height(300)
  .border({
    width: 1,
    style: BorderStyle.Dashed
  })
  .backgroundImage($r("app.media.icon"))
  .backgroundImagePosition({
    x: vp2px(200),
    y: vp2px(100)
  })
image-20240325223408483
image-20240325223408483

背景尺寸大小

设置背景尺寸大小等同于缩放背景图片,语法为.backgroundImageSize(宽高对象 或 枚举)。参数可以选择{width: 尺寸, height: 尺寸},也可以使用枚举,语法为ImageSize,可选值有:

  • Contain:等比例缩放背景图,当宽或高与组件尺寸相同停止缩放;
  • Cover:等比例缩放背景图至图片完全覆盖组件范围;
  • Auto:默认,原图尺寸。
Text("设置背景图")
  .padding(20)
  .width(200)
  .height(100)
  .border({
    width: 1,
    style: BorderStyle.Dashed
  })
  .fontColor(Color.White)
  .backgroundImage($r("app.media.bg"))
  .backgroundImageSize(ImageSize.Cover)
image-20240325224153394
image-20240325224153394

界面开发 - 布局

线性布局

线性布局通过线性容器Column和Row创建。其中,Column内的元素沿垂直方向排列,Row内的元素沿水平方向排列。

线性布局 - 排列在主方向上的对齐方式

语法:.justifyContent(FlexAlign.XXX),其中XXX的可选值有:

  • Start:顶部对齐,
  • Center:中心对齐,
  • End:底部对齐,
  • SpaceBetween:第一个和最后一个元素紧贴顶部和底部,然后剩余元素在中央等距排布,
  • SpaceAround:所有元素等间隔排列,非最两边的元素等距排列,最两边的元素和边隔开一半的间隔,
  • SpaceEvently:所有元素和边等间隔排列。
Column({ space: 20 }) {
// Row({ space: 20 }) {
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
// .justifyContent(FlexAlign.End)
// .justifyContent(FlexAlign.Center)
// .justifyContent(FlexAlign.SpaceBetween)
// .justifyContent(FlexAlign.SpaceAround)
// .justifyContent(FlexAlign.SpaceEvenly)

线性布局 - 排列在交叉轴方向上的对齐方式

交叉轴即元素在水平或垂直方向上的对齐方式,语法为.alignItems(参数),参数按照交叉轴的方向选择对应的枚举类型。

交叉轴在水平方向上使用HorizontalAlign对齐,三个枚举分别为Start、Center和End,在Column组件内使用。交叉轴在垂直方向上使用VerticalAlign对齐,三个枚举分别为Top、Center、Bottom,在Row组件内使用。

  Text("")  
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Start)
// .alignItems(HorizontalAlign.Center)
// .alignItems(HorizontalAlign.End)
Row({ space: 20 }) {
  Text("")  
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
  Text("")
    .width(200)
    .height(50)
    .backgroundColor(Color.Gray)
}
.width('100%')
.height('100%')
.alignItems(VerticalAlign.Top)
// .alignItems(VerticalAlign.Center)
// .alignItems(VerticalAlign.Bottom)

自适应伸缩

设置了自适应伸缩的子元素和兄弟元素,会按照权重分配主轴的空间。语法为.layoutWeight(数字)

Row(){
  Text("左侧自适应")
    .layoutWeight(1)
    .height(40)
    .backgroundColor(Color.Green)
  Text("右侧固定")
    .width(80)
    .height(40)
    .backgroundColor(Color.Orange)
}

Row(){
  Text("第一块")
    .layoutWeight(1)
    .height(40)
    .backgroundColor(Color.Pink)
  Text("第二块")
    .layoutWeight(2)
    .height(40)
    .backgroundColor(Color.Orange)
  Text("第二块")
    .layoutWeight(3)
    .height(40)
    .backgroundColor(Color.Yellow)
}
image-20240327225013637
image-20240327225013637

弹性布局

弹性布局Flex用于让元素流式布局,它们可以具有一定的排列顺序、对齐方式,在元素超出宽度时还可以换行。弹性布局的语法为Flex(){ 组件 }

Flex() {
  Text()
    .width(100)
    .height(100)
    .backgroundColor(Color.Green)
    .border({
      color: Color.Red,
      width: 2
    })
  Text()
    .width(100)
    .height(100)
    .backgroundColor(Color.Green)
    .border({
      color: Color.Red,
      width: 2
    })
  Text()
    .width(100)
    .height(100)
    .backgroundColor(Color.Green)
    .border({
      color: Color.Red,
      width: 2
    })
}
.width(400)
.height(400)
.backgroundColor("#f1f1f1")
image-20240401224419120
image-20240401224419120

Flex()支持传递一个对象,里面可以定义对齐方式。

Flex({
    direction: FlexDirection.XXX,
    justifyContent: FlexAlign.XXX,
    alignItems: ItemsAlign.XXX
}) {
    组件们
}

direction的可选值有Row、Column、RowReverse、ColumnReverse,分别对应从左向右、从上到下、从右到左,从下到上排列。

justifyContent的可选值有Start、Center、End、SpaceBetween、SpaceAround、SpaceEvenly,它们的效果和线性布局对应的值效果相同。

alignItems的可选值有Auto、Start、Center、End、Baseline、Stretch。

最后一个就是Flex布局的元素若超出是否换行。若不换行则元素的宽度会自适应调节。

修改Flex布局的换行规则通过Flex传入参数对象的wrap属性更改,它的值可选为FlexWrap.NoWrap或FlexWrap.Wrap。

    Flex({
      wrap: FlexWrap.Wrap
    }) {
      Text()
        .width(100)
        .height(100)
        .backgroundColor(Color.Green)
        .border({
          color: Color.Red,
          width: 2
        })
      Text()
        .width(100)
        .height(100)
        .backgroundColor(Color.Green)
        .border({
          color: Color.Red,
          width: 2
        })
      Text()
        .width(100)
        .height(100)
        .backgroundColor(Color.Green)
        .border({
          color: Color.Red,
          width: 2
        })
      Text()
        .width(100)
        .height(100)
        .backgroundColor(Color.Green)
        .border({
          color: Color.Red,
          width: 2
        })
      Text()
        .width(100)
        .height(100)
        .backgroundColor(Color.Green)
        .border({
          color: Color.Red,
          width: 2
        })
    }
    .width(400)
    .height(400)
    .backgroundColor("#f1f1f1")
image-20240401225402906
image-20240401225402906
上次编辑于:
贡献者: 棋.