前言

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

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

项目简介

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

核心特性

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

开源计划

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

一、概述

RcButton是一个功能完善的HarmonyOS6按钮组件,采用ComponentV2装饰器实现,支持多种类型、尺寸、形状以及丰富的交互状态。本文将深入解析组件的核心架构、设计思想以及实现细节。

案例展示:

二、组件架构设计

2.1 装饰器体系

组件使用@ComponentV2装饰器,这是HarmonyOS6 ArkUI的新一代组件定义方式,相比传统的@Component具有更好的性能和类型推断能力。

@ComponentV2
export struct RcButton {
  // 组件实现
}

核心特性:

  • 支持更精细的状态管理
  • 提供更好的类型安全
  • 优化了渲染性能

2.2 状态管理策略

组件采用多层次的状态管理机制:

全局状态连接
@Local baseStyle: RcUIBaseStyleObjType = AppStorageV2.connect(RcUIBaseStyle, RcStorageKey.BASE_STYLE)!
@Local config: RcGlobalConfig = AppStorageV2.connect(RcGlobalConfig, RcStorageKey.GLOBAL_CONFIG)!

通过AppStorageV2.connect连接全局样式配置,实现:

  • 主题统一管理: 所有RcButton实例共享全局样式配置
  • 动态主题切换: 修改全局配置后所有按钮自动响应
  • 配置隔离: 不同应用可以有独立的样式体系
组件参数状态

使用@Param装饰器定义可配置属性:

@Param text?: string = ''
@Param type?: RcButtonType = RcButtonType.DEFAULT
@Param btnSize?: RcButtonSize = RcButtonSize.NORMAL

设计亮点:

  • 所有参数都设置了合理的默认值
  • 使用可选类型(?)提供灵活性
  • 参数命名清晰,避免与系统关键字冲突(如bordersColor而非borderColor)
内部状态管理
@Local lastClickTime: number = 0

用于实现节流等内部逻辑,不对外暴露,保持API简洁。

2.3 类型系统设计

组件定义了完整的类型枚举体系:

按钮类型枚举
export enum RcButtonType {
  DEFAULT = 'default',
  PRIMARY = 'primary',
  SUCCESS = 'success',
  WARNING = 'warning',
  ERROR = 'error',
  INFO = 'info'
}

设计考量:

  • 使用字符串枚举,便于调试和序列化
  • 覆盖常见的UI语义类型
  • 每种类型对应特定的视觉风格
尺寸枚举
export enum RcButtonSize {
  LARGE = 'large',
  NORMAL = 'normal',
  SMALL = 'small',
  MINI = 'mini'
}

提供4档尺寸,满足不同场景需求:

  • LARGE (48px): 首要操作、底部固定按钮
  • NORMAL (40px): 标准场景,默认尺寸
  • SMALL (32px): 次要操作、卡片内按钮
  • MINI (28px): 紧凑空间、表格操作
形状枚举
export enum RcButtonShape {
  SQUARE = 'square',
  ROUND = 'round',
  CIRCLE = 'circle'
}

使用场景:

  • SQUARE: 常规方形按钮,圆角4px
  • ROUND: 胶囊按钮,圆角为高度的一半
  • CIRCLE: 圆形按钮,适合纯图标按钮

2.4 配置接口设计

尺寸配置接口
export interface RcButtonSizeConfig {
  height: number       // 按钮高度
  fontSize: number     // 文字大小
  paddingH: number     // 水平内边距
  iconSize: number     // 图标大小
}

这个接口封装了尺寸相关的所有配置,确保不同尺寸的按钮各元素比例协调。

颜色配置接口
export interface RcButtonColorConfig {
  bg: ResourceColor        // 背景色
  text: ResourceColor      // 文字色
  border: ResourceColor    // 边框色
  activeBg: ResourceColor  // 激活背景色
}

统一管理一种类型按钮的所有颜色状态,便于主题扩展。

三、核心设计模式

