HarmonyOS—UI 开发性能提升的推荐方法
开发者若使用低性能的代码实现功能场景可能不会影响应用的正常运行,但却会对应用的性能造成负面影响。本章节列举出了一些可提升性能的场景供开发者参考,以避免应用实现上带来的性能劣化。
使用数据懒加载
开发者在使用长列表时,如果直接采用循环渲染方式,如下所示,会一次性加载所有的列表元素,一方面会导致页面启动时间过长,影响用户体验,另一方面也会增加服务器的压力和流量,加重系统负】
@Entry
@Component
struct MyComponent {@State arr: number[] = Array.from(Array(100), (v,k) =>k); //构造0-99的数组build() {List() {ForEach(this.arr, (item: number) => {ListItem() {Text(`item value: ${item}`)}}, (item: number) => item.toString())}}
}
上述代码会在页面加载时将 100 个列表元素全部加载,这并非我们需要的,我们希望从数据源中按需迭代加载数据并创建相应组件,因此需要使用数据懒加载,如下所示:
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []public totalCount(): number {return 0}public getData(index: number): any {return undefined}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener')this.listeners.push(listener)}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener')this.listeners.splice(pos, 1)}}notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded()})}notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index)})}notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index)})}notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index)})}notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {listener.onDataMove(from, to)})}
}class MyDataSource extends BasicDataSource {private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2']public totalCount(): number {return this.dataArray.length}public getData(index: number): any {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 MyComponent {private data: MyDataSource = new MyDataSource()build() {List() {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(20).margin({ left: 10 })}}.onClick(() => {this.data.pushData('item value: ' + this.data.totalCount())})}, item => item)}}
}

上述代码在页面加载时仅初始化加载三个列表元素,之后每点击一次列表元素,将增加一个列表元素。
设置 List 组件的宽高
在使用 Scroll 容器组件嵌套 List 组件加载长列表时,若不指定 List 的宽高尺寸,则默认全部加载。
说明
Scroll 嵌套 List 时:
● List 没有设置宽高,会布局 List 的所有子组件。
● List 设置宽高,会布局 List 显示区域内的子组件。
● List 使用ForEach加载子组件时,无论是否设置 List 的宽高,都会加载所有子组件。
● List 使用LazyForEach加载子组件时,没有设置 List 的宽高,会加载所有子组件,设置了 List 的宽高,会加载 List 显示区域内的子组件。
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []public totalCount(): number {return 0}public getData(index: number): any {return undefined}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener')this.listeners.push(listener)}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener')this.listeners.splice(pos, 1)}}notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded()})}notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index)})}notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index)})}notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index)})}notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {listener.onDataMove(from, to)})}
}class MyDataSource extends BasicDataSource {private dataArray: Array<string> = new Array(100).fill('test')public totalCount(): number {return this.dataArray.length}public getData(index: number): any {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 MyComponent {private data: MyDataSource = new MyDataSource()build() {Scroll() {List() {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Row() {Text('item value: ' + item + (index + 1)).fontSize(20).margin(10)}}})}}}
}
因此,此场景下建议设置 List 子组件的宽高。
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []public totalCount(): number {return 0}public getData(index: number): any {return undefined}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener')this.listeners.push(listener)}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener')this.listeners.splice(pos, 1)}}notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded()})}notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index)})}notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index)})}notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index)})}notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {listener.onDataMove(from, to)})}
}class MyDataSource extends BasicDataSource {private dataArray: Array<string> = new Array(100).fill('test')public totalCount(): number {return this.dataArray.length}public getData(index: number): any {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 MyComponent {private data: MyDataSource = new MyDataSource()build() {Scroll() {List() {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Text('item value: ' + item + (index + 1)).fontSize(20).margin(10)}.width('100%')})}.width('100%').height(500)}.backgroundColor(Color.Pink)}
}

使用条件渲染替代显隐控制
如下所示,开发者在使用 visibility 通用属性控制组件的显隐状态时,仍存在组件的重新创建过程,造成性能上的损耗。
@Entry
@Component
struct MyComponent {@State isVisible: Visibility = Visibility.Visible;build() {Column() {Button("显隐切换").onClick(() => {if (this.isVisible == Visibility.Visible) {this.isVisible = Visibility.None} else {this.isVisible = Visibility.Visible}})Row().visibility(this.isVisible).width(300).height(300).backgroundColor(Color.Pink)}.width('100%')}
}
要避免这一问题,可使用 if 条件渲染代替 visibility 属性变换,如下所示:
@Entry
@Component
struct MyComponent {@State isVisible: boolean = true;build() {Column() {Button("显隐切换").onClick(() => {this.isVisible = !this.isVisible})if (this.isVisible) {Row().width(300).height(300).backgroundColor(Color.Pink)}}.width('100%')}
}

