一、应用概述
评分器(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
})
}
}
核心逻辑分析:
- 双评分状态 :
currentRating保存用户最终确认的评分,hoverRating保存悬停时的临时评分。二者分离可以实现在悬停预览后恢复到原有评分的功能。 - 计算属性 :
displayRating根据是否处于悬停状态返回对应的评分值,确保 UI 展示正确的评分。 - 文字匹配 :
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;
}
交互设计要点:
- 半星切换逻辑 :当
allowHalf开启时,点击已选中的整颗星会降级为半星,为用户提供更精细的评分控制。 - 悬停预览 :悬停时只修改
hoverRating,不会覆盖currentRating,鼠标移出后恢复显示用户的最终评分。 - 触觉反馈:评分确认时调用振动接口,提供物理反馈,增强交互确认感。
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 // 依次延时的级联效果
})
动画设计亮点:
- 弹性动画(SpringMotion):星标选中时使用弹性曲线,模拟物理弹性效果,视觉反馈生动自然。
- 级联延迟:每颗星依次延迟 50ms 执行动画,形成流水般的波浪效果。
- 数字过渡:评分数值变化时配合淡入和位移动画,避免生硬的数字跳变。
五、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 框架开发的评分器组件,涵盖了以下核心技术内容:
- 声明式状态管理 :使用
@State管理评分状态,实现 UI 的自动更新 - 自定义组件封装 :通过
@Component实现组件的模块化和复用 - 交互事件处理:掌握点击、悬停、触摸等事件的处理方式
- 动画系统:运用 ArkTS 动画 API 提升交互体验
- 组件通信:理解父子组件之间的数据传递和事件回调
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 组件化开发技巧将为开发者带来更广阔的职业发展空间。