# 评分器组件开发实战:HarmonyOS ArkTS 五星评分交互应用解析

一、应用概述

评分器(Star Rating)是各类电商、内容平台、生活服务类应用中最常见的交互组件之一。用户通过点击或悬浮操作选择星级评分,直观地表达对商品、服务或内容的评价。本文将以 HarmonyOS 原生开发框架 ArkTS 为基础,详细解析一个五星评分器组件的完整开发过程,涵盖评分展示、交互反馈、悬停预览等核心功能。

1.1 功能特性

  • 五星评分体系:支持 0.5 到 5.0 的评分精度,每颗星代表 1 分
  • 悬停预览(Hover):鼠标或手指在星标上悬停时实时显示当前评分值
  • 点击确认:用户点击确定最终的评分结果
  • 动态反馈:选中的星标以高亮金色显示,未选中的星标显示灰色
  • 数值显示:实时显示当前评分数值和对应的文字评价(如"非常好"、"一般"等)
  • 可配置参数:评分组件的星级数量、颜色、大小等均可通过参数配置

1.2 适用场景

  • 商品评价系统
  • 电影/书籍评分
  • 服务质量反馈
  • 用户满意度调查
  • 内容质量评级

1.3 技术亮点

本组件虽然功能聚焦,却涵盖了 ArkTS 开发中的多个关键技术点:组件状态管理、触摸事件处理、自定义组件封装、动画反馈、组件间通信等,是学习 ArkTS 组件化开发的经典案例。


二、技术架构

2.1 整体架构概览

评分器组件采用"容器组件 + 子组件"的层级架构,利用 ArkTS 的 @Component@Builder 实现高度模块化。

复制代码
┌────────────────────────────────────────────┐
│              RatingContainer               │
│        (评分容器组件 - 状态管理)           │
├────────────────────────────────────────────┤
│  ┌────────────────────────────────────────┐│
│  │          StarComponent                 ││
│  │      (单个星标组件 - 独立状态)        ││
│  │  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ││
│  │  │ Star1│ │ Star2│ │ Star3│ │ Star4│ ││
│  │  └──────┘ └──────┘ └──────┘ └──────┘ ││
│  └────────────────────────────────────────┘│
├────────────────────────────────────────────┤
│         RatingDisplay(评分信息展示区)      │
└────────────────────────────────────────────┘

2.2 核心数据结构

arkts 复制代码
// 评分配置接口
interface RatingConfig {
  totalStars: number;         // 星标总数(默认5)
  starSize: number;           // 星标尺寸(默认32)
  activeColor: ResourceColor; // 选中颜色(默认金色)
  inactiveColor: ResourceColor; // 未选中颜色(默认灰色)
  allowHalf: boolean;         // 是否允许半星
  showText: boolean;          // 是否显示评分文本
}

// 评分等级映射
const RATING_TEXT_MAP: Record<number, string> = {
  0.5: '非常差',
  1.0: '差',
  1.5: '较差',
  2.0: '一般',
  2.5: '中等',
  3.0: '中上',
  3.5: '良好',
  4.0: '好',
  4.5: '很好',
  5.0: '非常好'
};

2.3 组件层级关系

复制代码
@Entry MainPage
  └── Column (主容器)
      ├── Text (标题: "请评价我们的服务")
      └── RatingContainer (自定义评分组件)
          ├── Row (星标行)
          │   ├── Star (第1颗星)
          │   ├── Star (第2颗星)
          │   ├── Star (第3颗星)
          │   ├── Star (第4颗星)
          │   └── Star (第5颗星)
          ├── Text (评分数值: "4.5")
          └── Text (评价文本: "非常好")

三、核心代码分析

3.1 评分容器组件

RatingContainer 是整个评分器的核心组件,负责管理评分状态和处理交互逻辑。

arkts 复制代码
@Component
struct RatingContainer {
  // 外部传入的配置参数
  private config: RatingConfig = {
    totalStars: 5,
    starSize: 40,
    activeColor: '#FFD700',
    inactiveColor: '#E0E0E0',
    allowHalf: false,
    showText: true
  };
  
  // 内部状态
  @State currentRating: number = 0;     // 当前评分值
  @State hoverRating: number = 0;       // 悬停时的临时评分值
  @State isHovering: boolean = false;   // 是否处于悬停状态
  
  // 对外回调
  private onRatingChange?: (rating: number) => void;
  
  // 计算显示的评分值(优先显示悬停值)
  get displayRating(): number {
    return this.isHovering ? this.hoverRating : this.currentRating;
  }
  
