跳至主要內容

鸿蒙开发

大约 24 分钟约 7098 字

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

配置环境 & 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

绝对定位

绝对定位可以控制组件位置实现层叠效果。它参照父组件左上角进行偏移,不再占有原来的位置。

语法:.position({ x: 水平偏移量, y: 垂直偏移量 })

Column() {
  Text()
    .width(100)
    .height(100)
    .backgroundColor(Color.Green)
    .border({
      color: Color.Red,
      width: 2
    })
  Text()
    .width(100)
    .height(100)
    .backgroundColor(Color.Orange)
    .border({
      color: Color.Red,
      width: 2
    })
    .position({
      x: 150,
      y: 150
    })
  Text()
    .width(100)
    .height(100)
    .backgroundColor(Color.Green)
    .border({
      color: Color.Red,
      width: 2
    })
}
image-20240406112331243
image-20240406112331243

可以看到原来三个元素为顺序排列,但是加了position绝对定位之后,原来的位置已经不占了,然后相对于父元素的左上角向右向下平移。

层级

若多个应用了position的组件相互覆盖,有些组件会被遮住,且这些组件的排列顺序是按照声明顺序从最顶部排列到最底部。我们可以通过使用层级显式定义组件的层级位置。

语法:.zIndex(整数)。若整数值越高,这个组件就越接近顶部。

Text()
  .width(100)
  .height(100)
  .backgroundColor(Color.Green)
  .border({
    color: Color.Red,
    width: 2
  })
Text()
  .width(100)
  .height(100)
  .backgroundColor(Color.Orange)
  .border({
    color: Color.Red,
    width: 2
  })
  .position({
    x: 50,
    y: 50
  }).zIndex(3)
Text()
  .width(100)
  .height(100)
  .backgroundColor(Color.Green)
  .border({
    color: Color.Red,
    width: 2
  })
image-20240406112935341
image-20240406112935341

层叠布局

绝对加层级定位可以很方便地为组件布局,但代码过多,层叠布局为我们提供了简洁的布局方式,可以更方便地使用绝对和层叠定位。

语法:Stack({ alignContent:Alignment.对齐方式 }){ 组件们 }

Stack({
    alignContent: Alignment.Center //默认值
}) {
    Text("Item1")
        .width(400)
        .height(400)
        .backgroundColor(Color.Orange)
    Text("Item2")
        .width(300)
        .height(300)
        .backgroundColor(Color.Green)
    Text("Item3")
        .width(200)
        .height(200)
        .backgroundColor(Color.Yellow)
}
.width(600)
.height(600)
.backgroundColor(Color.Pink)
image-20240406115915810
image-20240406115915810

Alignment的可选值有TopStart、Top、TopEnd、Start、Center、End、BottomStart、Bottom、BottomEnd。

高级

字符串拼接

由于ArkTS为强类型语言,有很多组件要求传入的参数都为字符串,如Text()。直接将数字变量传入进去会报错,这时我们就需要将数字转换成字符串。若我们需要使用组件中的数字进行运算,我们还需要将字符串转化为数字。

