鸿蒙新特性——Rating 星级评分组件详解
一、引言
在移动端应用中,评分是连接用户与内容的核心交互之一。电影评分、商品评价、服务反馈、应用商店评分——这些场景都需要一个直观、可视化的评分方式。星级评分(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 是一个电影评分页面,模拟电影评论应用的评分流程:
- 星级评分交互:点击星星为当前电影评分,大号数字实时显示分数和文字评价
- 只读模式切换:一键切换评分模式与展示模式,只读模式下星星不可点击
- 半星精度:Toggle 控制整星 vs 半星评分,半星模式支持 0.5 粒度
- 星数调节:从 3/5/7/10 颗星中选择,适应不同评分体系(3 星=简洁评价,10 星=IMDb 风格)
- 提交评分:将当前评分保存到历史记录,电影名自动轮换
- 评分记录:展示历史评分列表,含电影名、分数/总分、评价文字和时间戳
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;
}
步骤解析:
- 零分拦截:评分 0 表示用户尚未评分,直接 return 不提交
- 时间戳生成:使用
Date()获取当前时分,padStart(2, '0')保证两位数 - 电影名轮换:
this.records.length % this.movies.length实现循环取电影名——第 1 条"《星际穿越》“,第 2 条”《肖申克的救赎》“,以此类推,第 6 条又回到”《星际穿越》" - 数组追加:使用
concat()在数组头部追加新记录(新记录显示在列表顶部) - 重置评分:
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 星级评分组件的核心用法。主要涵盖以下知识点:
- Rating 构造函数:
rating绑定评分值、indicator控制交互/只读模式 - 链式方法:
.stars(n)设置星数、.stepSize(n)控制精度、.onChange()获取用户评分 - indicator 双模式:交互模式(用户点击评分)vs 只读模式(展示已有评分)
- 半星精度:
stepSize: 0.5实现 0.5 粒度的细粒度评分 - 星数动态切换:3/5/7/10 星体系切换,适应不同评分标准
- 评分文字/颜色语义:数值→文字等级→颜色的二级映射,提升信息传达效率
Rating 是 ArkUI 评星场景的标准解决方案。从电影评分到商品评价,从 NPS 调查到内容质量打分,Rating 以其简洁的 API 和丰富的配置能力,帮助开发者用几行代码实现完整的星级评分功能。希望本文能帮助你在实际项目中高效运用 Rating 组件。
本文基于 HarmonyOS NEXT API 24 编写,代码经 DevEco Studio 6.1.1 编译验证通过。
更多推荐


所有评论(0)