HarmonyOS6半年磨一剑 - RcIcon组件使用最佳实践与性能优化

HarmonyOS6半年磨一剑 - RcIcon组件使用最佳实践与性能优化

文章目录

前言

各位开发者,大家好!我是若城。

在鸿蒙应用开发过程中,我发现许多组件样式和工具方法具有高度的复用性,但每次新项目都需要重复编写,这极大地降低了开发效率。因此,我决定投入半年时间,打造一款专为鸿蒙生态设计的 UI 组件库 —— rchoui

项目简介

rchoui 是一个面向 HarmonyOS6 的企业级 UI 组件库,旨在提供开箱即用的高质量组件,让开发者告别"重复造轮子"。

核心特性

  • 丰富组件:涵盖基础组件、表单组件、弹窗组件、布局组件等
  • 设计规范:遵循统一的色彩体系和设计语言
  • 工具集成:内置常用工具方法,提升开发效率
  • 完善文档:每个模块都配有详细的设计思路和使用说明

开源计划

项目预计于 2026 年 7 月中旬正式开源,届时可通过三方库直接下载使用。在此期间,我会通过系列文章逐一介绍每个模块的设计思路与实现细节。

rchoui官网

目前暂定 rchoui 官网地址:http://rchoui.ruocheng.site/

需要注意的是 当前官网还在完善当中, 会在后续更新中逐步完善。届时可以为大家提供更加完善的说明文档

一、实战应用案例

1.1 底部导航栏

import{ RcIcon, IconName }from"rchoui"/** * Tab 项配置接口 */interfaceTabItem{/** Tab 名称 */ name:string/** 激活状态图标 */ icon:string/** 未激活状态图标(线型) */ iconOutline:string}@Entry@Component struct RcIconTab {@State activeIndex:number=0private tabItems: TabItem[]=[{ name:'首页', icon: IconName.HOME, iconOutline: IconName.HOME_OUTLINE},{ name:'发现', icon: IconName.COMPASS, iconOutline: IconName.COMPASS_OUTLINE},{ name:'消息', icon: IconName.MESSAGE_CIRCLE, iconOutline: IconName.MESSAGE_CIRCLE_OUTLINE},{ name:'我的', icon: IconName.PERSON, iconOutline: IconName.PERSON_OUTLINE}]build(){Row(){ForEach(this.tabItems,(item:TabItem, index)=>{Column({ space:4}){RcIcon({ name:this.activeIndex === index ? item.icon : item.iconOutline, iconSize:24, color:this.activeIndex === index ?'#1890ff':'#666666',onIconClick:()=>{this.activeIndex = index }})Text(item.name).fontSize(12).fontColor(this.activeIndex === index ?'#1890ff':'#666666')}.layoutWeight(1).padding(8)})}.width('100%').height(56).backgroundColor('#ffffff')}}

实现要点:

  • 使用风格切换表达激活状态
  • 颜色与风格同步变化
  • 统一的视觉反馈

1.2 工具栏按钮组

interfaceToolsItem{ id:string name:string}@Component struct ToolbarExample {@State selectedTool:string=''private tools:ToolsItem[]=[{ id:'edit', name: IconName.EDIT},{ id:'copy', name: IconName.COPY},{ id:'trash', name: IconName.TRASH},{ id:'share', name: IconName.SHARE}]build(){Row({ space:16}){ForEach(this.tools,(tool:ToolsItem)=>{RcIcon({ name: tool.name, iconSize:20, color:this.selectedTool === tool.id ?'#1890ff':'#666666',onIconClick:()=>{this.selectedTool = tool.id this.handleToolClick(tool.id)}})})}.padding(12).backgroundColor('#f5f5f5').borderRadius(8)}handleToolClick(toolId:string){console.log(`工具 ${toolId} 被点击`)}}

1.3 列表项装饰