3.1 配置映射模式

组件通过私有方法将枚举类型映射为具体配置:

private getSizeConfig(): RcButtonSizeConfig {
  switch (this.btnSize) {
    case RcButtonSize.LARGE:
      return { height: 48, fontSize: 16, paddingH: 24, iconSize: 20 }
    case RcButtonSize.SMALL:
      return { height: 32, fontSize: 14, paddingH: 16, iconSize: 16 }
    case RcButtonSize.MINI:
      return { height: 28, fontSize: 12, paddingH: 12, iconSize: 14 }
    case RcButtonSize.NORMAL:
    default:
      return { height: 40, fontSize: 15, paddingH: 20, iconSize: 18 }
  }
}

优势:

  • 集中管理尺寸配置,修改方便
  • 使用对象返回,扩展性强
  • 提供默认分支,确保健壮性

3.2 策略模式

颜色配置采用策略模式,根据不同按钮类型返回对应配置:

private getColorConfig(): RcButtonColorConfig {
  // 优先使用自定义颜色
  if (this.color !== undefined) {
    return {
      bg: this.color,
      text: Color.White,
      border: this.color,
      activeBg: this.color
    }
  }

  // 根据类型返回颜色
  switch (this.type) {
    case RcButtonType.PRIMARY:
      return {
        bg: RcPrimary,
        text: Color.White,
        border: RcPrimary,
        activeBg: RcPrimaryDark
      }
    // 其他类型...
  }
}

设计特点:

  • 支持自定义颜色覆盖预设
  • 每种类型有完整的颜色状态
  • 包含激活态,提供更好的反馈

3.3 计算属性模式

组件使用大量getter方法实现计算属性:

private getButtonWidth(): Length | undefined {
  if (this.btnWidth !== undefined) {
    return this.btnWidth
  }
  return this.block ? '100%' : undefined
}

private getButtonHeight(): Length {
  if (this.btnHeight !== undefined) {
    return this.btnHeight
  }
  return this.getSizeConfig().height
}

优先级规则:

  1. 自定义属性优先级最高
  2. 特殊状态配置次之(如block)
  3. 默认配置兜底

这种设计使组件既灵活又有合理的默认行为。

3.4 组合优于继承

组件内部使用RcIcon等子组件 :

RcIcon 组件的讲解在后面的章节会有介绍哦~~

if (!this.loading && this.icon) {
  RcIcon({
    name: this.icon,
    iconSize: this.iconSize || this.getSizeConfig().iconSize,
    color: this.getTextColor()
  })
    .margin({ right: this.text ? 6 : 0 })
}

通过组合实现复杂功能,而非继承,符合"组合优于继承"原则。

四、状态协调机制

4.1 互斥状态处理

某些状态之间存在互斥关系:

.backgroundColor(this.disabled ?
  this.getDisabledColor() :
  (this.plain || this.textButton ? Color.Transparent : this.getColorConfig().bg))

状态优先级:

  1. disabled状态最高优先级
  2. plain和textButton影响背景透明度
  3. 默认使用类型配置

4.2 联动状态

加载状态会影响多个方面:

// 禁用点击
.enabled(!this.disabled && !this.loading)

// 显示加载图标
if (this.loading) {
  LoadingProgress()
    .width(this.iconSize || this.getSizeConfig().iconSize)
    .height(this.iconSize || this.getSizeConfig().iconSize)
    .color(this.getTextColor())
}

// 切换文本
if (this.loading && this.loadingText) {
  Text(this.loadingText)
} else if (this.text) {
  Text(this.text)
}

loading状态自动:

  • 禁用按钮交互
  • 隐藏普通图标,显示加载动画
  • 支持切换加载文本

4.3 样式状态

使用stateStyles定义不同交互状态的样式:

.stateStyles({
  normal: {
    .opacity(1)
  },
  pressed: {
    .backgroundColor(this.disabled || this.plain || this.textButton ?
      undefined :
      this.getColorConfig().activeBg)
    .opacity(this.disabled ? 0.6 : (this.plain || this.textButton ? 0.7 : 1))
  },
  disabled: {
    .opacity(0.6)
  }
})

