一、引言

在移动端应用中,评分是连接用户与内容的核心交互之一。电影评分、商品评价、服务反馈、应用商店评分——这些场景都需要一个直观、可视化的评分方式。星级评分(Star Rating)是最通用的评分 UI 范式:用户只需点击星星,就能将主观感受转化为量化的数值,既直观又高效。

在传统开发中,实现星级评分需要处理星星图片的渲染(亮星/暗星)、触摸事件的坐标计算(判断点在第几颗星)、半星逻辑的浮点截断、交互模式与只读模式的切换等细节。而 HarmonyOS 提供了 Rating 组件——一个内置完整评分交互逻辑的星级评分组件,通过声明式 API 即可实现从简单展示到复杂评分管理的所有功能。

本文通过一个电影评分 Demo 深入讲解 Rating 组件的核心用法:如何设置星星数量和步长精度?如何区分交互模式与只读模式?如何自定义星星样式?以及如何与评分记录联动,构建完整的评分管理系统。

阅读完本文,你将能够:

  • 使用 Rating 组件替代自建评星方案
  • 掌握 rating/stars/stepSize/indicator 四个核心参数
  • 理解交互式评分(indicator=false)与只读展示(indicator=true)的区别
  • 通过 onChange 回调获取实时评分结果
  • 构建电影评分提交与历史记录管理功能

二、Rating 组件 API 总览

2.1 构造函数

Rating(options?: RatingOptions)
interface RatingOptions {
  rating?: number;     // 当前评分值,默认 0
  indicator?: boolean; // 是否只读模式,默认 false(可交互)
  steps?: number;      // 步长精度,默认 1(整星),支持 0.5(半星)
  starStyle?: StarStyle; // 自定义星星样式(API 11+)
}
参数 类型 默认值 说明
rating number 0 当前评分值,范围 [0, stars]
indicator boolean false true 为只读展示模式,false 为可交互评分模式
steps number 1 已废弃(API 10),改用 .stepSize() 链式方法
starStyle StarStyle - 自定义星星图片样式(API 11+),两态/三态/四态

2.2 链式方法

// 星星总数
.stars(value: number): RatingAttribute

// 步长精度
.stepSize(value: number): RatingAttribute

// 评分变化回调
.onChange(callback: Callback<number>): RatingAttribute
方法 说明
stars(number) 星星总数,默认 5,常用值 3/5/7/10
stepSize(number) 评分步长,默认 1(整星),设为 0.5 支持半星
onChange(Callback<number>) 用户点击评分时的回调,参数为当前评分值

2.3 Rating vs 自建评星方案

特性 Rating 组件 自建方案
星数自定义 .stars(n) 一行搞定 需循环渲染 n 个 Image
半星支持 .stepSize(0.5) 即可 需手动计算位置 + 裁剪星星
交互反馈 内置触摸高亮和动画 需手动处理 onTouch 事件
只读模式 indicator: true 切换 需手动禁止交互
样式定制 starStyle 传入自定义图片 需管理多套图片资源
代码量 约 5-10 行 约 80-150 行

Rating 组件适合所有需要星级评分的标准场景。只有在需要完全非标星形 UI(如心形、点赞、表情评分)时,才考虑自建方案。
在这里插入图片描述

三、Demo 设计:电影评分

3.1 功能概述

Demo 是一个电影评分页面,模拟电影评论应用的评分流程:

  1. 星级评分交互:点击星星为当前电影评分,大号数字实时显示分数和文字评价
  2. 只读模式切换:一键切换评分模式与展示模式,只读模式下星星不可点击
  3. 半星精度:Toggle 控制整星 vs 半星评分,半星模式支持 0.5 粒度
  4. 星数调节:从 3/5/7/10 颗星中选择,适应不同评分体系(3 星=简洁评价,10 星=IMDb 风格)
  5. 提交评分:将当前评分保存到历史记录,电影名自动轮换
  6. 评分记录:展示历史评分列表,含电影名、分数/总分、评价文字和时间戳

3.2 交互点

# 交互 说明
1 点击星星评分 Rating 组件核心交互,评分实时显示分数和文字等级
2 半星模式 Toggle 切换 stepSize (1 vs 0.5),改变评分粒度
3 只读模式 Toggle 切换 indicator 属性,禁止/允许用户点击
4 星数选择 3/5/7/10 四档切换,适配不同评分体系
5 提交评分 保存到记录,电影名轮换,记录列表增长

在这里插入图片描述

四、完整代码实现

4.1 数据模型定义

interface RatingRecord {
  id: number;
  score: number;
  maxStars: number;
  movie: string;
  timestamp: string;
}