使用 Column/Row 替代 Flex
由于 Flex 容器组件默认情况下存在 shrink 导致二次布局,这会在一定程度上造成页面渲染上的性能劣化。
@Entry
@Component
struct MyComponent {build() {Flex({ direction: FlexDirection.Column }) {Flex().width(300).height(200).backgroundColor(Color.Pink)Flex().width(300).height(200).backgroundColor(Color.Yellow)Flex().width(300).height(200).backgroundColor(Color.Grey)}}
}
上述代码可将 Flex 替换为 Column、Row,在保证实现的页面布局效果相同的前提下避免 Flex 二次布局带来的负面影响。
@Entry
@Component
struct MyComponent {build() {Column() {Row().width(300).height(200).backgroundColor(Color.Pink)Row().width(300).height(200).backgroundColor(Color.Yellow)Row().width(300).height(200).backgroundColor(Color.Grey)}}
}

减少应用滑动白块
应用通过增大 List/Grid 控件的 cachedCount 参数,调整 UI 的加载范围。
cachedCount 表示屏幕外 List/Grid 预加载 item 的个数。
如果需要请求网络图片,可以在 item 滑动到屏幕显示之前,提前下载好内容,从而减少滑动白块。
如下是使用 cachedCount 参数的例子:
@Entry
@Component
struct MyComponent {private source: MyDataSource = new MyDataSource();build() {List() {LazyForEach(this.source, item => {ListItem() {Text("Hello" + item).fontSize(50).onAppear(() => {console.log("appear:" + item)})}})}.cachedCount(3) // 扩大数值appear日志范围会变大}
}class MyDataSource implements IDataSource {data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];public totalCount(): number {return this.data.length}public getData(index: number): any {return this.data[index]}registerDataChangeListener(listener: DataChangeListener): void {}unregisterDataChangeListener(listener: DataChangeListener): void {}
}

使用说明:
cachedCount 的增加会增大 UI 的 cpu、内存开销。使用时需要根据实际情况,综合性能和用户体验进行调整。
为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙(HarmonyOS)开发学习指南》需要的可以扫描免费领取!!!
《鸿蒙(HarmonyOS)开发学习指南》
第一章 快速入门
1、开发准备
2、构建第一个ArkTS应用(Stage模型)
3、构建第一个ArkTS应用(FA模型)
4、构建第一个JS应用(FA模型)
5、…

第二章 开发基础知识
1、应用程序包基础知识
2、应用配置文件(Stage模型)
3、应用配置文件概述(FA模型)
4、…

第三章 资源分类与访问
1、 资源分类与访问
2、 创建资源目录和资源文件
3、 资源访问
4、…

第四章 学习ArkTs语言
1、初识ArkTS语言
2、基本语法
3、状态管理
4、其他状态管理
5、渲染控制
6、…

第五章 UI开发
1.方舟开发框架(ArkUI)概述
2.基于ArkTS声明式开发范式
3.兼容JS的类Web开发范式
4…

第六章 Web开发
1.Web组件概述
2.使用Web组件加载页面
3.设置基本属性和事件
4.在应用中使用前端页面JavaScript
5.ArkTS语言基础类库概述
6.并发
7…

11.网络与连接
12.电话服务
13.数据管理
14.文件管理
15.后台任务管理
16.设备管理
17…

第七章 应用模型
1.应用模型概述
2.Stage模型开发指导
3.FA模型开发指导
4…

扫描下方二维码免费领取,《鸿蒙(HarmonyOS)开发学习指南》

