HarmonyOS 鸿蒙学习笔记1-DevEco Studio、ArtTS、ArkUI

打印 上一主题 下一主题

主题 1033|帖子 1033|积分 3099

一.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
  1. // String(原生的构造函数) vs string (ts中的类型) 
  2. var myname:string = "字符" 
  3. var mybool:boolean = false
  4. var mynumber:number = 100
  5. var mylist:Array<string> = ["111","222","3333"]
  6. var myname2:string | number | boolean = 100
  7. var myname3:string | number = "kerwin"
  8. var mylist2:Array<string| number> = [1,2,"kerwin"]
  9. var mylist3:(string| number)[] = [1,2,"kerwin"]
复制代码
1-2 定义平凡函数

接口形貌形状
```typescript
  1. interface SearchFunc {
  2.   (source: string, subString: string): boolean;
  3. }
  4. //对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 
  5. let mySearch: SearchFunc;
  6. mySearch = function(src: string, sub: string): boolean {
  7.   let result = src.search(sub);
  8.   return result > -1;
  9. }
复制代码

传参```js
 
  1. function Test(list:String[],text?:String,...args:String[]):void{
  2.         console.log(list,text,args)
  3.     }
  4.     Test(["1111","2222"]) 
  5.     //list:["1111","2222"] text: undefined args: []
  6.     
  7.     Test(["0","1"],"a","b","c") 
  8.     
  9.     //list:["0","1"] text: "a" args: ["b","c"]
复制代码
类型断言as```typescript
 
  1. function Test( mytext:string|number ){
  2.     console.log((mytext as string).length) //对
  3.     console.log((mytext as any).length) //对
  4.     console.log((mytext as string[]).length) //错,原声明没有这个类型,无法断言
  5. }
复制代码
1-3 接口形貌

```js
  1. interface PropInter {
  2.    name: string | number; 
  3.    firstName?: string;//可选属性
  4.    lastName?: string;//可选属性
  5.    // [propName: string]: any 任意属性
  6. }
复制代码
1-4 泛型

泛型是指代码在利用时才指定类型,即在实例化时作为参数指明这些类型
的设计模式。泛型的参数代表类的类型,而不是函数参数是类的某个实例。
> 在 API 文档中你会发现基础数组类型`List`的实际类型是`List`。 <…> 符号将 List 标记为 *泛型* (或 *参数化*) 类型。 这种类型具有形式化的参数。 通常情况下,利用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。
- 精确地指定泛型类型可以生成更好的代码;
- 泛型来减少很多重复的代码;
- 泛型可以在多种类型之间定义同一个实现;
- 泛型利用时可以静态查抄和推断类型,如果不符合泛型类型,会报编译错误。
2.声明式UI-总算入活了


**有参数/无参数**
```tsx
  1. Column() {
  2.   Text('item 1') //有参数
  3.   Divider() //无参数
  4.   Text('item 2')
  5. }
复制代码
**配置事件**
事件方法以“.”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。
- 利用箭头函数配置组件的事件方法。
  ```tsx
  1.   Button('Click me')
  2.     .onClick(() => {
  3.       this.myText = 'ArkUI';
  4.     })
复制代码

  
- 利用匿名函数表达式配置组件的事件方法,要求利用bind,以确保函数体中的this指向当前组件。
  ```tsx
  1.   Button('add counter')
  2.     .onClick(function(){
  3.       this.counter += 2;
  4.     }.bind(this))
复制代码

  
- 利用组件的成员函数配置组件的事件方法。
  ```tsx
  1.   myClickHandler(): void {
  2.     this.counter += 2;
  3.   }
  4.   ...
  5.   Button('add counter')
  6.     .onClick(this.myClickHandler.bind(this))
复制代码

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

Builder所装饰的函数遵照build()函数语法规则,开辟者可以将重复利用的UI元素抽象成一个方法,在build方法里调用。
**自定义组件内自定义构建函数**
定义的语法:
```
  1. @Builder MyBuilderFunction(){ ... }
复制代码

```
利用方法:
```
  1. this.MyBuilderFunction(){ ... }
复制代码

```
 **全局自定义构建函数**