RatingRecord 是评分的持久化模型。与评分瞬间的状态不同,记录需要保存评分时的上下文——评分多少分(score)、当时的满分是多少(maxStars,因为星数可变)、评价的电影名称(movie)、提交时间(timestamp)。这个设计让记录真实反映了用户的评分场景。

4.2 状态变量

@State currentRating: number = 0;
@State starCount: number = 5;
@State stepSize: number = 1;
@State isIndicator: boolean = false;
@State records: RatingRecord[] = [];
@State nextId: number = 1;

private movies: string[] = ['《星际穿越》', '《肖申克的救赎》', '《教父》', '《千与千寻》', '《盗梦空间》'];

关键状态说明:

  • currentRating:与 Rating 组件的 rating 参数绑定,用户在星星上点击后通过 onChange 回调更新。初始为 0(未评分状态)
  • starCount:星星数量,通过配置文件中的四个预设值切换(3/5/7/10)
  • stepSize:评分精度,1 = 整星(只能选 1、2、3、4、5),0.5 = 半星(可选 0.5、1.0、1.5 等)
  • isIndicator:控制只读模式,true 时 Rating 不可点击,仅用于展示
  • records:评分历史数组,使用 @State 确保增删操作触发 UI 刷新

4.3 评分等级映射

getScoreLabel(score: number): string {
  if (score === 0) return '还未评分';
  if (score <= 1) return '非常差';
  if (score <= 2) return '较差';
  if (score <= 3) return '一般';
  if (score <= 4) return '不错';
  if (score < 5) return '很好';
  return '神作';
}

getScoreColor(score: number): string {
  if (score === 0) return '#9999AA';
  if (score <= 2) return '#FF4D4F';
  if (score <= 3) return '#FF9800';
  if (score <= 4) return '#1677FF';
  return '#52C41A';
}

