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,一经查实,立即删除!

相关文章

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

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

使用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 列表如下, 依次…

【Go】二十、反射

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

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

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

【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 模式…

Flutter混淆方案对应用性能的影响分析与优化

在移动应用开发中&#xff0c;保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具&#xff0c;帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆&#xff0c;并提供了相关的操作步骤和注意事项。 &#x1f4dd; 摘要 本…

【C++】lambda 表达式 / 包装器 / bind 绑定

目录 一. lambda 表达式1. lambda 表达式的语法1. lambda 表达式的使用2. lambda 表达式的捕捉列表 二. 包装器三. bind 绑定 一. lambda 表达式 Lambda 表达式是 C11 标准引入的一种新特性, 它提供了一种方便的方式来定义匿名函数. lambda 表达式实际上是一个匿名的仿函数; …

【FTP,EMail】

文章目录 FTPFTP&#xff1a;文件传输协议FTP: 控制连接与数据连接分开FTP命令、响应 EMail电子邮件&#xff08;EMail&#xff09;邮件服务器EMail: SMTP [RFC 2821]SMTP&#xff1a;总结 FTP FTP&#xff1a;文件传输协议 向远程主机上传输文件或从远程主机接收文件。客户/服…

使用tcpdump和wireshark进行服务器抓包分析

目录 前言 1.tcpdump简介 2.Wireshark简介 3.实际案例 4.代码示例 5.总结 前言 服务器抓包分析是一种非常常见和有效的网络故障排查和性能优化手段。通过捕获服务器上的网络流量&#xff0c;可以帮助我们深入了解服务器与其它设备之间的通信情况&#xff0c;发现问题并进…

Java多线程实战-从零手搓一个简易线程池(三)线程工厂,核心线程与非核心线程逻辑实现

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️本系列源码仓库&#xff1a;多线程并发编程学习的多个代码片段(github) &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正…

Mac上怎么合并多张图片?

Mac上怎么合并多张图片&#xff1f;上班过的小伙伴都应该知道&#xff0c;合并拼接图片是一件非常重要且经常需要使用到的图片处理技术&#xff0c;将多张图片合并拼成一张之后能够展现出更多的图片内容。在Mac电脑上&#xff0c;合并多张图片是一项常见的任务&#xff0c;无论…

【教程】Kotlin语言学习笔记(六)——泛型

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【Kotlin语言学习】系列文章 第一章 《认识Kotlin》 第二章 《数据类型》 第三章 《数据容器》 第四章 《方法》 第五章 《L…

关系型数据库设计

目录 1.数据库设计的重要性及定义 1.1 数据库设计的重要性 1.1.1 失败的数据库设计造成的后果 1.1.2 优秀的数据库设计带来的好处 1.2 数据库设计的定义 2.数据库需求分析 2.1 需求分析的步骤 2.1.1 收集信息 2.1.2 标识实体 2.1.3 标识每个实体的详细信息 2.1…

【HTML】制作一个简单的动态SVG图形

目录 前言 开始 HTML部分 CSS部分 效果图 总结 前言 无需多言&#xff0c;本文将详细介绍一段HTML和CSS代码&#xff0c;该代码用于创建一个动态的SVG图形&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建两个文本文档&#xff0c;其中HTML的文…

【2023】kafka原生以及配合springboot的使用(Kafka-3)

&#x1f4bb;目录 前言 一、依赖二、原生使用kafka1、发送消息1.1、生产者同步发送消息1.2、生产者异步发送消息1.3、常用配置&#xff1a; 2、接收消息2.1、关于消费者的自动提交和手动提交2.2、长轮训poll消息2.3、消费者的健康状态检查2.4、指定分区和偏移量&#xff0c;时…

使用docker-tc对host容器进行限流

docker-tc是一个github开源项目&#xff0c;项目地址是https://github.com/lukaszlach/docker-tc。 运行docker-tc docker run -d \ --name docker-tc \ --network host \ --cap-add NET_ADMIN \ --restart always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /var…