瀏覽代碼

网盘的RemoteMusicPage增加了双指缩放可以list和Grid切换布局

onecold 6 月之前
父節點
當前提交
ba8722fc2a
共有 1 個文件被更改,包括 677 次插入61 次删除
  1. 677 61
      entry/src/main/ets/view/RemoteMusicPage.ets

+ 677 - 61
entry/src/main/ets/view/RemoteMusicPage.ets

@@ -36,6 +36,10 @@ import { taskpool } from '@kit.ArkTS';
 const NAVIDROME_PLAYLIST_ID = 'navidrome-playlist';
 const NAVIDROME_SEARCH_LIMIT = 500;
 
+const ITEM_HEIGHT_SMALL: number = 48; // 列表项小高度
+const ITEM_HEIGHT: number = 65; // 列表项中高度
+const ITEM_HEIGHT_BIG: number = 78; // 列表项大高度
+
 interface PlaylistEventData {
   playlistId: string;
   playlistName: string;
@@ -110,6 +114,13 @@ interface AccountData {
 
 @Component
 export struct RemoteMusicPage {
+  private gridScroller: Scroller = new Scroller()
+  private waterScroller: Scroller = new Scroller()
+  @State isGridMusic: boolean = false //是否网格布局
+  @State twoFingerType: number = 3 //双指放大缩小的类型 (1=List小, 2=List中, 3=Grid大, 4=WaterFlow)
+  @State scaleValue: number = 1 //缩放值
+  @State columns: number = 4 //瀑布流列数
+
   @State isShowTitleBar: boolean = true //是否显示分类导航条
   private scroller: Scroller = new Scroller()
   private prevOffsetY: number = 0; // 记录上一次的Y轴偏移量
@@ -281,6 +292,8 @@ export struct RemoteMusicPage {
     this.sortType = PreferencesUtil.getNumberSync('navidromeSortType', 0);
     this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
     this.autoHideTitle = PreferencesUtil.getBooleanSync('autoHideTitle', true)
+    this.twoFingerType = PreferencesUtil.getNumberSync(SettingPage.TWO_FINGER_TYPE, 3)
+    this.isGridMusic = PreferencesUtil.getBooleanSync(SettingPage.IS_GRID_MUSIC, false)
   }
 
   aboutToAppear() {
@@ -2823,8 +2836,8 @@ export struct RemoteMusicPage {
       Row({ space: 12 }) {
         // 歌曲封面
         Image(song.pixelMapPath ? song.pixelMapPath : $r('app.media.nocover'))
-          .width(52)
-          .height(52)
+          .height(this.twoFingerType == 3 ? 55 : this.twoFingerType == 2 ? 50 : 40)
+          .width(this.twoFingerType == 3 ? 55 : this.twoFingerType == 2 ? 50 : 40)
           .borderRadius(9)
           .sourceSize({ width: 38, height: 38 })
           .draggable(false)
@@ -2850,21 +2863,21 @@ export struct RemoteMusicPage {
         // 歌曲信息
         Column({ space: 4 }) {
           Text(song.name)
-            .fontSize(15)
+            .fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
             .fontColor(this.currentSong?.filePath == song.filePath ? this.themeColor : $r('app.color.index_tab_font_color'))
             .maxLines(1)
             .textOverflow({ overflow: TextOverflow.Ellipsis })
 
           Row() {
             Text((song.artist ?? '') + "  ")
-              .fontSize(13)
+              .fontSize(this.twoFingerType == 1 ? 11 : this.twoFingerType == 2 ? 12 : 14)
               .fontColor(this.currentSong?.filePath == song.filePath ? this.themeColor : $r('app.color.index_tab_font_color'))
               .opacity(0.6)
               .maxLines(1)
               .visibility(song.artist ? Visibility.Visible : Visibility.None)
               .textOverflow({ overflow: TextOverflow.Ellipsis })
             Text(this.buildSongMetaLine(song))
-              .fontSize(13)
+              .fontSize(this.twoFingerType == 1 ? 11 : this.twoFingerType == 2 ? 12 : 14)
               .fontColor(this.currentSong?.filePath == song.filePath ? this.themeColor : $r('app.color.index_tab_font_color'))
               .opacity(0.6)
               .maxLines(1)
@@ -2893,6 +2906,7 @@ export struct RemoteMusicPage {
     }
     .width('100%')
     .padding(12)
+    .height(this.twoFingerType == 3 ? ITEM_HEIGHT_BIG : this.twoFingerType == 2 ? ITEM_HEIGHT : ITEM_HEIGHT_SMALL)
     .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
     .backgroundColor(Color.Transparent)
     .onClick(() => {
@@ -3414,63 +3428,11 @@ export struct RemoteMusicPage {
         .width('100%')
         .layoutWeight(1)
       } else {
-        List({scroller:this.scroller, space: 8 }) {
-          // 详情视图模式下显示筛选后的歌曲,否则根据标签页显示对应内容
-          if (this.selectedTab === 0||this.isDetailView) {
-            ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => {
-              ListItem() {
-                this.buildSongItem(item, index)
-              }
-            }, (item: VideoItem) => item.id)
-          } else if (this.selectedTab === 1) {
-            ForEach(this.artists, (artist: NavidromeRestArtist) => {
-              ListItem() {
-                this.buildArtistItem(artist)
-              }
-            }, (artist: NavidromeRestArtist) => artist.id)
-          } else if (this.selectedTab === 2) {
-            ForEach(this.albums, (album: NavidromeRestAlbum) => {
-              ListItem() {
-                this.buildAlbumItem(album)
-              }
-            }, (album: NavidromeRestAlbum) => album.id)
-          } else if (this.selectedTab === 3) {
-            ForEach(this.playlists, (playlist: NavidromeRestPlaylist) => {
-              ListItem() {
-                this.buildPlaylistItem(playlist)
-              }
-            }, (playlist: NavidromeRestPlaylist) => playlist.id)
-          }
+        if(this.isGridMusic){
+          this.getGridView()
+        }else {
+          this.getListView()
         }
-        .onScrollFrameBegin((offset: number) => {
-          // 获取当前滚动偏移量
-          if(this.autoHideTitle){
-            const currentOffsetY = this.scroller.currentOffset().yOffset;
-            // 判断滚动方向
-            if (currentOffsetY > this.prevOffsetY) {
-              this.isShowTitleBar = false
-            } else if (currentOffsetY < this.prevOffsetY) {
-              this.isShowTitleBar = true
-            }
-            // 更新前一次偏移量
-            this.prevOffsetY = currentOffsetY;
-          }
-
-          return { offsetRemain: offset };
-        })
-        .width('100%')
-        .height('100%')
-        .layoutWeight(1)
-        .cachedCount(3)
-        .padding({ top: 5, bottom: 5 })
-        .listDirection(Axis.Vertical)
-        .contentEndOffset(this.bottomSafeHeight+70)
-        .contentStartOffset(this.topSafeHeight + 98)
-        .scrollBar(BarState.Auto)
-        .edgeEffect(EdgeEffect.Spring)
-        .onReachEnd(() => {
-          void this.handleReachEnd();
-        })
       }
 
       // 空状态
@@ -3497,6 +3459,660 @@ export struct RemoteMusicPage {
     }
   }
 
+  @State startIndex: number = 0
+  @State endIndex: number = 0
+  @State pinchValue: number = 1
+  layoutOptionsForHeader: GridLayoutOptions = {
+    regularSize: [1, 1], // 只支持[1, 1]
+    irregularIndexes: [0], // 索引为0的GridItem占用一行
+  };
+  @Builder
+  getGridView() {
+
+    Grid(this.scroller, this.layoutOptionsForHeader) {
+      GridItem() {
+        Column(){
+          Blank().height(this.topSafeHeight + 98)
+        }
+
+      }
+
+
+      if (this.selectedTab === 0||this.isDetailView) {
+        ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => {
+          GridItem() {
+            this.buildSongItemGrid(item, index)
+          }
+        }, (item: VideoItem) => item.id)
+      } else if (this.selectedTab === 1) {
+        ForEach(this.artists, (artist: NavidromeRestArtist) => {
+          GridItem() {
+            this.buildArtistItemGrid(artist)
+          }
+        }, (artist: NavidromeRestArtist) => artist.id)
+      } else if (this.selectedTab === 2) {
+        ForEach(this.albums, (album: NavidromeRestAlbum) => {
+          GridItem() {
+            this.buildAlbumItemGrid(album)
+          }
+        }, (album: NavidromeRestAlbum) => album.id)
+      } else if (this.selectedTab === 3) {
+        ForEach(this.playlists, (playlist: NavidromeRestPlaylist) => {
+          GridItem() {
+            this.buildPlaylistItemGrid(playlist)
+          }
+        }, (playlist: NavidromeRestPlaylist) => playlist.id)
+      }
+
+
+
+    }
+    .width('94%')
+    .height('100%')
+    .editMode(true)
+    .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+      .animation({ duration: 500, curve: Curve.Ease }))
+    .padding({bottom:this.bottomSafeHeight+this.bottomSafeHeight+10})
+    .reuseId('grid_item')
+    .layoutWeight(1)
+    .cachedCount(this.twoFingerType==1?5:this.twoFingerType==2?4:3)
+    .scrollBar(BarState.Off)
+    .supportAnimation(true)
+    .onReachEnd(() => {
+      void this.handleReachEnd();
+    })
+    .columnsTemplate(
+      this.twoFingerType == 3 ? 'repeat(auto-fit, 160)' :
+        this.twoFingerType == 2 ? 'repeat(auto-fit, 110)' : 'repeat(auto-fit, 80)'
+    )
+    .rowsGap(this.isShowDrawer ? 25 : (this.twoFingerType == 3 ? 10 : this.twoFingerType == 2 ? 5 : 1))
+    .visibility(this.isGridMusic ? Visibility.Visible : Visibility.None)
+    .scale({ x: this.scaleValue, y: this.scaleValue, z: 1 })
+    .gesture(PinchGesture({ fingers: 2 })
+      .onActionEnd((event: GestureEvent) => {
+        this.setGridTwoFingers()
+      })
+      .onActionUpdate(e => {
+        this.scaleValue = this.pinchValue * e.scale
+      })
+      .onActionStart(e => {
+
+      }))
+    .enableScrollInteraction(true)
+    .onScrollIndex((start: number, end: number) => {
+      this.startIndex = start
+      this.endIndex = end
+    })
+    .onScrollFrameBegin((offset: number) => {
+      if(this.autoHideTitle){
+        const currentOffsetY = this.scroller.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 };
+    })
+
+  }
+
+  setGridTwoFingers() {
+    this.pinchValue = this.scaleValue;
+    Logger.info('this.pinchValue = ' + this.pinchValue);
+    if (this.pinchValue >= 1) {
+      this.twoFingerType++
+      Logger.info('this.twoFingerType1 = ' + this.twoFingerType);
+      if(this.twoFingerType >= 4){
+        this.twoFingerType = 3//切换成列表
+        this.isGridMusic = false
+        this.columns = 2
+      }
+    } else {
+      this.twoFingerType--
+      Logger.info('this.twoFingerType2 = ' + this.twoFingerType);
+      if (this.twoFingerType < 1) {
+        this.twoFingerType = 3
+        this.isGridMusic = false
+        PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+      }
+    }
+    PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
+    PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+    this.pinchValue = 1;
+    this.scaleValue = 1;
+  }
+
+
+  @Builder
+  buildSongItemGrid(item: VideoItem, index: number) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(item.pixelMapPath ? item.pixelMapPath : $r('app.media.nocover'))
+            .height(this.getGridHeight())
+            .width(this.getGridWight())
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)// 用于重采样后的抗锯齿
+            .autoResize(true) // 重采样,可减少内存占用
+            .alt(item.type==CommonConstants.TYPE_IS_DIR?$r('app.media.dir_alt'):$r('app.media.alt'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .draggable(false)
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out' // 可选动画曲线
+            })
+            .margin({
+              left: 25,
+              right:25
+            })
+          Column() {
+            Text(item.name)
+              .fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .animation({
+                duration: 555,
+                curve: 'Linear',
+              })
+
+              .fontColor(this.currentSong?.filePath === item.filePath ? this.themeColor :
+                $r('app.color.text_color'))
+
+            Column() {
+              // Text(`${this.getArtistSongCount(item.name)}首`)
+              //   .fontSize(11)
+              //   .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 14)
+              //   .fontColor($r('app.color.text_color'))
+              // Text(`${this.getAlbumSongCount(item.name)}首`)
+              //   .fontSize(11)
+              //   .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 14)
+              //   .fontColor($r('app.color.text_color'))
+              Row(){
+
+
+                Text((item.artist ?? '') + "  ")
+                  .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 13)
+                  .maxLines(1)
+                  .margin({ top: 2})
+                  .visibility(item.type == CommonConstants.TYPE_IS_ARTIST
+                    || item.type == CommonConstants.TYPE_IS_ALBUM
+                    || item.type == CommonConstants.TYPE_IS_DIR ? Visibility.None : Visibility.Visible)
+                  .fontWeight(FontWeight.Medium)
+                  .fontColor(this.currentSong?.filePath === item.filePath ? this.themeColor :
+                    $r('app.color.text_color'))
+              }
+
+
+            }
+            .alignItems(HorizontalAlign.Center) // 关键:使内容水平居中
+
+          }
+
+          .height(this.twoFingerType == 3 ? 60 : this.twoFingerType == 2 ? 48 : 35)
+          .width(this.getGridWight())
+          .justifyContent(FlexAlign.Center)
+          .margin({
+            left: 25,
+            right: 25
+          })
+
+        }
+        .width(this.getGridWight())
+        .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 })
+
+
+      }
+    }
+    .backgroundColor(Color.Transparent)
+    .width('100%')
+    .padding({ top: 15 })
+    .height(this.getGridAllHeight())
+    .onClick(() => {
+      this.playSong(item, index);
+
+    })
+
+  }
+
+  getGridAllHeight() {
+    let heightG = 222
+    switch (this.twoFingerType) {
+      case 1:
+        heightG = 143
+        break;
+      case 2:
+        heightG = 178
+        break;
+      case 3:
+        heightG = 222
+        break;
+    }
+    return heightG
+  }
+
+  getGridHeight() {
+    let heightG = 150
+    switch (this.twoFingerType) {
+      case 1:
+        heightG = 80
+        break;
+      case 2:
+        heightG = 110
+        break;
+      case 3:
+        heightG = 150
+        break;
+    }
+    return heightG
+  }
+
+  getGridWight() {
+    let wightG = 158
+    switch (this.twoFingerType) {
+      case 1:
+        wightG = 80
+        break;
+      case 2:
+        wightG = 110
+        break;
+      case 3:
+        wightG = 158
+        break;
+    }
+    return wightG
+  }
+
+  @Builder
+  buildArtistItemGrid(artist: NavidromeRestArtist) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(artist.coverUrl ?? $r('app.media.nocover'))
+            .height(this.getGridHeight())
+            .width(this.getGridWight())
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)
+            .autoResize(true)
+            .alt($r('app.media.nocover'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .draggable(false)
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .margin({
+              left: 25,
+              right: 25
+            })
+            .sourceSize({ width: 200, height: 200 })
+            .fillColor(artist.coverUrl ? undefined : this.themeColor)
+            .objectFit(ImageFit.Cover)
+
+          Column() {
+            Text(artist.name ?? Constants.UNKNOWN_ARTIST)
+              .fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .fontColor($r('app.color.text_color'))
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .animation({
+                duration: 555,
+                curve: 'Linear',
+              })
+
+            Column() {
+              Row() {
+                Text(this.buildArtistMetaLine(artist))
+                  .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 13)
+                  .maxLines(1)
+                  .margin({ top: 2 })
+                  .fontWeight(FontWeight.Medium)
+                  .fontColor($r('app.color.text_color'))
+                  .opacity(0.6)
+                  .textOverflow({ overflow: TextOverflow.Ellipsis })
+                  .visibility((artist.albumCount ?? 0) === 0 && (artist.songCount ?? 0) === 0 ? Visibility.None : Visibility.Visible)
+              }
+            }
+            .alignItems(HorizontalAlign.Center)
+
+          }
+          .height(this.twoFingerType == 3 ? 60 : this.twoFingerType == 2 ? 48 : 35)
+          .width(this.getGridWight())
+          .justifyContent(FlexAlign.Center)
+          .margin({
+            left: 25,
+            right: 25
+          })
+
+        }
+        .width(this.getGridWight())
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(artist.coverUrl) ? $r('app.media.nocover') : artist.coverUrl)
+        .backgroundImageSize({ height: '100%', width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+
+      }
+    }
+    .backgroundColor(Color.Transparent)
+    .width('100%')
+    .padding({ top: 15 })
+    .height(this.getGridAllHeight())
+    .onClick(() => {
+      this.onArtistSelected(artist);
+    })
+  }
+
+  @Builder
+  buildAlbumItemGrid(album: NavidromeRestAlbum) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(album.coverUrl ?? $r('app.media.nocover'))
+            .height(this.getGridHeight())
+            .width(this.getGridWight())
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)
+            .autoResize(true)
+            .alt($r('app.media.nocover'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .draggable(false)
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .margin({
+              left: 25,
+              right: 25
+            })
+            .sourceSize({ width: 200, height: 200 })
+            .fillColor(album.coverUrl ? undefined : this.themeColor)
+            .objectFit(ImageFit.Cover)
+
+          Column() {
+            Text(album.name ?? '未知专辑')
+              .fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .fontColor($r('app.color.text_color'))
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .animation({
+                duration: 555,
+                curve: 'Linear',
+              })
+
+            Column() {
+              Row() {
+                Text(album.artist ?? Constants.UNKNOWN_ARTIST)
+                  .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 13)
+                  .maxLines(1)
+                  .margin({ top: 2 })
+                  .fontWeight(FontWeight.Medium)
+                  .fontColor($r('app.color.text_color'))
+                  .opacity(0.6)
+                  .textOverflow({ overflow: TextOverflow.Ellipsis })
+              }
+            }
+            .alignItems(HorizontalAlign.Center)
+
+          }
+          .height(this.twoFingerType == 3 ? 60 : this.twoFingerType == 2 ? 48 : 35)
+          .width(this.getGridWight())
+          .justifyContent(FlexAlign.Center)
+          .margin({
+            left: 25,
+            right: 25
+          })
+
+        }
+        .width(this.getGridWight())
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(album.coverUrl) ? $r('app.media.nocover') : album.coverUrl)
+        .backgroundImageSize({ height: '100%', width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+
+      }
+    }
+    .backgroundColor(Color.Transparent)
+    .width('100%')
+    .padding({ top: 15 })
+    .height(this.getGridAllHeight())
+    .onClick(() => {
+      this.onAlbumSelected(album);
+    })
+  }
+
+  @Builder
+  buildPlaylistItemGrid(playlist: NavidromeRestPlaylist) {
+    Button({ type: ButtonType.Normal, stateEffect: false }) {
+      Stack() {
+        Column() {
+          Image(playlist.coverUrl ?? $r('app.media.nocover'))
+            .height(this.getGridHeight())
+            .width(this.getGridWight())
+            .draggable(false)
+            .interpolation(ImageInterpolation.High)
+            .autoResize(true)
+            .alt($r('app.media.nocover'))
+            .clip(true)
+            .borderRadius({
+              topLeft: 12,
+              topRight: 12,
+              bottomLeft: 0,
+              bottomRight: 0
+            })
+            .draggable(false)
+            .animation({
+              duration: 666,
+              curve: 'ease-in-out'
+            })
+            .margin({
+              left: 25,
+              right: 25
+            })
+            .sourceSize({ width: 200, height: 200 })
+            .fillColor(playlist.coverUrl ? undefined : this.themeColor)
+            .objectFit(ImageFit.Cover)
+
+          Column() {
+            Text(playlist.name ?? '未知歌单')
+              .fontSize(this.twoFingerType == 1 ? 12 : this.twoFingerType == 2 ? 15 : 18)
+              .maxLines(1)
+              .fontWeight(FontWeight.Bold)
+              .fontColor($r('app.color.text_color'))
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+              .animation({
+                duration: 555,
+                curve: 'Linear',
+              })
+
+            Column() {
+              Row() {
+                Text(this.buildPlaylistMetaLine(playlist))
+                  .fontSize(this.twoFingerType == 1 ? 10 : this.twoFingerType == 2 ? 12 : 13)
+                  .maxLines(1)
+                  .margin({ top: 2 })
+                  .fontWeight(FontWeight.Medium)
+                  .fontColor($r('app.color.text_color'))
+                  .opacity(0.6)
+                  .textOverflow({ overflow: TextOverflow.Ellipsis })
+              }
+            }
+            .alignItems(HorizontalAlign.Center)
+
+          }
+          .height(this.twoFingerType == 3 ? 60 : this.twoFingerType == 2 ? 48 : 35)
+          .width(this.getGridWight())
+          .justifyContent(FlexAlign.Center)
+          .margin({
+            left: 25,
+            right: 25
+          })
+
+        }
+        .width(this.getGridWight())
+        .borderRadius(12)
+        .backgroundImage(StrUtil.isEmpty(playlist.coverUrl) ? $r('app.media.nocover') : playlist.coverUrl)
+        .backgroundImageSize({ height: '100%', width: '100%' })
+        .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+        .animation({ curve: Curve.Sharp, duration: 300 })
+
+      }
+    }
+    .backgroundColor(Color.Transparent)
+    .width('100%')
+    .padding({ top: 15 })
+    .height(this.getGridAllHeight())
+    .onClick(() => {
+      this.onPlaylistSelected(playlist);
+    })
+  }
+
+  @Builder
+  getListView() {
+    List({scroller:this.scroller, space: 8 }) {
+      // 详情视图模式下显示筛选后的歌曲,否则根据标签页显示对应内容
+      if (this.selectedTab === 0||this.isDetailView) {
+        ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => {
+          ListItem() {
+            this.buildSongItem(item, index)
+          }
+        }, (item: VideoItem) => item.id)
+      } else if (this.selectedTab === 1) {
+        ForEach(this.artists, (artist: NavidromeRestArtist) => {
+          ListItem() {
+            this.buildArtistItem(artist)
+          }
+        }, (artist: NavidromeRestArtist) => artist.id)
+      } else if (this.selectedTab === 2) {
+        ForEach(this.albums, (album: NavidromeRestAlbum) => {
+          ListItem() {
+            this.buildAlbumItem(album)
+          }
+        }, (album: NavidromeRestAlbum) => album.id)
+      } else if (this.selectedTab === 3) {
+        ForEach(this.playlists, (playlist: NavidromeRestPlaylist) => {
+          ListItem() {
+            this.buildPlaylistItem(playlist)
+          }
+        }, (playlist: NavidromeRestPlaylist) => playlist.id)
+      }
+    }
+    .onScrollFrameBegin((offset: number) => {
+      // 获取当前滚动偏移量
+      if(this.autoHideTitle){
+        const currentOffsetY = this.scroller.currentOffset().yOffset;
+        // 判断滚动方向
+        if (currentOffsetY > this.prevOffsetY) {
+          this.isShowTitleBar = false
+        } else if (currentOffsetY < this.prevOffsetY) {
+          this.isShowTitleBar = true
+        }
+        // 更新前一次偏移量
+        this.prevOffsetY = currentOffsetY;
+      }
+
+      return { offsetRemain: offset };
+    })
+    .width('100%')
+    .height('100%')
+    .layoutWeight(1)
+    .cachedCount(3)
+    .padding({ top: 5, bottom: 5 })
+    .listDirection(Axis.Vertical)
+    .contentEndOffset(this.bottomSafeHeight+70)
+    .contentStartOffset(this.topSafeHeight + 98)
+    .scrollBar(BarState.Auto)
+    .scale({ x: this.scaleValue, y: this.scaleValue, z: 1 })
+    .edgeEffect(EdgeEffect.Spring)
+    .gesture(PinchGesture({ fingers: 2 })
+      .onActionEnd((event: GestureEvent) => {
+        this.setlistTwoFingers()
+      })
+      .onActionUpdate(e => {
+        this.scaleValue = this.pinchValue * e.scale
+      })
+    )
+    .onReachEnd(() => {
+      void this.handleReachEnd();
+    })
+  }
+
+
+  // ==================== 双指缩放切换视图功能 ====================
+
+  // 双指缩放切换 List/Grid/WaterFlow
+  setlistTwoFingers() {
+    this.pinchValue = this.scaleValue;
+    Logger.info('this.pinchValue = ' + this.pinchValue);
+    if (this.pinchValue >= 1) {
+      this.twoFingerType++
+      Logger.info('this.twoFingerType1 = ' + this.twoFingerType);
+      if (this.twoFingerType > 3) {
+        this.twoFingerType = 1
+        this.isGridMusic = true
+        PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+      }
+    } else {
+      this.twoFingerType--
+      Logger.info('this.twoFingerType2 = ' + this.twoFingerType);
+      if (this.twoFingerType < 1) {
+        this.twoFingerType = 3
+        this.isGridMusic = true
+        this.columns = 2
+      }
+    }
+    PreferencesUtil.put(SettingPage.TWO_FINGER_TYPE, this.twoFingerType)
+    PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
+    this.pinchValue = 1;
+    this.scaleValue = 1;
+  }
+
+  // WaterFlow 列数切换
+  private columnChanged: boolean = false;
+  private oldColumn: number = 4;
+
+  changeColumns(scale: number): void {
+    if (scale > (this.columns / (this.columns - 0.5)) && this.columns > 1) {
+      this.columns--;
+      this.columnChanged = true;
+    } else if (scale < ((this.columns - 1) / this.columns) && this.columns < 6) {
+      this.columns++;
+      this.columnChanged = true;
+    }
+  }
+
   build() {
 
     Stack() {