  // 获取评分的文字描述
  get ratingText(): string {
    const keys = Object.keys(RATING_TEXT_MAP).map(Number).sort((a, b) => a - b);
    let closest = keys[0];
    for (const key of keys) {
      if (Math.abs(key - this.displayRating) <= Math.abs(closest - this.displayRating)) {
        closest = key;
      }
    }
    return RATING_TEXT_MAP[closest] || '';
  }
  
  build() {
    Column({ space: 12 }) {
      // 星标行
      Row({ space: 8 }) {
        ForEach(
          Array.from({ length: this.config.totalStars }, (_, i) => i + 1),
          (starIndex: number) => {
            StarComponent({
              starIndex: starIndex,
              currentRating: this.displayRating,
              starSize: this.config.starSize,
              activeColor: this.config.activeColor,
              inactiveColor: this.config.inactiveColor,
              onStarClick: (index: number) => {
                this.handleStarClick(index);
              },
              onStarHover: (index: number) => {
                this.handleStarHover(index);
              },
              onStarLeave: () => {
                this.handleStarLeave();
              }
            })
          }
        )
      }
      
      // 评分信息展示
      if (this.config.showText) {
        Row({ space: 16 }) {
          Text(this.displayRating > 0 ? this.displayRating.toFixed(1) : '未评分')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor(this.config.activeColor)
          
          Text(this.displayRating > 0 ? this.ratingText : '点击星标进行评分')
            .fontSize(16)
            .fontColor('#666666')
        }
      }
    }
    .padding(24)
    .backgroundColor(Color.White)
    .borderRadius(16)
    .shadow({
      radius: 8,
      color: 'rgba(0, 0, 0, 0.06)',
      offsetX: 0,
      offsetY: 4
    })
  }
}

核心逻辑分析

  1. 双评分状态currentRating 保存用户最终确认的评分,hoverRating 保存悬停时的临时评分。二者分离可以实现在悬停预览后恢复到原有评分的功能。
  2. 计算属性displayRating 根据是否处于悬停状态返回对应的评分值,确保 UI 展示正确的评分。
  3. 文字匹配ratingText 通过计算绝对值最小的差值,找到最接近当前评分的文字描述。

3.2 单颗星组件

StarComponent 是评分器的最小单元,负责单颗星的渲染和交互事件处理。

arkts 复制代码
@Component
struct StarComponent {
  private starIndex: number;          // 当前星的序号(1-based)
  private currentRating: number;      // 当前评分值
  private starSize: number;           // 星标大小
  private activeColor: ResourceColor; // 激活颜色
  private inactiveColor: ResourceColor; // 未激活颜色
  
  private onStarClick?: (index: number) => void;
  private onStarHover?: (index: number) => void;
  private onStarLeave?: () => void;
  
  // 计算当前星标的状态
  get starState(): 'full' | 'half' | 'empty' {
    if (this.currentRating >= this.starIndex) {
      return 'full';
    } else if (this.currentRating >= this.starIndex - 0.5) {
      return 'half';
    } else {
      return 'empty';
    }
  }
  
  build() {
    // 使用 Stack 堆叠容器实现半星效果
    Stack() {
      // 灰色背景星(未选中)
      Image($r('app.media.star_empty'))
        .width(this.starSize)
        .height(this.starSize)
        .objectFit(ImageFit.Contain)
      
      // 金色前景星(选中部分)
      Image($r('app.media.star_filled'))
        .width(this.starSize)
        .height(this.starSize)
        .objectFit(ImageFit.Contain)
        .clip(new Rect(0, 0, 
          this.starState === 'full' ? this.starSize : 
          this.starState === 'half' ? this.starSize / 2 : 0, 
          this.starSize))
        .opacity(this.starState === 'empty' ? 0 : 1)
    }
    .width(this.starSize)
    .height(this.starSize)
    .onClick(() => {
      this.onStarClick?.(this.starIndex);
    })
    .onHover((isHover: boolean) => {
      if (isHover) {
        this.onStarHover?.(this.starIndex);
      } else {
        this.onStarLeave?.();
      }
    })
    .animation({
      duration: 200,
      curve: Curve.EaseInOut
    })
  }
}

半星实现原理

通过 clip 属性对金色星标进行裁剪来实现半星效果:

  • full 状态:裁剪宽度为整颗星(完全显示金色)
  • half 状态:裁剪宽度为半颗星(显示一半金色)
  • empty 状态:裁剪宽度为 0(完全不显示金色)

3.3 交互事件处理

评分器的交互核心是鼠标悬停和点击事件的处理逻辑:

