onecold 6 месяцев назад
Родитель
Сommit
fad24e759d
1 измененных файлов с 492 добавлено и 10 удалено
  1. 492 10
      entry/src/main/ets/view/RemoteMusicPage.ets

+ 492 - 10
entry/src/main/ets/view/RemoteMusicPage.ets

@@ -121,6 +121,7 @@ export struct RemoteMusicPage {
   @State twoFingerType: number = 3 //双指放大缩小的类型 (1=List小, 2=List中, 3=Grid大, 4=WaterFlow)
   @State scaleValue: number = 1 //缩放值
   @State columns: number = 4 //瀑布流列数
+  @State isWaterFlowScrolling: boolean = false //WaterFlow是否正在滚动
 
   @State isShowTitleBar: boolean = true //是否显示分类导航条
   private scroller: Scroller = new Scroller()
@@ -3485,10 +3486,14 @@ export struct RemoteMusicPage {
         .layoutWeight(1)
         .justifyContent(FlexAlign.Center)
       } else {
-        if(this.isGridMusic){
-          this.getGridView()
-        }else {
-          this.getListView()
+        if(this.twoFingerType==4){
+          this.getWaterView()
+        }else{
+          if(this.isGridMusic){
+            this.getGridView()
+          }else {
+            this.getListView()
+          }
         }
       }
 
@@ -3628,9 +3633,8 @@ export struct RemoteMusicPage {
       this.twoFingerType++
       Logger.info('this.twoFingerType1 = ' + this.twoFingerType);
       if(this.twoFingerType >= 4){
-        this.twoFingerType = 3//切换成列表
-        this.isGridMusic = false
-        this.columns = 2
+        this.twoFingerType = 4//切换成瀑布流
+        this.columns = 4
       }
     } else {
       this.twoFingerType--
@@ -4145,9 +4149,8 @@ export struct RemoteMusicPage {
       this.twoFingerType--
       Logger.info('this.twoFingerType2 = ' + this.twoFingerType);
       if (this.twoFingerType < 1) {
-        this.twoFingerType = 3
-        this.isGridMusic = true
-        this.columns = 2
+        this.twoFingerType = 4
+        this.columns = 1
       }
     }
     PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
@@ -4199,6 +4202,484 @@ export struct RemoteMusicPage {
     .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
 
   }
+
+
+  @Builder
+  getWaterView(){
+    Scroll(this.scroller) {
+      Column() {
+        if(!this.isWaterFlowScrolling&&this.isShowTitleBar){
+          Blank().height(this.topSafeHeight + 115)
+        }
+        WaterFlow({
+          scroller:this.waterScroller,
+          layoutMode: WaterFlowLayoutMode.SLIDING_WINDOW
+        }) {
+
+          if (this.selectedTab === 0) {
+            // 歌曲瀑布流
+            LazyForEach(this.songDataSource, (item: VideoItem, index: number) => {
+              FlowItem() {
+                this.buildSongWaterCardItem(item, index)
+              }
+              .width('100%')
+              .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.9 })
+            }, (item: VideoItem) => item.id)
+          } else if (this.selectedTab === 1) {
+            // 艺术家瀑布流
+            LazyForEach(this.artistDataSource, (artist: NavidromeRestArtist) => {
+              FlowItem() {
+                this.buildArtistWaterCardItem(artist)
+              }
+              .width('100%')
+              .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.9 })
+            }, (artist: NavidromeRestArtist) => artist.id)
+          } else if (this.selectedTab === 2) {
+            // 专辑瀑布流
+            LazyForEach(this.albumDataSource, (album: NavidromeRestAlbum) => {
+              FlowItem() {
+                this.buildAlbumWaterCardItem(album)
+              }
+              .width('100%')
+              .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.9 })
+            }, (album: NavidromeRestAlbum) => album.id)
+          } else if (this.selectedTab === 3) {
+            // 歌单瀑布流
+            LazyForEach(this.playlistDataSource, (playlist: NavidromeRestPlaylist) => {
+              FlowItem() {
+                this.buildPlaylistWaterCardItem(playlist)
+              }
+              .width('100%')
+              .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.9 })
+            }, (playlist: NavidromeRestPlaylist) => playlist.id)
+          }
+        }
+        .id('waterflow') // 设置id用于截图
+        .columnsTemplate('1fr '.repeat(this.columns))  // 动态生成列模板,如:'1fr 1fr 1fr'表示3列等宽
+        .columnsGap(12) // 列间距
+        .rowsGap(16) // 行间距
+        .cachedCount(6)
+        .padding({bottom:this.bottomSafeHeight+52})
+        .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+          .animation({ duration: 500, curve: Curve.Ease }))
+        .scrollBar(BarState.Off)
+        .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
+        .width('90%')
+        .height('100%')
+        .onReachEnd(() => {
+          // 触底加载下一页
+          Logger.info('heanup RemoteMusicPage', `WaterFlow onReachEnd`);
+          void this.handleReachEnd();
+        })
+        .onAppear(() => {
+          this.scroller.scrollEdge(Edge.Top)
+        })
+        .nestedScroll({
+          scrollForward: NestedScrollMode.PARENT_FIRST,
+          scrollBackward: NestedScrollMode.SELF_FIRST
+        })
+        .onScrollFrameBegin((offset: number) => {
+          // 获取当前滚动偏移量
+          this.isWaterFlowScrolling = true
+          if(this.autoHideTitle){
+            const currentOffsetY = this.waterScroller.currentOffset().yOffset;
+            // 判断滚动方向
+            if (currentOffsetY > this.prevOffsetY) {
+              // console.log("onecold 向上滚动");
+              this.isShowTitleBar = false
+            } else if (currentOffsetY < this.prevOffsetY) {
+              // console.log("onecold 向下滚动");
+              this.isShowTitleBar = true
+            }
+            // 更新前一次偏移量
+            this.prevOffsetY = currentOffsetY;
+          }
+
+          return { offsetRemain: offset };
+        })
+        .onScrollStop(() => {
+          // 滚动停止
+          this.isWaterFlowScrolling = false
+        })
+        .priorityGesture(
+          PinchGesture()
+            .onActionStart((event: GestureEvent) => {
+              // 双指捏合手势识别成功时截图
+              this.pinchValue = this.scaleValue;
+              this.columnChanged = false;
+              this.oldColumn = this.columns;
+              this.getUIContext().getComponentSnapshot().get('waterflow', (error: Error, pixmap: PixelMap) => {
+                if (error) {
+                  console.info('error:' + JSON.stringify(error));
+                  return;
+                }
+
+                if ((this.oldColumn === 1 )  && event.scale > 1|| (this.oldColumn === 4 && event.scale < 1)) {
+                  console.info("onecold onActionStart oldColumn:" + this.oldColumn)
+                  if (this.oldColumn ===4) {
+                    this.twoFingerType =3
+                    this.isGridMusic = true
+                  }else if (this.oldColumn ===1) {
+                    this.twoFingerType =1
+                    this.isGridMusic = false
+                  }
+                  PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
+                  PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+                  return;
+                }
+              })
+            })
+            .onActionUpdate((event: GestureEvent) => {
+              // 缩放比例小于1时(缩小),增加列数;大于1时(放大),减少列数
+              if (event.scale > 1) {
+                // 放大,减少列数
+                if (this.columns > 1 && this.columnChanged === false) {
+                  if (event.scale > 1.25) {
+                    this.columns = Math.max(1, this.columns - 1);
+                    this.columnChanged = true;
+                    console.info("onecold onActionUpdate columns111:" + this.columns)
+                  }
+                }
+              } else {
+                // 缩小,增加列数
+                if (this.columns < 6 && this.columnChanged === false) {
+                  if (event.scale < 0.8) {
+                    this.columns = Math.min(6, this.columns + 1);
+                    this.columnChanged = true;
+                    console.info("onecold onActionUpdate columns222:" + this.columns)
+                  }
+                }
+              }
+            })
+            .onActionEnd((event: GestureEvent) => {
+              const currentTimestamp: number = event.timestamp;
+              if (currentTimestamp - this.pinchValue < 300) {
+                console.info("onecold onActionEnd 快速操作,不保存");
+                this.columnChanged = false;
+                this.columns = this.oldColumn;
+                return;
+              }
+              if (this.columnChanged) {
+                if (this.columns === 1) {
+                  this.twoFingerType = 1
+                  this.isGridMusic = false
+                  PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
+                  PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+                } else if (this.columns === 6) {
+                  this.twoFingerType = 3
+                  this.isGridMusic = true
+                  PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
+                  PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+                } else {
+                  this.twoFingerType = 4
+                  this.isGridMusic = true
+                  PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
+                  PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+                }
+                Logger.info('heanup RemoteMusicPage', '列数已切换为:' + this.columns)
+              }
+              this.columnChanged = false;
+            })
+        )
+
+      }
+      .width('100%')
+      .height('100%')
+    }
+    .width('100%')
+    .height('100%')
+    .scrollBar(BarState.Off)
+    .edgeEffect(EdgeEffect.Spring)
+    .visibility(this.twoFingerType == 4 ? Visibility.Visible : Visibility.None)
+  }
+
+  // 歌曲瀑布流卡片项
+  @Builder
+  buildSongWaterCardItem(item: VideoItem, index: number) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(item.pixelMapPath ? item.pixelMapPath : $r('app.media.alt'))
+            .backgroundImageSize(ImageSize.Auto)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .draggable(false)
+            .autoResize(true)
+            .interpolation(ImageInterpolation.High)
+            .width('100%')
+            .height('auto')
+            .alt($r('app.media.alt'))
+            .objectFit(ImageFit.Auto)
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .margin({
+              left: 20,
+              right: 20
+            })
+
+          Column() {
+            Text(item.name.startsWith('.') ? item.name.replace(/\./g, '') : item.name)
+              .fontSize(this.columns == 4 || this.columns == 3 ? 12 : this.columns == 2 ? 15 : 18)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .animation({
+                duration: 555,
+                curve: 'Linear',
+              })
+              .fontColor(this.currentSong?.filePath === item.filePath ? this.themeColor : $r('app.color.text_color'))
+
+            Row() {
+              Text(item.artist || '')
+                .fontSize(this.columns == 4 || this.columns == 3 ? 10 : this.columns == 2 ? 12 : 13)
+                .maxLines(1)
+                .textOverflow({ overflow: TextOverflow.Ellipsis })
+                .margin({ top: 2 })
+                .fontWeight(FontWeight.Medium)
+                .fontColor(this.currentSong?.filePath === item.filePath ? this.themeColor : $r('app.color.text_color'))
+            }
+            .alignItems(VerticalAlign.Center)
+          }
+          .height(this.columns == 4 || this.columns == 3 ? 40 : this.columns == 2 ? 48 : 58)
+          .width('100%')
+          .alignItems(HorizontalAlign.Center) // 关键:使内容水平居中
+          .justifyContent(FlexAlign.Center)
+          .margin({
+            left: 20,
+            right: 20
+          })
+        }
+        .width('100%')
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(item.pixelMapPath) ? $r('app.media.alt') : item.pixelMapPath)
+        .backgroundImageSize({ height: '100%', width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+      }
+    }
+    .reuseId('card_item')
+    .width('100%')
+    .shadow({
+      radius: 10,
+      type: ShadowType.BLUR,
+      color: 'on_primary'
+    })
+    .height('auto')
+    .onClick(() => {
+      this.playSong(item, index);
+
+    })
+    .backgroundColor(Color.Transparent)
+  }
+
+  // 艺术家瀑布流卡片项
+  @Builder
+  buildArtistWaterCardItem(artist: NavidromeRestArtist) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(artist.coverUrl ?? $r('app.media.dir_alt'))
+            .objectFit(ImageFit.Auto)
+            .width('100%')
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)
+            .autoResize(true)
+            .alt($r('app.media.dir_alt'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .sourceSize({ width: 200, height: 200 })
+            .fillColor(artist.coverUrl ? undefined : this.themeColor)
+
+          Column() {
+            Text(artist.name)
+              .fontSize(14)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .fontColor($r('app.color.text_color'))
+
+            if (artist.albumCount !== undefined) {
+              Text(`${artist.albumCount} 张专辑`)
+                .fontSize(11)
+                .fontColor($r('app.color.text_color'))
+            }
+          }
+          .padding(12)
+          .width('100%')
+          .height(48)
+          .alignItems(HorizontalAlign.Center)
+        }
+        .width('100%')
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(artist.coverUrl) ? $r('app.media.dir_alt') : artist.coverUrl)
+        .backgroundImageSize({  width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+
+      }
+    }
+    .width('100%')
+    .shadow({
+      radius: 10,
+      type: ShadowType.BLUR,
+      color: 'on_primary'
+    })
+    .onClick(() => {
+      this.onArtistSelected(artist);
+    })
+    .backgroundColor(Color.Transparent)
+  }
+
+  // 专辑瀑布流卡片项
+  @Builder
+  buildAlbumWaterCardItem(album: NavidromeRestAlbum) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(album.coverUrl ?? $r('app.media.dir_alt'))
+            .width('100%')
+            .height('auto')
+            .objectFit(ImageFit.Auto)
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)
+            .autoResize(true)
+            .alt($r('app.media.dir_alt'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .sourceSize({ width: 200, height: 200 })
+            .fillColor(album.coverUrl ? undefined : this.themeColor)
+
+          Column() {
+            Text(album.name)
+              .fontSize(14)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .fontColor($r('app.color.text_color'))
+
+            Text(album.artist ?? '')
+              .fontSize(11)
+              .maxLines(1)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .fontColor($r('app.color.text_color'))
+          }
+          .height(48)
+          .padding(12)
+          .width('100%')
+          .alignItems(HorizontalAlign.Center)
+        }
+        .width('100%')
+        .height('auto')
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(album.coverUrl) ? $r('app.media.dir_alt') : album.coverUrl)
+        .backgroundImageSize({  width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+
+      }
+    }
+    .width('100%')
+    .shadow({
+      radius: 10,
+      type: ShadowType.BLUR,
+      color: 'on_primary'
+    })
+    .backgroundColor(Color.Transparent)
+    .onClick(() => {
+      this.onAlbumSelected(album);
+    })
+  }
+
+  // 歌单瀑布流卡片项
+  @Builder
+  buildPlaylistWaterCardItem(playlist: NavidromeRestPlaylist) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(playlist.coverUrl ?? $r('app.media.dir_alt'))
+            .width('100%')
+            .objectFit(ImageFit.Auto)
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)
+            .autoResize(true)
+            .alt($r('app.media.dir_alt'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .sourceSize({ width: 200, height: 200 })
+            .fillColor(playlist.coverUrl ? undefined : this.themeColor)
+
+
+          Column() {
+            Text(playlist.name)
+              .fontSize(14)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .fontColor($r('app.color.text_color'))
+
+            if (playlist.songCount !== undefined) {
+              Text(`${playlist.songCount} 首歌曲`)
+                .fontSize(11)
+                .fontColor($r('app.color.text_color'))
+            }
+          }
+          .padding(12)
+          .height(48)
+          .width('100%')
+          .alignItems(HorizontalAlign.Center)
+        }
+        .width('100%')
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(playlist.coverUrl) ? $r('app.media.dir_alt') : playlist.coverUrl)
+        .backgroundImageSize({  width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+      }
+    }
+    .width('100%')
+    .shadow({
+      radius: 10,
+      type: ShadowType.BLUR,
+      color: 'on_primary'
+    })
+    .onClick(() => {
+      this.onPlaylistSelected(playlist);
+    })
+    .backgroundColor(Color.Transparent)
+  }
 }
 
 /**
@@ -4434,3 +4915,4 @@ async function loadNavidromeDataTask(accountData: AccountData, ticket: number):
 
   return result;
 }
+