HarmonyOS 应用开发之自定义组件冻结功能

自定义组件处于非激活状态时,状态变量将不响应更新,即@Watch不会调用,状态变量关联的节点不会刷新。通过freezeWhenInactive属性来决定是否使用冻结功能,不传参数时默认不使用。支持的场景有:页面路由,TabContent,LazyforEach,Navigation。

说明:

从API version 11开始,支持自定义组件冻结功能。

当前支持的场景

页面路由

  • 当页面A调用router.pushUrl接口跳转到页面B时,页面A为隐藏不可见状态,此时如果更新页面A中的状态变量,不会触发页面A刷新。

  • 当应用退到后台运行时无法被冻结。

页面A:

import router from '@ohos.router';@Entry
@Component({ freezeWhenInactive: true })
struct FirstTest {@StorageLink('PropA') @Watch("first") storageLink: number = 47;first() {console.info("first page " + `${this.storageLink}`)}build() {Column() {Text(`From fist Page ${this.storageLink}`).fontSize(50)Button('first page storageLink + 1').fontSize(30).onClick(() => {this.storageLink += 1})Button('go to next page').fontSize(30).onClick(() => {router.pushUrl({ url: 'pages/second' })})}}
}

页面B:

import router from '@ohos.router';@Entry
@Component({ freezeWhenInactive: true })
struct SecondTest {@StorageLink('PropA') @Watch("second") storageLink2: number = 1;second() {console.info("second page: " + `${this.storageLink2}`)}build() {Column() {Text(`second Page ${this.storageLink2}`).fontSize(50)Button('Change Divider.strokeWidth').onClick(() => {router.back()})Button('second page storageLink2 + 2').fontSize(30).onClick(() => {this.storageLink2 += 2})}}
}

在上面的示例中:

1.点击页面A中的Button “first page storLink + 1”,storLink状态变量改变,@Watch中注册的方法first会被调用。

2.通过router.pushUrl({url: ‘pages/second’}),跳转到页面B,页面A隐藏,状态由active变为inactive。

3.点击页面B中的Button “this.storLink2 += 2”,只回调页面B@Watch中注册的方法second,因为页面A的状态变量此时已被冻结。

4.点击“back”,页面B被销毁,页面A的状态由inactive变为active,重新刷新在inactive时被冻结的状态变量,页面A@Watch中注册的方法first被再次调用。

TabContent

  • 对Tabs中当前不可见的TabContent进行冻结,不会触发组件的更新。

  • 需要注意的是:在首次渲染的时候,Tab只会创建当前正在显示的TabContent,当切换全部的TabContent后,TabContent才会被全部创建。

@Entry
@Component
struct TabContentTest {@State @Watch("onMessageUpdated") message: number = 0;onMessageUpdated() {console.info(`TabContent message callback func ${this.message}`)}build() {Row() {Column() {Button('change message').onClick(() => {this.message++})Tabs() {TabContent() {FreezeChild({ message: this.message })}.tabBar('one')TabContent() {FreezeChild({ message: this.message })}.tabBar('two')}}.width('100%')}.height('100%')}
}@Component({ freezeWhenInactive: true })
struct FreezeChild {@Link @Watch("onMessageUpdated") message: numberprivate index: number = 0onMessageUpdated() {console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`)}build() {Text("message" + `${this.message}, index: ${this.index}`).fontSize(50).fontWeight(FontWeight.Bold)}
}

在上面的示例中:

1.点击“change message”更改message的值,当前正在显示的TabContent组件中的@Watch中注册的方法onMessageUpdated被触发。

2.点击“two”切换到另外的TabContent,TabContent状态由inactive变为active,对应的@Watch中注册的方法onMessageUpdated被触发。

3.再次点击“change message”更改message的值,仅当前显示的TabContent子组件中的@Watch中注册的方法onMessageUpdated被触发。

LazyforEach

  • 对LazyforEach中缓存的自定义组件进行冻结,不会触发组件的更新。
// Basic implementation of IDataSource to handle data listener
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = [];private originDataArray: string[] = [];public totalCount(): number {return 0;}public getData(index: number): string {return this.originDataArray[index];}// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener');this.listeners.push(listener);}}// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener');this.listeners.splice(pos, 1);}}// 通知LazyForEach组件需要重载所有子组件notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded();})}// 通知LazyForEach组件需要在index对应索引处添加子组件notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index);})}// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index);})}// 通知LazyForEach组件需要在index对应索引处删除该子组件notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index);})}
}class MyDataSource extends BasicDataSource {private dataArray: string[] = [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data);this.notifyDataAdd(index);}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}@Entry
@Component
struct LforEachTest {private data: MyDataSource = new MyDataSource();@State @Watch("onMessageUpdated") message: number = 0;onMessageUpdated() {console.info(`LazyforEach message callback func ${this.message}`)}aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {Column() {Button('change message').onClick(() => {this.message++})List({ space: 3 }) {LazyForEach(this.data, (item: string) => {ListItem() {FreezeChild({ message: this.message, index: item })}}, (item: string) => item)}.cachedCount(5).height(500)}}
}@Component({ freezeWhenInactive: true })
struct FreezeChild {@Link @Watch("onMessageUpdated") message: number;private index: string = "";aboutToAppear() {console.info(`FreezeChild aboutToAppear index: ${this.index}`)}onMessageUpdated() {console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`)}build() {Text("message" + `${this.message}, index: ${this.index}`).width('90%').height(160).backgroundColor(0xAFEEEE).textAlign(TextAlign.Center).fontSize(30).fontWeight(FontWeight.Bold)}
}

