HarmonyOS 开发

环境搭建

下载DevEcoStudio

安装

配置开发环境

环境错误处理

运行第一个程序 Hello World

应用预览

TypeScript 基本语法

变量声明

TypeScript在JavaScript的基础上加入了静态类型检查功能,因此每一个变量都有固定的数据类型。
let msg: String = 'Hello World'
let 代表 声明变量的关键字,const 则代表常量
msg 则为可自定义的变量名
String 则为该变量的数据类型


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// String: 字符串,可以用单引号或双引号
let msg: String = 'Hello World'
// number 数值:整数、浮点数都ok
let age: number = 21.5
// boolean 布尔
let finished: boolean = true
// any:不确定类型,可以是任意类型
let a: any = 'Bob'
// union:联合类型,可以是多个指定类型的一种
let u: string|number|boolean = 21
// Object 对象
let o = {name: 'Jason', age: 20}
console.log(o.name)
console.log(o['age'])
// Array: 数组、元素可以是任意其他类型
let names: Array<String> = ['Bob','Tom','Jack']
let ages: number[] = [1,5,2,7,8]
console.log(names[0])

条件控制

TypeScript 与 大多数编程语言一样支持if-elseswitch的条件控制语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let num: number = 21
// 判断奇数
if(num % 2 === 1){
console.log(num + '是奇数')
} else {
console.log(num + '是偶数')
}

if(num > 0) {
console.log(num + '是正数')
}else if (num < 0) {
console.log(num + '是负数')
}else {
console.log(num + '为零')
}

注意: 在TypeScript空字符串数字零nullunderfined,都被认为是False,其他值则为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let grade: string = 'A'
switch (grade) {
case 'A': {
console.log('优秀')
break
}
case 'B': {
console.log('合格')
break
}
case 'C': {
console.log('不合格')
break
}
default: {
console.log('非法输入')
break
}
}

循环迭代

TypeScript支持 forwhile循环,并且为一些内置类型如 Array提供了快捷迭代语法。

1
2
3
4
5
6
7
8
9
10
11
12
// for 循环
for(let i = 0; i < 10; i++) {
console.log('输出' + i + '次')
}

// while 循环
let i = 1
while(i <= 10) {
console.log('输入' + i + '次')
i++
}

1
2
3
4
5
6
7
8
9
10
11
12
// 定义 字符串数组
let names: string[] = ['Jack','Lucy']

// for in 迭代器
for(const i in names) {
console.log(i + ':' + names[i])
}

// for of 迭代器
for(const name of names) {
console.log(name)
}

函数

TypeScript通常利用 function关键字来声明函数、并支持可选参数、默认参数、剪头函数等特殊语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 无返回值函数,返回值 void 可以省略
function sayHello(name: string):void {
console.log('你好' + name + '!')
}
sayHello('Jack')

// 有返回值函数
function sum(x:number, y:number):number {
return x + y
}
let result = sum(21,18)
console.log('21 + 18 = ' + result)

// 箭头函数
const sayHi = (name: string) => {
console.log('Hi' + name +'!')
}
sayHi('Jack')
1
2
3
4
5
6
7
8
9
// 可选参数,在参数名后面加 ?,表示该参数是可选的 
function sayHello(name?: String) {
// 判断 name 是否有值,如果无值则赋给一个默认值
name = name ? name : '陌生人'
console.log('你好,' + name + '!')
}

sayHello('Jack')
sayHello()
1
2
3
4
5
6
7
// 参数默认值,在参数后面赋值,表示参数默认值
// 如果调用者没有传参,则使用默认值
function sayHello(name: string = '陌生人') {
console.log('你好,' + name + '!')
}
sayHello('Jack')
sayHello()

类和接口

TypeScript具备面向对象变成的基本语法,例如 interfaceclassenum等。也具备 继承、多态等面向对象基本特征。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义枚举
enum Msg {
HI = 'HI',
HELLO = 'Hello'
}

// 定义接口,抽象方法接受枚举参数
interface A {
say(msg: Msg):void
}

// 实现接口
class B implements A {
say(msg: Msg):void {
console.log(msg + ', I am B')
}
}

const a:A = new B()
a.say(Msg.HI)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 定义矩形类
class Rectangle {
// 定义私有成员变量
private width: number
private length: number
// 构造函数
constructor(width: number, length: number){
this.width = width
this.length = length
}
// 成员方法
public area(): number {
return this.width * this.length
}
}

class Square extends Rectangle {
constructor(side: number) {
// 调用父类构造
super(side, side)
}
}

let s = new Square(10)
console.log('正方形的面积为:' + s.area())

模块开发

