尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
HarmonyOS7 ArkUI 歌词滚动播放器 - 歌词同步高亮、进度控制与翻译切换实战:从状态到页面反馈
文章目录前言真实页面会关心什么列表、状态和反馈关键片段片段 1get currentLineIndex(): number {片段 2get progress(): number {使用方式完整代码可扩展点前言如果刚开始写 HarmonyOS7 页面我建议多拆这种案例。代码量不夸张但能把声明式 UI 的味道摸清楚。它的主线是歌词滚动播放器细节落在歌词同步高亮、进度控制与翻译切换。这些细节刚好能把页面状态、用户操作和组件刷新串起来。真实页面会关心什么LyricPlayerPage不是一个只展示静态内容的页面。它会根据用户点击、输入、切换或计时来改变界面所以阅读代码时可以先抓三条线观察点在这个案例里的表现页面主题歌词滚动播放器主要文案晴天,周杰伦,叶惠美,故事的小黄花,从出生那年就飘着,童年的荡秋千常用组件Column,ForEach,Row,Scroll,Slider,Stack,Text适合练习状态驱动 UI、条件样式、列表渲染、事件回调列表、状态和反馈ArkUI 的页面刷新依赖状态变化。下面这些State字段就是页面的“开关”和“数据源”不用手动找 DOM也不用自己通知某个控件刷新。状态字段类型我会怎么理解它currentTimenumber当前选中项决定高亮和内容切换isPlayingboolean布尔开关控制显示隐藏或模式切换|showTranslation|boolean| 布尔开关控制显示隐藏或模式切换 ||fontSize|number| 记录页面当前选择、输入或展示状态 ||timerHandle|number| 记录页面当前选择、输入或展示状态 |一个经验先把State看完再看build()页面逻辑会清楚很多。否则很容易被一长串布局代码带偏。关键片段片段 1get currentLineIndex(): number {这段代码承担的是页面里的“判断”或“动作”把它从布局里抽出来后build()会清爽很多。后面要加新条件也不会到处翻 UI 代码。getcurrentLineIndex():number{letidx0for(leti0;ithis.lyrics.length;i){if(this.currentTimethis.lyrics[i].time){idxi}}returnidx}片段 2get progress(): number {这段代码承担的是页面里的“判断”或“动作”把它从布局里抽出来后build()会清爽很多。后面要加新条件也不会到处翻 UI 代码。getprogress():number{returnthis.currentTime/this.song.duration}使用方式把下面代码放进 ArkTS 页面文件中即可运行。用于正式项目时我会继续把数据模型、卡片 Builder 和页面事件拆出去页面文件只负责组合。建议练习顺序先改状态字段 - 再改交互事件 - 最后调整 UI 样式 检查重点点击是否刷新、列表 key 是否稳定、条件样式是否集中管理这份代码可以继续扩展的方向把模拟数据替换成接口数据抽出复用卡片组件增加空状态和异常状态补充深色模式或主题色适配对输入类场景增加校验提示完整代码// LyricPlayerPage - 歌词滚动播放器 - 歌词同步高亮、进度控制与翻译切换interfaceLyricLine{time:numbertext:stringtranslation:string}interfaceSongInfo_lnjm{title:stringartist:stringalbum:stringduration:number}EntryComponentstruct LyricPlayerPage{StatecurrentTime:number45StateisPlaying:booleanfalseStateshowTranslation:booleantrueStatefontSize:number16StatetimerHandle:number-1privatesong:SongInfo_lnjm{title:晴天,artist:周杰伦,album:叶惠美,duration:269,}privatelyrics:LyricLine[][{time:0,text:故事的小黄花,translation:The little dandelion of the story},{time:5,text:从出生那年就飘着,translation:Has been floating since the year of birth},{time:10,text:童年的荡秋千,translation:Swinging on the childhood swing},{time:15,text:随记忆一直晃到现在,translation:Swaying along with memories until now},{time:20,text:Rui Bing说他不明白,translation:Rui Bing says he doesn\t understand},{time:25,text:为什么我吉他总在半夜弹,translation:Why I always play guitar at midnight},{time:30,text:她说她喜欢男生弹吉他的样子,translation:She said she likes how boys look playing guitar},{time:35,text:我说这简单,translation:I said it\s simple},{time:40,text:等你学会,translation:Wait till you learn},{time:45,text:我再好好说清楚,translation:I\ll explain it properly},{time:50,text:那等等,translation:Then wait a moment},{time:55,text:我先把歌唱完,translation:Let me finish the song first},{time:60,text:你今天哭了吗,translation:Did you cry today},{time:65,text:不管有没有眼泪,translation:Whether or not there are tears},{time:70,text:在你脸上来回,translation:Running down your face},{time:75,text:你笑了吗,translation:Did you smile},{time:80,text:不管有没有阳光,translation:Whether or not there is sunlight},{time:85,text:那个秋天的童年,translation:That childhood autumn},{time:90,text:印象太深刻了,translation:The impression is too deep},{time:95,text:不知不觉开始忘了,translation:Gradually started to forget},{time:100,text:晴天,translation:Sunny day},{time:108,text:盼望着晴天,translation:Hoping for a sunny day},]getcurrentLineIndex():number{letidx0for(leti0;ithis.lyrics.length;i){if(this.currentTimethis.lyrics[i].time){idxi}}returnidx}getprogress():number{returnthis.currentTime/this.song.duration}formatTime(seconds:number):string{constmMath.floor(seconds/60)constsMath.floor(seconds%60)return${m.toString().padStart(2,0)}:${s.toString().padStart(2,0)}}togglePlay(){this.isPlaying!this.isPlayingif(this.isPlaying){this.timerHandlesetInterval((){if(this.currentTimethis.song.duration){this.currentTime}else{this.currentTime0clearInterval(this.timerHandle)this.isPlayingfalse}},1000)}else{clearInterval(this.timerHandle)}}aboutToDisappear(){if(this.timerHandle0)clearInterval(this.timerHandle)}build(){Column({space:0}){// 顶部区域Column({space:16}){// 专辑封面Stack(){Column().width(160).height(160).borderRadius(80).linearGradient({angle:135,colors:[[#667eea,0],[#764ba2,0.5],[#f64f59,1.0]]}).shadow({radius:20,color:#667eea50,offsetX:0,offsetY:8}).rotate({x:0,y:0,z:1,angle:this.isPlaying?360:0}).animation({duration:8000,curve:Curve.Linear,iterations:-1,playMode:PlayMode.Normal})Circle().width(40).height(40).fill(#ffffff)Circle().width(16).height(16).fill(#333333)}Column({space:4}){Text(this.song.title).fontSize(22).fontWeight(FontWeight.Bold).fontColor(#1a1a1a)Text(${this.song.artist}·${this.song.album}).fontSize(14).fontColor(#888888)}.alignItems(HorizontalAlign.Center)}.width(100%).padding({top:24,bottom:20}).backgroundColor(#ffffff).alignItems(HorizontalAlign.Center)// 歌词显示区域Column({space:0}){// 工具栏Row(){Text(A-).fontSize(14).fontColor(#666666).padding({left:12,right:12,top:6,bottom:6}).backgroundColor(#f0f0f0).borderRadius(16).onClick((){if(this.fontSize12)this.fontSize-2})Text(${this.fontSize}px).fontSize(13).fontColor(#999999).margin({left:8,right:8})Text(A).fontSize(14).fontColor(#666666).padding({left:12,right:12,top:6,bottom:6}).backgroundColor(#f0f0f0).borderRadius(16).onClick((){if(this.fontSize22)this.fontSize2})Blank()Row({space:6}){Text(译).fontSize(13).fontColor(this.showTranslation?#ffffff:#666666).backgroundColor(this.showTranslation?#6c5ce7:#f0f0f0).padding({left:12,right:12,top:6,bottom:6}).borderRadius(16).onClick((){this.showTranslation!this.showTranslation})}}.padding({left:16,right:16,top:10,bottom:10}).backgroundColor(#ffffff)// 歌词列表Scroll(){Column({space:0}){ForEach(this.lyrics,(line:LyricLine,idx:number){Column({space:4}){Text(line.text).fontSize(idxthis.currentLineIndex?this.fontSize2:this.fontSize).fontWeight(idxthis.currentLineIndex?FontWeight.Bold:FontWeight.Normal).fontColor(idxthis.currentLineIndex?#6c5ce7:idxthis.currentLineIndex?#bbbbbb:#444444).textAlign(TextAlign.Center).width(100%).animation({duration:300,curve:Curve.EaseOut})if(this.showTranslation){Text(line.translation).fontSize(this.fontSize-3).fontColor(idxthis.currentLineIndex?#a29bfe:#cccccc).textAlign(TextAlign.Center).width(100%)}}.padding({top:10,bottom:10,left:24,right:24}).backgroundColor(idxthis.currentLineIndex?#f5f2ff:transparent).borderRadius(8).margin({left:8,right:8})})Column().height(40)}.padding({top:16})}.layoutWeight(1).backgroundColor(#fafafa)}.layoutWeight(1)// 播放控制区Column({space:16}){// 进度条Column({space:6}){Slider({value:this.currentTime,min:0,max:this.song.duration,step:1,style:SliderStyle.OutSet,}).width(100%).trackColor(#e0e0e0).selectedColor(#6c5ce7).onChange((v:number){this.currentTimeMath.round(v)})Row(){Text(this.formatTime(this.currentTime)).fontSize(12).fontColor(#999999)Blank()Text(this.formatTime(this.song.duration)).fontSize(12).fontColor(#999999)}.width(100%)}// 播放按钮Row({space:32}){Text(⏮).fontSize(28).fontColor(#444444)Stack(){Circle().width(60).height(60).fill(#6c5ce7)Text(this.isPlaying?⏸:▶).fontSize(22).fontColor(#ffffff)}.onClick(()this.togglePlay())Text(⏭).fontSize(28).fontColor(#444444)}.width(100%).justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Center)}.padding({left:24,right:24,top:16,bottom:32}).backgroundColor(#ffffff)}.width(100%).height(100%).backgroundColor(#ffffff)}}可扩展点这个案例可以当成一个小模板先把页面会变的东西放进状态再用函数或 Builder 处理重复逻辑最后让布局只关心展示。写 HarmonyOS7 页面时这个顺序通常比一上来堆 UI 更稳。
RELATED

相关推荐

C# 从零搭建企业级身份账户系统

C# 从零搭建企业级身份账户系统

在企业级后台管理系统中,身份认证与权限管理永远是第一道门槛,也是最容易埋下技术债务的模块。很多团队要么重复造轮子,要么硬套第三方方案,最终在用户管理、角色权限、Token 续签、多端登录等需求面前疲于奔命。 今天我们就用 ASP.NET Core Identity 从零搭一套可直接落地…

📅 2026/9/11 17:51:22
永磁同步电机无感控制:电压电流互补混合磁链观测器的解析与实现

永磁同步电机无感控制:电压电流互补混合磁链观测器的解析与实现

目录 1.引言 2.有源磁链 2.1 有源磁链的直观理解 3. 有源磁链的数学推导 3.1 dq轴坐标系下的磁链方程转换 3.2 有源磁链的幅值的提取 3.3 有源磁链模型的基本表达式 3.4 为什么有源磁链可以用来提取转子位置? 3.5 有源磁链模型表达式的物理意义 4.定子磁链…

📅 2026/9/8 22:22:15
Spekt8核心功能揭秘:Pod状态监控、服务依赖分析与资源消耗统计

Spekt8核心功能揭秘:Pod状态监控、服务依赖分析与资源消耗统计

Spekt8核心功能揭秘:Pod状态监控、服务依赖分析与资源消耗统计 【免费下载链接】spekt8 Visualize your Kubernetes cluster in real time 项目地址: https://gitcode.com/gh_mirrors/sp/spekt8 Spekt8是一款强大的Kubernetes集群实时可视化工具,…

📅 2026/9/8 2:49:40
MORE NEWS

更多资讯

📰

LeetCode冗余连接:并查集判环原理与实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

程序员职业倦怠自救指南:如何找回写代码的欲望与热情

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

AI提示词工程的硬核试金石:八字排盘实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

塞梅普雷斯 如是说 (第二部/3.你看到我看到的了吗)

//2017-11-09 20:383.你看到我看到的了吗一个烈日炎炎的正午,塞梅普雷斯来到了一家小酒馆里,上到二楼,选了一张白色的方桌坐下,虽然快到饭点了,但食客不多,正对面是一扇大落地窗,店家擦拭的很干净.酒过三巡,菜过五味,客人渐渐多起来,蓝色的天空飘过朵朵黑云,酒馆庭院里的柳树在…

📰

Codex桌面版Windows安装指南:绕过微软商店,GitHub可信部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

MiniMax-M2.7 接口限流故障排查全记录:从告警到恢复

从凌晨告警到恢复:MiniMax-M2.7 接口限流故障排查全记录凌晨两点零四分,告警群开始刷屏。先是零星几条,紧接着像多米诺骨牌一样倒下去,日志里密密麻麻全是同一段报错:“OpenAIException - 当前服务集群负载较高&#x…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