在上面的示例中:

1.点击“change message”更改message的值,当前正在显示的ListItem中的子组件@Watch中注册的方法onMessageUpdated被触发。缓存节点@Watch中注册的方法不会被触发。(如果不加组件冻结,当前正在显示的ListItem和cachcount缓存节点@Watch中注册的方法onMessageUpdated都会触发watch回调。)

2.List区域外的ListItem滑动到List区域内,状态由inactive变为active,对应的@Watch中注册的方法onMessageUpdated被触发。

3.再次点击“change message”更改message的值,仅有当前显示的ListItem中的子组件@Watch中注册的方法onMessageUpdated被触发。

Navigation

  • 对当前不可见的页面进行冻结,不会触发组件的更新,当返回该页面时,触发@Watch回调进行刷新。
@Entry
@Component
struct MyNavigationTestStack {@Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();@State @Watch("info") message: number = 0;@State logNumber: number = 0;info() {console.info(`freeze-test MyNavigation message callback ${this.message}`);}@BuilderPageMap(name: string) {if (name === 'pageOne') {pageOneStack({ message: this.message, logNumber: this.logNumber })} else if (name === 'pageTwo') {pageTwoStack({ message: this.message, logNumber: this.logNumber })} else if (name === 'pageThree') {pageThreeStack({ message: this.message, logNumber: this.logNumber })}}build() {Column() {Button('change message').onClick(() => {this.message++;})Navigation(this.pageInfo) {Column() {Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPath({ name: 'pageOne' }); //将name指定的NavDestination页面信息入栈})}}.title('NavIndex').navDestination(this.PageMap).mode(NavigationMode.Stack)}}
}@Component
struct pageOneStack {@Consume('pageInfo') pageInfo: NavPathStack;@State index: number = 1;@Link message: number;@Link logNumber: number;build() {NavDestination() {Column() {NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })Text("cur stack size:" + `${this.pageInfo.size()}`).fontSize(30).fontWeight(FontWeight.Bold)Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPathByName('pageTwo', null);})Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pop();})}.width('100%').height('100%')}.title('pageOne').onBackPressed(() => {this.pageInfo.pop();return true;})}
}@Component
struct pageTwoStack {@Consume('pageInfo') pageInfo: NavPathStack;@State index: number = 2;@Link message: number;@Link logNumber: number;build() {NavDestination() {Column() {NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })Text("cur stack size:" + `${this.pageInfo.size()}`).fontSize(30).fontWeight(FontWeight.Bold)Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPathByName('pageThree', null);})Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pop();})}.width('100%').height('100%')}.title('pageTwo').onBackPressed(() => {this.pageInfo.pop();return true;})}
}@Component
struct pageThreeStack {@Consume('pageInfo') pageInfo: NavPathStack;@State index: number = 3;@Link message: number;@Link logNumber: number;build() {NavDestination() {Column() {NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })Text("cur stack size:" + `${this.pageInfo.size()}`).fontSize(30).fontWeight(FontWeight.Bold)Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPathByName('pageOne', null);})Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pop();})}.width('100%').height('100%')}.title('pageThree').onBackPressed(() => {this.pageInfo.pop();return true;})}
}@Component({ freezeWhenInactive: true })
struct NavigationContentMsgStack {@Link @Watch("info") message: number;@Link index: number;@Link logNumber: number;info() {console.info(`freeze-test NavigationContent message callback ${this.message}`);console.info(`freeze-test ---- called by content ${this.index}`);this.logNumber++;}build() {Column() {Text("msg:" + `${this.message}`).fontSize(30).fontWeight(FontWeight.Bold)Text("log number:" + `${this.logNumber}`).fontSize(30).fontWeight(FontWeight.Bold)}}
}