字符串拼接使用 + 运算符,或使用模板字符串。+ 运算符可以直接相加两个字符串对象,模板字符串则是使用`表示字符串对象,然后在内部使用${变量}。后者可以将字符串内部的占位符替换成具体的变量值。

let num: number = 123;
let str: string = "这是一个字符串";
console.log(`字符串的值:${str}   数字的值:${num}`)  // 字符串的值:这是一个字符串   数字的值:123

若 + 旁边的两个运算对象有一个为字符串,那么得到的结果也是字符串。

类型转换

  1. 字符串转换为数字
  • Number():字符串,直接转为数字,若转换失败则返回NaN。(转换失败是由于字符串中含有非数字字符)

  • parseInt():去掉小数部分转数字,转换失败返回NaN;parseFloat():保留小数部分,转换失败返回NaN。

实际使用时常常使用Number()转换为数字。

console.log("Number(num1)", Number(num1))  // 1.1
console.log("Number(num2)", Number(num2))  // 1.9
console.log("Number(num3)", Number(num3))  // NaN
console.log("Number(num4)", Number(num4))  // NaN

console.log("parseInt(num1)", parseInt(num1))  // 1
console.log("parseInt(num2)", parseInt(num2))  // 1
console.log("parseInt(num3)", parseInt(num3))  // 1
console.log("parseInt(num4)", parseInt(num4))  // NaN

console.log("parseFloat(num1)", parseFloat(num1))  // 1.1
console.log("parseFloat(num2)", parseFloat(num2))  // 1.9
console.log("parseFloat(num3)", parseFloat(num3))  // 1.1
console.log("parseFloat(num4)", parseFloat(num4))  // NaN
  1. 数字转换为字符串
  • toString():数字直接转换为字符串。

  • toFixed():数字转换为字符串,可选择保留几位小数,四舍五入。

let num1: number = 1.234
let num2: number = 1234

@Entry
@Component
struct Index {
  build() { // 在这里面写代码
    Column() {
      Text(num1.toString())
      Text(num2.toString())
      Text(num1.toFixed(2))
      Text(num2.toFixed(2))
    }
  }
}

事件交互

事件交互可以让我们为组件的事件被触发后执行某些事情,比如点击一个按钮后弹出一个文字提示。

我们以最简单的点击事件为例。点击事件使用.onClick(callback)语法,可以加在按钮上。callback为一个函数,在事件被触发时执行。

function show() {
  AlertDialog.show({
    message: "弹出一个文本提示"
  })
}

@Entry
@Component
struct Index {
  build() { // 在这里面写代码
    Column() {
      Row() {
        Button("按钮")
          .onClick(show)
      }.height("100%")
    }.width("100%")
  }
}
image-20240406202143589
image-20240406202143589

状态管理

这里先引入两个概念:

  • 普通变量:只在初始化时渲染,后续将不再刷新。
  • 状态变量:需要装饰器装饰,改变会引起UI的渲染刷新(必须设置类型和初始值)

所以我们要改变元素的状态时,就可以通过改变状态变量的值来达到目的。

@Entry
@Component
struct Index {
  @State msg: string = "Hello World!"
  build() {
    Row() {
      Column() {
        Text(this.msg)
        Button("改变文字").onClick(() => {
          this.msg = "你好,世界!"
        })
      }.width("100%")
    }.height("100%")
  }
}

在组件内定义的变量,无论是何种变量,都需要使用this进行访问。

let name: string = "张三";

@Entry
@Component
struct Index {
  age: number = 18;
  @State msg: string = "Hello World!"
  build() {
    Row() {
      Column() {
        Text(name).onClick(()=>{
          name = "李四";
          console.log("name: ", name)  // 值被改变,但是渲染不变
        })
        Text(this.age.toString()).onClick(()=>{
          this.age = 20;
          console.log("this.age: ", this.age)  // 值被改变,但是渲染不变
        })
        Text(this.msg).onClick(()=>{
          this.msg = "你好世界";
          console.log("this.msg: ", this.msg)  // 值被改变,渲染也被改变
        })
      }.width("100%")
    }.height("100%")
  }
}

运算符

ArkTS中数字间可进行加减乘除和取余运算。

  • +:相加,返回两个数的和。
  • -:相减,返回两个数的差。
  • *:相乘,返回两个数的积。
  • /:相除,返回两个数的商。
  • %:取余,返回两个数相除后的余数。

这五个运算符和赋值运算符=配合,又可以组合出五个新的运算符。如+=num += 1,相当于num = num + 1

let num: number = 7
console.log("num + 2 = ", num + 2) // 9
console.log("num - 2 = ", num - 2) // 5
console.log("num * 2 = ", num * 2) // 14
console.log("num / 2 = ", num / 2) // 3.5
console.log("num % 2 = ", num % 2) // 1

一元运算符

ArkTS有两个一元运算符:++--,它们的作用相当于让变量增减一。

假设num为变量,则num++表示先使用num的值,再让num加一。--运算符也是这样。

let num: number = 1;
console.log("num++", num++);  //1 num=2
console.log("++num", ++num);  //3 num=3
console.log("--num", --num);  //2 num=2
console.log("num--", num--);  // 2 num=1

比较运算符

比较运算符用于比较两个数据的大小,返回一个布尔值。

  • >:大于。
  • >=:大于等于。
  • <:小于。
  • <=:小于等于。
  • ==:等于等于。
  • !=:不等于。
console.log("1>1:", 1 > 1);
console.log("1>2:", 1 > 2);
console.log("1<2:", 1 < 2);
console.log("1>=2:", 1 >= 2);
console.log("1<=2:", 1 <= 2);

==也可以判断两个字符串字面值是否一致。

逻辑运算符

逻辑运算符用于将并列的布尔运算合并,有三个:

  • &&:若两个布尔值有一个为假,则整个表达式为假。

  • ||:若两个布尔值有一个为真,则整个表达式为真。

  • !:反转运算布尔值。

console.log("false && false:", false && false);  // false && false: false
console.log("true && true:", true && true);  // true && true: true
console.log("false && true:", false && true);  // false && true: false
console.log("false || false:", false || false);  // false || false: false
console.log("true || true:", true || true);  // true || true: true
console.log("false || true:", false || true);  // false || true: true
console.log("!false:", !false);  // !false: true
console.log("!true:", !true);  // !true: false

运算符优先级

运算符优先级表示互相组合的运算符运算顺序,如我们的相加和赋值操作同时存在时,先相加再赋值,是因为赋值运算符的优先级低于相加运算符的优先级。

下表表示了运算符相互组合时运算符的优先顺序,从上往下优先级递减。

优先级顺序
小括号()
一元运算符++--!
算数运算符*/%先于+-
比较运算符><>=<=
比较运算符==!=
逻辑运算符&&后`
赋值=

