HarmonyOS

发布于:2024-12-06 ⋅ 阅读:(24) ⋅ 点赞:(0)

每天学习一点点,持续更新此帖

UIAbility

UIAbility 组件是一种包含UI的应用组件,主要用于和用户交互
设计理念:原生支持应用组件的跨端迁移和多端协同、支持多设备和多窗口的形态
UIAbility组件是系统调度的基本单位,为应用提供绘制界面的窗口。

在这里插入图片描述

/**
为使应用能够正常使用UIAbility,
需要在module.json5配置文件的abilities标签中声明UIAbility
的名称、入口、标签等相关信息。
*/
{
  "module": {
    // ...
    "abilities": [
      {
        "name": "EntryAbility", // UIAbility组件的名称
        "srcEntry": "./ets/entryability/EntryAbility.ets", // UIAbility组件的代码路径
        "description": "$string:EntryAbility_desc", // UIAbility组件的描述信息
        "icon": "$media:icon", // UIAbility组件的图标
        "label": "$string:EntryAbility_label", // UIAbility组件的标签
        "startWindowIcon": "$media:icon", // UIAbility组件启动页面图标资源文件的索引
        "startWindowBackground": "$color:start_window_background", // UIAbility组件启动页面背景颜色资源文件的索引
        // ...
      }
    ]
  }
}

生命周期

生命周期

Create状态

Create状态为在应用加载过程中,UIAbility实例创建完成时触发,系统会调用onCreate()回调。可以在该回调中进行页面初始化操作,例如变量定义资源加载等,用于后续的UI展示。

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 页面初始化
    // Want是对象间信息传递的载体,可以用于应用组件间的信息传递
  }
  // ...
}

WindowStageCreate和WindowStageDestroy状态

UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI加载、设置WindowStage的事件订阅。
在UIAbility实例销毁之前,则会先进入onWindowStageDestroy()回调,可以在该回调中释放UI资源。

在这里插入图片描述

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = '[EntryAbility]';
const DOMAIN_NUMBER: number = 0xFF00;

export default class EntryAbility extends UIAbility {
  // ...
  onWindowStageCreate(windowStage: window.WindowStage): void {

	windowStage.loadContent('pages/Index', (err, data) => {
      // 应用中的UIAbility在启动过程中,需要指定启动页面,
      // 否则应用启动后会因为没有默认加载页面而导致白屏。
    });
    // 设置WindowStage的事件订阅(
    // 获焦/失焦、切到前台/切到后台、前台可交互/前台不可交互)
    try {
      windowStage.on('windowStageEvent', (data) => {
        let stageEventType: window.WindowStageEventType = data;
        switch (stageEventType) {
          case window.WindowStageEventType.SHOWN: // 切到前台
            hilog.info(DOMAIN_NUMBER, TAG, `windowStage foreground.`);
            break;
          case window.WindowStageEventType.ACTIVE: // 获焦状态
            hilog.info(DOMAIN_NUMBER, TAG, `windowStage active.`);
            break;
          case window.WindowStageEventType.INACTIVE: // 失焦状态
            hilog.info(DOMAIN_NUMBER, TAG, `windowStage inactive.`);
            break;
          case window.WindowStageEventType.HIDDEN: // 切到后台
            hilog.info(DOMAIN_NUMBER, TAG, `windowStage background.`);
            break;
          case window.WindowStageEventType.RESUMED: // 前台可交互状态
            hilog.info(DOMAIN_NUMBER, TAG, `windowStage resumed.`);
            break;
          case window.WindowStageEventType.PAUSED: // 前台不可交互状态
            hilog.info(DOMAIN_NUMBER, TAG, `windowStage paused.`);
            break;
          default:
            break;
        }
      });
    } catch (exception) {
      hilog.error(DOMAIN_NUMBER, TAG,
        `Failed to enable the listener for window stage event changes. Cause: ${JSON.stringify(exception)}`);
    }
    hilog.info(DOMAIN_NUMBER, TAG, `%{public}s`, `Ability onWindowStageCreate`);
    // 设置UI加载
    windowStage.loadContent('pages/Index', (err, data) => {
      // ...
    });
  }
	