状态细分:

  • normal: 正常状态
  • pressed: 按下状态,实体按钮切换背景色,镂空按钮降低透明度
  • disabled: 禁用状态,统一降低透明度

五、设计原则体现

5.1 单一职责原则

每个私有方法职责单一:

  • getSizeConfig(): 只负责尺寸配置
  • getColorConfig(): 只负责颜色配置
  • handleClick(): 只负责点击处理

5.2 开闭原则

组件对扩展开放,对修改封闭:

  • 通过枚举类型扩展新的按钮类型
  • 通过自定义属性覆盖默认行为
  • 无需修改核心逻辑

5.3 依赖倒置原则

组件依赖抽象(接口)而非具体实现:

  • 依赖RcButtonSizeConfig接口而非具体数值
  • 依赖ResourceColor类型而非具体颜色值
  • 通过全局配置注入而非硬编码

5.4 最少知识原则

组件只暴露必要的属性和事件:

  • 内部计算逻辑通过private方法封装
  • 内部状态(如lastClickTime)不对外暴露
  • 提供清晰的API接口

六、性能优化策略

6.1 计算缓存

尺寸和颜色配置通过方法调用获取,避免不必要的重复计算:

const sizeConfig = this.getSizeConfig()

虽然每次都调用方法,但switch语句执行速度很快,且返回的是静态对象。

6.2 条件渲染

根据状态条件渲染不同内容:

if (this.loading) {
  LoadingProgress()
} else if (!this.loading && this.icon) {
  RcIcon()
}

避免渲染不需要的组件,减少节点数量。

6.3 事件节流

内置节流机制防止重复点击:

private handleClick = (event: ClickEvent): void => {
  if (this.disabled || this.loading) {
    return
  }

  const currentTime = Date.now()
  if (this.throttleTime && this.throttleTime > 0) {
    if (currentTime - this.lastClickTime < this.throttleTime) {
      return
    }
    this.lastClickTime = currentTime
  }

  this.onBtnClick(event)
}

实现细节:

  • 记录上次点击时间
  • 在节流时间内的点击直接忽略
  • 节流时间可配置,默认为0(不节流)

七、扩展性设计

7.1 自定义样式支持

组件预留了多个自定义属性:

  • btnWidth/btnHeight: 自定义尺寸
  • fontSize: 自定义文字大小
  • color/textColor/bordersColor: 自定义颜色
  • bordersRadius/bordersWidth: 自定义边框
  • customStyle: 完全自定义样式对象

7.2 主题系统集成

通过全局配置对接主题系统:

@Local baseStyle: RcUIBaseStyleObjType = AppStorageV2.connect(RcUIBaseStyle, RcStorageKey.BASE_STYLE)!
@Local config: RcGlobalConfig = AppStorageV2.connect(RcGlobalConfig, RcStorageKey.GLOBAL_CONFIG)!

应用可以通过修改全局配置实现:

  • 品牌色定制
  • 圆角风格调整
  • 字体大小适配
  • 深色模式切换

7.3 组件组合能力

RcButton可以与其他组件灵活组合:

  • 可以嵌入表单组件
  • 可以与对话框配合
  • 可以组成按钮组

八、总结

RcButton组件通过精心的架构设计,实现了:

  1. 灵活性: 丰富的配置选项,满足各种使用场景
  2. 一致性: 统一的类型系统和样式规范
  3. 可维护性: 清晰的代码结构和设计模式
  4. 可扩展性: 预留自定义接口,支持主题定制
  5. 性能优化: 合理的状态管理和渲染策略

组件的成功在于平衡了易用性和灵活性,既提供了开箱即用的预设样式,又保留了充分的定制空间。这种设计思想值得在其他UI组件开发中借鉴。
好了下课~~~~

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