前言:工具安装与使用
1.安装DevEco Studio
HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者
2.工具使用指南及如何构建应用
DevEco Studio使用指南-工具-HarmonyOS应用开发
构建第一个ArkTS应用(Stage模型)-快速入门-入门-HarmonyOS应用开发
一.初识ArkTS语言
ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。
1.ArkTS在TS的基础上主要扩展了如下能力:
1.1 声明式UI、自定义组件
- ArkTS使用声明式UI描述用户界面,布局更直观易懂。
- 用户可以自定义组件,动态扩展UI元素的能力增强
1.2 多维度的状态管理机制
- 在UI开发框架中,与UI相关联的数据可以在组件内使用。
- 可以在不同组件层级间传递数据,比如父子组件之间、爷孙组件之间。
- 可以在应用全局范围内传递或跨设备传递数据。
- 从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。
1.3 渲染控制的能力
- 条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。
- 循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
- 数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
2.基本语法概述
2.1ArkTS的基本组成
- 装饰器: 用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。
- UI描述:以声明式的方式来描述UI的结构,例如build()方法中的代码块。
- 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。
- 系统组件:ArkUI框架中默认内置的基础和容器组件,可直接被开发者调用,比如示例中的Column、Text、Divider、Button。
- 属性方法:组件可以通过链式调用配置多项属性,如fontSize()、width()、height()、backgroundColor()等。
- 事件方法:组件可以通过链式调用设置多个事件的响应逻辑,如跟随在Button后面的onClick()。
2.2声明式UI描述
无参数
如果组件的接口定义没有包含必选构造参数,则组件后面的“()”不需要配置任何内容。例如,Divider组件不包含构造参数:
Column() {
Text('item 1')
Divider()
Text('item 2')
}
有参数
如果组件的接口定义包含构造参数,则在组件后面的“()”配置相应参数。
Image('https://xyz/test.jpg')
3.自定义组件
自定义组件具有以下特点:
- 可组合:允许开发者组合使用系统组件、及其属性和方法。
- 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
- 数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。
- 使用@Component修饰
自定义组件的基本结构
- struct:自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。
说明:自定义组件名、类名、函数名不能和系统组件(如:Text,Image)名相同。
- @Component:@Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰。
- build()函数:build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数。
@Component
struct MyComponent {
build() {
}
}
- @Entry:@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件。
@Entry
@Component
struct MyComponent {
build() {
}
}
自定义组件的基本用法
@Component
struct HelloComponent {
@State message: string = 'Hello, World!';
build() {
// HelloComponent自定义组件组合系统组件Row和Text
Row() {
Text(this.message)
.onClick(() => {
// 状态变量message的改变驱动UI刷新,UI从'Hello, World!'刷新为'Hello, ArkUI!'
this.message = 'Hello, ArkUI!';
})
}
}
}
说明:如果在另外的文件中引用该自定义组件,需要使用export关键字导出,并在使用的页面import该自定义组件。
@Component
//导出
export default struct HelloComponent {
...
build() {
...
}
}
//导入
import HelloComponent from '文件路径'
HelloComponent可以在其他自定义组件中的build()函数中多次创建,实现自定义组件的重用。
@Entry
@Component
struct ParentComponent {
build() {
Column() {
Text('ArkUI message')
HelloComponent({ message: 'Hello, World!' });
Divider()
HelloComponent({ message: '你好!' });
}
}
}
4.装饰器
4.1@Builder装饰器:自定义构建函数
用于抽取部分公共代码封装为自定义构建函数:如下封装一个删除按钮
PS:要是@Builder装饰器在组件内不需要加 function 修饰
//组件外部
@Builder function DeleteButton (index){...}
//使用方法:
DeleteButton(index)
//组件内部
@Builder DeleteButton (index){...}
//使用方法:
this.DeleteButton(index)
- 允许在自定义组件内定义一个或多个@Builder方法,该方法被认为是该组件的私有、特殊类型的成员函数。
- 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。
- 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
- 全局的自定义构建函数可以被整个应用获取,不允许使用this和bind方法。
- 如果不涉及组件状态变化,建议使用全局的自定义构建方法。
@Builder DeleteButton (index){
Button(){
Image($r('app.media.ic_public_delete_filled'))
.fillColor(Color.White)
.width(20)
.height(20)
}
.width(40)
.height(40)
.backgroundColor(Color.Red)
.margin({right:10})
.onClick(()=>{
// 1.删除任务
this.tasks.splice(index,1)
// 2.更新任务总数
this.handlerTotal()
})
}
4.2 @Styles装饰器:定义组件重用样式
封装公共样式:如下封装卡片样式
- @Styles方法不支持参数,反例如下。
// 反例: @Styles不支持参数
@Styles function globalFancy (value: number) {
.width(value)
}
- @Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
// 全局
@Styles function functionName() { ... }
// 在组件内
@Component
struct FancyUse {
@Styles fancy() {
.height(100)
}
}
- 定义在组件内的@Styles可以通过this访问组件的常量和状态变量,并可以在@Styles里通过事件来改变状态变量的值,示例如下:
@Component
struct FancyUse {
@State heightValue: number = 100
@Styles fancy() {
.height(this.heightValue)
.backgroundColor(Color.Yellow)
.onClick(() => {
this.heightValue = 200
})
}
}
- 组件内@Styles的优先级高于全局@Styles。
框架优先找当前组件内的@Styles,如果找不到,则会全局查找。
使用场景
// 定义在全局的@Styles封装的样式
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@Entry
@Component
struct FancyUse {
@State heightValue: number = 100
// 定义在组件内的@Styles封装的样式
@Styles fancy() {
.width(200)
.height(this.heightValue)
.backgroundColor(Color.Yellow)
.onClick(() => {
this.heightValue = 200
})
}
build() {
Column({ space: 10 }) {
// 使用全局的@Styles封装的样式
Text('FancyA')
.globalFancy ()
.fontSize(30)
// 使用组件内的@Styles封装的样式
Text('FancyB')
.fancy()
.fontSize(30)
}
}
}
4.3 @Extend装饰器:定义扩展组件样式
- 和@Styles不同,@Extend仅支持定义在全局,不支持在组件内部定义。
- 和@Styles不同,@Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的@Extend的方法。
如下是Text组件的私有属性的公共样式
@Extend(Text) function finishedTask(){
.decoration({type:TextDecorationType.LineThrough})
.fontColor('#B1B2B1')
.fontSize(18)
}
//预定义的finishedTask 也可以当做传参
@Extend(Text) function finishedTask(size:number){
.decoration({type:TextDecorationType.LineThrough})
.fontColor('#B1B2B1')
.fontSize(size)
}
@Entry
@Component
struct Task{
build() {
Row({ space: 10 }) {
Text('Task')
.finishedTask(16)
Text('Task')
.finishedTask(24)
}
}
}
4.4 stateStyles:多态样式
stateStyles是属性方法,可以根据UI内部状态来设置样式,类似于css伪类,但语法不同。ArkUI提供以下四种状态:
- focused:获焦态。
- normal:正常态。
- pressed:按压态。
- disabled:不可用态(禁用状态)。
Button('Button1')
.stateStyles({
focused: {
.backgroundColor(Color.Pink)
},
pressed: {
.backgroundColor(Color.Black)
},
normal: {
.backgroundColor(Color.Red)
}
})
.margin(20)
5.参数传递规则
按引用传递参数
按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。
ABuilder( $$ : { paramA1: string, paramB1 : string } );
@Builder function ABuilder($$: { paramA1: string }) {
Row() {
Text(`UseStateVarByReference: ${$$.paramA1} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
// 在Parent组件中调用ABuilder的时候,将this.label引用传递给ABuilder
ABuilder({ paramA1: this.label })
Button('Click me').onClick(() => {
// 点击“Click me”后,UI从“Hello”刷新为“ArkUI”
this.label = 'ArkUI';
})
}
}
}
按值传递参数
调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候按引用传递。
@Builder function ABuilder(paramA1: string) {
Row() {
Text(`UseStateVarByValue: ${paramA1} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
ABuilder(this.label)
}
}
}