定义的语法:
```
  1. @Builder function MyGlobalBuilderFunction(){ ... }
复制代码

```
利用方法:
```
  1. MyGlobalBuilderFunction()
复制代码

```
 **按引用转达参数**
按引用转达参数时,转达的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用转达参数的范式。
**按值转达参数**
调用@Builder装饰的函数默认按值转达。当转达的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当利用状态变量的时间,保举利用按引用转达参数
4.组件重用样式-简单又便宜

如果每个组件的样式都需要单独设置,在开辟过程中会出现大量代码在举行重复样式设置,固然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式举行复用的装饰器@Styles。
@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。
- 当前@Styles仅支持[通用属性]和[通用事件]。
- @Styles方法不支持参数
- @Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
**留意:**
组件内@Styles的优先级高于全局@Styles。
框架优先找当前组件内的@Styles,如果找不到,则会全局查找。
5.扩展组件样式-留意和上面的区别

```tsx
  1. @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
  1. @Entry
  2. @Component
  3. struct StateStylesSample {
  4.   build() {
  5.     Column() {
  6.       Button('Click me')
  7.         .stateStyles({
  8.           focused: {
  9.             .backgroundColor(Color.Pink)
  10.           },
  11.           pressed: {
  12.             .backgroundColor(Color.Black)
  13.           },
  14.           normal: {
  15.             .backgroundColor(Color.Yellow)
  16.           }
  17.         })
  18.     }.margin('30%')
  19.   }
  20. }
复制代码

```
7.循环渲染-ForEach

```typescript
  1. ForEach(
  2.   arr: Array,
  3.   itemGenerator: (item: any, index?: number) => void,
  4.   keyGenerator?: (item: any, index?: number): string => string
  5. )
复制代码

```
在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
  1. @Component
  2. struct HelloComponent {
  3.   @State message: string = 'Hello, World!';
  4.   build() {
  5.     // HelloComponent自定义组件组合系统组件Row和Text
  6.     Row() {
  7.       Text(this.message)
  8.         .onClick(() => {
  9.           // 状态变量message的改变驱动UI刷新,UI从'Hello, World!'刷新为'Hello, ArkUI!'
  10.           this.message = 'Hello, ArkUI!';
  11.         })
  12.     }
  13.   }
  14. }
复制代码
```
- 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
  1. // 创建新实例并使用给定对象初始化
  2. let storage = new LocalStorage({ 'PropA': 47 });
  3. @Component
  4. struct Child {
  5.   // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
  6.   @LocalStorageLink('PropA') storLink2: number = 1;
  7.   build() {
  8.     Button(`Child from LocalStorage ${this.storLink2}`)
  9.       // 更改将同步至LocalStorage中的'PropA'以及Parent.storLink1
  10.       .onClick(() => this.storLink2 += 1)
  11.   }
  12. }
  13. // 使LocalStorage可从@Component组件访问
  14. @Entry(storage)
  15. @Component
  16. struct CompA {
  17.   // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
  18.   @LocalStorageLink('PropA') storLink1: number = 1;
  19.   build() {
  20.     Column({ space: 15 }) {
  21.       Button(`Parent from LocalStorage ${this.storLink1}`) // initial value from LocalStorage will be 47, because 'PropA' initialized already
  22.         .onClick(() => this.storLink1 += 1)
  23.       // @Component子组件自动获得对CompA LocalStorage实例的访问权限。
  24.       Child()
  25.     }
  26.   }
  27. }
复制代码
```
17.AppStorage-应用全局的UI状态存储

AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。
AppStorage是在应用启动的时间会被创建的单例。它的目标是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。
AppStorage可以和UI组件同步,且可以在应用业务逻辑中被访问。
```tsx
  1. AppStorage.SetOrCreate('PropA', 47);
  2. let storage = new LocalStorage({ 'PropA': 48 });
  3. @Entry(storage)
  4. @Component
  5. struct CompA {
  6.   @StorageLink('PropA') storLink: number = 1;
  7.   @LocalStorageLink('PropA') localStorLink: number = 1;
  8.   build() {
  9.     Column({ space: 20 }) {
  10.       Text(`From AppStorage ${this.storLink}`)
  11.         .onClick(() => this.storLink += 1)
  12.       Text(`From LocalStorage ${this.localStorLink}`)
  13.         .onClick(() => this.localStorLink += 1)
  14.     }
  15.   }
  16. }
复制代码
```
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
  1. PersistentStorage.PersistProp('aProp', 47);
  2. @Entry
  3. @Component
  4. struct Index {
  5.   @State message: string = 'Hello World'
  6.   @StorageLink('aProp') aProp: number = 48
  7.   build() {
  8.     Row() {
  9.       Column() {
  10.         Text(this.message)
  11.         // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
  12.         Text(`${this.aProp}`)
  13.           .onClick(() => {
  14.             this.aProp += 1;
  15.           })
  16.       }
  17.     }
  18.   }
  19. }
复制代码

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

当开辟者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操纵。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增长了该功能。为解决此问题,ArkUI引入了@BuilderParam装饰器,@BuilderParam用来装饰指向@Builder方法的变量,开辟者可在初始化自定义组件时对此属性举行赋值,为自定义组件增长特定的功能。该装饰器用于声明任意UI形貌的一个元素,类似slot占位符。
```tsx
  1. @Component
  2. struct Child {
  3.   label: string = `Child`
  4.   @BuilderParam aBuilder0: () => void;
  5.   build() {
  6.     Column() {
  7.       this.aBuilder0()
  8.     }
  9.   }
  10. }
  11. @Entry
  12. @Component
  13. struct Parent {
  14.   label: string = `Parent`
  15.   @Builder componentBuilder() {
  16.     Text(`${this.label}`)
  17.   }
  18.   build() {
  19.     Column() {
  20.       this.componentBuilder()
  21.       Child({ aBuilder0: this.componentBuilder })
  22.     }
  23.   }
  24. }
复制代码

```
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
  1. // 定义一个返回按钮的点击事件处理函数
  2. function onBackClick(): void {
  3.   // 调用router.showAlertBeforeBackPage()方法,设置返回询问框的信息
  4.   try {
  5.     router.showAlertBeforeBackPage({
  6.       message: '您还没有完成支付,确定要返回吗?' // 设置询问框的内容
  7.     });
  8.   } catch (err) {
  9.     console.error(`Invoke showAlertBeforeBackPage failed, code is ${err.code}, message is ${err.message}`);
  10.   }
  11.   // 调用router.back()方法,返回上一个页面
  12.   router.back();
  13. }
复制代码
```
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
  1. Text(this.message)
  2. .width(100)
  3. .textOverflow({
  4. overflow:TextOverflow.Ellipsis
  5. })
  6. .maxLines(1)
  7. .fontSize(50)
  8. .fontWeight(FontWeight.Bold)
复制代码

```
2-2Button

**设置按钮类型**
Button有三种可选类型,分别为Capsule(胶囊类型)、Circle(圆形按钮)和Normal(平凡按钮),通过type举行设置。
**创建包含子组件的按钮**
```tsx
  1. Button(){
  2.           Row(){
  3.             LoadingProgress().width(50).height(50).color(Color.White)
  4.             Text("加载中").fontColor(Color.White)
  5.           }
  6.         }
  7.           .width(120)
  8.           .type(ButtonType.Capsule)
  9.           .onClick(()=>{
  10.             
  11.         })
复制代码

```
2-3TextInput

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

2-4Checkbox