数组的增加删除替换

操作语法
查找数组名[下标]、数组名.length
修改数组名[下标] = 新值
增加数组名.push(数据1, 数据2, ...)、数组名.unshift(数据1, 数据2, ...)
前者为像数组的末尾追加值,后者为向数组的开始增加值
删除数组名.pop()、数组名.shift()
任意位置删除或增加数组名.splice(操作的起始位置, 删除的个数, 新增1, 新增2)
// 数组的查看和修改

// 1. 定义一个数组
let names: string[] = ['刘小备', '吕小布', '张大飞']
console.log('整个数组',names)

// 2. 数组取值(通过下标)
console.log('数组取值', names[1])
console.log('数组长度', names.length)

// 3. 数组修改(通过下标)
names[2] = '赵云'
console.log('修改数组', names)
// 数组的增加和删除

// 定义一个数组
let songs: string[] = ['告白气球', '洋葱', '吻别']

// 添加
// 1. 往开头新增 unshift(新增的值)  返回操作后的数组的长度
songs.unshift('彩虹')
console.log('返回数组长度', songs.unshift('七里香'))
console.log('数组songs', songs)

// 2. 往结尾新增 push(新增的值)  返回操作后的数组的长度
songs.push('光辉岁月', '海阔天空')
console.log('数组', songs)

// 删除
// 1. 从开头删 shift
console.log('返回删除的项', songs.shift())
console.log('返回删除的项', songs.shift())
console.log('数组', songs)

// 2. 从结尾删 pop
songs.pop()
songs.pop()
songs.pop()
console.log('数组', songs)

// 开头(S): unshift(开头增)  shift(开头删)
// 结尾(P): push(结尾增)  pop(结尾删)
// 在任意位置删除数组元素

// 定义一个数组
let songs: string[] = ['告白气球', '洋葱', '吻别', '双节棍', '曹操']
// splice 在任意位置进行删除或新增内容
// 数组名.splice(操作的起始位置, 删除几个, 新增的项1, 新增的项2, ...)

// 1. 删除(任意位置)
songs.splice(2, 2)
console.log('数组songs', songs)

// 2. 新增(任意位置)
songs.splice(1, 0, '彩虹') // 新增

// 3. 替换(删了一项, 又加了一项)
songs.splice(1, 1, '彩虹')
console.log('数组songs', songs)

分支语句 - if和else

上次编辑于:
贡献者: 棋.