HarmonyOS 鸿蒙学习笔记

一.Hello HarmonyOS

1-开发环境搭建-支持中文放心吧

HUAWEI DevEco Studio(以下简称DevEco Studio)是基于IntelliJ IDEA Community开源版本打造,为运行在HarmonyOS和OpenHarmony系统上的应用和服务(以下简称应用/服务)提供一站式的开发平台。

作为一款开发工具,除了具有基本的代码开发、编译构建及调测等功能外,DevEco Studio还具有如下特点:

- 高效智能代码编辑:支持ArkTS、JS、C/C++等语言的代码高亮、代码智能补齐、代码错误检查、代码自动跳转、代码格式化、代码查找等功能,提升代码编写效率。
- 低代码可视化开发:丰富的UI界面编辑能力,支持自由拖拽组件和可视化数据绑定,可快速预览效果,所见即所得;同时支持卡片的零代码开发,降低开发门槛和提升界面开发效率。
- 多端双向实时预览:支持UI界面代码的双向预览、实时预览、动态预览、组件预览以及多端设备预览,便于快速查看代码运行效果。
- 多端设备模拟仿真:提供HarmonyOS本地模拟器,支持手机等设备的模拟仿真,便捷获取调试环境。

> [官方文档-搭建开发环境流程](https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/installation_process-0000001071425528-V3)

 2-不学语法,我先做个界面?

HarmonyOS低代码开发方式,具有丰富的UI界面编辑功能,例如基于图形化的自由拖拽、数据的参数化配置等,遵循[HarmonyOS JS开发规范](https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-overview-0000001056361791),通过可视化界面开发方式快速构建布局,可有效降低用户的时间成本和提升用户构建UI界面的效率。

3-兼容JS的类Web开发范式-看看就行

兼容JS的类Web开发范式的方舟开发框架,采用经典的HML、CSS、JavaScript三段式开发方式。使用HML标签文件进行布局搭建,使用CSS文件进行样式描述,使用JavaScript文件进行逻辑处理。UI组件与数据之间通过单向数据绑定的方式建立关联,当数据发生变化时,UI界面自动触发更新。此种开发方式,更接近Web前端开发者的使用习惯,快速将已有的Web应用改造成方舟开发框架应用。主要适用于界面较为简单的中小型应用开发。

4-ArkTS声明式开发范式-官方推荐

在开发一款新应用时,推荐采用声明式开发范式来构建UI,主要基于以下几点考虑:

- **开发效率:**声明式开发范式更接近自然语义的编程方式,开发者可以直观地描述UI,无需关心如何实现UI绘制和渲染,开发高效简洁。

- **应用性能:**如下图所示,两种开发范式的UI后端引擎和语言运行时是共用的,但是相比类Web开发范式,声明式开发范式无需JS框架进行页面DOM管理,渲染更新链路更为精简,占用内存更少,应用性能更佳。

- **发展趋势**:声明式开发范式后续会作为主推的开发范式持续演进,为开发者提供更丰富、更强大的能力。

5-运行应用-需要买个华为手机吗?

DevEco Studio提供模拟器供开发者运行和调试HarmonyOS应用/服务,对于Phone、TV和Wearable可以使用本地模拟器(**Local Emulator**)和远程模拟器(**Remote Emulator**),对于Tablet可以使用**Remote Emulator**运行应用/服务,对于Lite Wearable和Smart Vision可以使用**Simulator**运行应用/服务。

[在Phone和Tablet中运行应用/服务](https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/run_phone_tablat-0000001064774652-V3)

二.仓颉造字, 这是ArkTS的艺术

1.TS回顾-ArtTS继承了他

ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在[TypeScript](https://www.typescriptlang.org/)(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。因此,在学习ArkTS语言之前,建议开发者具备TS语言开发能力。

当前,ArkTS在TS的基础上主要扩展了如下能力:

- [**基本语法**]:ArkTS定义了声明式UI描述、自定义组件和动态扩展UI元素的能力,再配合ArkUI开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。
- [**状态管理**]:ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
- [**渲染控制**]:ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。

TypeScript 的定位是静态类型语言,在写代码阶段就能检查错误,而非运行阶段

类型系统是最好的文档,增加了代码的可读性和可维护性。

有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)等

1-1 变量声明

```typescript

// String(原生的构造函数) vs string (ts中的类型) 
var myname:string = "字符" 
var mybool:boolean = false
var mynumber:number = 100
var mylist:Array<string> = ["111","222","3333"]

var myname2:string | number | boolean = 100
var myname3:string | number = "kerwin"
var mylist2:Array<string| number> = [1,2,"kerwin"]
var mylist3:(string| number)[] = [1,2,"kerwin"]
1-2 定义普通函数

接口描述形状

```typescript

interface SearchFunc {
  (source: string, subString: string): boolean;
}
//对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > -1;
}

传参```js
 

 function Test(list:String[],text?:String,...args:String[]):void{
        console.log(list,text,args)
    }

    Test(["1111","2222"]) 
    //list:["1111","2222"] text: undefined args: []
    
    Test(["0","1"],"a","b","c") 
    
    //list:["0","1"] text: "a" args: ["b","c"]

类型断言as```typescript
 

function Test( mytext:string|number ){

    console.log((mytext as string).length) //对
    console.log((mytext as any).length) //对
    console.log((mytext as string[]).length) //错,原声明没有这个类型,无法断言
}
1-3 接口描述

