music-player-patterns.mdc 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. ---
  2. description: 音乐播放器开发模式与最佳实践
  3. globs: ["**/view/**/*.ets", "**/viewmodel/**/*.ets", "**/controller/**/*.ets"]
  4. alwaysApply: false
  5. ---
  6. # 音乐播放器开发模式与最佳实践
  7. ## 播放状态管理
  8. ### 播放状态枚举
  9. 使用 `PlayStatus` 枚举管理播放状态:
  10. ```typescript
  11. import { PlayStatus } from '../common/PlayStatus';
  12. // 在组件中使用
  13. @State playStatus: PlayStatus = PlayStatus.INIT;
  14. ```
  15. ### 状态转换模式
  16. ```typescript
  17. // 播放/暂停切换
  18. togglePlay() {
  19. if (this.playStatus === PlayStatus.PLAY) {
  20. this.pauseMusic();
  21. this.playStatus = PlayStatus.PAUSE;
  22. } else {
  23. this.playMusic();
  24. this.playStatus = PlayStatus.PLAY;
  25. }
  26. }
  27. ```
  28. ## 音频会话管理
  29. ### AvSessionController使用
  30. ```typescript
  31. import { AvSessionController } from '../controller/AvSessionController';
  32. // 获取控制器实例
  33. private avSessionController = AvSessionController.getInstance();
  34. // 在页面生命周期中注册/注销
  35. aboutToAppear() {
  36. this.avSessionController.registerSessionListener();
  37. }
  38. aboutToDisappear() {
  39. this.avSessionController.unregisterSessionListener();
  40. }
  41. ```
  42. ## 歌词处理模式
  43. ### 歌词解析与显示
  44. ```typescript
  45. // 使用lib中的LyricHelper
  46. import { LyricHelper } from '@lib/LyricHelper';
  47. // 解析歌词文件
  48. LyricHelper.parseLyricFile(lyricPath)
  49. .then(lyrics => {
  50. this.lyrics = lyrics;
  51. })
  52. .catch((err: Error) => {
  53. Logger.error(`歌词解析失败: ${err.message}`);
  54. });
  55. ```
  56. ### 歌词同步显示
  57. ```typescript
  58. // 根据当前播放时间获取对应歌词行
  59. getCurrentLyricLine(currentTime: number): LyricLine | null {
  60. if (!this.lyrics || this.lyrics.length === 0) {
  61. return null;
  62. }
  63. for (let i = 0; i < this.lyrics.length; i++) {
  64. if (this.lyrics[i].time > currentTime) {
  65. return i > 0 ? this.lyrics[i - 1] : null;
  66. }
  67. }
  68. return this.lyrics[this.lyrics.length - 1];
  69. }
  70. ```
  71. ## 媒体文件处理
  72. ### 支持的音频格式
  73. 使用 `CommonConstants.REAL_MUSIC_FORMAT` 检查文件格式:
  74. ```typescript
  75. import { CommonConstants } from '../common/constants/CommonConstants';
  76. function isMusicFile(fileName: string): boolean {
  77. const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
  78. return CommonConstants.REAL_MUSIC_FORMAT.includes(ext);
  79. }
  80. ```
  81. ### 媒体扫描与索引
  82. ```typescript
  83. // 扫描本地音乐文件
  84. async scanLocalMusic(): Promise<MusicItem[]> {
  85. const musicFiles: MusicItem[] = [];
  86. const context = getContext(this);
  87. // 使用文件系统API扫描
  88. // 实现细节取决于具体需求
  89. return musicFiles;
  90. }
  91. ```
  92. ## 播放列表管理
  93. ### 播放列表模型
  94. ```typescript
  95. import { Playlist } from '../viewmodel/Playlist';
  96. // 创建播放列表
  97. const playlist = new Playlist();
  98. playlist.name = "我的播放列表";
  99. playlist.songs = [song1, song2, song3];
  100. // 保存到本地存储
  101. PreferencesUtil.saveObject('playlist_' + playlist.id, playlist);
  102. ```
  103. ### 播放模式
  104. ```typescript
  105. // 播放模式枚举
  106. enum PlayMode {
  107. SEQUENCE, // 顺序播放
  108. LOOP, // 循环播放
  109. RANDOM, // 随机播放
  110. SINGLE // 单曲循环
  111. }
  112. // 切换播放模式
  113. switchPlayMode() {
  114. const modes = Object.values(PlayMode);
  115. const currentIndex = modes.indexOf(this.playMode);
  116. this.playMode = modes[(currentIndex + 1) % modes.length];
  117. }
  118. ```
  119. ## 主题适配
  120. ### 主题切换
  121. ```typescript
  122. import { myTheme } from '../common/AppTheme';
  123. // 在组件中使用主题颜色
  124. @Builder
  125. PlayerControl() {
  126. Row() {
  127. Button('播放')
  128. .backgroundColor($r('app.color.brand'))
  129. .fontColor($r('app.color.fontOnPrimary'))
  130. }
  131. .backgroundColor($r('app.color.backgroundPrimary'))
  132. }
  133. ```
  134. ### 动态主题更新
  135. ```typescript
  136. // 更新主题
  137. updateTheme(themeIndex: number) {
  138. const themeList = [DefaultTheme, TwilightTheme, ForestTheme, CoralTheme, MidnightTheme];
  139. AppStorage.SetOrCreate('themeColor', themeList[themeIndex].colors.brand);
  140. }
  141. ```
  142. ## 性能优化
  143. ### 图片加载优化
  144. ```typescript
  145. // 使用缓存和懒加载
  146. Image(this.coverUrl)
  147. .width(50)
  148. .height(50)
  149. .borderRadius(8)
  150. .objectFit(ImageFit.Cover)
  151. .alt($r('app.media.default_music_icon'))
  152. .onComplete(() => {
  153. // 加载完成回调
  154. })
  155. .onError(() => {
  156. // 加载失败回调
  157. })
  158. ```
  159. ### 列表性能优化
  160. ```typescript
  161. // 使用LazyForEach和缓存
  162. LazyForEach(this.musicData, (item: MusicItem, index: number) => {
  163. ListItem() {
  164. MusicItemComponent({ musicItem: item })
  165. }
  166. }, (item: MusicItem) => item.id.toString())
  167. ```
  168. ## 错误处理
  169. ### 播放错误处理
  170. ```typescript
  171. // 播放错误处理
  172. handlePlaybackError(error: Error) {
  173. Logger.error(`播放错误: ${error.message}`);
  174. this.playStatus = PlayStatus.INIT;
  175. // 显示错误提示
  176. ToastUtil.showToast(`播放失败: ${error.message}`);
  177. }
  178. ```
  179. ### 网络请求错误处理
  180. ```typescript
  181. // API请求错误处理
  182. fetchLyrics(title: string, artist: string) {
  183. const url = `${CommonConstants.LRC_API}?title=${encodeURIComponent(title)}&artist=${encodeURIComponent(artist)}`;
  184. fetch(url)
  185. .then(response => {
  186. if (!response.ok) {
  187. throw new Error(`HTTP error! status: ${response.status}`);
  188. }
  189. return response.json();
  190. })
  191. .then(data => {
  192. this.processLyrics(data);
  193. })
  194. .catch((err: Error) => {
  195. Logger.error(`获取歌词失败: ${err.message}`);
  196. // 使用备用API或显示错误
  197. });
  198. }
  199. ```