在上面的示例中:

1.点击“change message”更改message的值,当前正在显示的MyNavigationTestStack组件中的@Watch中注册的方法info被触发。

2.点击“Next Page”切换到PageOne,创建pageOneStack节点。

3.再次点击“change message”更改message的值,仅pageOneStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

4.再次点击“Next Page”切换到PageTwo,创建pageTwoStack节点。

5.再次点击“change message”更改message的值,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

6.再次点击“Next Page”切换到PageThree,创建pageThreeStack节点。

7.再次点击“change message”更改message的值,仅pageThreeStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

8.点击“Back Page”回到PageTwo,此时,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

9.再次点击“Back Page”回到PageOne,此时,仅pageOneStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

10.再次点击“Back Page”回到初始页,此时,无任何触发。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/788468.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完美解决layui多图上传时,动态给每个图片增加参数

需求&#xff1a;当我在使用layui框架上传多图时&#xff0c;我想动态给每一张图都把它的名称传给后台 data: {id: function(){return $(#id).val();} } 这是官网给的动态传参的方法 我自己根据官网给的案例设置 data:{filesIndex:function(index, file){return file.name}},…

什么是智慧公厕?智慧旅游下的智慧公厕功能和特点

智慧旅游下的智慧公厕功能和特点&#xff1f;智慧旅游是景区、公园、游乐场、文化场馆等领域的一种信息化解决方案&#xff0c;智慧公厕是智慧旅游极为重要的一部分&#xff0c;能大大提升游客满意度。智慧公厕采用物联网、互联网、大数据、云计算等技术&#xff0c;实现旅游景…

gitlab备份与恢复

1.1.1 查看系统版本和软件版本 cat /etc/debian_version cat /opt/gitlab/embedded/service/gitlab-rails/VERSION 1.1.2 数据备份 打开/etc/gitlab/gitlab.rb配置文件&#xff0c;查看一个和备份相关的配置项 sudo vim /etc/gitlab/gitlab.rb gitlab_rails[backup_path] &q…

ffmpeg点对点音视频udp协议传输

参考&#xff1a;https://zhuanlan.zhihu.com/p/636152437?utm_id0 ffmpeg查看可用设备&#xff1a; ffmpeg -list_devices true -f dshow -i dummy1、音频 局域网内两台设备间 设备1-音频&#xff1a; ffmpeg -f dshow -i audio"麦克风阵列 (适用于数字麦克风的英特…

使用Excel连接Azure DevOps自动退出的问题

Azure DevOps Server (原名TFS)是微软公司的软件开发管理平台&#xff0c;也是著名的软件开发过程管理工具&#xff1b;系统中记录了软件开发过程中的需求、问题、缺陷和迭代计划等各种软件开发工作项数据。 对于工作项数据的批量操作(例如新增和编辑)&#xff0c;Excel是一个非…

zabbix图表时间与服务器时间不一致问题

部署完zabbix后&#xff0c;有时候会发现zabbix服务器的时间明明是对的&#xff0c;但是图标的时间不对&#xff0c;通过以下的配置可以快速解决。 登录zabbix-nginx容器 docker exec -u root -it docker-compose-zabbix-zabbix-web-nginx-mysql-1 bash修改php配置文件 vi /e…

SpringData ElasticSearch - 简化开发,完美适配 Spring 生态

目录 一、SpringData ElasticSearch 1.1、环境配置 1.2、创建实体类 1.3、ElasticsearchRestTemplate 的使用 1.3.1、创建索引 设置映射 1.3.2、简单的增删改查 1.3.3、搜索 1.4、ElasticsearchRepository 1.4.1、使用方式 1.4.2、简单的增删改查 1.4.3、分页排序查…

Linux系统下安装jdk与tomcat【linux】

一、yum介绍 linux下的jdk安装以及环境配置&#xff0c;有两种常用方法&#xff1a; 1.使用yum一键安装。 2.手动安装&#xff0c;在Oracle官网下载好需要的jdk版本&#xff0c;上传解压并配置环境。 这里介绍第一种方法&#xff0c;在此之前简单了解下yum。 yum 介绍 yum&…