```js

interface PropInter {
   name: string | number; 
   firstName?: string;//可选属性
   lastName?: string;//可选属性
   // [propName: string]: any 任意属性
}
1-4 泛型

泛型是指代码在使用时才指定类型,即在实例化时作为参数指明这些类型
的设计模式。泛型的参数代表类的类型,而不是函数参数是类的某个实例。

> 在 API 文档中你会发现基础数组类型`List`的实际类型是`List`。 <…> 符号将 List 标记为 *泛型* (或 *参数化*) 类型。 这种类型具有形式化的参数。 通常情况下,使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。

- 正确地指定泛型类型可以生成更好的代码;
- 泛型来减少很多重复的代码;
- 泛型可以在多种类型之间定义同一个实现;
- 泛型使用时可以静态检查和推断类型,如果不符合泛型类型,会报编译错误。

2.声明式UI-总算入活了

**有参数/无参数**

```tsx

Column() {
  Text('item 1') //有参数
  Divider() //无参数
  Text('item 2')
}

**配置事件**

事件方法以“.”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。

- 使用箭头函数配置组件的事件方法。

  ```tsx

  Button('Click me')
    .onClick(() => {
      this.myText = 'ArkUI';
    })


  

- 使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数体中的this指向当前组件。

  ```tsx

  Button('add counter')
    .onClick(function(){
      this.counter += 2;
    }.bind(this))


  

- 使用组件的成员函数配置组件的事件方法。

  ```tsx

  myClickHandler(): void {
    this.counter += 2;
  }
  ...
  Button('add counter')
    .onClick(this.myClickHandler.bind(this))


  ```

3.自定义构建函数-便宜的复用

Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。

**自定义组件内自定义构建函数**

定义的语法:

```

@Builder MyBuilderFunction(){ ... }


```

使用方法:

```

this.MyBuilderFunction(){ ... }


```

 **全局自定义构建函数**

定义的语法:

```

@Builder function MyGlobalBuilderFunction(){ ... }


```

使用方法:

```

MyGlobalBuilderFunction()


```

 **按引用传递参数**

按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。

**按值传递参数**

调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递参数

4.组件重用样式-简单又便宜

如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器@Styles。

@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。

- 当前@Styles仅支持[通用属性]和[通用事件]。
- @Styles方法不支持参数

- @Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。

**注意:**

组件内@Styles的优先级高于全局@Styles。

框架优先找当前组件内的@Styles,如果找不到,则会全局查找。

5.扩展组件样式-注意和上面的区别

```tsx

@Extend(UIComponentName) function functionName { ... }


```

- 和@Styles不同,@Extend仅支持定义在全局,不支持在组件内部定义。
- 和@Styles不同,@Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的@Extend的方法。
- 和@Styles不同,@Extend装饰的方法支持参数,开发者可以在调用时传递参数,调用遵循TS方法传值调用。
- @Extend装饰的方法的参数可以为function,作为Event事件的句柄。

- @Extend的参数可以为[状态变量](https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/arkts-state-management-overview-0000001524537145-V3),当状态变量改变时,UI可以正常的被刷新渲染。

6.多态样式-真像css伪类

stateStyles是属性方法,可以根据UI内部状态来设置样式,类似于css伪类,但语法不同。ArkUI提供以下四种状态:

- focused:获焦态。
- normal:正常态。
- pressed:按压态。
- disabled:不可用态。

```tsx

@Entry
@Component
struct StateStylesSample {
  build() {
    Column() {
      Button('Click me')
        .stateStyles({
          focused: {
            .backgroundColor(Color.Pink)
          },
          pressed: {
            .backgroundColor(Color.Black)
          },
          normal: {
            .backgroundColor(Color.Yellow)
          }
        })
    }.margin('30%')
  }
}


```

7.循环渲染-ForEach

```typescript

ForEach(
  arr: Array,
  itemGenerator: (item: any, index?: number) => void,
  keyGenerator?: (item: any, index?: number): string => string
)


```

在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。

ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即**(item: any, index: number) => { return index + '__' + JSON.stringify(item); }。**

只有当开发者在itemGenerator函数中声明了index参数,并且自定义的keyGenerator函数返回值中不包含index参数时,ArkUI框架才会在开发者自定义的keyGenerator函数返回值前添加index参数,作为最终的键值

8.条件渲染-ifelse

ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,使用if、else和else if渲染对应状态下的UI内容。

当if、else if后跟随的状态判断中使用的状态变量值变化时,条件渲染语句会进行更新,更新步骤如下:

1. 评估if和else if的状态判断条件,如果分支没有变化,请无需执行以下步骤。如果分支有变化,则执行2、3步骤:
2. 删除此前构建的所有子组件。
3. 执行新分支的构造函数,将获取到的组件添加到if父容器中。如果缺少适用的else分支,则不构建任何内容。

9.自定义组件-不便宜的复用

在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。

自定义组件具有以下特点:

- 可组合:允许开发者组合使用系统组件、及其属性和方法。
- 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
- 数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。

```tsx

@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!';
        })
    }
  }
}

```

- struct:自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。
- @Component:@Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰。
- @Entry:@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件。
- build()函数:build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数。
  - build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点。
  - 不允许声明本地变量
  - 不允许在UI描述里直接使用console.info,但允许在方法或者函数里使用
  - 不允许调用没有用@Builder装饰的方法
  - 不允许switch语法,如果需要使用条件判断,请使用if。
  - 不允许使用表达式,比如三目

10.@State组件内状态-我的心情很重要

@State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。

- 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
- 当装饰的数据类型为class或者Object时,可以观察到自身的赋值的变化,和其属性赋值的变化
  - 嵌套属性的赋值观察不到。
- 当装饰的对象是array时,可以观察到数组本身的赋值和添加、删除、更新数组的变化。

