PlayerWidgetLarge.ets 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. import { WidgetController } from '../../common/widget/WidgetController';
  2. /**
  3. * 大尺寸播放器卡片 (4x3)
  4. * 显示专辑封面、完整信息和扩展控制
  5. * 需求: 5.3, 2.3, 2.4
  6. */
  7. @Entry
  8. @Component
  9. struct PlayerWidgetLarge {
  10. @LocalStorageProp('isPlaying') isPlaying: boolean = false;
  11. @LocalStorageProp('songTitle') songTitle: string = '暂无播放';
  12. @LocalStorageProp('songArtist') songArtist: string = '未知艺术家';
  13. @LocalStorageProp('songAlbum') songAlbum: string = '未知专辑';
  14. @LocalStorageProp('coverImage') coverImage: string = '';
  15. @LocalStorageProp('currentTime') currentTime: string = '00:00';
  16. @LocalStorageProp('totalTime') totalTime: string = '00:00';
  17. @LocalStorageProp('progressPercentage') progressPercentage: number = 0;
  18. @LocalStorageProp('hasNext') hasNext: boolean = false;
  19. @LocalStorageProp('hasPrevious') hasPrevious: boolean = false;
  20. @LocalStorageProp('showProgress') showProgress: boolean = true;
  21. @LocalStorageProp('showCover') showCover: boolean = true;
  22. @LocalStorageProp('isLoading') isLoading: boolean = false;
  23. /**
  24. * 获取播放按钮图标
  25. */
  26. private getPlayButtonIcon(): Resource {
  27. if (this.isLoading) {
  28. return $r('app.media.icon_load');
  29. }
  30. return this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play');
  31. }
  32. /**
  33. * 格式化显示文本
  34. */
  35. private getDisplayText(text: string, defaultText: string): string {
  36. return (!text || text.trim() === '') ? defaultText : text;
  37. }
  38. /**
  39. * 获取文本颜色
  40. */
  41. private getTextColor(text: string, defaultText: string, normalColor: string): string {
  42. return text === defaultText ? '#99000000' : normalColor;
  43. }
  44. /**
  45. * 获取专辑封面图片
  46. */
  47. private getCoverImage(): Resource | string {
  48. if (this.coverImage && this.coverImage.trim() !== '' && this.coverImage !== 'undefined') {
  49. return this.coverImage;
  50. }
  51. return $r('app.media.bg_music');
  52. }
  53. build() {
  54. Column() {
  55. // 顶部信息区域
  56. Row() {
  57. // 专辑封面 - 更大更圆润
  58. Stack() {
  59. Image(this.getCoverImage())
  60. .width(72)
  61. .height(72)
  62. .borderRadius(16)
  63. .objectFit(ImageFit.Cover)
  64. // 播放状态指示器 - 简化版本
  65. if (this.isPlaying) {
  66. Text('♪')
  67. .fontSize(12)
  68. .fontColor('#FFFFFF')
  69. .padding(4)
  70. .backgroundColor('#80000000')
  71. .borderRadius(6)
  72. .position({ x: 4, y: 4 })
  73. }
  74. }
  75. .margin({ right: 16 })
  76. .stateStyles({
  77. pressed: {
  78. .opacity(0.7)
  79. },
  80. normal: {
  81. .opacity(1.0)
  82. }
  83. })
  84. .onClick(() => {
  85. console.info('Heanup PlayerWidgetLarge: Cover image clicked');
  86. postCardAction(this, {
  87. 'action': 'router',
  88. 'abilityName': 'EntryAbility'
  89. });
  90. })
  91. // 歌曲信息 - 更现代的排版
  92. Column() {
  93. // 歌曲标题 - 更大更醒目
  94. Text(this.getDisplayText(this.songTitle, '暂无播放'))
  95. .fontSize(18)
  96. .fontColor(this.getTextColor(this.songTitle, '暂无播放', '#1A1A1A'))
  97. .fontWeight(FontWeight.Bold)
  98. .maxLines(2)
  99. .textOverflow({ overflow: TextOverflow.Ellipsis })
  100. .width('100%')
  101. .textAlign(TextAlign.Start)
  102. .lineHeight(24)
  103. // 艺术家信息 - 更柔和的颜色
  104. Text(this.getDisplayText(this.songArtist, '未知艺术家'))
  105. .fontSize(14)
  106. .fontColor('#666666')
  107. .fontWeight(FontWeight.Medium)
  108. .maxLines(1)
  109. .textOverflow({ overflow: TextOverflow.Ellipsis })
  110. .width('100%')
  111. .textAlign(TextAlign.Start)
  112. .margin({ top: 4 })
  113. // 专辑信息 - 更小更淡
  114. Text(this.getDisplayText(this.songAlbum, '未知专辑'))
  115. .fontSize(12)
  116. .fontColor('#999999')
  117. .fontWeight(FontWeight.Normal)
  118. .maxLines(1)
  119. .textOverflow({ overflow: TextOverflow.Ellipsis })
  120. .width('100%')
  121. .textAlign(TextAlign.Start)
  122. .margin({ top: 2 })
  123. }
  124. .layoutWeight(1)
  125. .alignItems(HorizontalAlign.Start)
  126. .justifyContent(FlexAlign.Start)
  127. .stateStyles({
  128. pressed: {
  129. .opacity(0.7)
  130. },
  131. normal: {
  132. .opacity(1.0)
  133. }
  134. })
  135. .onClick(() => {
  136. console.info('Heanup PlayerWidgetLarge: Song info area clicked');
  137. postCardAction(this, {
  138. 'action': 'router',
  139. 'abilityName': 'EntryAbility'
  140. });
  141. })
  142. }
  143. .width('100%')
  144. .alignItems(VerticalAlign.Top)
  145. .margin({ bottom: 20 })
  146. // 播放进度区域 - 重新设计
  147. if (this.showProgress) {
  148. Column() {
  149. // 进度条 - 更粗更现代
  150. Stack() {
  151. // 背景进度条
  152. Row()
  153. .width('100%')
  154. .height(6)
  155. .backgroundColor('#F0F0F0')
  156. .borderRadius(3)
  157. // 当前进度
  158. Row()
  159. .width(`${this.progressPercentage}%`)
  160. .height(6)
  161. .backgroundColor('#FF6B6B') // 使用纯色替代渐变
  162. .borderRadius(3)
  163. }
  164. .width('100%')
  165. .stateStyles({
  166. pressed: {
  167. .opacity(0.7)
  168. },
  169. normal: {
  170. .opacity(1.0)
  171. }
  172. })
  173. .onClick((event) => {
  174. if (!this.isLoading) {
  175. console.info('Heanup PlayerWidgetLarge: Progress bar clicked');
  176. const width = Number(event.target.area.width);
  177. const percentage = width > 0 ? (event.x / width * 100) : 0;
  178. postCardAction(this, {
  179. 'action': 'message',
  180. 'params': {
  181. "func":"seek_to",
  182. 'percentage': percentage
  183. }
  184. });
  185. }
  186. })
  187. // 时间显示 - 更现代的样式
  188. Row() {
  189. Text(this.currentTime)
  190. .fontSize(12)
  191. .fontColor('#666666')
  192. .fontWeight(FontWeight.Medium)
  193. Blank()
  194. Text(this.totalTime)
  195. .fontSize(12)
  196. .fontColor('#666666')
  197. .fontWeight(FontWeight.Medium)
  198. }
  199. .width('100%')
  200. .margin({ top: 8 })
  201. }
  202. .width('100%')
  203. .margin({ bottom: 20 })
  204. }
  205. // 播放控制区域 - 重新设计
  206. Row() {
  207. // 上一首按钮 - 更现代的设计
  208. Button() {
  209. Image($r('app.media.ic_previous'))
  210. .width(24)
  211. .height(24)
  212. .fillColor(this.hasPrevious ? '#333333' : '#CCCCCC')
  213. }
  214. .width(48)
  215. .height(48)
  216. .backgroundColor('#F8F8F8')
  217. .borderRadius(24)
  218. .enabled(!this.isLoading)
  219. .opacity(this.hasPrevious ? 1.0 : 0.6)
  220. .stateStyles({
  221. pressed: {
  222. .opacity(0.7)
  223. .backgroundColor('#EEEEEE')
  224. },
  225. normal: {
  226. .opacity(this.hasPrevious ? 1.0 : 0.6)
  227. .backgroundColor('#F8F8F8')
  228. }
  229. })
  230. .onClick(() => {
  231. console.info(`Heanup PlayerWidgetLarge: Previous button clicked, hasPrevious=${this.hasPrevious}, isLoading=${this.isLoading}`);
  232. if (!this.isLoading) {
  233. console.info('Heanup PlayerWidgetLarge: Previous button action sent');
  234. postCardAction(this, {
  235. 'action': 'message',
  236. 'params': {
  237. "func":"prev_song"
  238. }
  239. });
  240. } else {
  241. console.info('Heanup PlayerWidgetLarge: Previous button disabled due to loading');
  242. }
  243. })
  244. Blank()
  245. // 播放/暂停按钮 - 更大更突出
  246. Button() {
  247. Image(this.getPlayButtonIcon())
  248. .width(32)
  249. .height(32)
  250. .fillColor('#FFFFFF')
  251. }
  252. .width(64)
  253. .height(64)
  254. .backgroundColor(this.isLoading ? '#CCCCCC' : '#FF6B6B') // 使用纯色替代渐变
  255. .borderRadius(32)
  256. .enabled(!this.isLoading)
  257. .stateStyles({
  258. pressed: {
  259. .opacity(0.8)
  260. .backgroundColor(this.isLoading ? '#AAAAAA' : '#FF5252')
  261. },
  262. normal: {
  263. .opacity(1.0)
  264. .backgroundColor(this.isLoading ? '#CCCCCC' : '#FF6B6B')
  265. }
  266. })
  267. .onClick(() => {
  268. if (!this.isLoading) {
  269. console.info('Heanup PlayerWidgetLarge: Play/Pause button clicked');
  270. postCardAction(this, {
  271. 'action': 'message',
  272. 'params': {
  273. "func":"play_pause"
  274. }
  275. });
  276. }
  277. })
  278. Blank()
  279. // 下一首按钮 - 与上一首对称
  280. Button() {
  281. Image($r('app.media.ic_next'))
  282. .width(24)
  283. .height(24)
  284. .fillColor(this.hasNext ? '#333333' : '#CCCCCC')
  285. }
  286. .width(48)
  287. .height(48)
  288. .backgroundColor('#F8F8F8')
  289. .borderRadius(24)
  290. .enabled(!this.isLoading && this.hasNext) // 确保按钮状态正确
  291. .opacity(this.hasNext ? 1.0 : 0.6)
  292. .stateStyles({
  293. pressed: {
  294. .opacity(0.7)
  295. .backgroundColor('#EEEEEE')
  296. },
  297. normal: {
  298. .opacity(this.hasNext ? 1.0 : 0.6)
  299. .backgroundColor('#F8F8F8')
  300. }
  301. })
  302. .onClick(() => {
  303. console.info(`Heanup PlayerWidgetLarge: Next button clicked, hasNext=${this.hasNext}, isLoading=${this.isLoading}`);
  304. if (!this.isLoading && this.hasNext) {
  305. console.info('Heanup PlayerWidgetLarge: Next button action sent');
  306. postCardAction(this, {
  307. 'action': 'message',
  308. 'params': {
  309. "func":"next_song"
  310. }
  311. });
  312. } else {
  313. console.info('Heanup PlayerWidgetLarge: Next button disabled due to loading or no next song');
  314. }
  315. })
  316. }
  317. .width('100%')
  318. .justifyContent(FlexAlign.SpaceBetween)
  319. .alignItems(VerticalAlign.Center)
  320. }
  321. .width('100%')
  322. .height('100%')
  323. .padding(20)
  324. .backgroundColor('#FFFFFF')
  325. .borderRadius(20)
  326. .alignItems(HorizontalAlign.Start)
  327. .justifyContent(FlexAlign.SpaceBetween)
  328. .shadow({
  329. radius: 8,
  330. color: '#20000000',
  331. offsetX: 0,
  332. offsetY: 4
  333. })
  334. // 移除手势支持,form应用不支持复杂交互
  335. }
  336. }