interfaceListItem{ id:string title:string icon:string status:'success'|'warning'|'error'|'info'}@Component struct ListExample {private items: ListItem[]=[{ id:'1', title:'任务已完成', icon: IconName.CHECKMARK_CIRCLE, status:'success'},{ id:'2', title:'待处理事项', icon: IconName.ALERT_CIRCLE, status:'warning'},{ id:'3', title:'操作失败', icon: IconName.CLOSE_CIRCLE, status:'error'},{ id:'4', title:'通知消息', icon: IconName.INFO, status:'info'}]build(){List({ space:12}){ForEach(this.items,(item: ListItem)=>{ListItem(){Row({ space:12}){RcIcon({ name: item.icon, iconSize:20, color:this.getStatusColor(item.status)})Text(item.title).fontSize(16).layoutWeight(1)RcIcon({ name: IconName.CHEVRON_RIGHT, iconSize:16, color:'#cccccc'})}.width('100%').padding(16).backgroundColor('#ffffff').borderRadius(8)}})}.padding(16)}getStatusColor(status:string): ResourceColor {const colorMap ={'success':'#52c41a','warning':'#faad14','error':'#ff4d4f','info':'#1890ff'}return colorMap[status]||'#666666'}}

1.4 输入框前后缀

@Component struct InputExample {@State searchText:string=''@State showClear:boolean=falsebuild(){Row(){// 前缀符号RcIcon({ name: IconName.SEARCH, iconSize:18, color:'#999999'}).margin({ left:12})// 输入框TextInput({ placeholder:'搜索内容', text:this.searchText }).layoutWeight(1).backgroundColor(Color.Transparent).padding({ left:8, right:8}).onChange((value:string)=>{this.searchText = value this.showClear = value.length >0})// 后缀清除按钮if(this.showClear){RcIcon({ name: IconName.CLOSE_CIRCLE, iconSize:16, color:'#cccccc',onIconClick:()=>{this.searchText =''this.showClear =false}}).margin({ right:12})}}.width('100%').height(40).backgroundColor('#f5f5f5').borderRadius(20)}}

1.5 加载状态指示

@Component struct LoadingExample {@State isLoading:boolean=false@State rotationAngle:number=0build(){Column({ space:16}){// 旋转加载动画if(this.isLoading){RcIcon({ name: IconName.LOADER_OUTLINE, iconSize:32, color:'#1890ff', iconAnimation:{ duration:1000, curve: Curve.Linear, iterations:-1}}).rotate({ angle:this.rotationAngle }).onAppear(()=>{this.rotationAngle =360})Text('加载中...').fontSize(14).fontColor('#666666')}else{RcIcon({ name: IconName.CHECKMARK_CIRCLE, iconSize:32, color:'#52c41a'})Text('加载完成').fontSize(14).fontColor('#52c41a')}Button(this.isLoading ?'停止':'开始加载').onClick(()=>{this.isLoading =!this.isLoading if(!this.isLoading){this.rotationAngle =0}})}}}

1.6 空状态页面

@Component struct EmptyStateExample {@Prop emptyType:'noData'|'noNetwork'|'error'='noData'build(){Column({ space:20}){RcIcon({ name:this.getEmptyIcon(), iconSize:80, color:'#cccccc'})Text(this.getEmptyText()).fontSize(16).fontColor('#666666')if(this.emptyType ==='error'){Button('重新加载').onClick(()=>{// 重新加载逻辑})}}.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor('#f5f5f5')}getEmptyIcon():string{const iconMap ={'noData': IconName.FILE_OUTLINE,'noNetwork': IconName.WIFI_OFF_OUTLINE,'error': IconName.ALERT_TRIANGLE_OUTLINE}return iconMap[this.emptyType]}getEmptyText():string{const textMap ={'noData':'暂无数据','noNetwork':'网络连接失败','error':'加载失败'}return textMap[this.emptyType]}}

1.7 评分组件

@Component struct RatingExample {@State rating:number=0private maxRating:number=5build(){Row({ space:8}){ForEach(Array.from({ length:this.maxRating }),(_, index)=>{RcIcon({ name: index <this.rating ? IconName.STAR: IconName.STAR_OUTLINE, iconSize:24, color: index <this.rating ?'#faad14':'#d9d9d9',onIconClick:()=>{this.rating = index +1}})})}}}

1.8 标签页组件

@Component struct TabsExample {@State activeTab:number=0private tabs =[{ title:'推荐', icon: IconName.HOME},{ title:'关注', icon: IconName.HEART},{ title:'热门', icon: IconName.FLASH}]build(){Column(){// 标签页头部Row(){ForEach(this.tabs,(tab, index)=>{Column({ space:4}){RcIcon({ name: tab.icon, iconSize:20, color:this.activeTab === index ?'#1890ff':'#666666'})Text(tab.title).fontSize(14).fontColor(this.activeTab === index ?'#1890ff':'#666666')}.layoutWeight(1).padding(12).onClick(()=>{this.activeTab = index })})}.width('100%').backgroundColor('#ffffff')// 标签页内容区if(this.activeTab ===0){Text('推荐内容')}elseif(this.activeTab ===1){Text('关注内容')}else{Text('热门内容')}}}}

二、性能优化实践

2.1 避免过度渲染

❌ 不推荐:每次都重新创建

@Component struct BadExample {@State count:number=0build(){Column(){// 问题:每次count变化,所有符号都重新渲染RcIcon({ name: IconName.HOME, iconSize:20})RcIcon({ name: IconName.SEARCH, iconSize:20})RcIcon({ name: IconName.PERSON, iconSize:20})Text(this.count.toString())}}}

✅ 推荐:使用@Reusable优化

@Reusable@Component struct IconItem {@Param iconName:string=''build(){RcIcon({ name:this.iconName, iconSize:20})}}@Component struct GoodExample {@State count:number=0build(){Column(){IconItem({ iconName: IconName.HOME})IconItem({ iconName: IconName.SEARCH})IconItem({ iconName: IconName.PERSON})Text(this.count.toString())}}}

2.2 列表性能优化

使用LazyForEach优化大列表

classBasicDataSourceimplementsIDataSource{private listeners: DataChangeListener[]=[]private data:string[]=[]getData(index:number):string{returnthis.data[index]}totalCount():number{returnthis.data.length }registerDataChangeListener(listener: DataChangeListener):void{this.listeners.push(listener)}unregisterDataChangeListener(listener: DataChangeListener):void{const index =this.listeners.indexOf(listener)if(index >=0){this.listeners.splice(index,1)}}}@Component struct OptimizedListExample {private dataSource: BasicDataSource =newBasicDataSource()build(){List(){LazyForEach(this.dataSource,(item:string, index:number)=>{ListItem(){Row({ space:12}){RcIcon({ name: IconName.FILE, iconSize:20, color:'#666666'})Text(item).fontSize(16)}.padding(16)}},(item:string)=> item)}}}

2.3 动画性能优化

控制动画数量

@Component struct AnimationExample {@State visibleIcons:number=5private maxIcons:number=100build(){Column(){// 只为可见的符号添加动画ForEach(Array.from({ length: Math.min(this.visibleIcons,this.maxIcons)}),(_, index)=>{RcIcon({ name: IconName.STAR, iconSize:20, iconAnimation:{ duration:300, delay: index *50// 错开动画时间}})})}}}

使用动画开关

@StorageLink('enableAnimations') enableAnimations:boolean=trueRcIcon({ name: IconName.LOADER_OUTLINE, iconSize:24, iconAnimation:this.enableAnimations ?{ duration:1000, iterations:-1}:undefined})

2.4 资源加载优化

延迟加载非关键符号

@Component struct LazyLoadExample {@State showDetails:boolean=falsebuild(){Column({ space:12}){// 始终显示的关键符号RcIcon({ name: IconName.HOME, iconSize:24})Button('显示详情').onClick(()=>{this.showDetails =true})// 延迟加载的符号if(this.showDetails){Row({ space:8}){RcIcon({ name: IconName.EDIT, iconSize:20})RcIcon({ name: IconName.TRASH, iconSize:20})RcIcon({ name: IconName.SHARE, iconSize:20})}}}}}

2.5 内存优化

及时清理资源

@Component struct MemoryOptimizedExample {@State rotationAngle:number=0private animationTimer?:numberaboutToAppear(){this.startAnimation()}aboutToDisappear(){// 清理定时器if(this.animationTimer){clearInterval(this.animationTimer)this.animationTimer =undefined}}startAnimation(){this.animationTimer =setInterval(()=>{this.rotationAngle =(this.rotationAngle +10)%360},50)}build(){RcIcon({ name: IconName.SYNC, iconSize:30}).rotate({ angle:this.rotationAngle })}}

三、可访问性支持

3.1 语义化标签

RcIcon({ name: IconName.CLOSE}).accessibilityText('关闭按钮').accessibilityDescription('点击关闭当前窗口')

3.2 焦点管理

@State isFocused:boolean=falseRcIcon({ name: IconName.BUTTON, color:this.isFocused ?'#0066cc':'#0080ff'}).focusable(true).onFocus(()=>{this.isFocused =true}).onBlur(()=>{this.isFocused =false})

3.3 触摸区域扩展

// 符号本身较小时,扩大触摸区域Stack(){RcIcon({ name: IconName.SMALL, iconSize:16})}.width(44)// 最小44x44的触摸区域.height(44).onClick(()=>{// 点击处理})

四、跨平台适配

4.1 屏幕密度适配

@State screenDensity:number= display.getDefaultDisplaySync().densityDPI /160RcIcon({ name: IconName.LOGO, iconSize:24*this.screenDensity // 根据密度缩放})

4.2 暗黑模式适配

@StorageLink('colorMode') colorMode: ThemeColorMode = ThemeColorMode.LIGHTRcIcon({ name: IconName.THEME, color:this.colorMode === ThemeColorMode.DARK?'#ffffff':'#000000'})

4.3 多语言支持

// 对于方向性符号,考虑RTL布局@StorageLink('isRTL') isRTL:boolean=falseRcIcon({ name:this.isRTL ? IconName.ARROW_LEFT: IconName.ARROW_RIGHT, iconSize:20})

五、调试技巧

5.1 符号渲染检查

// 开发模式下显示符号名称@State showDebugInfo:boolean= BuildProfile.DEBUGRcIcon({ name: IconName.TEST}).border({ width:this.showDebugInfo ?1:0, color:'#ff0000'})if(this.showDebugInfo){Text('IconName.TEST').fontSize(10).fontColor('#ff0000')}

5.2 性能监控

@Component struct PerformanceMonitor {@State renderCount:number=0aboutToAppear(){this.renderCount++console.log(`组件渲染次数: ${this.renderCount}`)}build(){RcIcon({ name: IconName.MONITOR})}}

六、常见错误与解决方案

6.1 符号不显示

问题: 符号名称拼写错误

// ❌ 错误:拼写错误RcIcon({ name:'icon-houi_hom'})// ✅ 正确:使用常量避免拼写错误RcIcon({ name: IconName.HOME})

问题: 字体未加载

// 确保字体文件存在于rawfile目录// 路径:entry/src/main/resources/rawfile/rc_font.woff

6.2 颜色不生效

问题: 图片格式不支持颜色填充

// ❌ PNG图片不支持fillColorRcIcon({ name:'https://example.com/logo.png', color:'#ff0000'// 无效})// ✅ 使用SVG或字体符号RcIcon({ name: IconName.LOGO, color:'#ff0000'// 有效})

6.3 动画卡顿

问题: 同时运行过多动画

// ❌ 100个符号同时动画ForEach(Array.from({ length:100}),()=>{RcIcon({ name: IconName.STAR, iconAnimation:{...}})})// ✅ 限制同时动画数量ForEach(Array.from({ length:100}),(_, index)=>{RcIcon({ name: IconName.STAR, iconAnimation: index <10?{...}:undefined})})

6.4 内存泄漏

问题: 动画未清理

// ❌ 忘记清理定时器privatestartRotation(){setInterval(()=>{this.angle +=10},50)}// ✅ 在生命周期中清理private timer?:numberaboutToAppear(){this.timer =setInterval(()=>{this.angle +=10},50)}aboutToDisappear(){if(this.timer){clearInterval(this.timer)}}

七、版本兼容性

7.1 API版本检查

import{ systemCapability }from'@ohos.ability.ability'if(systemCapability.querySystemCapability('SystemCapability.ArkUI.ArkUI.Full')){// 使用新APIRcIcon({ name: IconName.NEW_FEATURE, iconAnimation:{...}})}else{// 降级处理RcIcon({ name: IconName.NEW_FEATURE})}

7.2 功能降级

// 检测是否支持动画@State supportsAnimation:boolean=trueRcIcon({ name: IconName.LOADER, iconAnimation:this.supportsAnimation ?{ duration:1000, iterations:-1}:undefined})

八、总结

8.1 核心要点

  1. 类型安全优先:使用IconName常量避免错误
  2. 性能意识:合理控制渲染和动画
  3. 用户体验:提供清晰的视觉反馈
  4. 可访问性:支持无障碍功能
  5. 可维护性:编写清晰、可测试的代码

8.2 应用价值

RcIcon作为UI库的基础组件,在实际项目中:

  • 减少50%以上的符号集成时间
  • 提供统一的视觉语言
  • 保证跨平台一致性
  • 支持主题定制和扩展
  • 性能优化开箱即用

Read more

计算机毕业设计Python+大模型美团大众点评情感分析 餐厅推荐系统 美食推荐系统 美团餐饮评论情感分析 大数据毕业设计(源码+LW+PPT+讲解)

计算机毕业设计Python+大模型美团大众点评情感分析 餐厅推荐系统 美食推荐系统 美团餐饮评论情感分析 大数据毕业设计(源码+LW+PPT+讲解)

温馨提示:文末有 ZEEKLOG 平台官方提供的学长联系方式的名片! 温馨提示:文末有 ZEEKLOG 平台官方提供的学长联系方式的名片! 温馨提示:文末有 ZEEKLOG 平台官方提供的学长联系方式的名片! 技术范围:SpringBoot、Vue、爬虫、数据可视化、小程序、安卓APP、大数据、知识图谱、机器学习、Hadoop、Spark、Hive、大模型、人工智能、Python、深度学习、信息安全、网络安全等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码、文档辅导、LW文档降重、长期答辩答疑辅导、腾讯会议一对一专业讲解辅导答辩、模拟答辩演练、和理解代码逻辑思路。 🍅文末获取源码联系🍅 🍅文末获取源码联系🍅 🍅文末获取源码联系🍅 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及LW文档编写等相关问题都可以给我留言咨询,

By Ne0inhk
从 0 到 1:用 Trae 插件 Builder 模式开发端午包粽子小游戏

从 0 到 1:用 Trae 插件 Builder 模式开发端午包粽子小游戏

前言 Trae插件获取:https://www.trae.com.cn/plugin 在编程的世界里,效率就是生命。我们开发者常常为了一个项目的搭建,重复着创建文件夹、初始化项目配置、编写样板代码等一系列繁琐的操作,耗费了大量的时间和精力。而如今,Trae 插件的 Builder 模式横空出世,为我们的编程之旅带来了一束全新的光亮。它就像是一个智能化的Ai工程师,能够理解我们的需求,快速搭建起项目的框架,极大地减少了重复性工作,让我们得以将更多的精力投入到核心逻辑的开发中。由于马上就要端午节了,那么接下来我将利用Trae插件的builder模式,带大家从0到1开发一个端午包粽子小游戏。 Trae插件builder模式介绍 什么是Trae 插件的Builder 模式呢?通俗点来说,就好比你想盖一座房子,正常情况下,你得先画图纸、挖地基、砌墙、装窗户等等,一步步来,这中间要操心好多琐碎又重复的活儿。而 Builder 模式就像是有个智能的建筑工人,你跟它说 “我要盖一座两层楼的别墅,要有个大大的客厅、三间卧室、一个花园”

By Ne0inhk
【Python踩坑全纪录】-Windows 系统如何彻底卸载所有 pip install 安装的包?

【Python踩坑全纪录】-Windows 系统如何彻底卸载所有 pip install 安装的包?

Windows 系统如何彻底卸载所有 pip 安装的包 在 Windows 系统上卸载所有通过 pip 安装的包有多种方法,我将详细介绍每种方法及其适用场景。 方法一:使用 pip freeze 和 pip uninstall(推荐) 这是最常用和最安全的方法,可以批量卸载所有第三方包。 步骤 1:生成已安装包列表 # 生成包含所有已安装包的列表文件 pip freeze > requirements.txt 步骤 2:批量卸载所有包 # 从列表文件卸载所有包 pip uninstall -r requirements.txt -y 参数解释: * -r requirements.txt:从文件读取要卸载的包列表 * -y:自动确认卸载,无需手动确认每个包 步骤 3:清理临时文件

By Ne0inhk
python Tkinter Frame 深度解析与实战指南

python Tkinter Frame 深度解析与实战指南

一、Frame 的核心定位与架构价值 在 Tkinter 的组件体系中,Frame(框架)是最基础却最重要的容器类组件。它本质上是一个矩形区域,充当其他组件的"父容器"和"布局单元"。与功能性组件(Button、Entry 等)不同,Frame 的核心使命是组织与分层。 在复杂的 GUI 应用中,直接将所有组件挂载到根窗口(Tk())会导致布局混乱、代码难以维护。Frame 通过引入层级化布局概念,允许开发者将界面划分为逻辑独立的模块(如导航栏、内容区、状态栏),每个模块内部独立管理布局,最终组合成完整界面。这种"分而治之"的思想是构建专业级桌面应用的基石。 二、基础创建与属性配置 2.1 基本实例化

By Ne0inhk