11.@Prop父子单向同步-孩子你要听话

@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。

@Prop装饰的变量和父组件建立单向的同步关系:

- @Prop变量允许在本地修改,但修改后的变化不会同步回父组件。
- 当父组件中的数据源更改时,与之相关的@Prop装饰的变量都会自动更新。如果子组件已经在本地修改了@Prop装饰的相关变量值,而在父组件中对应的@State装饰的变量被修改后,子组件本地修改的@Prop装饰的相关变量值将被覆盖。

**注意:**

| @Prop变量装饰器    | 说明                                |
| ------------------ | ----------------------------------- |
| 允许装饰的变量类型 | string、number、boolean、enum类型。 |

12.@Link父子双向同步-互相听取意见

子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。

| @Link变量装饰器    | 说明                                                         |
| ------------------ | ------------------------------------------------------------ |
| 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。类型必须被指定,且和双向绑定状态变量的类型相同。不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。说明不支持Length、ResourceStr、ResourceColor类型,Length、ResourceStr、ResourceColor为简单类型和复杂类型的联合类型。 |
| 被装饰变量的初始值 | 无,禁止本地初始化。                                         |

##### 13.嵌套类对象属性变化-藏得深也能监控你

@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:

- 被@Observed装饰的类,可以被观察到属性的变化;
- 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
- 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。

| @Observed类装饰器 | 说明                                                  |
| ----------------- | ----------------------------------------------------- |
| 类装饰器          | 装饰class。需要放在class的定义前,使用new创建类对象。 |

| @ObjectLink变量装饰器 | 说明                                                         |
| --------------------- | ------------------------------------------------------------ |
| 同步类型              | 不与父组件中的任何类型同步变量。                             |
| 允许装饰的变量类型    | 必须为被@Observed装饰的class实例,必须指定类型。不支持简单类型,可以使用@Prop。@ObjectLink的属性是可以改变的,但是变量的分配是不允许的,也就是说这个装饰器装饰变量是只读的,不能被改变。 |
| 被装饰变量的初始值    | 不允许。                                                     |

14.与后代组件双向同步-从谏如流

@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。

其中@Provide装饰的变量是在祖先节点中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先节点提供的变量。

| @Provide变量装饰器 | 说明                                                         |
| ------------------ | ------------------------------------------------------------ |
| 装饰器参数         | 别名:常量字符串,可选。如果指定了别名,则通过别名来绑定变量;如果未指定别名,则通过变量名绑定变量。 |
| 同步类型           | 双向同步。从@Provide变量到所有@Consume变量以及相反的方向的数据同步。双向同步的操作与@State和@Link的组合相同。 |
| 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。必须指定类型。@Provide变量的@Consume变量的类型必须相同。说明不支持Length、ResourceStr、ResourceColor类型,Length、ResourceStr、ResourceColor为简单类型和复杂类型的联合类型。 |

15.@Watch状态变量更改通知

@Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数。

| @Watch补充变量装饰器   | 说明                                                         |
| ---------------------- | ------------------------------------------------------------ |
| 装饰器参数             | 必填。常量字符串,字符串需要有引号。是(string) => void自定义成员函数的方法的引用。 |
| 可装饰的自定义组件变量 | 装饰器@State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp以及@StorageLink所装饰的变量均可以通过@Watch监听其变化。不允许监听常规变量。 |
| 装饰器的顺序           | 建议@State、@Prop、@Link等装饰器在@Watch装饰器之前。         |

16.LocalStorage-页面间共享状态

LocalStorage是页面级的UI状态存储,通过@Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage也可以在UIAbility内,页面间共享状态。

LocalStorage根据与@Component装饰的组件的同步类型不同,提供了两个装饰器:

- [@LocalStorageProp]():@LocalStorageProp装饰的变量和与LocalStorage中给定属性建立单向同步关系。
- [@LocalStorageLink]():@LocalStorageLink装饰的变量和在@Component中创建与LocalStorage中给定属性建立双向同步关系。

```tsx

// 创建新实例并使用给定对象初始化
let storage = new LocalStorage({ 'PropA': 47 });

@Component
struct Child {
  // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
  @LocalStorageLink('PropA') storLink2: number = 1;

  build() {
    Button(`Child from LocalStorage ${this.storLink2}`)
      // 更改将同步至LocalStorage中的'PropA'以及Parent.storLink1
      .onClick(() => this.storLink2 += 1)
  }
}
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompA {
  // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
  @LocalStorageLink('PropA') storLink1: number = 1;

  build() {
    Column({ space: 15 }) {
      Button(`Parent from LocalStorage ${this.storLink1}`) // initial value from LocalStorage will be 47, because 'PropA' initialized already
        .onClick(() => this.storLink1 += 1)
      // @Component子组件自动获得对CompA LocalStorage实例的访问权限。
      Child()
    }
  }
}

```

17.AppStorage-应用全局的UI状态存储

AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。

AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。

AppStorage可以和UI组件同步,且可以在应用业务逻辑中被访问。

```tsx

AppStorage.SetOrCreate('PropA', 47);
let storage = new LocalStorage({ 'PropA': 48 });

@Entry(storage)
@Component
struct CompA {
  @StorageLink('PropA') storLink: number = 1;
  @LocalStorageLink('PropA') localStorLink: number = 1;

  build() {
    Column({ space: 20 }) {
      Text(`From AppStorage ${this.storLink}`)
        .onClick(() => this.storLink += 1)

      Text(`From LocalStorage ${this.localStorLink}`)
        .onClick(() => this.localStorLink += 1)
    }
  }
}

```