  onWindowStageDestroy() {
    // 释放UI资源
  }
}

Foreground和Background状态

Foreground和Background状态分别在UIAbility实例切换至前台和切换至后台时触发,对应于onForeground()回调和onBackground()回调。
onForeground()回调,在UIAbility的UI可见之前,如UIAbility切换至前台时触发。可以在onForeground()回调中申请系统需要的资源,或者重新申请在onBackground()中释放的资源。
onBackground()回调,在UIAbility的UI完全不可见之后,如UIAbility切换至后台时候触发。可以在onBackground()回调中释放UI不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。

import { UIAbility } from '@kit.AbilityKit';

export default class EntryAbility extends UIAbility {
  // ...

  onForeground(): void {
    // 申请系统需要的资源,或者重新申请在onBackground()中释放的资源
  }

  onBackground(): void {
    // 释放UI不可见时无用的资源,或者在此回调中执行较为耗时的操作
    // 例如状态保存等
  }
}

当应用的UIAbility实例已创建,且UIAbility配置为singleton启动模式时,再次调用startAbility()方法启动该UIAbility实例时,只会进入该UIAbility的onNewWant()回调,不会进入其onCreate()和onWindowStageCreate()生命周期回调。应用可以在该回调中更新要加载的资源和数据等,用于后续的UI展示。

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';

export default class EntryAbility extends UIAbility {
  // ...

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 更新资源、数据
  }
}

Destroy状态

Destroy状态在UIAbility实例销毁时触发。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作。

UIAbility组件启动模式

  • singleton(单实例模式)

    每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例(由于启动的还是原来的UIAbility实例,并未重新创建一个新的UIAbility实例,此时只会进入该UIAbility的onNewWant()回调,不会进入其onCreate()和onWindowStageCreate()生命周期回调)。

  • multiton(多实例模式)

    multiton启动模式为多实例模式,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。这种情况下可以将UIAbility配置为multiton(多实例模式)。

  • specified(指定实例模式)

    specified启动模式为指定实例模式,针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)。

UIAbility组件与UI的数据同步

  • 使用EventHub进行数据通信:在基类Context中提供了EventHub对象,可以通过发布订阅方式来实现事件的传递。在事件传递前,订阅者需要先进行订阅,当发布者发布事件时,订阅者将接收到事件并进行相应处理。
// UIAbility
import { hilog } from '@kit.PerformanceAnalysisKit';
import { UIAbility, Context, Want, AbilityConstant } from '@kit.AbilityKit';

const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[EventAbility]';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 获取eventHub
    let eventhub = this.context.eventHub;
    // 执行订阅操作
    eventhub.on('event1', this.eventFunc);
    eventhub.on('event1', (data: string) => {
      // 触发事件,完成相应的业务操作
    });
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');
  }

  // ...
  eventFunc(argOne: Context, argTwo: Context): void {
    hilog.info(DOMAIN_NUMBER, TAG, '1. ' + `${argOne}, ${argTwo}`);
    return;
  }
}