应用复杂时,我们可以把通用功能抽取到单独的ts中,每一个文件都是模块(module)。模块可以相互加载,提高代码复用性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export class Rectangle {
// 定义私有成员变量
private width: number
private length: number
// 构造函数
constructor(width: number, length: number){
this.width = width
this.length = length
}
}
// 定义工具方法,并通过export导出
export function area(rec: Rectangle):number {
return rec.width * rec.length
}
1
2
3
4
5
6
// 通过 import语法导入,from后面写文件地址
import {Rectangle, area} from '../Rectangle'

let r = new Rectangle(10,20)

console.log('面积为:' + area(r))

快速入门

ArkUI基本结构

image.png
这个示例中所包含的ArkTS声明式开发范式的基本组成说明如下:

  • 装饰器

用来装饰类、结构体、方法以及变量,赋予其特殊的含义,如上述示例中 @Entry@Component@State 都是装饰器。具体而言, @Component 表示这是个自定义组件; @Entry 则表示这是个入口组件; @State 表示组件中的状态变量,此状态变化会引起 UI 变更。

  • 自定义组件

可复用的 UI 单元,可组合其它组件,如上述被 @Component 装饰的 struct Hello

  • UI 描述

声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。

  • 内置组件

框架中默认内置的基础和布局组件,可直接被开发者调用,比如示例中的 ColumnTextDividerButton

  • 事件方法

用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()。

  • 属性方法

用于组件属性的配置,统一通过属性方法进行设置,如fontSize()width()height()color() 等,可通过链式调用的方式设置多项属性。
从UI框架的需求角度,ArkTSTS的类型系统的基础上,做了进一步的扩展:定义了各种装饰器、自定义组件和UI描述机制,再配合UI开发框架中的UI内置组件、事件方法、属性方法等共同构成了应用开发的主体。在应用开发中,除了UI的结构化描述之外,还有一个重要的方面:状态管理。如上述示例中,用 @State 装饰过的变量 myText ,包含了一个基础的状态管理机制,即 myText 的值的变化会自动触发相应的 UI 变更 (Text组件)。ArkUI 中进一步提供了多维度的状态管理机制。和 UI 相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间,爷孙组件之间,也可以是全局范围内的传递,还可以是跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和 UI 的联动。
总体而言,ArkUI开发框架通过扩展成熟语言、结合语法糖或者语言原生的元编程能力、以及UI组件、状态管理等方面设计了统一的UI开发范式,结合原生语言能力共同完成应用开发。这些构成了当前ArkTS基于TS的主要扩展。

ArkUI组件 - Image 组件

  1. 使用Image组件并设置图片源

Image(src: string|PixelMap|Resource)

  1. string格式,通常用来加载网络图片,需要申请网络权限:ohos.permission.INTERNET

Image('https://xxx.png')
b. PixelMap格式,可以加载像素图,常用在图片编辑中
Image(pixelMapObject)
c. Resource格式,可以加载本地图片,推荐使用
Image($r('app.media.mate60'))
Image($rawfile('mate60.png'))

  1. 添加图片属性
    1
    2
    3
    4
    5
    Image($r('app.media.icon'))
    .width(100) // 宽度
    .height(100) // 高度
    .borderRadius(10) // 边框圆角
    .interpolation(ImageInterpolation.HIGH) // 图片插值

ArkUI组件 - Text

  1. 使用Text组件来声明文本内容

Text(content?: string|Resource)
a. string格式,直接书写文本内容
Text('图片宽度')
b. Resource资源,读取本地资源文件
Text($r('app.string.width_label'))

1
2
3
4
Text('')
.fontSize(24)
.fontWidth(FontWeight.Bolder)
.fontColor('#666')

ArkUI组件 - TextInput

  1. 声明 TextInput组件:

TextInput({placeholder?: ResourceStr, text?: ResourceStr})
a. placeholder 输入框中无输入时提示的文本
TextInput({placeholder: '请输入账号或手机号'})
b. text 输入框当前的文本内容
TextInput({text: 'it'})

  1. 添加属性和事件
    1
    2
    3
    4
    5
    6
    7
    8
    TextInput({text: '当前输入文本'})
    .width(150) // 宽
    .height(50) // 高
    .backgroundColor('#fff') // 背景色
    .type(InputType.password) // 输入框内容
    .onChange((value:string) => { // 回调函数
    this.imageWidth = value
    })

ArkUI组件 - Button

  1. 声明 Button组件,label是按钮文字