18.PersistentStorage-持久化存储UI状态

PersistentStorage是应用程序中的可选单例对象。此对象的作用是持久化存储选定的AppStorage属性,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。

PersistentStorage允许的类型和值有:

- number, string, boolean, enum 等简单类型。
- 可以被JSON.stringify()和JSON.parse()重构的对象。例如Date, Map, Set等内置类型则不支持,以及对象的属性方法不支持持久化。

注意:

**PersistentStorage的持久化变量最好是小于2kb的数据,不要大量的数据持久化,因为PersistentStorage写入磁盘的操作是同步的,大量的数据本地化读写会同步在UI线程中执行,影响UI渲染性能。如果开发者需要存储大量的数据,建议使用数据库api。**

**PersistentStorage只能在UI页面内使用,否则将无法持久化数据。**

```tsx

PersistentStorage.PersistProp('aProp', 47);

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  @StorageLink('aProp') aProp: number = 48

  build() {
    Row() {
      Column() {
        Text(this.message)
        // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
        Text(`${this.aProp}`)
          .onClick(() => {
            this.aProp += 1;
          })
      }
    }
  }
}


```

19.@BuilderParam装饰器-插槽你懂吗

当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题,ArkUI引入了@BuilderParam装饰器,@BuilderParam用来装饰指向@Builder方法的变量,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素,类似slot占位符。

```tsx

@Component
struct Child {
  label: string = `Child`
  @BuilderParam aBuilder0: () => void;

  build() {
    Column() {
      this.aBuilder0()
    }
  }
}

@Entry
@Component
struct Parent {
  label: string = `Parent`

  @Builder componentBuilder() {
    Text(`${this.label}`)
  }

  build() {
    Column() {
      this.componentBuilder()
      Child({ aBuilder0: this.componentBuilder })
    }
  }
}


```

20.生命周期-组件的生命历程

页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:

  • - [onPageShow]():页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效。
  • - [onPageHide]():页面每次隐藏时触发一次,包括路由过程、应用进入前后台等场景,仅@Entry装饰的自定义组件生效。
  • - [onBackPress]():当用户点击返回按钮时触发,仅@Entry装饰的自定义组件生效。

组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:

  • - [aboutToAppear]():组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
  • - [aboutToDisappear]():在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。

21.再探路由-之前讲过吗?

页面路由指在应用程序中实现不同页面之间的跳转和数据传递。HarmonyOS提供了Router模块,通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面。

Router模块提供了两种跳转模式,分别是[router.pushUrl()]()和[router.replaceUrl()]()。这两种模式决定了目标页是否会替换当前页。

- router.pushUrl():目标页不会替换当前页,而是压入页面栈。这样可以保留当前页的状态,并且可以通过返回键或者调用[router.back()]()方法返回到当前页。
- router.replaceUrl():目标页会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页。

同时,Router模块提供了两种实例模式,分别是Standard和Single。这两种模式决定了目标url是否会对应多个实例。

- Standard:标准实例模式,也是默认情况下的实例模式。每次调用该方法都会新建一个目标页,并压入栈顶。
- Single:单实例模式。即如果目标页的url在页面栈中已经存在同url页面,则离栈顶最近的同url页面会被移动到栈顶,并重新加载;如果目标页的url在页面栈中不存在同url页面,则按照标准模式跳转。

**你懂了吗?**

在单实例模式下:如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近同url页面会被移动到栈顶,移动后的页面为新建页,原来的页面仍然存在栈中,页面栈的元素数量不变;如果目标页面的url在页面栈中不存在同url页面,按多实例模式跳转,页面栈的元素数量会加1。

在单实例模式下:如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近同url页面会被移动到栈顶,替换当前页面,并销毁被替换的当前页面,移动后的页面为新建页,页面栈的元素数量会减1;如果目标页面的url在页面栈中不存在同url页面,按多实例模式跳转,页面栈的元素数量不变。

**场景用法:**

场景一:有一个主页(Home)和一个详情页(Detail),希望从主页点击一个商品,跳转到详情页。同时,需要保留主页在页面栈中,以便返回时恢复状态。这种场景下,可以使用pushUrl()方法,并且使用Standard实例模式(或者省略)。

场景二:有一个登录页(Login)和一个个人中心页(Profile),希望从登录页成功登录后,跳转到个人中心页。同时,销毁登录页,在返回时直接退出应用。这种场景下,可以使用replaceUrl()方法,并且使用Standard实例模式(或者省略)。

场景三:有一个设置页(Setting)和一个主题切换页(Theme),希望从设置页点击主题选项,跳转到主题切换页。同时,需要保证每次只有一个主题切换页存在于页面栈中,在返回时直接回到设置页。这种场景下,可以使用pushUrl()方法,并且使用Single实例模式。

场景四:有一个搜索结果列表页(SearchResult)和一个搜索结果详情页(SearchDetail),希望从搜索结果列表页点击某一项结果,跳转到搜索结果详情页。同时,如果该结果已经被查看过,则不需要再新建一个详情页,而是直接跳转到已经存在的详情页。这种场景下,可以使用replaceUrl()方法,并且使用Single实例模式。

**页面返回前增加一个询问框**

```typescript

// 定义一个返回按钮的点击事件处理函数
function onBackClick(): void {
  // 调用router.showAlertBeforeBackPage()方法,设置返回询问框的信息
  try {
    router.showAlertBeforeBackPage({
      message: '您还没有完成支付,确定要返回吗?' // 设置询问框的内容
    });
  } catch (err) {
    console.error(`Invoke showAlertBeforeBackPage failed, code is ${err.code}, message is ${err.message}`);
  }

  // 调用router.back()方法,返回上一个页面
  router.back();
}

```

