HarmonyOS6半年磨一剑 - RcIcon组件核心架构与字体图形渲染机制
各位开发者,大家好!我是若城。在鸿蒙应用开发过程中,我发现许多组件样式和工具方法具有高度的复用性,但每次新项目都需要重复编写,这极大地降低了开发效率。因此,我决定投入半年时间,打造一款专为鸿蒙生态设计的 UI 组件库 ——rchoui。rchoui是一个面向 HarmonyOS6 的企业级 UI 组件库,旨在提供开箱即用的高质量组件,让开发者告别"重复造轮子"。RcIcon是rchoui组件库中的
前言
各位开发者,大家好!我是若城。
在鸿蒙应用开发过程中,我发现许多组件样式和工具方法具有高度的复用性,但每次新项目都需要重复编写,这极大地降低了开发效率。因此,我决定投入半年时间,打造一款专为鸿蒙生态设计的 UI 组件库 —— rchoui。
项目简介
rchoui 是一个面向 HarmonyOS6 的企业级 UI 组件库,旨在提供开箱即用的高质量组件,让开发者告别"重复造轮子"。
核心特性
- 丰富组件:涵盖基础组件、表单组件、弹窗组件、布局组件等
- 设计规范:遵循统一的色彩体系和设计语言
- 工具集成:内置常用工具方法,提升开发效率
- 完善文档:每个模块都配有详细的设计思路和使用说明
开源计划
项目预计于 2026 年 7 月中旬正式开源,届时可通过三方库直接下载使用。在此期间,我会通过系列文章逐一介绍每个模块的设计思路与实现细节。
rchoui官网
目前暂定 rchoui 官网地址:http://rchoui.ruocheng.site/
需要注意的是 当前官网还在完善当中, 会在后续更新中逐步完善。届时可以为大家提供更加完善的说明文档
一、组件概述
RcIcon是rchoui组件库中的核心基础组件,负责渲染各类图形化符号。它是一个高度灵活的图形显示组件,支持字体符号和图片两种渲染模式,内置492个精心设计的矢量符号(246个线型+246个实底),能够满足绝大多数业务场景需求。
1.1 设计目标
统一性:提供统一的符号渲染接口,屏蔽不同资源类型的差异
易用性:简化符号使用方式,提供类型安全的常量引用
性能优化:基于字体的矢量渲染,确保任意缩放不失真
扩展性:支持自定义符号字体和外部图片资源
二、组件架构设计
2.1 装饰器体系
@ComponentV2
export struct RcIcon {
@Local baseStyle: RcUIBaseStyleObjType = AppStorageV2.connect(...)
@Local config: RcGlobalConfig = AppStorageV2.connect(...)
@Param @Require name: keyof RcIconDataType | ResourceStr
@Param color?: ResourceColor = undefined
@Param iconSize: RcStringNumber = this.baseStyle.fontSizeLg as string
// ... 其他属性
}
装饰器解析:
| 装饰器 | 作用 | 应用场景 |
|---|---|---|
@ComponentV2 |
HarmonyOS6新一代组件定义 | 性能更优,类型推断更强 |
@Local |
连接全局状态 | 全局样式、配置响应式同步 |
@Param |
组件参数定义 | 接收外部传入的配置 |
@Require |
必需参数标记 | 确保关键参数不为空 |
@Event |
事件回调定义 | 处理用户交互事件 |
2.2 核心架构图
┌─────────────────────────────────────────┐
│ RcIcon 组件 │
├─────────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ │
│ │ 参数层 │ ───> │ 判断层 │ │
│ │ Props │ │ Type Check│ │
│ └────────────┘ └────────────┘ │
│ │ │ │
│ ├──────────┬────────┤ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌────────┐ ┌────────┐ │
│ │ 字体符号 │ │ 在线图片│ │ 本地资源│ │
│ └─────────┘ └────────┘ └────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────┐ │
│ │ 渲染层 (build) │ │
│ │ Text组件 或 Image组件 │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────┘
2.3 依赖关系
// 全局样式依赖
@Local baseStyle: RcUIBaseStyleObjType =
AppStorageV2.connect(RcUIBaseStyle, RcStorageKey.BASE_STYLE)!
// 全局配置依赖
@Local config: RcGlobalConfig =
AppStorageV2.connect(RcGlobalConfig, RcStorageKey.GLOBAL_CONFIG)!
AppStorageV2连接机制:
- 组件初始化时自动连接全局存储
- 全局样式变化时组件自动响应更新
- 避免手动管理状态同步,降低复杂度
三、字体渲染机制
3.1 字体注册流程
aboutToAppear(): void {
if (!this.fontRegistered) {
try {
let font: Font = this.getUIContext().getFont();
font.registerFont({
familyName: 'rcFont',
familySrc: $rawfile('rc_font.woff')
})
this.fontRegistered = true
} catch (error) {
console.error('RcIcon: Font registration failed', error)
}
}
}
注册流程详解:
组件挂载 (aboutToAppear)
│
▼
检查 fontRegistered 标志
│
├── 已注册 ──> 跳过
│
└── 未注册 ──> 获取 UIContext
│
▼
获取 Font 管理器
│
▼
调用 registerFont()
│
├── 成功 ──> fontRegistered = true
│
└── 失败 ──> 记录错误日志
关键点分析:
- 时机选择:在
aboutToAppear生命周期注册,确保渲染前字体可用 - 单次注册:使用
fontRegistered标志避免重复注册 - 错误处理:使用try-catch捕获注册失败,不影响组件正常渲染
- 字体来源:从rawfile目录加载woff格式字体文件
3.2 Unicode映射机制
// config.ets 中的映射表
export const RcIconList: RcIconDataType = {
"icon-houi_home": "\ue7c7",
"icon-houi_heart": "\ue71b",
"icon-houi_star": "\ue784",
"icon-houi_search": "\ue77a",
// ... 492个符号映射
}
映射表结构:
| 符号名称 | Unicode编码 | 用途 |
|---|---|---|
icon-houi_home |
\ue7c7 |
主页 |
icon-houi_heart |
\ue71b |
收藏/喜欢 |
icon-houi_star |
\ue784 |
星标/评分 |
icon-houi_search |
\ue77a |
搜索 |
字符获取逻辑:
private getIconContent(): string {
if (typeof this.name === 'string') {
// 使用Object方式访问以符合ArkTS规范
return Object(RcIconList)[this.name as string] || this.name
}
return ''
}
处理流程:
- 检查name参数类型是否为字符串
- 从RcIconList映射表中查找对应的Unicode字符
- 如果找到则返回Unicode字符,否则返回原始name(可能是自定义Unicode)
- 非字符串类型返回空字符串(交由Image组件处理)
3.3 字体渲染流程
build() {
if (typeof this.name === "string" && !this.isOnlineImage()) {
Text(this.getIconContent())
.fontFamily('rcFont')
.fontColor(this.color)
.fontSize(getSizeByUnit(this.iconSize, true))
.borderRadius(getSizeByUnit(this.iconRadius))
.animation(this.iconAnimation)
.onTouch(this.handleTouch)
} else {
// Image渲染逻辑
}
}
渲染步骤解析:
参数判断
│
▼
是否为字符串类型?
│
├── 是 ──> 是否为在线图片URL?
│ │
│ ├── 否 ──> 【字体渲染路径】
│ │ │
│ │ ├─> 获取Unicode字符
│ │ ├─> 设置字体族 (rcFont)
│ │ ├─> 应用颜色样式
│ │ ├─> 应用尺寸样式
│ │ ├─> 应用圆角样式
│ │ ├─> 应用动画效果
│ │ └─> 绑定触摸事件
│ │
│ └── 是 ──> 【图片渲染路径】
│
└── 否 ──> 【ResourceStr路径】(本地资源)
四、类型判断策略
4.1 在线图片判断
private isOnlineImage(): boolean {
return typeof this.name === 'string' && this.name.startsWith("http")
}
判断逻辑:
- 字符串类型
- 以"http"开头(包含http和https)
- 返回true表示是在线图片URL
4.2 Unicode字符判断
private isSingleUnicodeEscape(str: string): boolean {
return str.length === 1 && /^[\u0000-\uFFFF]$/.test(str)
}
验证条件:
- 字符串长度为1
- 字符编码在Unicode基本多文种平面(BMP)范围内(\u0000-\uFFFF)
应用场景:
- 验证从映射表获取的字符是否有效
- 防止错误的多字符字符串被当作单个Unicode字符
4.3 渲染路径决策树
name 参数类型
│
├─ ResourceStr (Resource类型)
│ └──> Image组件 (本地资源)
│
└─ string (字符串)
│
├─ 以"http"开头
│ └──> Image组件 (在线图片)
│
└─ 其他字符串
│
├─ 存在于RcIconList映射表
│ └──> Text组件 + Unicode字符
│
└─ 不存在于映射表
└──> Text组件 + 原始字符串
五、性能优化设计
5.1 字体加载优化
单次加载机制:
private fontRegistered: boolean = false
aboutToAppear(): void {
if (!this.fontRegistered) {
// 注册字体逻辑
this.fontRegistered = true
}
}
优势:
- 避免重复注册浪费资源
- 组件实例间共享字体资源
- 首次加载后后续实例无需等待
5.2 条件渲染优化
build() {
if (typeof this.name === "string" && !this.isOnlineImage()) {
// Text组件分支
} else {
// Image组件分支
}
}
优化策略:
- 提前判断:在build方法入口进行类型判断
- 分支明确:Text和Image组件分别在独立分支渲染
- 避免重复判断:一次判断确定整个渲染路径
5.3 事件处理优化
private handleTouch = (event: TouchEvent): void => {
if (event.type === TouchType.Up) {
this.onIconClick(event)
}
}
优化点:
- 只响应TouchType.Up事件,减少不必要的回调
- 使用箭头函数保持this上下文,避免bind开销
- 事件委托统一处理,降低代码重复
5.4 尺寸计算缓存
.fontSize(getSizeByUnit(this.iconSize, true))
.borderRadius(getSizeByUnit(this.iconRadius))
getSizeByUnit工具函数特点:
- 统一处理数字和字符串类型尺寸
- 自动添加单位(vp/px/lpx)
- 结果可被框架缓存,避免重复计算
六、错误处理机制
6.1 字体注册失败处理
try {
let font: Font = this.getUIContext().getFont();
font.registerFont({
familyName: 'rcFont',
familySrc: $rawfile('rc_font.woff')
})
this.fontRegistered = true
} catch (error) {
console.error('RcIcon: Font registration failed', error)
}
容错策略:
- 使用try-catch捕获异常
- 记录详细错误信息到控制台
- 即使注册失败,组件仍可正常渲染(使用系统默认字体)
6.2 符号映射失败回退
private getIconContent(): string {
if (typeof this.name === 'string') {
return Object(RcIconList)[this.name as string] || this.name
}
return ''
}
回退机制:
- 映射表中找不到对应Unicode时,返回原始name
- 允许用户直接传入Unicode字符
- 空值返回空字符串,避免渲染错误
6.3 类型安全保护
@Param @Require name: keyof RcIconDataType | ResourceStr
TypeScript类型约束:
keyof RcIconDataType:限制为已定义的符号名称ResourceStr:支持HarmonyOS资源引用@Require:确保必需参数不为空
七、扩展性设计
7.1 自定义字体支持
@Param fontName: ResourceStr = ""
// 渲染时应用自定义字体
Text(this.getIconContent())
.fontFamily(this.fontName || 'rcFont')
扩展场景:
- 使用第三方字体
- 项目特定字体库
- 多语言字体需求
7.2 样式可定制性
组件提供了丰富的样式属性:
color:颜色定制iconSize:尺寸调整iconRadius:圆角控制iconAnimation:动画效果
设计原则:
- 提供合理默认值
- 允许完全自定义
- 保持API简洁一致
7.3 事件扩展性
@Event onIconClick: (event: TouchEvent) => void = () => {}
事件系统特点:
- 标准TouchEvent对象
- 支持事件冒泡和捕获
- 可获取完整触摸信息(坐标、时间戳等)
八、ArkTS规范适配
8.1 Object访问方式
// 符合ArkTS规范的访问方式
return Object(RcIconList)[this.name as string] || this.name
// 不推荐的方式(可能不符合ArkTS严格模式)
// return RcIconList[this.name]
原因分析:
- ArkTS对动态属性访问有严格限制
- 使用
Object()包装确保类型安全 - 通过类型断言明确字符串索引意图
8.2 类型声明规范
// 接口定义清晰
export interface RcIconDataType extends OutLineIconsType, FilledIconsType {}
// 参数类型明确
@Param name: keyof RcIconDataType | ResourceStr
规范要点:
- 接口继承关系明确
- 联合类型使用恰当
- keyof操作符提供类型约束
九、设计模式应用
9.1 策略模式
通过条件判断选择不同的渲染策略:
- 字体渲染策略:Text组件 + fontFamily
- 图片渲染策略:Image组件 + src
9.2 工厂模式
getIconContent()方法作为工厂方法,根据输入产生不同的内容:
- 符号名称 → Unicode字符
- Unicode字符 → 原样返回
- 其他 → 空字符串
9.3 单例模式
字体注册采用单例思想:
fontRegistered标志确保全局只注册一次- 多个组件实例共享同一字体资源
十、总结
10.1 核心优势
- 双模式支持:字体符号和图片资源无缝切换
- 高性能:矢量字体渲染,任意缩放不失真
- 易用性:简洁的API,丰富的符号库
- 扩展性:支持自定义字体和外部资源
- 类型安全:完整的TypeScript类型定义
10.2 技术亮点
- 智能判断:自动识别资源类型选择最优渲染方案
- 容错机制:多层错误处理确保组件稳定运行
- 性能优化:单次字体加载、条件渲染、事件优化
- ArkTS适配:严格遵守HarmonyOS6开发规范
更多推荐


所有评论(0)