Button(label?: ResourceStr)
a.文字性按钮
Button('点我)
b.自定义按钮
Button{
Image($r('app.meida.search)).width(20).margin(10)
}

  1. 添加属性和事件
    1
    2
    3
    4
    5
    6
    7
    Button('点我')
    .width(100)
    .height(30)
    .type(ButtonType.Normal)
    .onClick(() => { //按钮类型
    // 处理点击事件
    })

ArkUI组件 - Slider

  1. 声明Silder滑动条租金

Silder(options?:SliderOptions)

  1. 添加属性和事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Slider({
    min: 0, // 最小值
    max: 200, // 最大值
    value: 30, // 当前值
    step: 10, // 滑动步长
    style: SliderStyle.OutSet, // Inset
    direction: Axis.Horizontal, // Vertical
    reverse: false // 是否反向滑动
    })
    .width('90%')
    .showTips(true) //是否展示value百分比
    .blockColor('#36D')
    .onChange(value => {
    // value 则为当前滑块值
    })

ArkUI组件 - Column 和 Row

image.png


属性方法名 说明 参数
justifyContent 设置子元素主轴的对齐格式 FlexAlign枚举
alignItems 设置子元素交叉轴的对齐格式 Row容器使用VerticalAlign枚举
Column容器使用HorizontalAlign枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entry
@Component
struct Index {
build(){
Column({space : 20}){
Text('item1')
Text('item2')
Text('item3')
Text('item4')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

关于子元素主轴方向的对齐方向 属性名:justifyContent 参数:FlexAlign枚举
关于子元素交叉轴方向的对齐方向 属性名:alignItems 参数:Row容器使用VerticalAlign枚举
Column容器使用HorizontalAlign枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@Entry
@Component
struct Demo {
@State imageWidth: number = 200

build(){
Column(){
Row(){
Image($r('app.media.mate60'))
.width(this.imageWidth)
}
.width('100%')
.height(400)
.justifyContent(FlexAlign.Center)



Row(){
Text('图片宽度:')
.fontSize(22)
.fontWeight(FontWeight.Bolder)
.fontColor('#666')

TextInput({text: this.imageWidth.toFixed(0)})
.width(200)
.onChange((value:string) => {
this.imageWidth = parseInt(value)
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({left: 20, right: 20,bottom: 10})

Divider()
.width('90%')

Row(){
Button('缩小')
.width(80)
.type(ButtonType.Normal)
.onClick(() => {
if (this.imageWidth && this.imageWidth > 10) {
this.imageWidth -= 10
}
})

Button('放大')
.width(80)
.type(ButtonType.Normal)
.onClick(() => {
if (this.imageWidth && this.imageWidth < 300) {
this.imageWidth += 10
}
})
}
.margin({top: 20})
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)

Slider({
min: 0,
max: 300,
value: this.imageWidth,
step: 10
})
.width('100%')
.blockColor('#36D')
.trackThickness(7)
.showTips(true)
.onChange(value => {
this.imageWidth = value
})
.margin({top: 200})
}
.height('100%')
.width('100%')

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Item {
name: string
image: ResourceStr
price: number
discount: number

constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}

@Entry
@Component
struct Index {

// 商品数据
private items: Array<Item> = [
new Item('华为Mate60', $r('app.media.mate60'),6999, 500),
new Item('MateBookProX', $r('app.media.mateBookProX'),13999),
new Item('WatchGT4', $r('app.media.watchGT4'),1438),
new Item('FreeBuds Pro3', $r('app.media.freeBudsPro3'),1499),
new Item('Mate X5', $r('app.media.mateX5'),12999)
]


build(){
Column({space: 8}){
Row(){
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.margin({bottom: 20})

Row({space: 10}){
Image($r('app.media.mate60'))
.width(100)
Column({space: 4}){
Text('华为Mate60')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('¥ 6999')
.fontColor('#F36')
.fontSize(18)
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.backgroundColor('#fff')
.width('100%')
.borderRadius(20)
.height(120)
.padding(10)
}
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class Item {
name: string
image: ResourceStr
price: number
discount: number

constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}

@Entry
@Component
struct Index {

// 商品数据
private items: Array<Item> = [
new Item('华为Mate60', $r('app.media.mate60'),6999, 500),
new Item('MateBookProX', $r('app.media.mateBookProX'),13999),
new Item('WatchGT4', $r('app.media.watchGT4'),1438),
new Item('FreeBuds Pro3', $r('app.media.freeBudsPro3'),1499),
new Item('Mate X5', $r('app.media.mateX5'),12999)
]


build(){
Column({space: 8}){
Row(){
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.margin({bottom: 20})

ForEach(
this.items,
(item: Item) => {
Row({space: 10}){
Image((item.image))
.width(100)
Column({space: 4}){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.backgroundColor('#fff')
.width('100%')
.borderRadius(20)
.height(120)
.padding(10)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)
}
}

完成最终效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class Item {
name: string
image: ResourceStr
price: number
discount: number

constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}

@Entry
@Component
struct Index {

// 商品数据
private items: Array<Item> = [
new Item('华为Mate60', $r('app.media.mate60'),6999, 500),
new Item('MateBookProX', $r('app.media.mateBookProX'),13999),
new Item('WatchGT4', $r('app.media.watchGT4'),1438),
new Item('FreeBuds Pro3', $r('app.media.freeBudsPro3'),1499),
new Item('Mate X5', $r('app.media.mateX5'),12999)
]


build(){
Column({space: 8}){
Row(){
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.margin({bottom: 20})

ForEach(
this.items,
(item: Item) => {
Row({space: 10}){
Image((item.image))
.width(100)
Column({space: 4}){
if(item.discount){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('原价:¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
.fontColor('#666')
.decoration({type: TextDecorationType.LineThrough})
Text('折扣价:¥ ' + (item.price - item.discount))
.fontColor('#F36')
.fontSize(18)
Text('补贴信息:¥ ' + item.discount)
.fontColor('#F36')
.fontSize(18)
} else {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.backgroundColor('#fff')
.width('100%')
.borderRadius(20)
.height(120)
.padding(10)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)
}
}

ArkUI组件 - List

  1. 列表 (List) 是一种复杂容器,具备以下特点:
    1. 列表项(ListItem) 数量过多超出屏幕后,会自动提供滚动功能
    2. 列表项(ListItem) 既可以纵向排列,也可以横向排列
1
2
3
4
5
6
List(space : 10}){
ForEach([1,2,3,4],item => {
// 列表项内容,只能包含一个根组件
})
}
.width('100%')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Item {
name: string
image: ResourceStr
price: number
discount: number

constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}

@Entry
@Component
struct Index {

// 商品数据
private items: Array<Item> = [
new Item('华为Mate60', $r('app.media.mate60'),6999, 500),
new Item('MateBookProX', $r('app.media.mateBookProX'),13999),
new Item('WatchGT4', $r('app.media.watchGT4'),1438),
new Item('FreeBuds Pro3', $r('app.media.freeBudsPro3'),1499),
new Item('Mate X5', $r('app.media.mateX5'),12999),
new Item('Mate X5', $r('app.media.mateX5'),12999),
new Item('Mate X5', $r('app.media.mateX5'),12999)
]


build(){
Column({space: 8}){
Row(){
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height(30)
.margin({bottom: 20})

List({space: 9}){
ForEach(
this.items,
(item: Item) => {
ListItem(){
Row({space: 10}){
Image((item.image))
.width(100)
Column({space: 4}){
if(item.discount){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('原价:¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
.fontColor('#666')
.decoration({type: TextDecorationType.LineThrough})
Text('折扣价:¥ ' + (item.price - item.discount))
.fontColor('#F36')
.fontSize(18)
Text('补贴信息:¥ ' + item.discount)
.fontColor('#F36')
.fontSize(18)
} else {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.backgroundColor('#fff')
.width('100%')
.borderRadius(20)
.height(120)
.padding(10)
}
}
)
}
.width('100%')
.layoutWeight(1)


}
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)
}
}

ArkUI组件 - 自定义组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class Item {
name: string
image: ResourceStr
price: number
discount: number

constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}


import {Header} from '../components/Header'

@Builder function itemCard(item:Item){
Row({space: 10}){
Image((item.image))
.width(100)
Column({space: 4}){
if(item.discount){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('原价:¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
.fontColor('#666')
.decoration({type: TextDecorationType.LineThrough})
Text('折扣价:¥ ' + (item.price - item.discount))
.fontColor('#F36')
.fontSize(18)
Text('补贴信息:¥ ' + item.discount)
.fontColor('#F36')
.fontSize(18)
} else {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('¥ ' + item.price)
.fontColor('#F36')
.fontSize(18)
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.backgroundColor('#fff')
.width('100%')
.borderRadius(20)
.height(120)
.padding(10)
}

@Entry
@Component
struct Index {

// 商品数据
private items: Array<Item> = [
new Item('华为Mate60', $r('app.media.mate60'),6999, 500),
new Item('MateBookProX', $r('app.media.mateBookProX'),13999),
new Item('WatchGT4', $r('app.media.watchGT4'),1438),
new Item('FreeBuds Pro3', $r('app.media.freeBudsPro3'),1499),
new Item('Mate X5', $r('app.media.mateX5'),12999)
]


build(){
Column({space: 8}){
// 标题部分
Header({title: '商品列表'})
.margin({bottom: 10})

ForEach(
this.items,
(item: Item) => {
itemCard(item)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)
}
}
@Component
export struct Header{
  private title: ResourceStr;
  build(){
    Row(){
      Text(this.title)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Blank()
      Image($r('app.media.ic_public_refresh'))
        .width(30)
    }
    .width('100%')
    .height(30)
  }
}