arkts 复制代码
// 处理星标点击事件
handleStarClick(starIndex: number) {
  let newRating: number;
  
  if (this.config.allowHalf) {
    // 如果已选中同一颗星,且当前为整星状态,降级为半星
    if (this.currentRating === starIndex) {
      newRating = starIndex - 0.5;
    } else {
      newRating = starIndex;
    }
  } else {
    newRating = starIndex;
  }
  
  this.currentRating = newRating;
  this.isHovering = false;
  this.onRatingChange?.(this.currentRating);
  
  // 添加触觉反馈
  vibrator.vibrate({
    type: 'touch',
    duration: 10
  });
}

// 处理悬停事件
handleStarHover(starIndex: number) {
  this.isHovering = true;
  this.hoverRating = starIndex;
}

// 处理悬停离开
handleStarLeave() {
  this.isHovering = false;
}

交互设计要点

  1. 半星切换逻辑 :当 allowHalf 开启时,点击已选中的整颗星会降级为半星,为用户提供更精细的评分控制。
  2. 悬停预览 :悬停时只修改 hoverRating,不会覆盖 currentRating,鼠标移出后恢复显示用户的最终评分。
  3. 触觉反馈:评分确认时调用振动接口,提供物理反馈,增强交互确认感。

3.4 使用自定义构建函数实现星标

除了使用 @Component,ArkTS 还提供了 @Builder 装饰器来构建 UI 片段:

arkts 复制代码
@Builder
StarIcon(active: boolean, size: number, color: ResourceColor) {
  Image(active ? $r('app.media.star_filled') : $r('app.media.star_empty'))
    .width(size)
    .height(size)
    .objectFit(ImageFit.Contain)
    .colorFilter(active ? 
      { brightness: 1, saturate: 1.5 } : 
      { brightness: 0.8, saturate: 0.5 })
}

// 在 build 方法中调用
Row({ space: 8 }) {
  ForEach(this.stars, (star) => {
    this.StarIcon(
      star.index <= this.displayRating,
      this.config.starSize,
      this.config.activeColor
    )
  })
}

@Builder 的优势在于它不需要创建新的组件实例,性能开销更小,适合渲染简单的 UI 片段。


四、HarmonyOS关键技术应用

4.1 响应式状态管理

评分器组件充分利用了 ArkTS 的状态管理机制:

状态层级设计

复制代码
@State currentRating    → 驱动评分主显示
@State hoverRating      → 驱动悬停预览显示
@State isHovering       → 控制显示模式切换

这三个状态变量构成了一个完整的状态机:

复制代码
                       悬停进入
┌─────────┐    ────────────────→    ┌─────────┐
│ 显示确认 │                          │ 显示预览 │
│ 评分状态 │    ←────────────────    │ 评分状态 │
└─────────┘    悬停离开/点击确认     └─────────┘

4.2 事件系统应用

HarmonyOS 提供了一套完善的事件系统,评分器中使用了以下事件:

事件类型 使用位置 触发时机 作用
onClick 星标组件 点击星标 确认评分
onHover 星标组件 鼠标进入/离开 悬停预览
onTouch 容器组件 触摸开始/移动/结束 滑动评分

滑动评分实现

arkts 复制代码
.onTouch((event: TouchEvent) => {
  if (event.type === TouchType.Move) {
    // 计算触摸点对应的评分值
    const touchX = event.touches[0].x;
    const starWidth = this.config.starSize + 8; // 星标宽度 + 间距
    const startX = this.starAreaStartX;
    let rating = (touchX - startX) / starWidth + 1;
    rating = Math.max(0.5, Math.min(5, Math.round(rating * 2) / 2));
    this.hoverRating = rating;
    this.isHovering = true;
  }
})

滑动评分为用户提供了另一种交互方式,手指从星标区域左侧滑到右侧即可快速选定评分,操作更加直觉化。

4.3 动画与过渡效果

ArkTS 内置的动画系统为评分器提供了流畅的视觉反馈:

arkts 复制代码
// 评分变化时的数字动画
Text(this.displayRating.toFixed(1))
  .fontSize(28)
  .fontWeight(FontWeight.Bold)
  .fontColor(this.config.activeColor)
  .animation({
    duration: 300,
    curve: Curve.EaseOut,
    delay: 0,
    iterations: 1
  })
  .transition({
    type: TransitionType.Insert,
    opacity: 0,
    translate: { x: 0, y: -10 }
  })

// 星标缩放动画
Image($r('app.media.star_filled'))
  .width(this.starSize)
  .height(this.starSize)
  .scale({
    x: this.starState === 'full' ? 1.1 : 1.0,
    y: this.starState === 'full' ? 1.1 : 1.0
  })
  .animation({
    duration: 200,
    curve: Curve.SpringMotion,
    delay: this.starIndex * 50 // 依次延时的级联效果
  })

