/** * 矩形封面播放器卡片 (2x4) * 显示专辑封面、歌曲信息和播放控制 */ // 定义对齐规则 const RectangleSingerAlignRules: Record> = { 'top': { 'anchor': 'musicTitle', 'align': VerticalAlign.Bottom }, 'left': { 'anchor': 'musicCover', 'align': HorizontalAlign.End } }; const RectangleCoverAlignRules: Record> = { 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }, 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top } }; const RectangleTitleAlignRules: Record> = { 'left': { 'anchor': 'musicCover', 'align': HorizontalAlign.End }, 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top } }; const RectanglePlayControlAlignRules: Record> = { 'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom }, 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }, 'right': { 'anchor': '__container__', 'align': HorizontalAlign.End } }; @Entry @Component struct PlayerWidgetRectangle { // 卡片数据属性 - 适配新的数据结构 @LocalStorageProp('formId') formId: string = '202504'; // New formId for this card // VideoItem 主要数据 @LocalStorageProp('id') songId: string = ''; @LocalStorageProp('name') songTitle: string = 'Dream It Possible'; @LocalStorageProp('artist') songArtist: string = 'Delacey'; @LocalStorageProp('album') songAlbum: string = '未知专辑'; @LocalStorageProp('pixelMapPath') coverImage: string = ''; @LocalStorageProp('duration') songDuration: number = 0; @LocalStorageProp('filePath') songFilePath: string = ''; // PlayerState 播放状态数据 @LocalStorageProp('playerState') playerState: Record = {}; // 平铺状态数据(优先使用) @LocalStorageProp('isPlaying') isPlaying: boolean = false; @LocalStorageProp('isPaused') isPaused: boolean = true; @LocalStorageProp('isLoading') isLoading: boolean = false; @LocalStorageProp('hasNext') hasNext: boolean = false; @LocalStorageProp('hasPrevious') hasPrevious: boolean = false; // 播放列表信息 @LocalStorageProp('playlistInfo') playlistInfo: Record = {}; @LocalStorageProp('currentIndex') currentIndex: number = 0; @LocalStorageProp('totalCount') totalCount: number = 0; // 时间信息 @LocalStorageProp('timeInfo') timeInfo: Record = {}; @LocalStorageProp('currentTimeText') currentTimeText: string = '00:00'; @LocalStorageProp('totalTimeText') totalTimeText: string = '00:00'; @LocalStorageProp('progressPercentage') progressPercentage: number = 0; // 兼容性属性(用于获取复合状态) @LocalStorageProp('imgName') imgName: string = ''; // 图片文件名,用于memory://协议 @LocalStorageProp('isFavorite') isFavorite: boolean = false; /** * 格式化歌曲标题显示 */ private getDisplayTitle(): string { if (!this.songTitle || this.songTitle.trim() === '') { return '暂无播放'; } return this.songTitle; } /** * 格式化艺术家显示 */ private getDisplayArtist(): string { if (!this.songArtist || this.songArtist.trim() === '') { return '未知艺术家'; } return this.songArtist; } /** * 检查是否为网络URL */ private isNetworkUrl(url: string): boolean { return url.startsWith('http://') || url.startsWith('https://'); } /** * 获取专辑封面 */ private getCoverImage(): Resource | string { console.info(`Heanup PlayerWidgetRectangle: getCoverImage called, coverImage='${this.coverImage}', imgName='${this.imgName}'`); // 优先使用通过formImages传递的本地图片(支持网络图片下载后的显示) if (this.imgName && this.imgName.trim() !== '') { const memoryUrl = 'memory://' + this.imgName; console.info(`Heanup PlayerWidgetRectangle: Using memory image: ${memoryUrl}`); return memoryUrl; } // 如果有coverImage且不是网络URL,使用本地路径 if (this.coverImage && this.coverImage.trim() !== '' && !this.isNetworkUrl(this.coverImage)) { console.info(`Heanup PlayerWidgetRectangle: Using local cover image: ${this.coverImage}`); return this.coverImage; } // 默认使用内置图片 console.info(`Heanup PlayerWidgetRectangle: Using default cover image`); return $r('app.media.ic_avatar4'); // 使用默认专辑封面 } build() { Stack() { // 背景和主要内容 RelativeContainer() { // 歌曲标题 Text(this.getDisplayTitle()) .fontSize(16) .fontWeight(FontWeight.Bold) .width('60%') .fontColor(Color.White) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .alignRules(RectangleTitleAlignRules) .margin({ left: 16, top: 8 }) .id('musicTitle') // 艺术家名称 Text(this.getDisplayArtist()) .fontSize(12) .fontColor('#CCFFFFFF') .fontWeight(FontWeight.Normal) .maxLines(1) .alignRules(RectangleSingerAlignRules) .margin({ left: 16, top: 2 }) .id('singerText') // 专辑封面 Image(this.getCoverImage()) .width(88) .height(88) .borderRadius(8) // Square cover with rounded corners .alignRules(RectangleCoverAlignRules) .id('musicCover') .onClick(() => { postCardAction(this, { 'action': 'router', 'abilityName': 'EntryAbility' }); }) // 播放控制按钮区域 Row() { // 上一首按钮 Button() { SymbolGlyph($r('sys.symbol.backward_end_fill')) .fontSize(36) .fontColor(['#E5FFFFFF']) } .width(40) .height(40) .backgroundColor(Color.Transparent) .opacity(this.hasPrevious ? 1.0 : 0.5) .onClick(() => { console.info(`Heanup PlayerWidgetRectangle: Previous button clicked`); if (!this.isLoading && this.hasPrevious) { postCardAction(this, { 'action': 'call', 'abilityName': 'EntryAbility', 'params': { 'formId': this.formId, 'method': 'prevSong' } }); } }) // 播放/暂停按钮 Button() { SymbolGlyph(this.isPlaying ? $r('sys.symbol.pause_round_triangle_fill') : $r('sys.symbol.play_round_triangle_fill')) .fontSize(36) .symbolEffect(new ReplaceSymbolEffect(EffectScope.WHOLE), true) .fontColor(['#E5FFFFFF']) } .width(48) .height(48) .backgroundColor(Color.Transparent) .margin({ left: 20, right: 20 }) .onClick(() => { console.info('Heanup PlayerWidgetRectangle: Play/Pause button clicked'); if (!this.isLoading) { postCardAction(this, { 'action': 'call', 'abilityName': 'EntryAbility', 'params': { 'formId': this.formId, 'method': 'playPause', 'widgetIsPlaying': this.isPlaying // 传递卡片当前显示的播放状态 } }); } }) // 下一首按钮 Button() { SymbolGlyph($r('sys.symbol.forward_end_fill')) .fontSize(36) .fontColor(['#E5FFFFFF']) } .width(40) .height(40) .backgroundColor(Color.Transparent) .opacity(this.hasNext ? 1.0 : 0.5) .onClick(() => { console.info(`Heanup PlayerWidgetRectangle: Next button clicked`); if (!this.isLoading && this.hasNext) { postCardAction(this, { 'action': 'call', 'abilityName': 'EntryAbility', 'params': { 'formId': this.formId, 'method': 'nextSong' } }); } }) } .width('100%') .justifyContent(FlexAlign.Center) .alignRules(RectanglePlayControlAlignRules) .id('playControls') } .height('100%') .width('100%') .padding(12) .backgroundImage(this.getCoverImage()) .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK) .backgroundImageSize(ImageSize.Cover) .onClick(() => { console.info('Heanup PlayerWidgetRectangle: Container clicked, jumping to main app'); postCardAction(this, { 'action': 'router', 'abilityName': 'EntryAbility' }); }) // 收藏按钮放在Stack顶层,确保可以正常点击 Button() { SymbolGlyph(this.isFavorite ? $r('sys.symbol.heart_fill') : $r('sys.symbol.heart')) .fontSize(24) .symbolEffect(new ReplaceSymbolEffect(EffectScope.WHOLE), true) .fontColor(['#E5FFFFFF']) } .width(40) .height(40) .backgroundColor(Color.Transparent) .onClick(() => { console.info(`Heanup PlayerWidgetRectangle: Favorite button clicked`); postCardAction(this, { 'action': 'call', 'abilityName': 'EntryAbility', 'params': { 'formId': this.formId, 'method': 'toggleFavorite' } }); }) .position({ x: '100%', y: 0 }) .translate({ x: -52, y: 12 }) // 调整到右上角位置 } .height('100%') .width('100%') } }