纯血鸿蒙Next 开发基础经验杂谈(2)

434 阅读6分钟

一.ImageAnimator组动画,类似于iOS中的组动画组件

官方解释:“提供帧动画组件来实现逐帧播放图片的能力,可以配置需要播放的图片列表,每张图片可以配置时长”。就是不停的播放展示一组图片。

/// 1.我们事先可以准备好图片资源数组,它是一个ImageFrameInfo类型的数组,里面有各种属性。 
export const ImageInfoList: ImageFrameInfo[] = [
    {
      src: $rawfile('greet/seq_0_0.png'),/// 图片资源
      width: 112,/// 宽
      height: 112 /// 高
      duration: 2000,/// 设置本张图片播放的时长。
    },
    {
      src: $rawfile('greet/seq_0_1.png'),
      width: 112,
      height: 112
    },
]
/// 2.在构建UI时使用此图片资源数组
@State animationImageList: ImageFrameInfo[] = ImageInfoList
/// 控制播放状态(
Initial:动画初始状态;
Running:动画处于播放状态;
Paused:动画处于暂停状态;
Stopped:动画处于停止状态
)
@State animationStatus: AnimationStatus = AnimationStatus.Running
/// 3.ImageAnimator组件.
ImageAnimator()
  .images(this.animationImageList)
  .state(this.animationStatus)
  .duration(2000) /// 注意:设置播放时长。当Images中任意一帧图片设置了单独的duration后,该属性设置无效。
  /// 设置播放次数,默认播放一次,设置为-1时表示无限次播放。
  .iterations(this.animationRepeatCount)
  .width(112)
  .aspectRatio(1)

.@arkts.math.Decimal (高精度数学库Decimal)

官方解释:Decimal用于提供高精度数学库,主要用于提供高精度浮点运算能力。其实就是我们在平时开发中,使用number类型时,对于坐标计算等极有可能精度不准确,我们就可以使用这个Decimal类型。

let convertedSize: number = 3.1415926;
/// 返回一个保留小数点后1位数的Decimal对象,不进行小数的取舍。
let number = new Decimal(convertedSize).toDecimalPlaces(1);

其实还有其他的常用数学方法,不是这个高精度数学库的。

/// 1.Math取绝对值.
Math.abs(-12)

/// 2.Math向下取整.
Math.floor(7.6)

/// 3.Math向上取整.
Math.ceil(7.6)

/// 4.Math就近取整,也叫四舍五入取整.
Math.round(5.8)

三.多线程

Worker的优缺点和替代方案

‌优点‌:提供多线程并发能力,适合执行耗时操作,避免主线程阻塞。 ‌缺点‌:需要手动管理生命周期,不支持设置调度优先级,性能上可能不如TaskPool。 ‌替代方案‌:在需要高性能和多任务处理的场景下,可以考虑使用TaskPool,它支持负载均衡和自动扩缩容,更适合大多数场景。

3.1 我们常用的Worker可以使用这种方式创建,比较方便。它会自动生成代码和Worker文件夹。

截屏2024-11-02 17.54.15.png

3.2 自动生成的文件会有 onmessage、onmessageerror、onerror三个方法,我们可以在此文件中定义我们需要操作的方法。Worker子线程和宿主线程之间的通信是基于消息传递的,Worker通过序列化机制与宿主线程之间相互通信,完成命令及数据交互。

也就是说子线程和宿主线程通过互相调用关键API postMessage和onmessage,来向对方发出消息和接收到对方发出的消息。

示范:

1.在UI侧

/// 声明阅读文件的worker.
private readWorker?: worker.ThreadWorker

 private startWorker() {
   /// 启动读取文件的worker,需要传入生成的worker文件的路径
   this.readWorker = new worker.ThreadWorker('包名/ets/workers/ReadFileWorker.ets')
   /// 接收来自worker侧的消息.
   this.readWorker.onmessage = (event: MessageEvents) => {
     console.log('接收到的来自worker耗时操作的数据',event.data)
   }
   // 开启读文件,通过postMessage向worker发出数据消息,ReadParam为定义的传输数据class
   this.readWorker.postMessage(json.stringify(new ReadParam(1, this.filePath)))
 }

2.在worker侧

 /// 我们在自动生成的onmessage方法中进行我们自己的耗时操作,接收来自UI侧的消息.
 workerPort.onmessage = (e: MessageEvents) => {
   let param = JSON.parse(e.data) as ReadParam
   // 读取文件
   readFile(param.stringData)
 }
 
 /// 耗时异步操作.
 async function readFile(filePath: string){
   /// 向UI侧发送获取到的文件数据.
   workerPort.postMessage(JSON.stringify(sendBytes))
 }
 

四.记录一些我经常忘记的属性意义

  1. onAreaChange(event: (oldValue: Area, newValue: Area) => void): T