```tsx
 
  1. Row(){
  2.          CheckboxGroup({group:"favor"})
  3.            // .height(0)
  4.            .selectedColor(Color.Red)
  5.            .onChange((result)=>{
  6.              console.log(JSON.stringify(result))
  7.            })
  8.          Text("全选/全不选")
  9.        }
  10.        Row(){
  11.          Row(){
  12.            Checkbox({name: '篮球',  group: 'favor'})
  13.            Text("篮球")
  14.          }
  15.          Row(){
  16.            Checkbox({name: '足球',  group: 'favor'})
  17.            Text("足球")
  18.          }
  19.          Row(){
  20.            Checkbox({name: '乒乓球',  group: 'favor'})
  21.            Text("乒乓球")
  22.          }
  23.        }
复制代码

```
3.常用组件-新面孔表态

3-1 Radio组件

```JSX
  1. Row(){
  2.           Row(){
  3.             Radio({group:"favor",value:"篮球"})
  4.               .onChange((value)=>{
  5.                  value && console.log("篮球")
  6.               })
  7.             Text("篮球")
  8.           }
  9.           Row(){
  10.             Radio({group:"favor",value:"足球"})
  11.               .onChange((value)=>{
  12.                 value && console.log("足球")
  13.               })
  14.             Text("足球")
  15.           }
  16.           Row(){
  17.             Radio({group:"favor",value:"乒乓球"})
  18.               .onChange((value)=>{
  19.                 value && console.log("乒乓球")
  20.               })
  21.             Text("乒乓球")
  22.           }
  23.         }
复制代码

```
3-2 Toggle组件

Toggle组件提供勾选框样式、状态按钮样式及开关样式。
```JSX
  1. Toggle({
  2.           type:ToggleType.Switch,
  3.           isOn:false
  4.         })
  5.         .selectedColor(Color.Red)
  6.         
  7. Toggle({
  8.           type:ToggleType.Checkbox,
  9.           isOn:false
  10.         })
  11.         .selectedColor(Color.Red)        
复制代码
```
3-3 Image组件

本地资源
```

  1. Image("images/image.jpg")
  2.           .width(300)
复制代码

```
resource资源
```
  1. Image($r('app.media.icon'))
复制代码

```
网络资源
```tsx
  1. Image('https://www.example.com/example.JPG') 
  2. 引入网络图片需申请权限ohos.permission.INTERNET,
  3.  "requestPermissions": [
  4.       {
  5.         "name" : "ohos.permission.INTERNET"
  6.       }
  7.     ],
复制代码

```
**属性objectFit**


```tsx
  1.  Image("images/image.jpg")
  2.           .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
  1. Column(){
  2.   Stack({ }) {
  3.     Column(){}.width('90%').height('100%').backgroundColor('#ff58b87c')
  4.     Text('text').width('60%').height('60%').backgroundColor('#ffc3f6aa')
  5.     Button('button').width('30%').height('30%').backgroundColor('#ff8ff3eb').fontColor('#000')
  6.   }.width('100%').height(150).margin({ top: 50 })
  7. }
复制代码

```

**对齐方式**

7.弹性布局-Flex

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

**自顺应拉伸**
flexGrow:设置父容器的剩余空间分配给此属性所在组件的比例。用于“瓜分”父组件的剩余空间。```tsx
  1. Flex() {
  2. Text('flexGrow(2)')
  3.   .flexGrow(2) 
  4.   .width(100)
  5.   .height(100)
  6.   .backgroundColor(0xF5DEB3)
  7. Text('flexGrow(3)')
  8.   .flexGrow(3)
  9.   .width(100)
  10.   .height(100)
  11.   .backgroundColor(0xD2B48C)
  12. Text('no flexGrow')
  13.   .width(100) 
  14.   .height(100)
  15.   .backgroundColor(0xF5DEB3)
  16. }.width(420).height(120).padding(10).backgroundColor(0xAFEEEE)
复制代码
```