两个工具方法将数值评分映射为文字等级和颜色:

  • 文字标签:0 分→"还未评分",1→"非常差",5→"神作"——分数越高评价越正面
  • 颜色语义:低分红(#FF4D4F,警告),中分橙(#FF9800,一般),中高分蓝(#1677FF,不错),高分绿(#52C41A,优秀),0 分灰(#9999AA,未操作)

这个映射让评分结果不仅是一个数字,而是有情感色彩的综合评价。

4.4 Rating 组件核心配置

Rating({ rating: this.currentRating, indicator: this.isIndicator })
  .stars(this.starCount)
  .stepSize(this.stepSize)
  .onChange((value: number) => {
    this.currentRating = value;
  })

逐行解析:

  • rating: this.currentRating:将 Rating 组件与 @State 变量绑定。当用户在星星上点击时,this.currentRating 更新,Rating 组件重新渲染以反映新的星星亮暗状态
  • indicator: this.isIndicator:核心模式切换。false(默认)时星星可点击,用户触摸哪个星星就点亮到哪个;true 时星星完全不可交互,仅作展示用途。这个属性无法通过链式方法设置,必须在构造函数中指定
  • .stars(this.starCount):星星总数。此值变化时 Rating 组件的星星数量立即重绘。例如从 5 切换到 10,Rating 会渲染 10 颗星星,当前评分值 currentRating 映射到 10 颗星的体系中
  • .stepSize(this.stepSize):评分步长。设为 1 时只能选择整星(1, 2, 3, 4, 5),设为 0.5 时可以选择半星(0.5, 1.0, 1.5…)。注意:半星模式需要用户触摸星星的左右半区来区分整星/半星——触左半区=当前星-0.5,触右半区=当前星
  • .onChange(callback):评分变化的回调,参数 value 是新的评分值。注意:只有用户交互触发的评分变化才会触发此回调,程序中直接修改 this.currentRating 不会触发

4.5 提交评分

submitRating() {
  if (this.currentRating === 0) return;
  const now = new Date();
  const ts = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
  const movie = this.movies[this.records.length % this.movies.length];
  this.records = this.records.concat([{
    id: this.nextId,
    score: this.currentRating,
    maxStars: this.starCount,
    movie: movie,
    timestamp: ts
  }]);
  this.nextId++;
  this.currentRating = 0;
}

步骤解析:

  1. 零分拦截:评分 0 表示用户尚未评分,直接 return 不提交
  2. 时间戳生成:使用 Date() 获取当前时分,padStart(2, '0') 保证两位数
  3. 电影名轮换this.records.length % this.movies.length 实现循环取电影名——第 1 条"《星际穿越》“,第 2 条”《肖申克的救赎》“,以此类推,第 6 条又回到”《星际穿越》"
  4. 数组追加:使用 concat() 在数组头部追加新记录(新记录显示在列表顶部)
  5. 重置评分this.currentRating = 0 重置星星为未评分状态,准备下一条评分

4.6 评分配置面板

// 半星模式 Toggle
Row() {
  Text('半星模式')
    .fontSize(14).fontColor('#1a1a2e')
    .fontWeight(FontWeight.Medium).layoutWeight(1)
  Toggle({ type: ToggleType.Switch, isOn: this.stepSize === 0.5 })
    .selectedColor('#1677FF')
    .onChange((value: boolean) => {
      this.stepSize = value ? 0.5 : 1;
    })
}

// 只读模式 Toggle
Row() {
  Text('只读模式')
    .fontSize(14).fontColor('#1a1a2e')
    .fontWeight(FontWeight.Medium).layoutWeight(1)
  Toggle({ type: ToggleType.Switch, isOn: this.isIndicator })
    .selectedColor('#1677FF')
    .onChange((value: boolean) => { this.isIndicator = value; })
}

// 星星数量选择
ForEach([3, 5, 7, 10], (n: number) => {
  Text(n.toString())
    .fontSize(13)
    .fontColor(this.starCount === n ? '#FFFFFF' : '#1a1a2e')
    .fontWeight(this.starCount === n ? FontWeight.Bold : FontWeight.Normal)
    .padding({ top: 4, bottom: 4, left: 14, right: 14 })
    .borderRadius(14)
    .backgroundColor(this.starCount === n ? '#1677FF' : '#F2F3F5')
    .margin({ left: 6 })
    .onClick(() => { this.starCount = n; })
})

三个配置项的设计要点:

  • 半星模式:Toggle isOn 通过 this.stepSize === 0.5 计算,确保切换状态与数据一致。切换时直接赋值 this.stepSize = value ? 0.5 : 1
  • 只读模式:当开启只读时,Rating 组件的星星不可点击,但配置面板的 Toggle 仍然可操作,用户可以随时切回交互模式
  • 星数切换:四个预设按钮,选中态白字蓝底,非选中态黑字灰底。切换星数时,当前评分 currentRating 保持不变——如果当前评分大于新星数(如 6 分切到 5 星),Rating 组件会自动裁剪到最大星数

4.7 评分记录列表

if (this.records.length > 0) {
  Column() {
    Row() {
      Text('评分记录').fontSize(12).fontColor('#9999AA')
        .fontWeight(FontWeight.Bold).layoutWeight(1)
      Text(`${this.records.length}`).fontSize(11).fontColor('#9999AA')
    }

    Column() {
      ForEach(this.records, (rec: RatingRecord, idx: number) => {
        Column() {
          Row() {
            Column() {
              Text(rec.movie)
                .fontSize(14).fontColor('#1a1a2e').fontWeight(FontWeight.Bold)
              Row() {
                Text(`${rec.score.toFixed(1)} / ${rec.maxStars}`)
                  .fontSize(12).fontColor(this.getScoreColor(rec.score))
                  .fontFamily('monospace')
                Text(this.getScoreLabel(rec.score))
                  .fontSize(11).fontColor(this.getScoreColor(rec.score))
              }
            }
            .alignItems(HorizontalAlign.Start).layoutWeight(1)

            Text(rec.timestamp)
              .fontSize(11).fontColor('#BBBBCC').fontFamily('monospace')
          }
          .width('100%')
        }
        .width('100%')
        .padding({ top: 14, bottom: 14, left: 4, right: 4 })

        if (idx < this.records.length - 1) {
          Divider().strokeWidth(0.5).color('#F2F3F5')
        }
      })
    }
  }
}

每条记录展示:电影名(粗体)、分数/满分 + 等级文字(彩色)、提交时间(灰色等宽字体)。最后一条记录不显示分隔线。由于使用 concat() 在数组头部插入,最新的评分始终显示在最上方。

五、关键技术点详解

5.1 indicator 属性:展示 vs 交互

indicator 是 Rating 组件最关键的模式切换参数。

// 交互式评分:用户可以点击星星
Rating({ rating: this.rating, indicator: false })

// 只读展示:星星不可点击,仅用于显示评分
Rating({ rating: 4.5, indicator: true })

适用场景对比

场景 indicator 说明
用户打分页 false 用户点击星星评分
商品详情页 true 展示商品的平均评分
影评列表 true 每条评论的评分展示
评分历史 true 展示用户历史评分

重要indicator 无法通过链式方法(.indicator())设置,必须在构造函数中指定。动态切换 indicator 时,Rating 组件需要重新创建——在 @State 变量驱动下,切换 isIndicator 会自动触发重建。

5.2 stepSize 的半星交互逻辑

.stepSize(0.5) 启用半星评分后,Rating 组件的行为发生了细微变化:

  • 整星模式(stepSize=1):用户触摸第 3 颗星,评分直接变为 3。每颗星是一个整体,要么全亮要么全暗
  • 半星模式(stepSize=0.5):每颗星被分为左右两个半区。用户触摸第 3 颗星的左半部分,评分为 2.5;触摸右半部分,评分为 3。触摸第 1 颗星的左半部分,评分为 0.5

这个二分逻辑由 Rating 组件内部处理,开发者无需关心触摸坐标计算。Half-star 在半星评分体系(如 IMDb)中非常实用,但在简单场景(全星即可)中反而增加了用户的认知负担,因此 Demo 提供了 Toggle 开关让用户自由选择。

5.3 stars 的动态切换

.stars(n) 改变星星总数后,当前评分 rating 的值不会自动变化:

// 当前评分 4.5,星数从 5 切换到 10
// Rating 组件显示:10 颗星中亮 4.5 颗(第 5 颗星半亮)
// rating 值仍然是 4.5,不会自动变为 9

这意味着切换到更多星数后,评分值保持不变。如果业务需要按比例转换(如 5 星制 4 分 = 10 星制 8 分),需要在 onChange 或 Toggle 回调中手动计算。

5.4 onChange 的触发时机

.onChange(callback) 只在用户通过触摸交互改变评分时触发。以下情况不会触发 onChange

  • 代码中设置 this.currentRating = 3(直接赋值不触发)
  • Rating 组件重新渲染(如 starCount 变化)
  • 页面首次加载(初始渲染)

这个设计的意义在于:onChange 是"用户行为"的钩子,而非"状态变化"的钩子。如果需要在状态变化时执行逻辑,应该在状态赋值的同时直接写逻辑,而不是依赖 onChange

5.5 评分颜色语义设计

分数颜色映射是提升评分体验的重要细节:

0分 → 灰色 #9999AA (无评分)
1-2分 → 红色 #FF4D4F (低评价,警告感)
2-3分 → 橙色 #FF9800 (中等评价,注意感)
3-4分 → 蓝色 #1677FF (良好评价,信任感)
4-5分 → 绿色 #52C41A (优秀评价,正面感)

这种颜色方案遵循了通用的交通灯语义(红→黄→绿),用户无需阅读文字就能从颜色判断评分的高低。这个设计在国内外评分应用中广泛使用,如豆瓣(1-2 星灰色、3 星黄色、4-5 星红色)、IMDb(整体金色/黄色)。

六、运行效果

6.1 初始状态

页面顶部显示标题栏"⭐ 电影评分",下方是组件介绍卡片。紧接着是当前待评分的电影名称(如"《星际穿越》“)和引导文字"点击星星为你选择的电影评分”。Rating 组件显示 5 颗灰色空星,下方大号 “0.0” 灰色数字和"还未评分"文字。提交按钮因未评分而置灰。配置面板默认:半星模式关闭、只读模式关闭、星数选中"5"。

6.2 评分交互

点击星星:第 3 颗星——Rating 显示 3 颗亮星 + 2 颗暗星,大号数字变为"3.0"蓝色和"一般"文字。开启半星模式:第 3 颗星左半部分——显示 2.5 颗亮星,大号数字"2.5"橙色。点击提交按钮:评分保存到记录列表,Rating 重置为 0 空星。

6.3 只读模式

开启"只读模式" Toggle,Rating 组件变为只读——点击星星无反应,没有触摸高亮。此时星星仅展示当前评分值(如已评分 3.0 则亮 3 颗)。

6.4 星数切换

点击星数选择器中的 “10”——Rating 组件重新渲染为 10 颗星。点击 “3”——变为 3 颗星,分数不变(如已评分 3.0 则 3 星全亮)。

6.5 评分记录

多次评分后,记录列表依次显示不同电影和不同分数。每条记录左侧显示电影名和色彩分数,右侧显示提交时间。不同分数的颜色差异让优秀评分和低劣评分一目了然。

七、最佳实践与注意事项

7.1 何时使用 Rating

  • 电影/图书/音乐评分:影评网站、图书评论、音乐 App
  • 商品/服务评价:电商商品的用户评分、外卖配送评分、酒店评分
  • 应用内反馈:NPS(净推荐值)调查、满意度调查
  • 内容质量评分:文章质量、课程评价、教师评分

7.2 何时自建评星方案

  • 非星形图标评分:如心形(点赞)、笑脸表情(满意度)、竖大拇指(赞同)
  • 需要自定义动画:如星星飞入、抖动、弹性缩放等击打效果
  • 非标准交互:如滑动连续评分(而非点击离散评分)

7.3 常见问题

Q: Rating 的星星大小可以调整吗?

A: 可以通过 .width().height() 通用属性间接控制整个 Rating 组件的尺寸,但单个星星的大小由组件内部根据总尺寸和星数自动计算。不能直接设置单个星星的像素大小。

Q: 如何设置默认评分?

A: 在构造函数中指定 rating 即可:Rating({ rating: 3.5 })。也可以在初始化 @State 变量时设置:@State currentRating: number = 3.5;

Q: indicator 模式下星星颜色会变灰吗?

A: 不会。indicator: true 只影响可交互性,不影响显示颜色。星星的亮/暗颜色由系统主题决定(亮色主题下亮星=金色,暗星=灰色),开发者暂时无法通过标准 API 修改星星颜色。如需自定义颜色,可以使用 starStyle 传入自定义星星图片。

Q: 如何实现左右滑动手势评分(而不只是点击)?

A: Rating 组件原生支持滑动手势——用户在星星区域滑动手指时,评分会跟随手指位置连续变化(配合 stepSize 精度)。这一点无需额外配置。

7.4 性能建议

  • Rating 组件是轻量级组件,即使渲染 10 颗星也不会产生性能问题
  • ForEach 循环中使用 Rating 展示只读评分时(如电影列表中的评分摘要),务必设置 indicator: true,避免大量可交互的 Rating 实例影响触摸事件处理
  • onChange 回调中应避免耗时操作(网络请求、大量计算),只做状态赋值

八、拓展思考

8.1 评分数据的持久化存储

本 Demo 的评分记录存储在 @State 中,App 退出后丢失。实际应用中需要持久化:

  • 单用户评分:使用 Preferences 存储用户对每部电影的评分,以电影 ID 为 key
  • 多用户评分聚合:使用 RelationalStore 存储所有用户的评分数据,前端展示平均分 + 评分人数
  • 评分同步:配合 Cloud DB 实现在线评分和跨设备同步

8.2 自定义星星样式

Rating 组件通过 starStyle 参数支持自定义星星样式:

// 两态星图:foreground=亮星,secondary=暗星,background=底色
Rating({ rating: this.rating, starStyle: {
  backgroundUri: '/assets/star_bg.png',
  foregroundUri: '/assets/star_fg.png',
  secondaryUri: '/assets/star_secondary.png'
}})

StarStyle 接口支持三种星星状态的图片:

  • backgroundUri:底色(未选中区域)
  • foregroundUri:前景色(选中区域,高亮)
  • secondaryUri:次要色(半选区域,用于半星模式下显示部分选择)

通过自定义星星图片,可以实现品牌化的评分 UI(如豆瓣风格的灰色/红色星、IMDb 风格的金色星)。

8.3 与其他 ArkUI 组件的配合

Rating 可以与其他组件组合实现更复杂的评分场景:

  • Rating + TextArea:评星 + 文字评论,构建完整的评价表单
  • Rating + List:电影列表中每条电影附带只读 Rating 展示平均分
  • Rating + CustomDialog:弹窗中弹出评分窗口,不离开当前页面
  • Rating + Progress:评分分布图(5 星 X%,4 星 Y%…)

九、总结

本文通过一个电影评分的实战 Demo,深入讲解了 HarmonyOS Rating 星级评分组件的核心用法。主要涵盖以下知识点:

  1. Rating 构造函数rating 绑定评分值、indicator 控制交互/只读模式
  2. 链式方法.stars(n) 设置星数、.stepSize(n) 控制精度、.onChange() 获取用户评分
  3. indicator 双模式:交互模式(用户点击评分)vs 只读模式(展示已有评分)
  4. 半星精度stepSize: 0.5 实现 0.5 粒度的细粒度评分
  5. 星数动态切换:3/5/7/10 星体系切换,适应不同评分标准
  6. 评分文字/颜色语义:数值→文字等级→颜色的二级映射,提升信息传达效率

Rating 是 ArkUI 评星场景的标准解决方案。从电影评分到商品评价,从 NPS 调查到内容质量打分,Rating 以其简洁的 API 和丰富的配置能力,帮助开发者用几行代码实现完整的星级评分功能。希望本文能帮助你在实际项目中高效运用 Rating 组件。


本文基于 HarmonyOS NEXT API 24 编写,代码经 DevEco Studio 6.1.1 编译验证通过。

Logo

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

更多推荐