22-应用入口-好家伙现在才讲到入口?

UIAbility是一种包含用户界面的应用组件,主要用于和用户进行交互。UIAbility也是系统调度的单元,为应用提供窗口在其中绘制界面。

每一个UIAbility实例,都对应于一个最近任务列表中的任务。

**UIAbility的生命周期**

当用户浏览、切换和返回到对应应用的时候,应用中的UIAbility实例会在其生命周期的不同状态之间转换。

UIAbility类提供了很多回调,通过这些回调可以知晓当前UIAbility的某个状态已经发生改变:例如UIAbility的创建和销毁,或者UIAbility发生了前后台的状态切换。

三.神农尝百草, ArkUI组件大杂烩

1.ArkUI-起步式

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

- **UI:**即用户界面。开发者可以将应用的用户界面设计为多个功能页面,每个页面进行单独的文件管理,并通过页面路由API完成页面间的调度管理如跳转、回退等操作,以实现应用内的功能解耦。
- **组件:**UI构建与显示的最小单位,如列表、网格、按钮、单选框、进度条、文本等。开发者通过多种组件的组合,构建出满足自身应用诉求的完整界面。

- FA(Feature Ability)模型:HarmonyOS早期版本开始支持的模型,已经不再主推。
- Stage模型:HarmonyOS 3.1 Developer Preview版本开始新增的模型,是目前主推且会长期演进的模型。在该模型中,由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的“舞台”,因此称这种应用模型为Stage模型。

**架构图**

- 声明式UI前端

  提供了UI开发范式的基础语言规范,并提供内置的UI组件、布局和动画,提供了多种状态管理机制,为应用开发者提供一系列接口支持。

- 语言运行时

  选用方舟语言运行时,提供了针对UI范式语法的解析能力、跨语言调用支持的能力和TS语言高性能运行环境。

- 声明式UI后端引擎

  后端引擎提供了兼容不同开发范式的UI渲染管线,提供多种基础组件、布局计算、动效、交互事件,提供了状态管理和绘制能力。

- 渲染引擎

  提供了高效的绘制能力,将渲染管线收集的渲染指令,绘制到屏幕的能力。

- 平台适配层

  提供了对系统平台的抽象接口,具备接入不同系统的能力,如系统渲染管线、生命周期调度等。

**流程**

2.常用组件-水煮老熟人

2-1Text

**读取本地资源**

**省略号**

```tsx

Text(this.message)
.width(100)
.textOverflow({
overflow:TextOverflow.Ellipsis
})
.maxLines(1)
.fontSize(50)
.fontWeight(FontWeight.Bold)


```

2-2Button

**设置按钮类型**

Button有三种可选类型,分别为Capsule(胶囊类型)、Circle(圆形按钮)和Normal(普通按钮),通过type进行设置。

**创建包含子组件的按钮**

```tsx

Button(){
          Row(){
            LoadingProgress().width(50).height(50).color(Color.White)
            Text("加载中").fontColor(Color.White)
          }
        }
          .width(120)
          .type(ButtonType.Capsule)
          .onClick(()=>{
            
        })


```

2-3TextInput

TextInput有5种可选类型,分别为Normal基本输入模式、Password密码输入模式、Email邮箱地址输入模式、Number纯数字输入模式、PhoneNumber电话号码输入模式。通过type属性进行设置:

2-4Checkbox

```tsx
 

Row(){
         CheckboxGroup({group:"favor"})

           // .height(0)
           .selectedColor(Color.Red)
           .onChange((result)=>{
             console.log(JSON.stringify(result))
           })
         Text("全选/全不选")
       }

       Row(){
         Row(){
           Checkbox({name: '篮球',  group: 'favor'})
           Text("篮球")
         }
         Row(){
           Checkbox({name: '足球',  group: 'favor'})
           Text("足球")
         }
         Row(){
           Checkbox({name: '乒乓球',  group: 'favor'})
           Text("乒乓球")
         }
       }


```

3.常用组件-新面孔亮相

3-1 Radio组件

```JSX

Row(){
          Row(){
            Radio({group:"favor",value:"篮球"})
              .onChange((value)=>{
                 value && console.log("篮球")
              })
            Text("篮球")
          }
          Row(){
            Radio({group:"favor",value:"足球"})
              .onChange((value)=>{
                value && console.log("足球")
              })
            Text("足球")
          }
          Row(){
            Radio({group:"favor",value:"乒乓球"})
              .onChange((value)=>{
                value && console.log("乒乓球")
              })
            Text("乒乓球")
          }
        }


```

3-2 Toggle组件

Toggle组件提供勾选框样式、状态按钮样式及开关样式。

```JSX

Toggle({
          type:ToggleType.Switch,
          isOn:false
        })
        .selectedColor(Color.Red)
        
Toggle({
          type:ToggleType.Checkbox,
          isOn:false
        })
        .selectedColor(Color.Red)        

```

3-3 Image组件

本地资源

```

Image("images/image.jpg")
          .width(300)


```

resource资源

```

Image($r('app.media.icon'))


```

网络资源

```tsx

Image('https://www.example.com/example.JPG') 

引入网络图片需申请权限ohos.permission.INTERNET,

 "requestPermissions": [
      {
        "name" : "ohos.permission.INTERNET"
      }
    ],


```

**属性objectFit**

```tsx

 Image("images/image.jpg")
          .width(300)
          .height(300)
          .border({ width: 1 })
          .objectFit(ImageFit.None)


```

4.像素单位-解惑

