PlayerWidgetSquare.ets 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /**
  2. * 方形播放器卡片 (2x2)
  3. * 显示专辑封面、歌曲信息和播放按钮
  4. * 参考华为官方设计,移除收藏功能
  5. * 需求: 5.1, 1.4, 1.5
  6. */
  7. // 定义对齐规则 - 参考华为官方代码
  8. const SquareSingerAlignRules: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
  9. 'top': { 'anchor': 'musicTitle', 'align': VerticalAlign.Bottom },
  10. 'left': { 'anchor': 'musicCover', 'align': HorizontalAlign.End }
  11. };
  12. const SquareCoverAlignRules: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
  13. 'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom }
  14. };
  15. const PlayAlignRules: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
  16. 'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom },
  17. 'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
  18. };
  19. @Entry
  20. @Component
  21. struct PlayerWidgetSquare {
  22. // 卡片数据属性 - 使用LocalStorageProp与FormExtensionAbility通信
  23. @LocalStorageProp('formId') formId: string = '12400633174999288';
  24. @LocalStorageProp('isPlaying') isPlaying: boolean = false;
  25. @LocalStorageProp('songTitle') songTitle: string = 'Dream It Possible';
  26. @LocalStorageProp('songArtist') songArtist: string = 'Delacey';
  27. @LocalStorageProp('coverImage') coverImage: string = '';
  28. @LocalStorageProp('isLoading') isLoading: boolean = false;
  29. @LocalStorageProp('imageColorHex') imageColorHex: string = '2A2A2A';
  30. @LocalStorageProp('imgName') imgName: string = ''; // 图片文件名,用于memory://协议
  31. /**
  32. * 格式化歌曲标题显示
  33. */
  34. private getDisplayTitle(): string {
  35. if (!this.songTitle || this.songTitle.trim() === '') {
  36. return '暂无播放';
  37. }
  38. return this.songTitle;
  39. }
  40. /**
  41. * 格式化艺术家显示
  42. */
  43. private getDisplayArtist(): string {
  44. if (!this.songArtist || this.songArtist.trim() === '') {
  45. return '未知艺术家';
  46. }
  47. return this.songArtist;
  48. }
  49. /**
  50. * 获取专辑封面
  51. */
  52. private getCoverImage(): Resource | string {
  53. console.info(`Heanup PlayerWidgetSquare: getCoverImage called, coverImage='${this.coverImage}', imgName='${this.imgName}'`);
  54. // 优先使用通过formImages传递的本地图片(支持网络图片下载后的显示)
  55. if (this.imgName && this.imgName.trim() !== '') {
  56. const memoryUrl = 'memory://' + this.imgName;
  57. console.info(`Heanup PlayerWidgetSquare: Using memory image: ${memoryUrl}`);
  58. return memoryUrl;
  59. }
  60. // 如果有coverImage且不是网络URL,使用本地路径
  61. if (this.coverImage && this.coverImage.trim() !== '' && !this.isNetworkUrl(this.coverImage)) {
  62. console.info(`Heanup PlayerWidgetSquare: Using local cover image: ${this.coverImage}`);
  63. return this.coverImage;
  64. }
  65. // 默认使用内置图片
  66. console.info(`Heanup PlayerWidgetSquare: Using default cover image`);
  67. return $r('app.media.ic_avatar4'); // 使用默认专辑封面
  68. }
  69. /**
  70. * 检查是否为网络URL
  71. */
  72. private isNetworkUrl(url: string): boolean {
  73. return url.startsWith('http://') || url.startsWith('https://');
  74. }
  75. build() {
  76. RelativeContainer() {
  77. // 歌曲标题 - 参考华为官方样式
  78. Text(this.getDisplayTitle())
  79. .fontSize(14)
  80. .fontWeight(700)
  81. .width('85%')
  82. .fontColor(Color.White)
  83. .textOverflow({ overflow: TextOverflow.Ellipsis })
  84. .maxLines(1)
  85. .id('musicTitle')
  86. // 艺术家名称 - 参考华为官方样式
  87. Text(this.getDisplayArtist())
  88. .fontSize(12)
  89. .fontColor('#CCFFFFFF')
  90. .fontWeight(500)
  91. .maxLines(1)
  92. .alignRules(SquareSingerAlignRules)
  93. .margin({ top: 2 })
  94. .id('singerText')
  95. // 专辑封面 - 参考华为官方黑胶唱片设计
  96. Stack({ alignContent: Alignment.Center }) {
  97. Image($r('app.media.ic_music_bg_mini'))
  98. .height(88)
  99. .width(88)
  100. Button()
  101. .backgroundImage(this.getCoverImage())
  102. .backgroundImageSize(ImageSize.Cover)
  103. .height(58)
  104. .width(58)
  105. .borderRadius(29)
  106. }
  107. .alignRules(SquareCoverAlignRules)
  108. .id('musicCover')
  109. .onClick(() => {
  110. console.info('Heanup PlayerWidgetSquare: Album cover clicked');
  111. postCardAction(this, {
  112. 'action': 'router',
  113. 'abilityName': 'EntryAbility'
  114. });
  115. })
  116. // 播放按钮 - 参考华为官方样式
  117. SymbolGlyph(this.isPlaying ? $r('sys.symbol.pause_round_triangle_fill') : $r('sys.symbol.play_round_triangle_fill'))
  118. .fontSize(36)
  119. .symbolEffect(new ReplaceSymbolEffect(EffectScope.WHOLE), true)
  120. .fontColor(['#E5FFFFFF'])
  121. .alignRules(PlayAlignRules)
  122. .onClick(() => {
  123. console.info('Heanup PlayerWidgetSquare: Play/Pause button clicked');
  124. if (!this.isLoading) {
  125. postCardAction(this, {
  126. 'action': 'call',
  127. 'abilityName': 'EntryAbility',
  128. 'params': {
  129. 'formId': this.formId,
  130. 'method': 'playPause',
  131. 'widgetIsPlaying': this.isPlaying // 传递卡片当前显示的播放状态
  132. }
  133. });
  134. }
  135. })
  136. }
  137. .height('100%')
  138. .width('100%')
  139. .linearGradient({
  140. direction: GradientDirection.Bottom,
  141. repeating: false,
  142. colors: [[`#ff${this.imageColorHex}`, 0.0], [`#ff${this.imageColorHex}`, 0.5], [`#ff${this.imageColorHex}`, 1.0]]
  143. })
  144. .padding(12)
  145. .onClick(() => {
  146. console.info('Heanup PlayerWidgetSquare: Container clicked, jumping to main app');
  147. postCardAction(this, {
  148. 'action': 'router',
  149. 'abilityName': 'EntryAbility'
  150. });
  151. })
  152. }
  153. }