相关文章:
HarmonyOS—UI 开发性能提升的推荐方法
开发者若使用低性能的代码实现功能场景可能不会影响应用的正常运行,但却会对应用的性能造成负面影响。本章节列举出了一些可提升性能的场景供开发者参考,以避免应用实现上带来的性能劣化。 使用数据懒加载 开发者在使用长列表时,如果直接采用…...
84 CTF夺旗-PHP弱类型异或取反序列化RCE
目录 案例1:PHP-相关总结知识点-后期复现案例2:PHP-弱类型对比绕过测试-常考点案例3:PHP-正则preg_match绕过-常考点案例4:PHP-命令执行RCE变异绕过-常考点案例5:PHP-反序列化考题分析构造复现-常考点涉及资源…...
Duilib List 控件学习
这是自带的一个示例; 一开始运行的时候List中是空的,点击Search按钮以后就填充列表框; 先看一下列表框列头是在xml文件中形成的; <List name="domainlist" bkcolor="#FFFFFFFF" ... menu="true"> <ListHeader height="24…...
详细了解Node.js的配置与使用!
详细了解Node.js的配置与使用! Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它允许开发者在服务器端运行 JavaScript,从而实现全栈 JavaScript 开发。本文将介绍 Node.js 的配置和 npm 的应用。 一、Node.js 配置 下载与安装 首先&…...
OpenCV 移动最小二乘图像变形
文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 在现实生活中,我们常常应用一些刚性的变换来实现物体的旋转平移,对于非刚性的变换我们都没有在意,其实这种变换也是无处不在的,如我们经常看的动画就可以通过一些非刚性的变换达到一些非常夸张的效果。这里,我…...
【深度学习】S2 数学基础 P4 概率论
目录 基本概率论概率论公理随机变量 多个随机变量联合概率条件概率贝叶斯定理求和法则独立性 期望与方差小结 基本概率论 机器学习本质上,就是做出预测。而概率论提供了一种量化和表达不确定性水平的方法,可以帮助我们量化对某个结果的确定性程度。 在…...
跟我学c++中级篇——静态多态
一、多态 Polymorphism,多态。学习过c的人如果不知道多态,基本上就是打入c内部的C程序员了。在前边曾经对多态进行过分析,对其中的虚函数(虚表等)也进行过较为详细的说明。 多态其实非常好理解,不要硬扣书…...
设计模式--桥接模式(Bridge Pattern)
桥接模式(Bridge Pattern)是一种结构型设计模式,它主要是用于将抽象部分与实现部分分离,使它们可以独立地变化。 桥接模式主要包含以下几个角色: Abstraction(抽象类):定义抽象类的…...
统计图饼图绘制方法(C语言)
统计图饼图绘制方法(C语言) 常用的统计图有条形图、柱形图、折线图、曲线图、饼图、环形图、扇形图。 前几类图比较容易绘制,饼图绘制较难。今值此介绍饼图的绘制方法。 本方法采用C语言的最基本功能: ( 1.)…...
洛谷C++简单题小练习day12—寻找最小值小程序
day12--寻找最小值--2.16 习题概述 题目描述 给出 n 和 n 个整数 ai,求这 n 个整数中最小值是什么。 输入格式 第一行输入一个正整数 n,表示数字个数。 第二行输入 n 个非负整数,表示 1,2…a1,a2…an,以空格隔开。 …...
相机图像质量研究(13)常见问题总结:光学结构对成像的影响--鬼影
系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结:光学结构对成…...
车载诊断协议DoIP系列 —— 车辆以太网节点需求汇总
车载诊断协议DoIP系列 —— 车辆以太网节点需求汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,…...
掘根宝典之C++包含对象的类,私有继承,保护继承,三大继承方式总结
包含对象成员的类 包含,组合和层次化:一个类里面的类成员之一是个类对象 我们来看个例子 #include<iostream> using namespace std; class AA { private:int a_; public:AA(int a):a_(a){}void A(){cout << a_ << endl;} }; class …...
第六篇:MySQL图形化管理工具
经过前五篇的学习,对于数据库这门技术的理解,我们已经在心中建立了一个城堡大致的雏形,通过命令行窗口(cmd)快速上手了【SQL语法-DDL-数据定义语言】等相关命令 道阻且长,数据库技术这一宝藏中还有数不清的…...
计算机网络——12DNS
DNS DNS的必要性 IP地址标识主机、路由器但IP地址不好记忆,不便于人类用使用(没有意义)人类一般倾向于使用一些有意义的字符串来标识Internet上的设备存在着“字符串”——IP地址的转换的必要性人类用户提供要访问机器的“字符串”名称由DN…...
vue3-应用规模化-工具链
工具链 项目脚手架 Vite Vite 是一个轻量级的、速度极快的构建工具,对 Vue SFC 提供第一优先级支持。作者是尤雨溪,同时也是 Vue 的作者! 要使用 Vite 来创建一个 Vue 项目,非常简单: (推荐)…...
EasyExcel动态列导出
测试代码地址:https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/easyexcel/dynamiccolumn 官方文档:https://easyexcel.opensource.alibaba.com/docs/2.x/quickstart/write 一、实现方式 1、根据需要导出的列…...
JAVA面试题11
什么是Java的访问修饰符,并列出它们的作用。 Java的访问修饰符包括public、private、protected和默认。它们的作用如下: public: 可以被任何其他类访问。 private: 只能被所在类访问,其他类无法访问。 protected: 可以被所在类和同一个包中的…...
工业数据采集的时间不确定性及PLC-Recorder的通道偏移功能
目录 一、缘起 二、效果展示 三、设置方法 四、小结 一、缘起 大家都知道采集软件首先要尽可能还原数据原来的状态,给用户提供一个可以信赖的参考。但是,数据采集又有很多随机因素:Windows是一个周期不严格的系统、以太网通讯有时间波动、…...
十五、Object 类
文章目录 Object 类6.1 public Object()6.2 toString方法6.3 hashCode和equals(Object)6.4 getClass方法6.5 clone方法6.6 finalize方法 Object 类 本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记 java.lang.Object类是类层次结构的根…...
当游戏语言成为障碍:如何用XUnity.AutoTranslator打破语言壁垒
当游戏语言成为障碍:如何用XUnity.AutoTranslator打破语言壁垒 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 想象一下,你终于等到了期待已久的日式角色扮演游戏,但打…...
5步突破:用RVC变声器从零到专业音色转换的实战指南
5步突破:用RVC变声器从零到专业音色转换的实战指南 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI 语音数据小于等于10分钟也可以用来训练一个优秀的变声模型! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voice-Co…...
自定义默认提示词:PandaWiki 问答 “一键贴合业务”,企业降本增效新方案
深耕企业数字化与知识管理 7 年,服务过数百家中大型企业,发现企业知识库普遍存在三大核心痛点:AI 问答泛化、风格混乱、效率低下、人力成本高。PandaWiki 的自定义默认提示词功能,搭配多平台客服 开源可控,为企业提供…...
STM32F103C8T6接KY-9250陀螺仪,串口数据解析与姿态角计算全流程(附避坑点)
STM32F103C8T6与KY-9250陀螺仪实战:从硬件对接到姿态解算的完整指南 第一次拿到STM32开发板和KY-9250模块时,那种既兴奋又忐忑的心情记忆犹新——兴奋于即将实现酷炫的姿态检测功能,忐忑于不知从何下手的迷茫。本文将以手把手的方式ÿ…...
用Wireshark抓包学LTE:手把手解析开机附着流程中的NAS/RRC消息
用Wireshark抓包学LTE:手把手解析开机附着流程中的NAS/RRC消息 1. LTE信令分析实战环境搭建 工欲善其事,必先利其器。在开始解析LTE信令前,我们需要搭建专业的分析环境。不同于传统教材的理论讲解,我们将从工程师视角构建完整的分…...
【ArkTS】编程规范
ArkTS 是 HarmonyOS 应用的默认开发语言,在 TypeScript(简称 TS)生态基础上做了扩展,保持 TS 的基本风格。通过规范定义,从而强化了开发期的静态检查和分析,提升了程序执行的稳定性和性能。 一、术语与定义 术语 缩略语 中文解释 ArkTS 无 ArkTS编程语言 TypeScript TS …...
Android Perfetto 系列 6:为什么是 120Hz?高刷新率的优势与挑战
Android Perfetto 系列 6:为什么是 120Hz?高刷新率的优势与挑战本文是 Android Perfetto 系列的第六篇,主要介绍 Android 设备上 120Hz 刷新率的相关知识。如今,120Hz 已成为 Android 旗舰手机的标配,本文将讨论高刷新…...
OpenClaw浏览器自动化:GLM-4.7-Flash驱动的智能搜索与数据采集
OpenClaw浏览器自动化:GLM-4.7-Flash驱动的智能搜索与数据采集 1. 为什么需要浏览器自动化助手 上周我需要做一个小型市场调研,收集20家竞品的产品定价和功能列表。手动打开每个网站、复制粘贴数据、整理成表格,花了整整一个下午。这种重复…...
SEO_从零开始,手把手教你制定SEO优化方案(126 )
<h2>SEO优化的基本概念</h2> <p>SEO,全称Search Engine Optimization,是搜索引擎优化的简称,旨在提高网站在搜索引擎中的自然排名,从而增加网站的可见度和流量。对于初学者来说,SEO可能听起来有点复…...
8路HD-SDI录播主机CYS-08
在广电录制、教育录播、会议记录等场景中,稳定、高清、易管理的视频录制设备至关重要。春源丽影CYS-08 推出的8路HD-SDI硬盘录像机,凭借全接口支持、双编码技术、智能存储等核心优势,为多路高清录制需求提供了专业级解决方案。8路高清输入&am…...