57 npm run build 和 npm run serve 的差异

前言 npm run serve 和 npm run build 的差异 这里主要是从 vue-cli 的流程 来看一下 我们经常用到的这两个命令, 他到传递给 webpack 打包的时候, 的一个具体的差异, 大致是配置了那些东西? 经过了那些流程 ? vue-cli 的 vue-plugin 的加载 内置的 plugin 列表如下, 依次…

【Servlet】session保存作用域

session保存作用域&#xff1a;一次会话范围都有效 Java的服务器端&#xff0c;有一块内存专门存储在session保存作用域的数据。 session保存作用域是和具体的某一个session对应的。 常用API&#xff1a; void session.setAttribute(k, v)Object session.getAttrivute(k) —…

平滑处理在眼动追踪数据分析中的应用

眼动追踪数据的平滑处理技术 眼动追踪技术作为研究人类视觉注意、界面设计效果评估以及增强现实&#xff08;AR&#xff09;和虚拟现实&#xff08;VR&#xff09;中交云互动的重要工具&#xff0c;其准确性和流畅性对实验结果和用户体验至关重要。然而&#xff0c;由于测量误…

【Go】二十、反射

文章目录 1、反射2、对基本数据类型反射3、对结构体进行反射4、获取变量的类别5、通过反射修改基本类型变量的值6、通过反射操作结构体的属性和方法 1、反射 //核心包 import ("reflect")通过反射&#xff1a; 可以在运行时动态获取变量的类型、获取结构体的信息&a…

Node.js基本命令

以下是一些常用的Node.js命令&#xff1a; Node.js基本命令 node&#xff1a;用于运行一个Node.js脚本文件。例如&#xff1a;node app.js。node -v&#xff1a;查看已安装的Node.js版本。 npm命令 npm是Node.js的包管理工具&#xff0c;用于安装、升级、卸载和管理Node.js…

【二叉树】Leetcode 124. 二叉树中的最大路径和【困难】

二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根…

机器学习的特征选择方法

有时候特征太多了也是一个问题&#xff0c;容易过拟合。为了降低模型的复杂程度&#xff0c;降低过拟合的可能性&#xff0c;我们会减少需要的学习的特征。 特征选择法主要有三种&#xff0c;分别是过滤法&#xff0c;包裹法和嵌入法。 1.过滤法 这种方法主要是基于统计检验…

【QT+QGIS跨平台编译】056:【pdal_json_schema+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、pdal_json_schema介绍二、pdal下载三、文件分析四、pro文件五、编译实践一、pdal_json_schema介绍 pdal_json_schema 是与 PDAL(Point Data Abstraction Library)相关的 JSON 模式文件。PDAL 是一个用于处理和分析点云数据的开源库。JSON 模式…

wpf Line

详细解释 <Canvas x:Name"canvas"><Line x:Name"flowLine" X1"20" Y1"40" X2"400" Y2"100" StrokeDashArray"2,1" Stroke"Green" StrokeThickness"8"/><Line X1&q…

安装gitlab笔记

安装vim编辑器 yum install vim -y安装ssh协议&#xff0c;在CentOs系统上安装所需的依赖:ssh&#xff0c;防火墙,postfix(用于邮件通知)wget&#xff0c;以下这些命令也会打开系统防火墙中的HTTP和SSH端口访问 yum install -y curl policycoreutils-python openssh-server设…

ISP代理和数据中心IP的不同

ISP代理和数据中心IP在网络应用中都扮演着重要的角色 【定义】 ISP代理是由互联网服务提供商&#xff08;ISP&#xff09;提供的代理服务器。ISP代理的IP地址由ISP分配&#xff0c;这使得用户可以获得一个合法且唯一的IP地址&#xff0c;该地址隐藏了用户的实际位置。而数据中…

AutoMQ 1.1.0-RC0 重磅更新:内核升级到 Apache Kafka 3.7.0

AutoMQ 在 2024.02 正式发布了基于 Apache Kafka 3.4.0 的云原生重构版本 1.0.0&#xff0c;AutoMQ 1.0.0 版本相比原版提供了 Serverless、自动负载均衡、秒级分区迁移和 All in 对象存储能力&#xff0c;让 Kafka 用户能充分利用云的弹性能力和廉价存储&#xff0c;实现十倍成…