动画设计亮点

  1. 弹性动画(SpringMotion):星标选中时使用弹性曲线,模拟物理弹性效果,视觉反馈生动自然。
  2. 级联延迟:每颗星依次延迟 50ms 执行动画,形成流水般的波浪效果。
  3. 数字过渡:评分数值变化时配合淡入和位移动画,避免生硬的数字跳变。

五、UI设计与交互

5.1 视觉层次设计

评分器的 UI 设计以简洁清晰为原则,突出评分操作本身:

布局结构

复制代码
┌──────────────────────────────────┐
│          ★★★★★               │
│          4.5  非常好             │
│                                  │
│    [确认评分]  [重置]             │
└──────────────────────────────────┘

视觉元素

  • 星标:采用系统图标资源,五角星形状,圆润饱满
  • 颜色系统
    • 选中:金色(#FFD700)--- 传递积极、高质的心理暗示
    • 未选中:浅灰色(#E0E0E0)--- 弱化视觉权重,不干扰用户
    • 悬停:亮金色(#FFC107)--- 比选中色略亮,与确认状态区分
  • 评分数字:大号加粗字体,与星标颜色一致,强化视觉关联
  • 评价文本:中性灰色,辅助理解评分含义

5.2 交互状态定义

评分器组件定义了六种交互状态:

状态 触发条件 视觉表现
初始 页面加载,未评分 所有星标灰色,显示"点击评分"提示
悬停 鼠标悬停某颗星 悬停位置前的星标亮起,数字实时变化
选中 点击确认评分 选中星标金色高亮,数字锁定
半星 点击已选整星(半星模式) 选中星标一半亮一半暗
确认 点击确认按钮 触发 onRatingChange 回调
重置 点击重置按钮 恢复到初始状态

5.3 无障碍设计

arkts 复制代码
// 为视障用户提供无障碍标签
Image($r('app.media.star_filled'))
  .accessibilityText(`第${this.starIndex}颗星,当前评分${this.displayRating}分`)
  .accessibilityLevel('yes')
  .accessibilityDescription('双击选择评分')

// 支持键盘操作
.onKeyEvent((event: KeyEvent) => {
  if (event.keyCode === KeyCode.KEYCODE_ENTER ||
      event.keyCode === KeyCode.KEYCODE_SPACE) {
    this.onStarClick?.(this.starIndex);
  }
})

六、性能优化与最佳实践

6.1 渲染性能优化

6.1.1 减少组件层级
arkts 复制代码
// ❌ 过度嵌套,降低渲染性能
Column() {
  Row() {
    Column() {
      Stack() {
        // 星标内容
      }
    }
  }
}

// ✅ 扁平化结构,减少组件实例数
Row({ space: 8 }) {
  // 直接使用 Stack 或 Image
}
6.1.2 使用 LazyForEach 处理大量评分项

如果需要在一个页面中展示多个评分项(如商品列表中的评分),应该使用 LazyForEach 代替 ForEach

arkts 复制代码
class RatingDataSource extends BasicDataSource {
  // 实现数据源接口
}

LazyForEach(new RatingDataSource(), (item: RatingItem) => {
  ListItem() {
    RatingContainer({
      config: item.config,
      onRatingChange: (rating) => item.onRate(rating)
    })
  }
}, (item: RatingItem) => item.id)
6.1.3 状态变量最小化原则
arkts 复制代码
// ❌ 状态粒度太粗,任何变化都会触发整个组件重建
@State ratingState: { value: number, hover: number, isActive: boolean } = {
  value: 0, hover: 0, isActive: false
};

// ✅ 分离独立状态,互不干扰
@State currentRating: number = 0;
@State hoverRating: number = 0;
@State isHovering: boolean = false;

6.2 组件化设计原则

6.2.1 单一职责

每个组件只负责一个功能领域:

  • RatingContainer:管理评分状态和整体布局
  • StarComponent:渲染单颗星并处理原始事件
  • RatingText:格式化并展示评分文字
6.2.2 接口最小化

对外暴露的属性和回调应该精简明确:

arkts 复制代码
// 只暴露必要的配置项
@Component
struct RatingContainer {
  @Prop rating: number;
  @Prop config: RatingConfig;
  onRatingChange?: (rating: number) => void;
  // 不暴露内部实现细节
}
6.2.3 默认参数

提供合理的默认值,降低使用门槛:

arkts 复制代码
private config: RatingConfig = {
  totalStars: 5,
  starSize: 32,
  activeColor: '#FFD700',
  inactiveColor: '#E0E0E0',
  allowHalf: false,
  showText: true
};

6.3 适配多种屏幕

arkts 复制代码
// 响应式适配
if (this.isWideScreen()) {
  this.starSize = 48;
} else {
  this.starSize = 32;
}

// 根据屏幕宽度动态调整间距
Row({ space: this.getResponsiveSpace() }) {
  // 星标行
}

// 横竖屏适配
.orientation(Orientation.Auto)

七、总结与扩展思路

7.1 项目总结

本文详细解析了基于 HarmonyOS ArkTS 框架开发的评分器组件,涵盖了以下核心技术内容:

  1. 声明式状态管理 :使用 @State 管理评分状态,实现 UI 的自动更新
  2. 自定义组件封装 :通过 @Component 实现组件的模块化和复用
  3. 交互事件处理:掌握点击、悬停、触摸等事件的处理方式
  4. 动画系统:运用 ArkTS 动画 API 提升交互体验
  5. 组件通信:理解父子组件之间的数据传递和事件回调

7.2 扩展思路

评分器组件可以从以下几个方向进一步扩展:

7.2.1 功能增强
  • 多维度评分:支持"外观"、"性能"、"价格"等多个维度的综合评分
  • 评分统计:展示评分分布直方图、平均分、评分人数等统计信息
  • 用户评分记录:记录用户的评分历史,支持修改和撤销
  • 图片评论:评分时允许附带图片和文字评论
7.2.2 交互升级
  • 拖拽评分:通过拖拽滑动条代替点击星标,适用于大屏设备
  • 动效增强:评选时烟花、粒子等庆祝动画效果
  • 语音评分:集成 HarmonyOS 语音识别,支持语音评价
  • 表情评分:在星标基础上增加表情符号(😊😐😞)的多模态评分
7.2.3 技术进阶
  • 跨设备协同:利用 HarmonyOS 分布式技术,在手机和平板间同步评分结果
  • AI 评分推荐:基于用户历史评分数据,使用 AI 算法推荐可能的评分
  • 数据可视化:使用 Canvas 组件绘制评分趋势图表
  • 性能监控:接入 ArkTS Profiler,优化组件渲染性能

7.3 应用场景展望

评分器组件作为最基础的交互组件之一,其设计思想可以延伸到更多场景:

复制代码
星级评分 → 表情评分 → 拇指评分(点赞/踩)
    ↓           ↓              ↓
  电商评价    社交反馈      内容互动
    ↓           ↓              ↓
  信任体系    情感分析      推荐算法

在 HarmonyOS 生态中,评分器组件还可以与元服务卡片(Ability Card)结合,在桌面直接展示和操作评分,实现真正的"即评即用"。


项目代码已完整开源,开发者可以直接复用评分器组件,或在此基础上进行定制化开发。随着 HarmonyOS 生态的不断壮大,掌握 ArkTS 组件化开发技巧将为开发者带来更广阔的职业发展空间。

相关推荐
b130538100491 小时前
HarmonyOS应用《玄象》开发实战:AiPhotoFengshuiPage AI 拍照风水:@ohos.multimedia.camera 相机调用
harmonyos·鸿蒙
爱写代码的阿森2 小时前
鸿蒙三方库 | harmony-utils之NumberUtil数值精确计算详解
华为·harmonyos·鸿蒙·huawei
●VON7 小时前
鸿蒙 PC Markdown 编辑器内核:在 ArkWeb 中离线运行 CodeMirror 6
安全·华为·编辑器·harmonyos·鸿蒙
YM52e9 小时前
鸿蒙Flutter Padding内边距:EdgeInsets详解
android·学习·flutter·华为·harmonyos·鸿蒙
爱写代码的阿木10 小时前
基于鸿蒙OS开发附近社交游戏平台(二十二)-ChatPage 即时通讯 UI
游戏·华为·harmonyos
红烧大青虫10 小时前
HarmonyOS应用开发实战:小事记 - 数据迁移策略:RDB 表结构变更的版本号管理与 onUpgrade 回调
后端·华为·harmonyos·鸿蒙系统
qizayaoshuap11 小时前
# 温度转换 — HarmonyOS 双向转换器与表情符号天气描述实战
华为·harmonyos
爱写代码的阿木12 小时前
基于鸿蒙OS开发附近社交游戏平台(二十三)-拉黑系统、屏蔽与全局过滤
游戏·华为·harmonyos
echohelloworld1112 小时前
HarmonyOS开发实战:笔友-main_pages.json 路由表与页面注册机制
harmonyos·鸿蒙