flexShrink: 当父容器空间不足时,子组件的压缩比例。
```tsx
  1. Flex({ direction: FlexDirection.Row }) {
  2.   Text('flexShrink(3)')
  3.     .fontSize(15)
  4.     .flexShrink(3)
  5.     .width(200)
  6.     .height(100)
  7.     .backgroundColor(0xF5DEB3)
  8.   Text('no flexShrink')
  9.     .width(200)
  10.     .height(100)
  11.     .backgroundColor(0xD2B48C)
  12.   Text('flexShrink(2)')
  13.     .flexShrink(2)
  14.     .width(200)
  15.     .height(100)
  16.     .backgroundColor(0xF5DEB3)
  17. }.width(400).height(120).padding(10).backgroundColor(0xAFEEEE)
复制代码
```

8.栅格布局-GridRow与GridCol

栅格布局是一种通用的辅助定位工具,对移动装备的界面设计有较好的借鉴作用。主要优势包括:
1. 提供可循的规律:栅格布局可以为布局提供规律性的布局,解决多尺寸多装备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素举行定位和排版。
2. 同一的定位标注:栅格布局可以为系统提供一种同一的定位标注,保证不同装备上各个模块的布局一致性。这可以减少设计和开辟的复杂度,提高工作效率。
3. 机动的间距调解方法:栅格布局可以提供一种机动的间距调解方法,满意特殊场景布局调解的需求。通过调解列与列之间和行与行之间的间距,可以控制整个页面的排版效果。
4. 自动换行和自顺应:栅格布局可以完成一对多布局的自动换行和自顺应。当页面元素的数量超出了一行或一列的容量时,他们会自动换到下一行或下一列,而且在不同的装备上自顺应排版,使得页面布局更加机动和顺应性强。

- ```js
 
  1. 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
  1. Grid() {
  2.   ...
  3. }
  4. .rowsTemplate('1fr 1fr 1fr')
  5. .columnsTemplate('1fr 2fr 1fr')
复制代码
```
9-2设置子组件所占行列数

```tsx
  1. GridItem() {
  2.   Text(key)
  3.     ...
  4. }
  5. .columnStart(1)
  6. .columnEnd(2)
  7. GridItem() {
  8.   Text(key)
  9.     ...
  10. }
  11. .rowStart(5)
  12. .rowEnd(6)
复制代码
```
9-3设置主轴方向

```tsx
  1. Grid() {
  2.   ...
  3. }
  4. .maxCount(3)
  5. .layoutDirection(GridDirection.Row)
复制代码

```
10.列表List-干货不少啊

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

```tsx
  1. @Component
  2. struct ContactsList {
  3.   ...
  4.   
  5.   @Builder itemHead(text: string) {
  6.     // 列表分组的头部组件,对应联系人分组A、B等位置的组件
  7.     Text(text)
  8.       .fontSize(20)
  9.       .backgroundColor('#fff1f3f5')
  10.       .width('100%')
  11.       .padding(5)
  12.   }
  13.   build() {
  14.     List() {
  15.       ListItemGroup({ header: this.itemHead('A') }) {
  16.         // 循环渲染分组A的ListItem
  17.         ...
  18.       }
  19.       ...
  20.       ListItemGroup({ header: this.itemHead('B') }) {
  21.         // 循环渲染分组B的ListItem
  22.         ...
  23.       }
  24.       ...
  25.     }
  26.   }
  27. }
复制代码
```
List组件的sticky属性配合ListItemGroup组件利用,用于设置ListItemGroup中的头部组件是否呈现吸顶效果或者尾部组件是否呈现吸底效果。
```tsx
  .sticky(StickyStyle.Header)  // 设置吸顶,实现粘性标题效果
```
侧滑菜单在许多应用中都很常见。例如,通讯类应用通常会给消息列表提供侧滑删除功能,即用户可以通过向左侧滑列表的某一项,再点击删除按钮删除消息,如下图所示。
ListItem的swipeAction属性可用于实现列表项的左右滑动功能。swipeAction属性方法初始化时有必填参数SwipeActionOptions,其中,start参数表现设置列表项右滑时起始端滑出的组件,end参数表现设置列表项左滑时尾端滑出的组件。
在消息列表中,end参数表现设置ListItem左滑时尾端划出自定义组件,即删除按钮。在初始化end方法时,将滑动列表项的索引传入删除按钮组件,当用户点击删除按钮时,可以根据索引值来删除列表项对应的数据,从而实现侧滑删除功能。
```tsx
  1. @Entry
  2. @Component
  3. struct MessageList {
  4.   @State messages: object[] = [
  5.     // 初始化消息列表数据
  6.     ...
  7.   ];
  8.   @Builder itemEnd(index: number) {
  9.     // 侧滑后尾端出现的组件
  10.     Button({ type: ButtonType.Circle }) {
  11.       Image($r('app.media.ic_public_delete_filled'))
  12.         .width(20)
  13.         .height(20)
  14.     }
  15.     .onClick(() => {
  16.       this.messages.splice(index, 1);
  17.     })
  18.     ...
  19.   }
  20.   build() {
  21.     ...
  22.       List() {
  23.         ForEach(this.messages, (item, index) => {
  24.           ListItem() {
  25.             ...
  26.           }
  27.           .swipeAction({ end: this.itemEnd.bind(this, index) }) // 设置侧滑属性
  28.         }, item => item.id.toString())
  29.       }
  30.     ...
  31.   }
  32. }
复制代码

```
11.Tabs容器-组件导航