// UI
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Page_EventHub {
  private context = getContext(this) as common.UIAbilityContext;

  eventHubFunc(): void {
    // 不带参数触发自定义“event1”事件
    this.context.eventHub.emit('event1');
    // 带1个参数触发自定义“event1”事件
    this.context.eventHub.emit('event1', 1);
    // 带2个参数触发自定义“event1”事件
    this.context.eventHub.emit('event1', 2, 'test');
    // 开发者可以根据实际的业务场景设计事件传递的参数
  }

  build() {
    Column() {
      // ...
      List({ initialIndex: 0 }) {
        ListItem() {
          Row() {
            // ...
          }
          .onClick(() => {
            this.eventHubFunc();
            promptAction.showToast({
              message: 'EventHubFuncA'
            });
          })
        }

        // ...
        ListItem() {
          Row() {
            // ...
          }
          .onClick(() => {
            this.context.eventHub.off('event1');
            promptAction.showToast({
              message: 'EventHubFuncB'
            });
          })
        }
        // ...
      }
      // ...
    }
    // ...
  }
}
  • 使用AppStorage/LocalStorage进行数据同步:ArkUI提供了AppStorage和LocalStorage两种应用级别的状态管理方案,可用于实现应用级别和UIAbility级别的数据同步。

AbilityStage组件容器

AbilityStage是一个Module级别的组件容器,应用的HAP在首次加载时会创建一个AbilityStage实例,可以对该Module进行初始化等操作。
AbilityStage与Module一一对应,即一个Module拥有一个AbilityStage。

DevEco Studio默认工程中未自动生成AbilityStage,如需要使用AbilityStage的能力,可以手动新建一个AbilityStage文件,具体步骤如下。

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为myabilitystage。

  2. 在myabilitystage目录,右键选择“New > ArkTS File”,新建一个文件并命名为MyAbilityStage.ets。

  3. 打开MyAbilityStage.ets文件,导入AbilityStage的依赖包,自定义类继承AbilityStage并加上需要的生命周期回调,示例中增加了一个onCreate()生命周期回调。

import { AbilityStage, Want } from '@kit.AbilityKit';

export default class MyAbilityStage extends AbilityStage {
  onCreate(): void {
    // 应用HAP首次加载时触发,可以在此执行该Module的初始化操作(例如资源预加载、线程创建等)。
  }
  onAcceptWant(): void {
	// UIAbility指定实例模式(specified)启动时候触发的事件回调
  }

  onAcceptWant(want: Want): string {
  	// 当系统调整内存时触发的事件
    // 仅specified模式下触发
    return 'MyAbilityStage';
  }
}
// 在module.json5配置文件中,
// 通过配置 srcEntry 参数来指定模块对应的代码路径,以作为HAP加载的入口。
{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/myabilitystage/MyAbilityStage.ets",
    // ...
  }
}

UIAbility组件间的交互

假设应用中有两个UIAbility:EntryAbility和FuncAbility(可以在同一个Module中,也可以在不同的Module中),需要从EntryAbility的页面中启动FuncAbility。

// EntryAbility
import { common, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;

@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
  private context = getContext(this) as common.UIAbilityContext;

  build() {
    Column() {
      //...
      List({ initialIndex: 0 }) {
        ListItem() {
          Row() {
            //...
          }
          .onClick(() => {
            // context为Ability对象的成员,在非Ability对象内部调用需要
            // 将Context对象传递过去
            let wantInfo: Want = {
              deviceId: '', // deviceId为空表示本设备
              bundleName: 'com.samples.stagemodelabilitydevelop',
              moduleName: 'entry', // moduleName非必选
              abilityName: 'FuncAbilityA',
              parameters: {
                // 自定义信息
                info: '来自EntryAbility Page_UIAbilityComponentsInteractive页面'
              },
            };
            // context为调用方UIAbility的UIAbilityContext
            this.context.startAbility(wantInfo).then(() => {
              hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
            }).catch((error: BusinessError) => {
              hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
            });
          })
        }
        //...
      }
      //...
    }
    //...
  }
}
// FuncAbility  UIAbility 
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';

export default class FuncAbilityA extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  	// 可以通过获取传递过来的want参数的parameters来获取拉起方UIAbility的PID、Bundle Name等信息。
    // 接收调用方UIAbility传过来的参数
    let funcAbilityWant = want;
    let info = funcAbilityWant?.parameters?.info;
  }
  //...
}

