前言

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

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

  1. 组件初始化时自动连接全局存储
  2. 全局样式变化时组件自动响应更新
  3. 避免手动管理状态同步,降低复杂度

三、字体渲染机制

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
                      │
                      └── 失败 ──> 记录错误日志

关键点分析:

  1. 时机选择:在aboutToAppear生命周期注册,确保渲染前字体可用
  2. 单次注册:使用fontRegistered标志避免重复注册
  3. 错误处理:使用try-catch捕获注册失败,不影响组件正常渲染
  4. 字体来源:从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 ''
}

处理流程:

  1. 检查name参数类型是否为字符串
  2. 从RcIconList映射表中查找对应的Unicode字符
  3. 如果找到则返回Unicode字符,否则返回原始name(可能是自定义Unicode)
  4. 非字符串类型返回空字符串(交由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. 字符串长度为1
  2. 字符编码在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组件分支
    }
}

优化策略:

  1. 提前判断:在build方法入口进行类型判断
  2. 分支明确:Text和Image组件分别在独立分支渲染
  3. 避免重复判断:一次判断确定整个渲染路径

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)
}

容错策略:

  1. 使用try-catch捕获异常
  2. 记录详细错误信息到控制台
  3. 即使注册失败,组件仍可正常渲染(使用系统默认字体)

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 核心优势

  1. 双模式支持:字体符号和图片资源无缝切换
  2. 高性能:矢量字体渲染,任意缩放不失真
  3. 易用性:简洁的API,丰富的符号库
  4. 扩展性:支持自定义字体和外部资源
  5. 类型安全:完整的TypeScript类型定义

10.2 技术亮点

  • 智能判断:自动识别资源类型选择最优渲染方案
  • 容错机制:多层错误处理确保组件稳定运行
  • 性能优化:单次字体加载、条件渲染、事件优化
  • ArkTS适配:严格遵守HarmonyOS6开发规范
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