当页面信息较多时,为了让用户能够聚焦于当前显示的内容,需要对页面内容举行分类,提高页面空间利用率。[Tabs])组件可以在一个页面内快速实现视图内容的切换,一方面提升查找信息的效率,另一方面精简用户单次获取到的信息量。
Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。TabContent是内容页,TabBar是导航页签栏,页面布局如下图所示,根据不同的导航类型,布局会有区别,可以分为底部导航、顶部导航、侧边导航,其导航栏分别位于底部、顶部和侧边。

每一个TabContent对应的内容需要有一个页签,可以通过TabContent的tabBar属性举行配置。在如下TabContent组件上设置属性tabBar,可以设置其对应页签中的内容,tabBar作为内容的页签。```tsx
  1. Tabs({ barPosition: BarPosition.Start }) {
  2.   TabContent() {
  3.     Text('首页的内容').fontSize(30)
  4.   }
  5.   .tabBar('首页')
  6.   TabContent() {
  7.     Text('推荐的内容').fontSize(30)
  8.   }
  9.   .tabBar('推荐')
  10.   TabContent() {
  11.     Text('发现的内容').fontSize(30)
  12.   }
  13.   .tabBar('发现')
  14.   
  15.   TabContent() {
  16.     Text('我的内容').fontSize(30)
  17.   }
  18.   .tabBar("我的")
  19. }
复制代码
```
**限制导航栏的滑动切换**
默认情况下,导航栏都支持滑动切换,在一些内容信息量需要举行多级分类的页面,如支持底部导航+顶部导航组合的情况下,底部导航栏的滑动效果与顶部导航出现冲突,此时需要限制底部导航的滑动,避免引起不好的用户体验。

```tsx
  1. Tabs({ barPosition: BarPosition.End }) {
  2.   TabContent(){
  3.     Column(){
  4.       Tabs(){
  5.         // 顶部导航栏内容
  6.         ...
  7.       }
  8.     }
  9.     .backgroundColor('#ff08a8f1')
  10.     .width('100%')
  11.   }
  12.   .tabBar('首页')
  13.   // 其他TabContent内容:发现、推荐、我的
  14.   ...
  15. }
  16. .scrollable(false)
复制代码
```
**自定义导航栏**
对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会组合文字以及对应语义图标表现页签内容,这种情况下,需要自定义导航页签的样式。