// FuncAbility UI
// 在FuncAbility业务完成之后,如需要停止当前UIAbility实例,
// 在FuncAbility中通过调用terminateSelf()方法实现。

import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = '[Page_FromStageModel]';
const DOMAIN_NUMBER: number = 0xFF00;

@Entry
@Component
struct Page_FromStageModel {
  build() {
    Column() {
      //...
      Button('FuncAbilityB')
        .onClick(() => {
        // UIAbilityContext
          let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; 
          // context为需要停止的UIAbility实例的AbilityContext
          context.terminateSelf((err) => {
            if (err.code) {
              hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
              return;
            }
          });
        })
    }
    //...
  }
}

官方文档

ArkUI

长列表优化

  1. 懒加载(缓存列表项 cachedCount)
@Entry
@Component
struct MyComponent {
  private data: MyDataSource = new MyDataSource();
   
  aboutToAppear() {
    for (let i = 0; i <= 20; i++) {
      this.data.pushData(`Hello ${i}`)
    }
  }

  build() {
    List({ space: 3 }) {
      LazyForEach(this.data, (item: string) => {
        ListItem() {
          Row() {
            Text(item).fontSize(50)
              .onAppear(() => {
                console.info("appear:" + item)
              })
          }.margin({ left: 10, right: 10 })
        }
      }, (item: string) => item)
    }.cachedCount(5)
  }
}

API文档

  1. @Reusable装饰器:组件复用
class MyDataSource implements IDataSource {
  private dataArray: string[] = [];
  private listener: DataChangeListener | undefined;

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
  }

  public reloadListener(): void {
    this.listener?.onDataReloaded();
  }

  public registerDataChangeListener(listener: DataChangeListener): void {
    this.listener = listener;
  }

  public unregisterDataChangeListener(listener: DataChangeListener): void {
    this.listener = undefined;
  }
}

@Entry
@Component
struct ReuseDemo {
  private data: MyDataSource = new MyDataSource();

  // ...
  build() {
    Column() {
      List() {
        LazyForEach(this.data, (item: string) => {
          ListItem() {
            CardView({ item: item })
          }
        }, (item: string) => item)
      }
    }
  }
}

// 复用组件
// 自定义组件被@Reusable装饰器修饰,即表示其具备组件复用的能力;
@Reusable
@Component
export struct CardView {
  @State item: string = '';
	
	// 当一个可复用的自定义组件从复用缓存中重新加入到节点树时,
	// 触发aboutToReuse生命周期回调,并将组件的构造参数传递给aboutToReuse;
  aboutToReuse(params: Record<string, Object>): void {
    this.item = params.item as string;
  }

  build() {
    Column() {
      Text(this.item)
        .fontSize(30)
    }
    .borderWidth(1)
    .height(100)
  }
}

API文档

  1. 布局优化

动画能力

  • 属性动画: 通过更改组件的属性值实现渐变过渡效果,例如缩放、旋转、平移等。支持的属性包括width、height、backgroundColor、opacity、scale、rotate、translate等。
  • 显示动画:可以通过用户的直接操作或应用程序的特定逻辑来触发,例如按钮点击时的缩放动画、列表项展开时的渐变动画等。HarmonyOS提供了全局animateTo显式动画接口来指定由于闭包代码导致状态变化的插入过渡动效。
  • 转场动画:转场动画可以实现平滑的界面切换效果,例如页面之间的淡入淡出、滑动切换、旋转切换等,增强了界面的连贯性和吸引力
  • 路径动画:指对象沿着指定路径进行移动的动画效果。通过设置路径可以实现视图沿着预定义的路径进行移动,例如曲线运动、圆周运动等,为用户呈现更加生动的交互效果。
  • 粒子动画:通过大量小颗粒的运动来形成整体动画效果。通过对粒子在颜色、透明度、大小、速度、加速度、自旋角度等维度变化做动画,来营造一种氛围感。

网站公告

今日签到

点亮在社区的每一天
去签到