PlayerWidgetLarge.ets 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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('#FF6B6B')
  71. .borderRadius(6)
  72. .position({ x: 4, y: 4 })
  73. } else {
  74. Text('⏸')
  75. .fontSize(12)
  76. .fontColor('#FFFFFF')
  77. .padding(4)
  78. .backgroundColor('#666666')
  79. .borderRadius(6)
  80. .position({ x: 4, y: 4 })
  81. }
  82. }
  83. .margin({ right: 16 })
  84. .stateStyles({
  85. pressed: {
  86. .opacity(0.7)
  87. },
  88. normal: {
  89. .opacity(1.0)
  90. }
  91. })
  92. .onClick(() => {
  93. console.info('Heanup PlayerWidgetLarge: Cover image clicked');
  94. postCardAction(this, {
  95. 'action': 'router',
  96. 'abilityName': 'EntryAbility'
  97. });
  98. })
  99. // 歌曲信息 - 更现代的排版
  100. Column() {
  101. // 歌曲标题 - 更大更醒目
  102. Text(this.getDisplayText(this.songTitle, '暂无播放'))
  103. .fontSize(18)
  104. .fontColor(this.getTextColor(this.songTitle, '暂无播放', '#1A1A1A'))
  105. .fontWeight(FontWeight.Bold)
  106. .maxLines(2)
  107. .textOverflow({ overflow: TextOverflow.Ellipsis })
  108. .width('100%')
  109. .textAlign(TextAlign.Start)
  110. .lineHeight(24)
  111. // 艺术家信息 - 更柔和的颜色
  112. Text(this.getDisplayText(this.songArtist, '未知艺术家'))
  113. .fontSize(14)
  114. .fontColor('#666666')
  115. .fontWeight(FontWeight.Medium)
  116. .maxLines(1)
  117. .textOverflow({ overflow: TextOverflow.Ellipsis })
  118. .width('100%')
  119. .textAlign(TextAlign.Start)
  120. .margin({ top: 4 })
  121. // 专辑信息 - 更小更淡
  122. Text(this.getDisplayText(this.songAlbum, '未知专辑'))
  123. .fontSize(12)
  124. .fontColor('#999999')
  125. .fontWeight(FontWeight.Normal)
  126. .maxLines(1)
  127. .textOverflow({ overflow: TextOverflow.Ellipsis })
  128. .width('100%')
  129. .textAlign(TextAlign.Start)
  130. .margin({ top: 2 })
  131. }
  132. .layoutWeight(1)
  133. .alignItems(HorizontalAlign.Start)
  134. .justifyContent(FlexAlign.Start)
  135. .stateStyles({
  136. pressed: {
  137. .opacity(0.7)
  138. },
  139. normal: {
  140. .opacity(1.0)
  141. }
  142. })
  143. .onClick(() => {
  144. console.info('Heanup PlayerWidgetLarge: Song info area clicked');
  145. postCardAction(this, {
  146. 'action': 'router',
  147. 'abilityName': 'EntryAbility'
  148. });
  149. })
  150. }
  151. .width('100%')
  152. .alignItems(VerticalAlign.Top)
  153. .margin({ bottom: 20 })
  154. // 播放进度区域 - 重新设计
  155. if (this.showProgress) {
  156. Column() {
  157. // 进度条 - 更粗更现代
  158. Stack() {
  159. // 背景进度条
  160. Row()
  161. .width('100%')
  162. .height(6)
  163. .backgroundColor('#F0F0F0')
  164. .borderRadius(3)
  165. // 当前进度
  166. Row()
  167. .width(`${this.progressPercentage}%`)
  168. .height(6)
  169. .backgroundColor('#FF6B6B') // 使用纯色替代渐变
  170. .borderRadius(3)
  171. }
  172. .width('100%')
  173. .stateStyles({
  174. pressed: {
  175. .opacity(0.7)
  176. },
  177. normal: {
  178. .opacity(1.0)
  179. }
  180. })
  181. .onClick((event) => {
  182. if (!this.isLoading) {
  183. console.info('Heanup PlayerWidgetLarge: Progress bar clicked');
  184. const width = Number(event.target.area.width);
  185. const percentage = width > 0 ? (event.x / width * 100) : 0;
  186. postCardAction(this, {
  187. 'action': 'message',
  188. 'params': {
  189. "func":"seek_to",
  190. 'percentage': percentage
  191. }
  192. });
  193. }
  194. })
  195. // 时间显示 - 更现代的样式
  196. Row() {
  197. Text(this.currentTime)
  198. .fontSize(12)
  199. .fontColor('#666666')
  200. .fontWeight(FontWeight.Medium)
  201. Blank()
  202. Text(this.totalTime)
  203. .fontSize(12)
  204. .fontColor('#666666')
  205. .fontWeight(FontWeight.Medium)
  206. }
  207. .width('100%')
  208. .margin({ top: 8 })
  209. }
  210. .width('100%')
  211. .margin({ bottom: 20 })
  212. }
  213. // 播放控制区域 - 重新设计
  214. Row() {
  215. // 上一首按钮 - 更现代的设计
  216. Button() {
  217. Image($r('app.media.ic_previous'))
  218. .width(24)
  219. .height(24)
  220. .fillColor(this.hasPrevious ? '#333333' : '#CCCCCC')
  221. }
  222. .width(48)
  223. .height(48)
  224. .backgroundColor('#F8F8F8')
  225. .borderRadius(24)
  226. .enabled(!this.isLoading)
  227. .opacity(this.hasPrevious ? 1.0 : 0.6)
  228. .stateStyles({
  229. pressed: {
  230. .opacity(0.7)
  231. .backgroundColor('#EEEEEE')
  232. },
  233. normal: {
  234. .opacity(this.hasPrevious ? 1.0 : 0.6)
  235. .backgroundColor('#F8F8F8')
  236. }
  237. })
  238. .onClick(() => {
  239. console.info(`Heanup PlayerWidgetLarge: Previous button clicked, hasPrevious=${this.hasPrevious}, isLoading=${this.isLoading}`);
  240. if (!this.isLoading) {
  241. console.info('Heanup PlayerWidgetLarge: Previous button action sent');
  242. postCardAction(this, {
  243. 'action': 'message',
  244. 'params': {
  245. "func":"prev_song"
  246. }
  247. });
  248. } else {
  249. console.info('Heanup PlayerWidgetLarge: Previous button disabled due to loading');
  250. }
  251. })
  252. Blank()
  253. // 播放/暂停按钮 - 更大更突出
  254. Button() {
  255. Image(this.getPlayButtonIcon())
  256. .width(32)
  257. .height(32)
  258. .fillColor('#FFFFFF')
  259. }
  260. .width(64)
  261. .height(64)
  262. .backgroundColor(this.isLoading ? '#CCCCCC' : '#FF6B6B') // 使用纯色替代渐变
  263. .borderRadius(32)
  264. .enabled(!this.isLoading)
  265. .stateStyles({
  266. pressed: {
  267. .opacity(0.8)
  268. .backgroundColor(this.isLoading ? '#AAAAAA' : '#FF5252')
  269. },
  270. normal: {
  271. .opacity(1.0)
  272. .backgroundColor(this.isLoading ? '#CCCCCC' : '#FF6B6B')
  273. }
  274. })
  275. .onClick(() => {
  276. if (!this.isLoading) {
  277. console.info('Heanup PlayerWidgetLarge: Play/Pause button clicked');
  278. postCardAction(this, {
  279. 'action': 'message',
  280. 'params': {
  281. "func":"play_pause"
  282. }
  283. });
  284. }
  285. })
  286. Blank()
  287. // 下一首按钮 - 与上一首对称
  288. Button() {
  289. Image($r('app.media.ic_next'))
  290. .width(24)
  291. .height(24)
  292. .fillColor(this.hasNext ? '#333333' : '#CCCCCC')
  293. }
  294. .width(48)
  295. .height(48)
  296. .backgroundColor('#F8F8F8')
  297. .borderRadius(24)
  298. .enabled(!this.isLoading && this.hasNext) // 确保按钮状态正确
  299. .opacity(this.hasNext ? 1.0 : 0.6)
  300. .stateStyles({
  301. pressed: {
  302. .opacity(0.7)
  303. .backgroundColor('#EEEEEE')
  304. },
  305. normal: {
  306. .opacity(this.hasNext ? 1.0 : 0.6)
  307. .backgroundColor('#F8F8F8')
  308. }
  309. })
  310. .onClick(() => {
  311. console.info(`Heanup PlayerWidgetLarge: Next button clicked, hasNext=${this.hasNext}, isLoading=${this.isLoading}`);
  312. if (!this.isLoading && this.hasNext) {
  313. console.info('Heanup PlayerWidgetLarge: Next button action sent');
  314. postCardAction(this, {
  315. 'action': 'message',
  316. 'params': {
  317. "func":"next_song"
  318. }
  319. });
  320. } else {
  321. console.info('Heanup PlayerWidgetLarge: Next button disabled due to loading or no next song');
  322. }
  323. })
  324. }
  325. .width('100%')
  326. .justifyContent(FlexAlign.SpaceBetween)
  327. .alignItems(VerticalAlign.Center)
  328. }
  329. .width('100%')
  330. .height('100%')
  331. .padding(20)
  332. .backgroundColor('#FFFFFF')
  333. .borderRadius(20)
  334. .alignItems(HorizontalAlign.Start)
  335. .justifyContent(FlexAlign.SpaceBetween)
  336. .shadow({
  337. radius: 8,
  338. color: '#20000000',
  339. offsetX: 0,
  340. offsetY: 4
  341. })
  342. // 移除手势支持,form应用不支持复杂交互
  343. }
  344. }