为开发者提供4种像素单位,框架采用vp为基准数据单位。

总结:

1. 1vp = 在160dpi中的1px ,vp保证了不同分辨率下 视觉效果的等价性,比如一个图标,在不同分辨率下都是视觉效果是等价。

   应用场景:适合绝大多数场景

2. 1fp = 在160dpi中的1px 乘以 系统的缩放比例。

   应用场景:用来设定字体大小,且需要支持系统字体大小调整时使用

3. designWidth = 1440 , 1lpx= 1px   

   应用场景:对于UI图,部分需要高度还原的场景使用

**像素单位转换**

提供其他单位与px单位互相转换的方法。

5.线性布局-Row与Column

线性布局(LinearLayout)是开发中最常用的布局,通过线性容器[Row]()和[Column]()构建。线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。线性布局的排列方向由所选容器组件决定,Column容器内子元素按照垂直方向排列,Row容器内子元素按照水平方向排列。根据不同的排列方向,开发者可选择使用Row或Column容器创建线性布局。

- 布局容器:具有布局能力的容器组件,可以承载其他元素作为其子元素,布局容器会对其子元素进行尺寸计算和布局排列。
- 布局子元素:布局容器内部的元素。
- 主轴:线性布局容器在布局方向上的轴线,子元素默认沿主轴排列。Row容器主轴为水平方向,Column容器主轴为垂直方向。
- 交叉轴:垂直于主轴方向的轴线。Row容器交叉轴为垂直方向,Column容器交叉轴为水平方向。
- 间距:布局子元素的间距。

5-1 Row容器内子元素在水平方向上的排列

5-2 Row容器内子元素在垂直方向上的排列

5-3 Column容器内子元素在垂直方向上的排列

5-4 Column容器内子元素在水平方向上的排列

6.层叠布局-Stack

层叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局。层叠布局通过Stack容器组件实现位置的固定定位与层叠,容器中的子元素(子组件)依次入栈,后一个子元素覆盖前一个子元素,子元素可以叠加,也可以设置位置。

```tsx

Column(){
  Stack({ }) {
    Column(){}.width('90%').height('100%').backgroundColor('#ff58b87c')
    Text('text').width('60%').height('60%').backgroundColor('#ffc3f6aa')
    Button('button').width('30%').height('30%').backgroundColor('#ff8ff3eb').fontColor('#000')
  }.width('100%').height(150).margin({ top: 50 })
}


```

**对齐方式**

7.弹性布局-Flex

弹性布局Flex 提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。容器默认存在主轴与交叉轴,子元素默认沿主轴排列,子元素在主轴方向的尺寸称为主轴尺寸,在交叉轴方向的尺寸称为交叉轴尺寸。弹性布局在开发场景中用例特别多,比如页面头部导航栏的均匀分布、页面框架的搭建、多行数据的排列等等。

**自适应拉伸**

flexGrow:设置父容器的剩余空间分配给此属性所在组件的比例。用于“瓜分”父组件的剩余空间。```tsx

Flex() {
Text('flexGrow(2)')
  .flexGrow(2) 
  .width(100)
  .height(100)
  .backgroundColor(0xF5DEB3)

Text('flexGrow(3)')
  .flexGrow(3)
  .width(100)
  .height(100)
  .backgroundColor(0xD2B48C)

Text('no flexGrow')
  .width(100) 
  .height(100)
  .backgroundColor(0xF5DEB3)
}.width(420).height(120).padding(10).backgroundColor(0xAFEEEE)

```

flexShrink: 当父容器空间不足时,子组件的压缩比例。

```tsx

Flex({ direction: FlexDirection.Row }) {
  Text('flexShrink(3)')
    .fontSize(15)
    .flexShrink(3)
    .width(200)
    .height(100)
    .backgroundColor(0xF5DEB3)

  Text('no flexShrink')
    .width(200)
    .height(100)
    .backgroundColor(0xD2B48C)

  Text('flexShrink(2)')
    .flexShrink(2)
    .width(200)
    .height(100)
    .backgroundColor(0xF5DEB3)
}.width(400).height(120).padding(10).backgroundColor(0xAFEEEE)

```

8.栅格布局-GridRow与GridCol

栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。主要优势包括:

1. 提供可循的规律:栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素进行定位和排版。
2. 统一的定位标注:栅格布局可以为系统提供一种统一的定位标注,保证不同设备上各个模块的布局一致性。这可以减少设计和开发的复杂度,提高工作效率。
3. 灵活的间距调整方法:栅格布局可以提供一种灵活的间距调整方法,满足特殊场景布局调整的需求。通过调整列与列之间和行与行之间的间距,可以控制整个页面的排版效果。
4. 自动换行和自适应:栅格布局可以完成一对多布局的自动换行和自适应。当页面元素的数量超出了一行或一列的容量时,他们会自动换到下一行或下一列,并且在不同的设备上自适应排版,使得页面布局更加灵活和适应性强。

- ```js
 

 breakpoints: {value: ['320vp', '520vp', '840vp', '1080vp']}


  ```

  表示启用xs、sm、md、lg、xl共5个断点,小于320vp为xs,320vp-520vp为sm,520vp-840vp为md,840vp-1080vp为lg,大于1080vp为xl。

9.网格布局-Grid与GridItem

网格布局是由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。网格布局具有较强的页面均分能力,子组件占比控制能力,是一种重要自适应布局,其使用场景有九宫格图片展示、日历、计算器等。

9-1设置行列数量与占比

通过设置行列数量与尺寸占比可以确定网格布局的整体排列方式。Grid组件提供了rowsTemplate和columnsTemplate属性用于设置网格布局行列数量与尺寸占比。