```
  1. @Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
  2.   Column() {
  3.     Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
  4.       .size({ width: 25, height: 25 })
  5.     Text(title)
  6.       .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
  7.   }
  8.   .width('100%')
  9.   .height(50)
  10.   .justifyContent(FlexAlign.Center)
  11. }
复制代码

```
```
  1. TabContent() {
  2.   Column(){
  3.     Text('我的内容')  
  4.   }
  5.   .width('100%')
  6.   .height('100%')
  7.   .backgroundColor('#007DFF')
  8. }
  9. .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
  1. // xxx.ets
  2. @Entry
  3. @Component
  4. struct WebComponent {
  5.  private WebController:WebviewController = new webview.WebviewController()
  6.   build() {
  7.     Column() {
  8.       Web({ src: 'https://developer.harmonyos.com/', controller: this.WebController })
  9.     }
  10.   }
  11. }
复制代码

```
**加载本地网页**
前面实现了Web组件加载在线网页,Web组件同样也可以加载本地网页。起首在main/resources/rawfile目次下创建一个HTML文件,然后通过$rawfile引用本地网页资源,示例代码如下:
```
  1. // xxx.ets
  2. @Entry
  3. @Component
  4. struct SecondPage {
  5.   private WebController:WebviewController = new webview.WebviewController()
  6.   build() {
  7.     Column() {
  8.       Web({ src: $rawfile('index.html'), controller: this.WebController })
  9.     }
  10.   }
  11. }
复制代码

```
**启用JavaScript**
如果您希望加载的网页在Web组件中运行JavaScript,则必须为您的Web组件启用JavaScript功能,默认情况下是允许JavaScript实行的。
```
  1. Web({ src:'https://www.example.com', controller:this.controller })
  2.     .javaScriptAccess(true)
复制代码

```
14.Video组件-播放视频

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


15.方便的动画

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

**显式动画的接口为:**
```js
  1. animateTo(value: AnimateParam, event: () => void): void
  2.  animateTo({ duration: 1000, curve: Curve.Ease }, () => {
  3.         // 动画闭包中根据标志位改变控制第一个Button宽高的状态变量,使第一个Button做宽高动画
  4.         if (this.flag) {
  5.           this.myWidth = 100;
  6.           this.myHeight = 50;
  7.         } else {
  8.           this.myWidth = 200;
  9.           this.myHeight = 100;
  10.         }
  11.         this.flag = !this.flag;
  12.       });
复制代码

```
**属性动画的接口为:**
```js
  1. animation(value: AnimateParam)
  2.  .animation({ duration: 1000, curve: Curve.Ease })
复制代码

```
**组件内转场动画的接口为:**
```js
  1. transition(value: TransitionOptions)
  2. Button()
  3.   .transition({ type: TransitionType.Insert, translate: { x: 200, y: -200 }, opacity: 0 })
  4.   .transition({ type: TransitionType.Delete, rotate: { x: 0, y: 0, z: 1, angle: 360 } })
复制代码
```
**PageTransitionEnter的接口为:**
```js
  1. PageTransitionEnter({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number})
复制代码

```
**PageTransitionExit的接口为:**
```js
  1. PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number})
复制代码

```
```js
  1. pageTransition() {
  2.     // 定义页面进入时的效果,从右侧滑入,时长为1000ms,页面栈发生push操作时该效果才生效
  3.     PageTransitionEnter({ type: RouteType.Push, duration: 1000 })
  4.       .slide(SlideEffect.Right)
  5.     // 定义页面进入时的效果,从左侧滑入,时长为1000ms,页面栈发生pop操作时该效果才生效
  6.     PageTransitionEnter({ type: RouteType.Pop, duration: 1000 })
  7.       .slide(SlideEffect.Left)
  8.     // 定义页面退出时的效果,向左侧滑出,时长为1000ms,页面栈发生push操作时该效果才生效
  9.     PageTransitionExit({ type: RouteType.Push, duration: 1000 })
  10.       .slide(SlideEffect.Left)
  11.     // 定义页面退出时的效果,向右侧滑出,时长为1000ms,页面栈发生pop操作时该效果才生效
  12.     PageTransitionExit({ type: RouteType.Pop, duration: 1000 })
  13.       .slide(SlideEffect.Right)
  14.   }
复制代码
```

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

去皮卡多

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表