组件区域变化时触发该回调。仅会响应由布局变化所导致的组件大小、位置发生变化时的回调。由绘制变化所导致的渲染属性变化不会响应回调,如translateoffset。 返回的Area对象,里面包含了宽、高、Position信息。

   /// 设置背景图片,它是通用属性,都可以用的。
   .backgroundImage($r('app.media.icon_voice_assistant_bg'), ImageRepeat.NoRepeat)
   /// 设置组件背景图片的宽高。参数为联合类型,既可以设置宽高也可以设置里面的枚举类型ImageSize
   .backgroundImageSize(1)

五.动态共享包HSP注意事项.

1.动态共享包(HSP)是可以创建页面并路由过去的,而静态共享包(HAR)却不行。因为HAR并没有像HSP包中的"main_pages.json"这么一个文件用来管理路由路径。

2.创建一个HSP。

2.1 点击New -> 选择Module 截屏2024-11-03 18.27.55.png

2.2 然后选择Shared Library WechatIMG266.jpg

2.3 创建好后的目录如下

WechatIMG267.jpg

2.4 如果你运行这时肯定报错,说找不到这个包。

WechatIMG269.jpg

这个问题是1.我们创建了但是并没有在整个工程的“oh-package.json5”文件中导入这个包 2.我们没有编辑Edit Configurations导致无法编译。

1.我们先这样 WechatIMG4854.jpg 设置完毕别忘了右上角点击同步按钮同步一下生效. 2.然后我们再这样去编辑一下,再运行就可以了,不会报错了。

WechatIMG4855.jpg

六.路由

路由的话大部分还是在用系统的router,但是官方已经不推荐使用它。推荐使用Navigation,但是大部分人嫌弃它有点麻烦,所以推荐用HMRouter,这是一个官方插件库。

好的,我们稍微介绍一下使用router,从一个hsp路由到另外一个hasp包的方法。

router.pushUrl({ url: '@bundle:com.xxx.yyyyy/yys_sport_club/ets/pages/SportClubMainPage' })

解释一下:

1.“@bundle:com.xxx.yyyyy”:是你整个App应用的包名。

2.“yys_sport_club”:要路由到的目标hsp包的包名。

3.“ets”:一般我们的代码就是放在这个文件夹里面的。

4."pages/SportClubMainPage":是我们的目标页面的路径,可以在目标hsp包的main_pages.json文件下面找到,呼应上。

七.设置前景色

它是一个公共属性:.colorBlend

官方的解释是:为组件添加颜色叠加效果。其实就是可以给图片设置前景色,给Text设置文字颜色等.

但是有些时候,利用它设置图片的前景色可能会出现色差或者无效,有人说是图片的问题,也有人说是系统版本问题。我实践的经验是:确实是前同事用的图片设置的有效正常,我搞的图片设置就有色差,不正常。。。代码都是一样的,啊哈哈哈哈///

八.获取共享屏幕的上下安全区和全屏等.

这个其实就需要在我们工程的根 EntryAbility 文件下的 onWindowStageCreate 生命周期中作文章了.

import { window } from '@kit.ArkUI';

onWindowStageCreate(windowStage: window.WindowStage): void {
  windowStage.loadContent('pages/Index', (err) => {
    if (err.code) {
      return;
    }
    
    let windowClass: window.Window = windowStage.getMainWindowSync();
    let isLayoutFullScreen = true;
    /// 设置为全屏.
    windowClass.setWindowLayoutFullScreen(isLayoutFullScreen);
    
    let indicatorType = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR;
    let indicatorArea = windowClass.getWindowAvoidArea(indicatorType);
    let safeBottom = indicatorArea.bottomRect.height;
    /// 获取到底部安全区并存储
    AppStorage.setOrCreate('safeBottom', safeBottom);

    let type = window.AvoidAreaType.TYPE_SYSTEM
    let statusBarArea = windowClass.getWindowAvoidArea(type);
    let safeTop = statusBarArea.topRect.height;
    /// 获取到顶部安全区并存储
    AppStorage.setOrCreate('safeTop', safeTop);

    windowClass.setWindowKeepScreenOn(true);
  });
}

好了,这下在全局的其他地方,我们就可以根据AppStorage存储的上下安全区数值布局我们的页面

/// 比如说我们在某个Entry中,通过StorageProp装饰器和safeTop获取到顶部的安全区.
@StorageProp('safeTop') safeTop: number = 0

九.获取到App信息

这里是指获取到App的工程信息,例如版本号,包名等。其实也就是获取到工程根目录下的AppScope文件夹下面的app.json5文件下面的字段信息.

import { bundleManager } from "@kit.AbilityKit";
import { BusinessError } from '@kit.BasicServicesKit';

/// 关键代码.
let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA;
try {
   bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => {
      hilog.info(0x0000, 'testTag', 'getBundleInfoForSelf successfully. Data: %{public}s', JSON.stringify(data));
      /// 这里就是示范获取到App的versionName字段信息.
      data.versionName
   }).catch((err: BusinessError) => {
      hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed. Cause: %{public}s', err.message);
   });
} catch (err) {
   let message = (err as BusinessError).message;
   hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed: %{public}s', message);
}