rowsTemplate和columnsTemplate属性值是一个由多个空格和'数字+fr'间隔拼接的字符串,fr的个数即网格布局的行或列数,fr前面的数值大小,用于计算该行或列在网格布局宽度上的占比,最终决定该行或列的宽度。

```tsx

Grid() {
  ...
}
.rowsTemplate('1fr 1fr 1fr')
.columnsTemplate('1fr 2fr 1fr')

```

9-2设置子组件所占行列数

```tsx

GridItem() {
  Text(key)
    ...
}
.columnStart(1)
.columnEnd(2)


GridItem() {
  Text(key)
    ...
}
.rowStart(5)
.rowEnd(6)

```

9-3设置主轴方向

```tsx

Grid() {
  ...
}
.maxCount(3)
.layoutDirection(GridDirection.Row)


```

10.列表List-干货不少啊

列表是一种复杂的容器,当列表项达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集,例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求(如通讯录、音乐列表、购物清单等)。

ListItemGroup用于列表数据的分组展示,其子组件也是ListItem。ListItem表示单个列表项,可以包含单个子组件。

```tsx

@Component
struct ContactsList {
  ...
  
  @Builder itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  build() {
    List() {
      ListItemGroup({ header: this.itemHead('A') }) {
        // 循环渲染分组A的ListItem
        ...
      }
      ...

      ListItemGroup({ header: this.itemHead('B') }) {
        // 循环渲染分组B的ListItem
        ...
      }
      ...
    }
  }
}

```

List组件的sticky属性配合ListItemGroup组件使用,用于设置ListItemGroup中的头部组件是否呈现吸顶效果或者尾部组件是否呈现吸底效果。

```tsx
  .sticky(StickyStyle.Header)  // 设置吸顶,实现粘性标题效果
```

侧滑菜单在许多应用中都很常见。例如,通讯类应用通常会给消息列表提供侧滑删除功能,即用户可以通过向左侧滑列表的某一项,再点击删除按钮删除消息,如下图所示。

ListItem的swipeAction属性可用于实现列表项的左右滑动功能。swipeAction属性方法初始化时有必填参数SwipeActionOptions,其中,start参数表示设置列表项右滑时起始端滑出的组件,end参数表示设置列表项左滑时尾端滑出的组件。

在消息列表中,end参数表示设置ListItem左滑时尾端划出自定义组件,即删除按钮。在初始化end方法时,将滑动列表项的索引传入删除按钮组件,当用户点击删除按钮时,可以根据索引值来删除列表项对应的数据,从而实现侧滑删除功能。

```tsx

@Entry
@Component
struct MessageList {
  @State messages: object[] = [
    // 初始化消息列表数据
    ...
  ];

  @Builder itemEnd(index: number) {
    // 侧滑后尾端出现的组件
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.ic_public_delete_filled'))
        .width(20)
        .height(20)
    }
    .onClick(() => {
      this.messages.splice(index, 1);
    })
    ...
  }
  build() {
    ...
      List() {
        ForEach(this.messages, (item, index) => {
          ListItem() {
            ...
          }
          .swipeAction({ end: this.itemEnd.bind(this, index) }) // 设置侧滑属性
        }, item => item.id.toString())
      }
    ...
  }
}


```

11.Tabs容器-组件导航

当页面信息较多时,为了让用户能够聚焦于当前显示的内容,需要对页面内容进行分类,提高页面空间利用率。[Tabs])组件可以在一个页面内快速实现视图内容的切换,一方面提升查找信息的效率,另一方面精简用户单次获取到的信息量。

Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。TabContent是内容页,TabBar是导航页签栏,页面结构如下图所示,根据不同的导航类型,布局会有区别,可以分为底部导航、顶部导航、侧边导航,其导航栏分别位于底部、顶部和侧边。

每一个TabContent对应的内容需要有一个页签,可以通过TabContent的tabBar属性进行配置。在如下TabContent组件上设置属性tabBar,可以设置其对应页签中的内容,tabBar作为内容的页签。```tsx

Tabs({ barPosition: BarPosition.Start }) {
  TabContent() {
    Text('首页的内容').fontSize(30)
  }
  .tabBar('首页')

  TabContent() {
    Text('推荐的内容').fontSize(30)
  }
  .tabBar('推荐')

  TabContent() {
    Text('发现的内容').fontSize(30)
  }
  .tabBar('发现')
  
  TabContent() {
    Text('我的内容').fontSize(30)
  }
  .tabBar("我的")
}

```

**限制导航栏的滑动切换**

默认情况下,导航栏都支持滑动切换,在一些内容信息量需要进行多级分类的页面,如支持底部导航+顶部导航组合的情况下,底部导航栏的滑动效果与顶部导航出现冲突,此时需要限制底部导航的滑动,避免引起不好的用户体验。

```tsx

Tabs({ barPosition: BarPosition.End }) {
  TabContent(){
    Column(){
      Tabs(){
        // 顶部导航栏内容
        ...
      }
    }
    .backgroundColor('#ff08a8f1')
    .width('100%')
  }
  .tabBar('首页')

  // 其他TabContent内容:发现、推荐、我的
  ...
}
.scrollable(false)

```

**自定义导航栏**

对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会组合文字以及对应语义图标表示页签内容,这种情况下,需要自定义导航页签的样式。

```

@Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
  Column() {
    Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
      .size({ width: 25, height: 25 })
    Text(title)
      .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
  }
  .width('100%')
  .height(50)
  .justifyContent(FlexAlign.Center)
}


```

```

TabContent() {
  Column(){
    Text('我的内容')  
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#007DFF')
}
.tabBar(this.TabBuilder('我的', 0, $r('app.media.mine_selected'), $r('app.media.mine_normal')))

```

12.Navigation容器-我跟前面的货没联系

[Navigation]()组件一般作为页面的根容器,包括单页面、分栏和自适应三种显示模式。同时,Navigation提供了属性来设置页面的标题栏、工具栏、导航栏等。

Navigation组件的页面包含主页和内容页。主页由标题栏、内容区和工具栏组成,可在内容区中使用[NavRouter]()子组件实现导航栏功能。内容页主要显示[NavDestination]()子组件中的内容。

NavRouter是配合Navigation使用的特殊子组件,默认提供点击响应处理,不需要开发者自定义点击事件逻辑。NavRouter有且仅有两个子组件,其中第二个子组件必须是NavDestination。NavDestination是配合NavRouter使用的特殊子组件,用于显示Navigation组件的内容页。当开发者点击NavRouter组件时,会跳转到对应的NavDestination内容区。

- 单页面模式

- 分栏模式

13.Web组件-前端活过来了

ArkUI为我们提供了Web组件来加载网页,借助它我们就相当于在自己的应用程序里嵌入一个浏览器,从而非常轻松地展示各种各样的网页。

**加载在线网页**

Web组件的使用非常简单,只需要在Page目录下的ArkTS文件中创建一个Web组件,传入两个参数就可以了。其中src指定引用的网页路径,controller为组件的控制器,通过controller绑定Web组件,用于实现对Web组件的控制。

```tsx

// xxx.ets
@Entry
@Component
struct WebComponent {
 private WebController:WebviewController = new webview.WebviewController()
  build() {
    Column() {
      Web({ src: 'https://developer.harmonyos.com/', controller: this.WebController })
    }
  }
}


```

**加载本地网页**

前面实现了Web组件加载在线网页,Web组件同样也可以加载本地网页。首先在main/resources/rawfile目录下创建一个HTML文件,然后通过$rawfile引用本地网页资源,示例代码如下:

```

// xxx.ets
@Entry
@Component
struct SecondPage {
  private WebController:WebviewController = new webview.WebviewController()

  build() {
    Column() {
      Web({ src: $rawfile('index.html'), controller: this.WebController })
    }
  }
}


```

**启用JavaScript**

如果您希望加载的网页在Web组件中运行JavaScript,则必须为您的Web组件启用JavaScript功能,默认情况下是允许JavaScript执行的。

```

Web({ src:'https://www.example.com', controller:this.controller })
    .javaScriptAccess(true)


```

14.Video组件-播放视频

在手机、平板或是智慧屏这些终端设备上,媒体功能可以算作是我们最常用的场景之一。无论是实现音频的播放、录制、采集,还是视频的播放、切换、循环,亦或是相机的预览、拍照等功能,媒体组件都是必不可少的。以视频功能为例,在应用开发过程中,我们需要通过ArkUI提供的Video组件为应用增加基础的视频播放功能。借助Video组件,我们可以实现视频的播放功能并控制其播放状态。常见的视频播放场景包括观看网络上的较为流行的短视频,也包括查看我们存储在本地的视频内容。

15.方便的动画

ArkUI中,产生动画的方式是改变属性值且指定动画参数。动画参数包含了如动画时长、变化规律(即曲线)等参数。当属性值发生变化后,按照动画参数,从原来的状态过渡到新的状态,即形成一个动画。

**显式动画的接口为:**

```js

animateTo(value: AnimateParam, event: () => void): void

 animateTo({ duration: 1000, curve: Curve.Ease }, () => {
        // 动画闭包中根据标志位改变控制第一个Button宽高的状态变量,使第一个Button做宽高动画
        if (this.flag) {
          this.myWidth = 100;
          this.myHeight = 50;
        } else {
          this.myWidth = 200;
          this.myHeight = 100;
        }
        this.flag = !this.flag;
      });


```

**属性动画的接口为:**

```js

animation(value: AnimateParam)

 .animation({ duration: 1000, curve: Curve.Ease })


```

**组件内转场动画的接口为:**

```js

transition(value: TransitionOptions)

Button()
  .transition({ type: TransitionType.Insert, translate: { x: 200, y: -200 }, opacity: 0 })
  .transition({ type: TransitionType.Delete, rotate: { x: 0, y: 0, z: 1, angle: 360 } })

```

**PageTransitionEnter的接口为:**

```js

PageTransitionEnter({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number})


```

**PageTransitionExit的接口为:**

```js

PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number})


```

```js

pageTransition() {
    // 定义页面进入时的效果,从右侧滑入,时长为1000ms,页面栈发生push操作时该效果才生效
    PageTransitionEnter({ type: RouteType.Push, duration: 1000 })
      .slide(SlideEffect.Right)
    // 定义页面进入时的效果,从左侧滑入,时长为1000ms,页面栈发生pop操作时该效果才生效
    PageTransitionEnter({ type: RouteType.Pop, duration: 1000 })
      .slide(SlideEffect.Left)
    // 定义页面退出时的效果,向左侧滑出,时长为1000ms,页面栈发生push操作时该效果才生效
    PageTransitionExit({ type: RouteType.Push, duration: 1000 })
      .slide(SlideEffect.Left)
    // 定义页面退出时的效果,向右侧滑出,时长为1000ms,页面栈发生pop操作时该效果才生效
    PageTransitionExit({ type: RouteType.Pop, duration: 1000 })
      .slide(SlideEffect.Right)
  }

```

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值